-
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
Fieldset support in Attributes/JSON adapters #2223
Conversation
begin | ||
object.class.model_name.to_s.underscore | ||
rescue ArgumentError | ||
'anonymous_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.
some of the tests use anonymous classes.. not sure how real world this is, but I figure giving it a descriptive fallback name is a good compromise
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 serialized POROs (i.e. whose class does not understand model_name
message), does this imply json_key
resolves to 'anonymous_object'
? Or do I miss something?
Rationale:
- with
:attributes
adapter there is noroot
; _type
is not required for AMS v0.9.x serializers;- PORO classes do not respond to
model_name
:
NoMethodError:
undefined method `model_name' for OpenStruct:Class
# /Users/user/.rvm/gems/ruby-2.3.1@gemset1/gems/active_model_serializers-0.10.7/
lib/active_model/serializer.rb:374:in `json_key'
rescue ArgumentError
won't catchNoMethodError
.
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.
Well, since this version of AMS describe what's a valid PORO, and even provides a linter for it, then it's reasonable to assume that's what you're using...
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 like AMS should rescue the NoMethodError and raise saying "no explicit root given, could not infer root from object" with maybe a link for more info.
The argument error is fine.
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.
begin
object.class.model_name.to_s.underscore
rescue ArgumentError
'anonymous_object'
rescue NoMethodError
raise 'no explicit root given; could not infer root from object.'
something like this? also can we provide the PORO link in the msg?
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.
@bf4 would you like to have another look at this, please?
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.
@wasifhossain that seems fine, though maybe raise a different error
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 you please point me out which error class can we use for the case :) do you mean creating a custom class?
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.
@wasifhossain I was thinking we could just define one. Maybe
UnknownSerializerRootError = Class.new(NoMethodError)
?
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 fine to me. will do this. thank you!
def serializable_hash(options = nil) | ||
options = serialization_options(options) | ||
options[:fields] ||= instance_options[:fields] | ||
serialized_hash = serializer.serializable_hash(instance_options, options, self) | ||
|
||
self.class.transform_key_casing!(serialized_hash, instance_options) | ||
end | ||
|
||
def fields_to_fieldset(fields) |
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.
Because, as per docs/general/fields.md
, we have a different syntax for fieldset in JSON adapter vs. JSON:API
The test usage in #2100 is [:title, {comments: [:body]}
. I'm not sure if we ever considered the fields
option for relationships
However, I'd accept just turning an array into an implicit fieldset, but otherwise require it to be jsonapi-like.
In other words, I have no idea at this time if the below code is introducing new or desired behavior. Needs thought
and in a specific controller, you want to return
access_token
only,fields
will help you:
class AnonymousController < ApplicationController
def create
render json: User.create(activation_state: 'anonymous'), fields: [:access_token], status: 201
end
end
Note that this is only valid for the
json
andattributes
adapter. For thejson_api
adapter, you would use
render json: @user, fields: { users: [:access_token] }
Where
users
is the JSONAPI type.
@@ -357,6 +357,9 @@ def associations(include_directive = ActiveModelSerializers.default_include_dire | |||
def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance) | |||
adapter_options ||= {} | |||
options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options) | |||
if (fieldset = adapter_options[:fieldset]) | |||
options[:fields] = fieldset.fields_for(json_key) | |||
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.
For some reason, this seemed to be the right place for this, code-wise. I think it's because the serializer has some responsibility for serializing associations
@@ -1,13 +1,32 @@ | |||
module ActiveModelSerializers | |||
module Adapter | |||
class Attributes < Base | |||
def initialize(*) | |||
super | |||
instance_options[:fieldset] ||= ActiveModel::Serializer::Fieldset.new(fields_to_fieldset(instance_options.delete(:fields))) |
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 fieldset for the adapter is set at init. The fields the adapter is told to use is passed to serializable_hash. Not sure how good a distinction this is, but it's also how the json:api adapter works
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.
Since the JSON Adapter delegates to Attributes here, this is the only necessary adapter change
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.
honestly, this particular comment answered my long-standing question for this PR 😄 clever work 👏
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 LGTM! Shall we rebase it against the latest 0-10-stable and go for a merge?
@bf4 very good explanations for each set of changes 👍 |
@bf any update on this? |
else fail ArgumentError, "Unknown conversion of fields to fieldset: '#{field.inspect}' in '#{fields.inspect}'" | ||
end | ||
end | ||
relationship_fields.merge(serializer.json_key.to_sym => resource_fields) |
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.
Could please change it either to merge!
or
relationship_fields[serializer.json_key.to_sym] = resource_fields
relationship_fields
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.
good point. I wonder why I didn't mutate a local var we own and mutate elsewhere
@wasifhossain you're welcome to take ownership of this PR if you'd like
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.
@bf4 thanks for the offer. I will rebase it as @cintamani suggested earlier as well as make this small fix (prefer using merge!
) before merging this PR. do you have any suggestion, pls?
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.
Only real suggestion is to bear in mind once merged we need to support it.
I didn't merge it in part due to lack of confidence it was the right change.
I don't like to add features without someone actually trying it out.
e527977
to
6a8994f
Compare
e300ce7
to
d796a0e
Compare
@wasifhossain It works as expected indeed 😄. A 👍 from my side. |
that's great news @johan-smits! Thanks very much for your valuable feedback 👍 will look forward to some more feedback from production usage of this PR in coming weeks and then merge it. |
@wasifhossain we are using the branch in acceptance tomorrow. Without negative feedback the coming week it is ok. |
No negative feedback from my side, looking forward to the new release! |
great feedback! @johan-smits I will work in this weekend to merge it. thanks |
@wasifhossain no pressure but no merge done yet? |
@johan-smits honestly I was running under a bit of pressure in past weeks. but looking forward to taking care of this in coming weekend. appreciate your patience 👍 thanks! |
d796a0e
to
c77fdda
Compare
so we are all green on travis. let's merge it 🚢 |
@johan-smits now you can use this feature by pointing your Gemfile to |
@wasifhossain testing... |
Guys, I think we are rescuing wrong error here:
In my case when I work with PORO's, it actually raises
And it totally makes sense, isn't it? :) P.S. I didn't have any issues with it on version |
@FunkyloverOne here is a good thread for you to follow: #2223 (comment) |
AMS expects serialized models to behave like an ActiveModel::Model now: rails-api/active_model_serializers#2223
AMS requires that `root` be set to a string: rails-api/active_model_serializers#2223
Supersedes stale PR #2100
Closes: #2100, #2278
Addresses partially: #2273