Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware to parse Rails style url parameters #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/goliath/contrib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

module Goliath
# autoload :MiddlewareName, "goliath/contrib/middleware_name"
# ...
autoload :PathParams, 'goliath/contrib/path_params'
end
61 changes: 61 additions & 0 deletions lib/goliath/contrib/path_params.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require 'rack/utils'

module Goliath
module Contrib

# A middleware to parse Rails-style path parameters. This will parse
# parameters from the request path based on the supplied pattern and
# place them in the _params_ hash of the Goliath::Env for the request.
#
# @example
# use Goliath::Contrib::Rack::PathParams, '/records/:artist/:title'
#
class PathParams

module Parser

PARAM = %r{:([^/]+)(\/|$)}
CAPTURE_GROUP = '(?<\1>[^\/\?]+)\2'

def retrieve_params(env)
md = matched_params(env)
md.names.each_with_object({}) do |key, params|
params[key] = md[key]
end
end

def matched_params(env)
request_path(env).match(regexp).tap do |matched|
raise Goliath::Validation::BadRequestError, "Request path does not match expected pattern: #{request_path(env)}" if matched.nil?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why this has to be exclusive? As in, currently if you include this middleware then you can't use any other type of route.

end
end

def request_path(env)
env[Goliath::Request::REQUEST_PATH]
end

def regexp
@regexp ||= %r{^#{@url_pattern.gsub(PARAM, CAPTURE_GROUP)}\/?$}
end

end

include Goliath::Rack::Validator
include Parser

def initialize(app, url_pattern)
@app = app
@url_pattern = url_pattern
end

def call(env)
Goliath::Rack::Validator.safely(env) do
env['params'] ||= {}
env['params'].merge! retrieve_params(env)
@app.call(env)
end
end

end
end
end
50 changes: 50 additions & 0 deletions spec/goliath/contrib/path_params_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'spec_helper'
require 'goliath/contrib/path_params'

describe Goliath::Contrib::PathParams do
it 'accepts an app and a url pattern' do
lambda { Goliath::Contrib::PathParams.new('my app', 'url') }.should_not raise_error
end

describe 'with middleware' do
before(:each) do
@app = mock('app').as_null_object
@env = { 'REQUEST_PATH' => '/records/the_fall/hex_enduction_hour' }
@path_params = Goliath::Contrib::PathParams.new(@app, '/records/:artist/:title')
end

it 'returns the app status, headers and body' do
app_headers = {'Content-Type' => 'app'}
app_body = {'b' => 'c'}
@app.should_receive(:call).and_return([201, app_headers, app_body])

status, headers, body = @path_params.call(@env)
status.should == 201
headers.should == app_headers
body.should == app_body
end

context 'a request that matches the url pattern' do
before do
@env = { 'REQUEST_PATH' => '/records/sparks/angst_in_my_pants' }
end

it 'parses the params from the path' do
@path_params.call(@env)
@env['params']['artist'].should == 'sparks'
@env['params']['title'].should == 'angst_in_my_pants'
end
end

context 'a request that does not match the url pattern' do
before do
@env = { 'REQUEST_PATH' => '/animals/cat/noah' }
end

it 'raises a BadRequestError' do
expect{ @path_params.retrieve_params(@env) }.to raise_error(Goliath::Validation::BadRequestError)
end
end

end
end
34 changes: 34 additions & 0 deletions spec/integration/rails_style_url_params_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'spec_helper'
require 'goliath/contrib/path_params'

class RailsStyleParams < Goliath::API
use Goliath::Contrib::PathParams, '/kittens/:name/:favourite_colour'

def response(env)
[200, {}, 'OK']
end
end

describe RailsStyleParams do
let(:err) { Proc.new { fail "API request failed" } }

context 'a valid path' do
it 'returns OK' do
with_api(RailsStyleParams) do
get_request({:path => '/kittens/ginger/blue'}, err) do |c|
c.response.should == 'OK'
end
end
end
end

context 'an invalid path' do
it 'returns an error' do
with_api(RailsStyleParams) do
get_request({:path => '/donkeys/toothy'}, err) do |c|
c.response.should == '[:error, "Request path does not match expected pattern: /donkeys/toothy"]'
end
end
end
end
end