diff --git a/.gitignore b/.gitignore index c1e0dafa8d..48fe6d6124 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,7 @@ tmtags coverage rdoc pkg +.rvmrc +.bundle ## PROJECT::SPECIFIC diff --git a/Gemfile b/Gemfile index a5f105cec6..183e28ea34 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,9 @@ gem 'rack' gem 'rack-mount' gem 'rack-jsonp' +gem 'json' +gem 'multi_json' + group :test do gem 'rspec', '>= 2.0.0.beta.19' gem 'rack-test' diff --git a/Gemfile.lock b/Gemfile.lock index edd1a8a368..252e3678b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,7 +10,9 @@ GEM diff-lcs (1.1.2) gherkin (2.1.5) trollop (~> 1.16.2) + json (1.4.3) json_pure (1.4.3) + multi_json (0.0.4) rack (1.2.1) rack-jsonp (1.0.0) rack-mount (0.6.9) @@ -33,6 +35,8 @@ PLATFORMS DEPENDENCIES cucumber (>= 0.8.5) + json + multi_json rack rack-jsonp rack-mount diff --git a/Rakefile b/Rakefile index 9ec72c44f6..6a44721f7f 100644 --- a/Rakefile +++ b/Rakefile @@ -10,6 +10,8 @@ begin gem.email = "michael@intridea.com" gem.homepage = "http://github.com/intridea/grape" gem.authors = ["Michael Bleigh"] + gem.add_dependency 'rack' + gem.add_dependency 'multi_json' gem.add_development_dependency "rspec", ">= 1.2.9" gem.add_development_dependency "cucumber", ">= 0" # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings diff --git a/lib/grape.rb b/lib/grape.rb index 75d2f8c25e..fc11937db2 100644 --- a/lib/grape.rb +++ b/lib/grape.rb @@ -3,4 +3,5 @@ require 'grape/middleware/base' require 'grape/middleware/prefixer' -require 'grape/middleware/versioner' \ No newline at end of file +require 'grape/middleware/versioner' +require 'grape/middleware/formatter' \ No newline at end of file diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb new file mode 100644 index 0000000000..429fd37136 --- /dev/null +++ b/lib/grape/middleware/error.rb @@ -0,0 +1,9 @@ +require 'grape/middleware/base' + +module Grape + module Middleware + class Error < Base + + end + end +end \ No newline at end of file diff --git a/lib/grape/middleware/formatter.rb b/lib/grape/middleware/formatter.rb new file mode 100644 index 0000000000..175969f9f1 --- /dev/null +++ b/lib/grape/middleware/formatter.rb @@ -0,0 +1,60 @@ +require 'grape/middleware/base' +require 'multi_json' + +module Grape + module Middleware + class Formatter < Base + CONTENT_TYPES = { + :xml => 'application/xml', + :json => 'application/json', + :atom => 'application/atom+xml', + :rss => 'application/rss+xml' + } + + def default_options + { + :default_format => :json, + :content_types => {} + } + end + + def content_types + CONTENT_TYPES.merge(options[:content_types]) + end + + def before + fmt = format_from_extension || format_from_header || options[:default_format] + + if content_types.key?(fmt) + env['api.format'] = fmt + else + env['api.error.status'] = 406 + env['api.error.message'] = 'The requested format is not supported.' + end + end + + def format_from_extension + parts = request.path.split('.') + hit = parts.last.to_sym + + if parts.size <= 1 + nil + else + hit + end + end + + def format_from_header + # TODO: Implement Accept header parsing. + end + + def after + status, headers, bodies = *@app_response + bodies.map! do |body| + MultiJson.encode(body) + end + [status, headers, bodies] + end + end + end +end \ No newline at end of file diff --git a/spec/grape/middleware/error_spec.rb b/spec/grape/middleware/error_spec.rb new file mode 100644 index 0000000000..a25bb28ed5 --- /dev/null +++ b/spec/grape/middleware/error_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Grape::Middleware::Error do + +end \ No newline at end of file diff --git a/spec/grape/middleware/formatter_spec.rb b/spec/grape/middleware/formatter_spec.rb new file mode 100644 index 0000000000..bde7319c02 --- /dev/null +++ b/spec/grape/middleware/formatter_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Grape::Middleware::Formatter do + subject{ Grape::Middleware::Formatter.new(app)} + before{ subject.stub!(:dup).and_return(subject) } + + let(:app){ lambda{|env| [200, {}, [@body]]} } + + context 'serialization' do + it 'should look at the bodies for possibly serializable data' do + @body = {"abc" => "def"} + status, headers, bodies = *subject.call({'PATH_INFO' => '/somewhere'}) + bodies.first.should == MultiJson.encode(@body) + end + end + + context 'detection' do + it 'should use the extension if one is provided' do + subject.call({'PATH_INFO' => '/info.xml'}) + subject.env['api.format'].should == :xml + subject.call({'PATH_INFO' => '/info.json'}) + subject.env['api.format'].should == :json + end + + it 'should use the default format if none is provided' do + subject.call({'PATH_INFO' => '/info'}) + subject.env['api.format'].should == :json + end + + it 'should throw an error on an unrecognized format' do + subject.call({'PATH_INFO' => '/info.barklar'}) + subject.env['api.error.status'].should == 406 + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7874c56f62..47a03ac529 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,11 +5,11 @@ require 'rubygems' require 'bundler' -Bundler.setup :test +Bundler.setup :default, :test require 'rspec' require 'rack/test' RSpec.configure do |config| - + config.include Rack::Test::Methods end