From 2f69f318e0b47cd2bd5ad6518b51984de0235139 Mon Sep 17 00:00:00 2001 From: Dan deHavilland Date: Tue, 14 May 2013 12:57:38 +0200 Subject: [PATCH 1/7] Reinstated parameter passing --- lib/grape-active_model_serializers/formatter.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/grape-active_model_serializers/formatter.rb b/lib/grape-active_model_serializers/formatter.rb index 6704548..2f4ce79 100644 --- a/lib/grape-active_model_serializers/formatter.rb +++ b/lib/grape-active_model_serializers/formatter.rb @@ -30,10 +30,8 @@ module Grape private - def serializer(endpoint, resource, options) - # default_options = controller.send(:default_serializer_options) || {} - options = {} #default_options.merge(options || {}) - + def serializer(endpoint, resource, options={}) + serializer = options.delete(:serializer) || (resource.respond_to?(:active_model_serializer) && resource.active_model_serializer) From e5cf9bdf8cb02c27e8203976280e18a1e9b08f36 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 15 May 2013 20:42:10 -0400 Subject: [PATCH 2/7] Adds support for namespace options --- README.md | 23 ++++++++++++++++++- .../formatter.rb | 14 +++++------ spec/grape_ams_spec.rb | 11 +++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fd1bc9a..584b528 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Grape::Formatter::ActiveModelSerializers.infer_serializers = false ``` -### Manually specifying a serializer +### Manually specifying serializer options Serializers can be specified at a route level by with the serializer option. A serializer can be specified by passing the the serializer class or the serializer name. The following are equivalent: @@ -81,6 +81,27 @@ get "/home", :serializer => :home ... ``` +You can also set a serializer at the namespace level. This serializer can/will be overriden if a serilizer is also specified on the route. + +```ruby +namespace 'foo', :serializer => :bar do + get "/" do + # will use "bar" serializer + end + + get "/home", :serializer => :home do + # will use "home" serializer + end +end +``` + +Other standard options for `ActiveModel::Serializers` can be provided at either the namespace or route level with the same overriding behavior. + +```ruby +get "/home", :root => 'world', :each_serializer => :fancy_home +... +``` + ### Example diff --git a/lib/grape-active_model_serializers/formatter.rb b/lib/grape-active_model_serializers/formatter.rb index 2f4ce79..cd86dda 100644 --- a/lib/grape-active_model_serializers/formatter.rb +++ b/lib/grape-active_model_serializers/formatter.rb @@ -6,14 +6,17 @@ module Grape module ActiveModelSerializers class << self attr_accessor :infer_serializers - attr_reader :env attr_reader :endpoint ActiveModelSerializers.infer_serializers = true def call(resource, env) - # @object = object - options = env['api.endpoint'].options[:route_options] + @endpoint = env["api.endpoint"] + + namespace = endpoint.settings[:namespace] + namespace_options = namespace ? namespace.options : {} + route_options = endpoint.options[:route_options] + options = namespace_options.merge(route_options) serializer = serializer(endpoint, resource, options) @@ -24,14 +27,9 @@ module Grape end end - # options = endpoint.options[:route_options][:serializer_options] || {} - # serializer.new(object, options).to_json - # end - private def serializer(endpoint, resource, options={}) - serializer = options.delete(:serializer) || (resource.respond_to?(:active_model_serializer) && resource.active_model_serializer) diff --git a/spec/grape_ams_spec.rb b/spec/grape_ams_spec.rb index 7bc2c64..d7845fa 100644 --- a/spec/grape_ams_spec.rb +++ b/spec/grape_ams_spec.rb @@ -74,6 +74,17 @@ describe Grape::ActiveModelSerializers do end end + it "uses namespace options when provided" do + subject.namespace :admin, :serializer => UserSerializer do + get('/jeff') do + User.new(first_name: 'Jeff') + end + end + + get "/admin/jeff" + last_response.body.should == "{\"user\":{\"first_name\":\"Jeff\",\"last_name\":null}}" + end + # [User2Serializer, 'user2', :user2].each do |serializer| # it "should render using serializer (#{serializer})" do # subject.get("/home", serializer: serializer) do From f8aff8dff2c72d12f14d408def77178b9574fdb5 Mon Sep 17 00:00:00 2001 From: John Allen Date: Wed, 15 May 2013 21:42:02 -0400 Subject: [PATCH 3/7] Makes Grape::Endpoint quack like an ActionController --- .../formatter.rb | 41 +++++-------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/lib/grape-active_model_serializers/formatter.rb b/lib/grape-active_model_serializers/formatter.rb index cd86dda..824a681 100644 --- a/lib/grape-active_model_serializers/formatter.rb +++ b/lib/grape-active_model_serializers/formatter.rb @@ -1,5 +1,14 @@ require 'active_record' -require 'pry' + +# Make the Grape::Endpoint quiack like a controller +module Grape + class Endpoint + def default_serializer_options; end + def serialization_scope; end + def _serialization_scope; end + def url_options; end + end +end module Grape module Formatter @@ -18,7 +27,7 @@ module Grape route_options = endpoint.options[:route_options] options = namespace_options.merge(route_options) - serializer = serializer(endpoint, resource, options) + serializer = ::ActiveModel::Serializer.build_json(endpoint, resource, options) if serializer serializer.to_json @@ -26,34 +35,6 @@ module Grape Grape::Formatter::Json.call resource, env end end - - private - - def serializer(endpoint, resource, options={}) - serializer = options.delete(:serializer) || - (resource.respond_to?(:active_model_serializer) && - resource.active_model_serializer) - - return serializer unless serializer - - if resource.respond_to?(:to_ary) - unless serializer <= ActiveModel::ArraySerializer - raise ArgumentError.new("#{serializer.name} is not an ArraySerializer. " + - "You may want to use the :each_serializer option instead.") - end - - if options[:root] != false && serializer.root != false - # the serializer for an Array is ActiveModel::ArraySerializer - options[:root] ||= serializer.root || resource.first.class.name.underscore.pluralize - end - end - - # options[:scope] = controller.serialization_scope unless options.has_key?(:scope) - # options[:scope_name] = controller._serialization_scope - # options[:url_options] = controller.url_options - - serializer.new(resource, options) - end end end end From 7f02d8f0d538b307a109bdfb62aa8f1afaab011b Mon Sep 17 00:00:00 2001 From: John Allen Date: Thu, 16 May 2013 09:06:37 -0400 Subject: [PATCH 4/7] Removes unnecessary dependencies There are some dependencies that are not required for this gem (eg. tilt) that I'm removing. In addition some of the dependencies were only necessary for development/test so I'm moving all of that into the gempsec and making that clear there. --- Gemfile | 15 -------- Gemfile.lock | 49 +++----------------------- grape-active_model_serializers.gemspec | 11 +++--- lib/grape-active_model_serializers.rb | 3 +- lib/grape/active_model_serializers.rb | 1 - spec/spec_helper.rb | 3 -- 6 files changed, 13 insertions(+), 69 deletions(-) delete mode 100644 lib/grape/active_model_serializers.rb diff --git a/Gemfile b/Gemfile index 4d97071..fa75df1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,18 +1,3 @@ source 'https://rubygems.org' gemspec - -group :test do - gem "rspec", "~> 2.12.0" - gem "rack-test" - gem "rake" - gem "activerecord-nulldb-adapter" -end - -group :development, :test do - gem "pry" - gem "pry-debugger" - gem "pry-rescue" - gem "pry-stack_explorer" - gem 'plymouth' -end diff --git a/Gemfile.lock b/Gemfile.lock index 77c2a79..b46a29c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,16 +2,12 @@ PATH remote: . specs: grape-active_model_serializers (0.1.0) - active_model_serializers - activerecord grape (~> 0.3) - i18n - tilt GEM remote: https://rubygems.org/ specs: - active_model_serializers (0.7.0) + active_model_serializers (0.8.1) activemodel (>= 3.0) activemodel (3.2.13) activesupport (= 3.2.13) @@ -28,18 +24,7 @@ GEM multi_json (~> 1.0) arel (3.0.2) backports (2.6.7) - binding_of_caller (0.7.1) - debug_inspector (>= 0.0.1) builder (3.0.4) - coderay (1.0.9) - columnize (0.3.6) - debug_inspector (0.0.2) - debugger (1.5.0) - columnize (>= 0.3.1) - debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.2.0) - debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.0) descendants_tracker (0.0.1) diff-lcs (1.1.3) grape (0.4.1) @@ -52,29 +37,10 @@ GEM rack-accept rack-mount virtus - hashie (2.0.3) + hashie (2.0.5) i18n (0.6.1) - interception (0.3) - method_source (0.8.1) - multi_json (1.7.2) + multi_json (1.7.3) multi_xml (0.5.3) - plymouth (0.3.3) - pry-exception_explorer (~> 0.1.7) - pry (0.9.12) - coderay (~> 1.0.5) - method_source (~> 0.8) - slop (~> 3.4) - pry-debugger (0.2.2) - debugger (~> 1.3) - pry (~> 0.9.10) - pry-exception_explorer (0.1.9) - pry-stack_explorer (>= 0.3.9) - pry-rescue (1.1.0) - interception (>= 0.3) - pry - pry-stack_explorer (0.4.9) - binding_of_caller (>= 0.7) - pry (~> 0.9.11) rack (1.5.2) rack-accept (0.4.5) rack (>= 0.4) @@ -91,8 +57,6 @@ GEM rspec-expectations (2.12.1) diff-lcs (~> 1.1.3) rspec-mocks (2.12.2) - slop (3.4.4) - tilt (1.3.6) tzinfo (0.3.37) virtus (0.5.4) backports (~> 2.6.1) @@ -102,13 +66,10 @@ PLATFORMS ruby DEPENDENCIES + active_model_serializers + activerecord activerecord-nulldb-adapter grape-active_model_serializers! - plymouth - pry - pry-debugger - pry-rescue - pry-stack_explorer rack-test rake rspec (~> 2.12.0) diff --git a/grape-active_model_serializers.gemspec b/grape-active_model_serializers.gemspec index 21021b2..b5ab1bb 100644 --- a/grape-active_model_serializers.gemspec +++ b/grape-active_model_serializers.gemspec @@ -16,8 +16,11 @@ Gem::Specification.new do |gem| gem.version = Grape::ActiveModelSerializers::VERSION gem.add_dependency "grape", "~> 0.3" - gem.add_dependency "activerecord" - gem.add_dependency "active_model_serializers" - gem.add_dependency "tilt" - gem.add_dependency "i18n" + + gem.add_development_dependency "activerecord" + gem.add_development_dependency "active_model_serializers" + gem.add_development_dependency "rspec", "~> 2.12.0" + gem.add_development_dependency "rack-test" + gem.add_development_dependency "rake" + gem.add_development_dependency "activerecord-nulldb-adapter" end diff --git a/lib/grape-active_model_serializers.rb b/lib/grape-active_model_serializers.rb index f26ef11..1c1e1d4 100644 --- a/lib/grape-active_model_serializers.rb +++ b/lib/grape-active_model_serializers.rb @@ -1,5 +1,4 @@ require 'active_model_serializers' require 'grape' -require 'hashie/hash' require "grape-active_model_serializers/version" -require "grape-active_model_serializers/formatter" \ No newline at end of file +require "grape-active_model_serializers/formatter" diff --git a/lib/grape/active_model_serializers.rb b/lib/grape/active_model_serializers.rb deleted file mode 100644 index 3d0dcd5..0000000 --- a/lib/grape/active_model_serializers.rb +++ /dev/null @@ -1 +0,0 @@ -require 'grape-active_model_serializers' \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 32a1f2b..d92f000 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,12 +8,9 @@ require 'active_support/core_ext/hash/conversions' require 'active_model' require "active_model_serializers" require "active_support/json" -require 'grape/active_model_serializers' require 'rspec' require 'rack/test' -require "pry" require 'nulldb_rspec' -# require 'plymouth' include NullDB::RSpec::NullifiedDatabase From 9d9f166925e95176799a8e1ff3dfe233ce94e5ec Mon Sep 17 00:00:00 2001 From: John Allen Date: Thu, 16 May 2013 09:09:28 -0400 Subject: [PATCH 5/7] Moves Grape::Endpoint extension into a module Adding methods into the Grape::Endpoint class that ActiveModel::Serializers are relying on from ActionController as well as a few convenience methods for Route and Namespace options. I believe we can expand on some of the empty methods here to allow for higher (api) level option setting and defaults. This is a first step in that direction. --- lib/grape-active_model_serializers.rb | 1 + .../formatter.rb | 16 +----------- lib/grape/endpoint_extension.rb | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 lib/grape/endpoint_extension.rb diff --git a/lib/grape-active_model_serializers.rb b/lib/grape-active_model_serializers.rb index 1c1e1d4..7847623 100644 --- a/lib/grape-active_model_serializers.rb +++ b/lib/grape-active_model_serializers.rb @@ -1,4 +1,5 @@ require 'active_model_serializers' require 'grape' +require 'grape/endpoint_extension' require "grape-active_model_serializers/version" require "grape-active_model_serializers/formatter" diff --git a/lib/grape-active_model_serializers/formatter.rb b/lib/grape-active_model_serializers/formatter.rb index 824a681..693acb2 100644 --- a/lib/grape-active_model_serializers/formatter.rb +++ b/lib/grape-active_model_serializers/formatter.rb @@ -1,15 +1,5 @@ require 'active_record' -# Make the Grape::Endpoint quiack like a controller -module Grape - class Endpoint - def default_serializer_options; end - def serialization_scope; end - def _serialization_scope; end - def url_options; end - end -end - module Grape module Formatter module ActiveModelSerializers @@ -21,11 +11,7 @@ module Grape def call(resource, env) @endpoint = env["api.endpoint"] - - namespace = endpoint.settings[:namespace] - namespace_options = namespace ? namespace.options : {} - route_options = endpoint.options[:route_options] - options = namespace_options.merge(route_options) + options = endpoint.namespace_options.merge(endpoint.route_options) serializer = ::ActiveModel::Serializer.build_json(endpoint, resource, options) diff --git a/lib/grape/endpoint_extension.rb b/lib/grape/endpoint_extension.rb new file mode 100644 index 0000000..944b836 --- /dev/null +++ b/lib/grape/endpoint_extension.rb @@ -0,0 +1,25 @@ +# +# Make the Grape::Endpoint quack like a ActionController +# +# This allows us to rely on the ActiveModel::Serializer#build_json method +# to lookup the approriate serializer. +# +module Grape + module EndpointExtension + def namespace_options + settings[:namespace] ? settings[:namespace].options : {} + end + + def route_options + options[:route_options] + end + + def default_serializer_options; end + def serialization_scope; end + def _serialization_scope; end + def url_options; end + def controller_name; end + end + + Endpoint.send(:include, EndpointExtension) +end From 7933a1796b8b0e8178b54a2a12f4244064e4c457 Mon Sep 17 00:00:00 2001 From: John Allen Date: Thu, 16 May 2013 09:56:13 -0400 Subject: [PATCH 6/7] Spec setup updates, almost passing --- spec/grape_ams_spec.rb | 1 + spec/spec_helper.rb | 3 +++ 2 files changed, 4 insertions(+) diff --git a/spec/grape_ams_spec.rb b/spec/grape_ams_spec.rb index d7845fa..c9f90b5 100644 --- a/spec/grape_ams_spec.rb +++ b/spec/grape_ams_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'spec_fakes' +require "grape-active_model_serializers" # require 'active_model' describe Grape::ActiveModelSerializers do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d92f000..93ff130 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,11 +5,14 @@ require 'bundler' Bundler.setup :default, :test require 'active_support/core_ext/hash/conversions' +require 'active_record' require 'active_model' require "active_model_serializers" require "active_support/json" require 'rspec' require 'rack/test' + +ActiveRecord::Base.establish_connection :adapter => :nulldb, :schema => 'db/schema.rb' require 'nulldb_rspec' include NullDB::RSpec::NullifiedDatabase From 3b02d16211bb65c71bb5d86f999c4079cd93de41 Mon Sep 17 00:00:00 2001 From: John Allen Date: Thu, 16 May 2013 18:58:35 -0400 Subject: [PATCH 7/7] Ensures there is a root available for arrays --- lib/grape-active_model_serializers/formatter.rb | 5 +++++ lib/grape/endpoint_extension.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/grape-active_model_serializers/formatter.rb b/lib/grape-active_model_serializers/formatter.rb index 693acb2..c0d8dae 100644 --- a/lib/grape-active_model_serializers/formatter.rb +++ b/lib/grape-active_model_serializers/formatter.rb @@ -13,6 +13,11 @@ module Grape @endpoint = env["api.endpoint"] options = endpoint.namespace_options.merge(endpoint.route_options) + if resource.is_a?(Array) && !resource.empty? + # ensure we have an root to fallback on + endpoint.controller_name = resource.first.class.name.underscore.pluralize + end + serializer = ::ActiveModel::Serializer.build_json(endpoint, resource, options) if serializer diff --git a/lib/grape/endpoint_extension.rb b/lib/grape/endpoint_extension.rb index 944b836..dac398f 100644 --- a/lib/grape/endpoint_extension.rb +++ b/lib/grape/endpoint_extension.rb @@ -6,6 +6,8 @@ # module Grape module EndpointExtension + attr_accessor :controller_name + def namespace_options settings[:namespace] ? settings[:namespace].options : {} end @@ -18,7 +20,6 @@ module Grape def serialization_scope; end def _serialization_scope; end def url_options; end - def controller_name; end end Endpoint.send(:include, EndpointExtension)