-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
This adds namespace lookup to serializer_for #1968
This adds namespace lookup to serializer_for #1968
Conversation
@NullVoxPopuli, thanks for your PR! By analyzing the history of the files in this pull request, we identified @bf4, @beauby and @groyoh to be potential reviewers. |
chain = [] | ||
|
||
resource_class_name = klass.name.demodulize | ||
resource_namespace = klass.name.deconstantize | ||
serializer_class_name = "#{resource_class_name}Serializer" | ||
|
||
chain.push("#{namespace.name}::#{serializer_class_name}") if namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is namespace.name
necessary? Seems it implies namespace is a class, whereas, if it is, when interpolating, Ruby will do the right thing whether it's a string, symbol, or class... i.e just namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not necessary. removed.
@NullVoxPopuli what is the |
@@ -18,6 +18,10 @@ def serialization_scope(scope) | |||
self._serialization_scope = :current_user | |||
end | |||
|
|||
def namespace_for_serializer | |||
@namespace_for_serializer ||= self.class.parent unless self.class.parent == Object | |||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think we should make this a public interface? Once we add it, we need to support it. (I know I resurrected it, just asking now in context :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure people would appreciate it. And I don't think it would be a problem to support, as tons of people want namespaced-scoped serializer lookup -- but maybe that'd be better for the lookup customization PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we add it, we need to support it.
:)
Where do you think docs should go? Any sense of where people would look for this? |
that's a good question -- must have gotten carried over from the serializer-lookup branch -- which I also don't remember editing that file. |
I'd say under rendering -- I'll add something real quick |
@@ -20,7 +20,8 @@ cache: | |||
|
|||
script: | |||
- bundle exec rake ci | |||
|
|||
after_success: | |||
- codeclimate-test-reporter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this working?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't error -- I'm not familiar enough with code climate to see if the results are published per-pull-request. But it works locally
The namespace for serializer lookup is based on the controller, via `ActionController::Serialization#namespace_for_serializer`. | ||
|
||
To configure the implicit namespace, in your controller, define | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replace the implementation of
namespace_for_serializer
or pass in the render option:namespace
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e. below is the current implementation. ooh,, this would be a good reason to add a setter
attr_writer :namespace_for_serializer
makes it cleaner and can do in a before filter ?
|
||
|
||
```ruby | ||
render json: @post, namespace: Api::V2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or
namespace: 'Api::V2'
ornamespace: :'Api::V2'
but notnamespace: :api_v2'
render json: @post, namespace: Api::V2 | ||
``` | ||
|
||
This tells the serializer lookup to check for the existence of `Api::V2::PostSerializer`, and if any relations are rendered with `@post`, they will also utilize the `Api::V2` namespace. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would be a better example with @post = Post.first
or something to reference the class of the model.
return nil unless config.serializer_lookup_enabled | ||
serializers_cache.fetch_or_store(klass) do | ||
# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs. | ||
serializer_class = serializer_lookup_chain_for(klass).map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer } | ||
lookup_chain = serializer_lookup_chain_for(klass, namespace) | ||
serializer_class = lookup_chain.map(&:safe_constantize).find { |x| x && x < ActiveModel::Serializer } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
@@ -106,6 +106,10 @@ def value(serializer, include_slice) | |||
# | |||
def build_association(parent_serializer, parent_serializer_options, include_slice = {}) | |||
reflection_options = options.dup | |||
|
|||
# Pass the parent's namespace onto the child serializer | |||
reflection_options[:namespace] ||= parent_serializer_options[:namespace] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this not included, oy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah, without coping the namespace from the parent to the reflection_options, the namespace isn't passed to all the children
@@ -55,7 +55,7 @@ def serializer | |||
@serializer ||= | |||
begin | |||
@serializer = serializer_opts.delete(:serializer) | |||
@serializer ||= ActiveModel::Serializer.serializer_for(resource) | |||
@serializer ||= ActiveModel::Serializer.serializer_for(resource, serializer_opts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
class Page < ::Model; end | ||
class Publisher < ::Model; end | ||
|
||
module Api |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can save space by
module Api; end
module Api::V3; end
class Api::V3::BookSerializer < ApplicationSerializer
# etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decided to keep the expanded syntax for a V2 serializer
# | ||
# expected = { title: 'A Post', author_name: 'hello' } | ||
# assert_equal expected, result | ||
# end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks about right except for the ::
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem I was having with this test is that I couldn't get a root level BookSerializer to be used if a namespace wasn't present.
@@ -0,0 +1,85 @@ | |||
require 'test_helper' | |||
|
|||
module ActiveModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good. needs controller test for implicit lookup as well as option passing, which is really covered below, here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests added
before_filter :use_my_namespace | ||
def use_my_namespace | ||
self.namespace_for_serializer = Api::V2 | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might as well just before_action { self.namespace_for_serializer = Api::V2 }
(before_filter is such old news :( :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent!
|
||
This tells the serializer lookup to check for the existence of `Api::V2::PostSerializer`, and if any relations are rendered with `@post`, they will also utilize the `Api::V2` namespace. | ||
|
||
The namespace can _only_ be in one of the following formats: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any object whose namespace can be represented by string interpolation (i.e. by callingto_s
)
- Module:
Api::V2
- String:
'Api::V2'
- Symbol:
:'Api::V2'
end | ||
|
||
def namespace_for_serializer | ||
@namespace_for_serializer ||= self.class.parent unless self.class.parent == Object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
return nil unless config.serializer_lookup_enabled | ||
serializers_cache.fetch_or_store(klass) do | ||
serializers_cache.fetch_or_store("#{klass}-#{namespace}") do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will look weird in some case.
ActiveSupport::Cache.expand_cache_key(klass, namespace)
also used to be in AMS...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, thanks
return nil unless config.serializer_lookup_enabled | ||
serializers_cache.fetch_or_store(klass) do | ||
|
||
cache_key = ActiveSupport::Cache.expand_cache_key(klass, namespace) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for now, is ok, but really needs to be in https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/concerns/caching.rb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cache updates and namespacing / configurable lookup chain sounds like a good set of updates for a release? ;-)
* This adds namespace lookup to serializer_for * address rubocop issue * address @bf4's feedback * add docs * update docs, add more tests * apparently rails master doesn't have before filter * try to address serializer cache issue between tests * update cache for serializer lookup to include namespace in the key, and fix the tests for explicit namespace * update docs, and use better cache key creation method * update docs [ci skip] * update docs [ci skip] * add to changelog [ci skip]
Purpose
To Provide the ability to namespace serializers in the same fashion that controllers are namespaced -- as it is very common to add
Api::V#
to both controllers and serializers.Changes
Pass the controllers namespace through serializer_options and serializer_for
Caveats
Namespaced lookup is first, given that
namespace
is passed in to the serializable resource options.Related GitHub issues
Subset of the work in #1757