Skip to content

Commit

Permalink
Clean up request body parsing logic
Browse files Browse the repository at this point in the history
Creates parity between request body parsing and response body
rendering.

Also delegates web form parsing to Rack::Request.

Signed-off-by: Stephen Celis <stephen.celis@gmail.com>
Signed-off-by: Evan Owen <kainosnoema@gmail.com>
  • Loading branch information
kainosnoema committed Apr 17, 2013
1 parent 8c31b2e commit 577c636
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 72 deletions.
1 change: 1 addition & 0 deletions lib/crepe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Crepe
autoload :Helper, 'crepe/helper'
autoload :Middleware, 'crepe/middleware'
autoload :Params, 'crepe/params'
autoload :Parser, 'crepe/parser'
autoload :Renderer, 'crepe/renderer'
autoload :Request, 'crepe/request'
autoload :Response, 'crepe/response'
Expand Down
13 changes: 12 additions & 1 deletion lib/crepe/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,18 @@ def use middleware, *args, &block

def respond_to *formats, **renderers
config[:endpoint][:formats] = formats | renderers.keys
config[:endpoint][:renderers].update renderers
renderers.each { |format, renderer| render format, with: renderer }
end

def render *formats, with: renderer
formats.each { |f| config[:endpoint][:renderers][f] = renderer }
end

def parse *content_types, with: parser
content_types.each do |type|
type = Rack::Mime.mime_type ".#{type}" if type.is_a? Symbol
config[:endpoint][:parsers][type] = parser
end
end

def rescue_from *exceptions, with: nil, &block
Expand Down
18 changes: 14 additions & 4 deletions lib/crepe/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Crepe
class Endpoint

# Raised when render is called and a response body is already present.
class RenderError < StandardError
class DoubleRenderError < StandardError
end

class << self
Expand All @@ -16,11 +16,11 @@ def default_config
callbacks: {
after: [],
before: [
Filter::Acceptance,
Filter::Parser
Filter::Acceptance
]
},
formats: [:json],
parsers: Hash.new(Parser::Simple),
renderers: Hash.new(Renderer::Tilt),
rescuers: {}
}
Expand Down Expand Up @@ -82,9 +82,14 @@ def content_type
end
end

def parse body, options = {}
request.body = parser.parse body
@params = params.merge request.body if request.body.is_a? Hash
end

def render object, options = {}
headers['Content-Type'] ||= content_type
raise RenderError, 'body already rendered' if response.body
raise DoubleRenderError, 'body already rendered' if response.body
response.body = catch(:head) { renderer.render object, options }
end

Expand Down Expand Up @@ -125,6 +130,7 @@ def call! env

payload = catch :halt do
begin
parse request.body unless request.body.blank?
run_callbacks :before
_run_handler
rescue => e
Expand Down Expand Up @@ -157,6 +163,10 @@ def handle_exception exception
end
end

def parser
config[:parsers][request.content_type].new self
end

def renderer
config[:renderers][format].new self
end
Expand Down
1 change: 0 additions & 1 deletion lib/crepe/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module Filter

autoload :Acceptance, 'crepe/filter/acceptance'
autoload :BasicAuth, 'crepe/filter/basic_auth'
autoload :Parser, 'crepe/filter/parser'

end
end
45 changes: 0 additions & 45 deletions lib/crepe/filter/parser.rb

This file was deleted.

7 changes: 7 additions & 0 deletions lib/crepe/parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Crepe
module Parser

autoload :Simple, 'crepe/parser/simple'

end
end
41 changes: 41 additions & 0 deletions lib/crepe/parser/simple.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'multi_json'
require 'multi_xml'

module Crepe
module Parser
# A base parser class that attempts to parse common MIME types.
class Simple

attr_reader :endpoint

def initialize endpoint
@endpoint = endpoint
end

delegate :error!, :request, to: :endpoint

def parse body
case request.media_type
when %r{application/x-www-form-urlencoded}
request.POST
when %r{application/json}
begin
MultiJson.load body
rescue MultiJson::DecodeError
error! :bad_request, "Invalid JSON"
end
when %r{application/xml}
begin
MultiXml.parse body
rescue MultiXml::ParseError
error! :bad_request, "Invalid XML"
end
else
error! :unsupported_media_type,
%(Content-Type "#{request.media_type}" not supported)
end
end

end
end
end
1 change: 0 additions & 1 deletion lib/crepe/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ class RenderError < StandardError

end
end

25 changes: 6 additions & 19 deletions lib/crepe/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class << self
attr_accessor :config
end

attr_writer :body

def method
@method ||= env['crepe.original_request_method'] || request_method
end
Expand All @@ -50,27 +52,12 @@ def headers
@headers ||= Hash.new { |h, k| h.fetch @@env_keys[k], nil }.update env
end

alias query_parameters GET

def POST
env['crepe.input'] || super
end
alias request_parameters POST

def path_parameters
@path_parameters ||= env['rack.routing_args'] || {}
def params
@params ||= (env['rack.routing_args'] || {}).merge super
end

def parameters
@parameters ||= path_parameters.merge self.GET.merge self.POST
end
alias params parameters

def body
env['crepe.input'] || begin
body = super
body.respond_to?(:read) ? body.read : body
end
@body ||= (b = super).respond_to?(:read) ? b.read.tap { b.rewind } : b
end

def credentials
Expand All @@ -81,7 +68,7 @@ def credentials
end

def query_version
query_parameters[self.class.config[:version][:name]].to_s
self.GET[self.class.config[:version][:name]].to_s
end

def header_versions
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/crepe/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
it 'merges GET, POST, and path parameters' do
request.stub GET: {'get'=>'true'}
request.stub POST: {'post'=>'true'}
request.stub path_parameters: {'path'=>'true'}
request.env['rack.routing_args'] = {'path'=>'true'}
request.params.should eq('get'=>'true', 'post'=>'true', 'path'=>'true')
end
end
Expand Down

0 comments on commit 577c636

Please sign in to comment.