diff --git a/lib/travis/api/app/helpers/respond_with.rb b/lib/travis/api/app/helpers/respond_with.rb index cfb1cc44..565f0172 100644 --- a/lib/travis/api/app/helpers/respond_with.rb +++ b/lib/travis/api/app/helpers/respond_with.rb @@ -6,8 +6,9 @@ class Travis::Api::App # convert (in addition to the return values supported by Sinatra, of # course). These values will be encoded in JSON. module RespondWith + include Accept + def respond_with(resource, options = {}) - options[:format] ||= env['travis.format'] result = respond(resource, options) result = result ? result.to_json : 404 halt result @@ -21,15 +22,24 @@ class Travis::Api::App private def respond(resource, options) - responders(resource, options).each do |const| - responder = const.new(self, resource, options) - resource = responder.apply if responder.apply? + resource = apply_service_responder(resource, options) + + acceptable_formats.find do |accept| + responders(resource, options).find do |const| + responder = const.new(self, resource, options.dup.merge(accept: accept)) + responder.apply if responder.apply? + end end + end + + def apply_service_responder(resource, options) + responder = Responders::Service.new(self, resource, options) + resource = responder.apply if responder.apply? resource end def responders(resource, options) - [:Service, :Json, :Image, :Xml, :Plain].map do |name| + [:Json, :Image, :Xml, :Plain].map do |name| Responders.const_get(name) end end diff --git a/lib/travis/api/app/middleware/rewrite.rb b/lib/travis/api/app/middleware/rewrite.rb index 5fb12586..8ac8b9f2 100644 --- a/lib/travis/api/app/middleware/rewrite.rb +++ b/lib/travis/api/app/middleware/rewrite.rb @@ -24,6 +24,7 @@ class Travis::Api::App def extract_format env['PATH_INFO'].sub!(FORMAT, '') + env['travis.format_from_path'] = $1 env['travis.format'] = $1 || accept_format end diff --git a/lib/travis/api/app/responders/base.rb b/lib/travis/api/app/responders/base.rb index 14f5df38..9c099cdd 100644 --- a/lib/travis/api/app/responders/base.rb +++ b/lib/travis/api/app/responders/base.rb @@ -31,5 +31,19 @@ module Travis::Api::App::Responders def headers endpoint.headers end + + def apply? + acceptable_format? + end + + def format + self.class.name.split('::').last.downcase + end + + def acceptable_format? + if accept = options[:accept] + accept.accepts?(Rack::Mime.mime_type(".#{format}")) + end + end end end diff --git a/lib/travis/api/app/responders/image.rb b/lib/travis/api/app/responders/image.rb index 2c4c4f69..c75a752f 100644 --- a/lib/travis/api/app/responders/image.rb +++ b/lib/travis/api/app/responders/image.rb @@ -1,7 +1,7 @@ module Travis::Api::App::Responders class Image < Base - def apply? - options[:format] == 'png' + def format + 'png' end def apply diff --git a/lib/travis/api/app/responders/json.rb b/lib/travis/api/app/responders/json.rb index 8ad49337..b4c2e2a6 100644 --- a/lib/travis/api/app/responders/json.rb +++ b/lib/travis/api/app/responders/json.rb @@ -4,7 +4,7 @@ class Travis::Api::App include Helpers::Accept def apply? - options[:format] == 'json' && !resource.is_a?(String) && !resource.nil? + super && !resource.is_a?(String) && !resource.nil? end def apply diff --git a/lib/travis/api/app/responders/plain.rb b/lib/travis/api/app/responders/plain.rb index c1bc0dfa..2320b1e2 100644 --- a/lib/travis/api/app/responders/plain.rb +++ b/lib/travis/api/app/responders/plain.rb @@ -1,12 +1,16 @@ module Travis::Api::App::Responders class Plain < Base + def format + 'txt' + end + def apply? # make sure that we don't leak anything by processing only Log # instances here. I don't want to create entire new API builder just # for log's content for now. # # TODO: think how to handle other formats correctly - options[:format] == 'txt' && resource.is_a?(Log) + super && resource.is_a?(Log) end def apply diff --git a/lib/travis/api/app/responders/xml.rb b/lib/travis/api/app/responders/xml.rb index b6a075a6..7a959a23 100644 --- a/lib/travis/api/app/responders/xml.rb +++ b/lib/travis/api/app/responders/xml.rb @@ -15,10 +15,6 @@ module Travis::Api::App::Responders started: 'Building' } - def apply? - options[:format] == 'xml' - end - def apply halt TEMPLATE % data end diff --git a/spec/integration/formats_handling_spec.rb b/spec/integration/formats_handling_spec.rb new file mode 100644 index 00000000..ede76766 --- /dev/null +++ b/spec/integration/formats_handling_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'App' do + before do + FactoryGirl.create(:test, :number => '3.1', :queue => 'builds.common') + + add_endpoint '/foo' do + get '/' do + respond_with(Log.first) + end + + get '/hash' do + respond_with foo: 'bar' + end + end + end + + it 'gives priority to format given the url' do + response = get '/foo.txt', {}, 'HTTP_ACCEPT' => 'application/json' + response.content_type.should =~ /^text\/plain/ + end + + it 'responds with first available type' do + response = get '/foo', {}, 'HTTP_ACCEPT' => 'image/jpeg, application/json' + response.content_type.should =~ /^application\/json/ + end +end