Skip to content
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

Add tests for fields option demonstrating usage on both attributes and relationships #1839

Merged

Conversation

NullVoxPopuli
Copy link
Contributor

@NullVoxPopuli NullVoxPopuli commented Jul 11, 2016

Purpose

Adds tests demonstrating proper syntax / format for using fields on both attributes and relationships.

Wierdness

The root key needs to be specified in order to whitelist relationships, which feels weird.

@mention-bot
Copy link

@NullVoxPopuli, thanks for your PR! By analyzing the annotation information on this pull request, we identified @arenoir, @beauby and @dubadub to be potential reviewers

@NullVoxPopuli
Copy link
Contributor Author

@NullVoxPopuli, thanks for your PR! By analyzing the annotation information on this pull request, we identified @arenoir, @beauby and @dubadub to be potential reviewers

waaaaaaaaaaaaaa O.O

neat

else
{}
end
JSONAPI::IncludeDirective.new(raw_fields).to_hash
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this would open us up to too many issues, but it works with all the tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the change should be in the adapter that passes in fields: {} instead of fields: nil when options[:fields].nil?

see

requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields)
resource_object[:attributes] = attributes if attributes.any?
resource_object
end
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or just that fields_for should return {}, n/m

def fields_for(type)
fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
end

      def fields_for(type)
-        fields[type.singularize.to_sym] || fields[type.pluralize.to_sym]
+        fields[type.singularize.to_sym] || fields[type.pluralize.to_sym] || {}
      end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, the issue IS with parsed_fields because it removes the way you have to specify relationships.

root_type: [:relationship_type]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, I could maybe change how the relationship's call to fields_for is handled. maybe it would be a bit more intuitive that way.

#
# JSONAPI::IncludeDirective supports these, but also changes the resulting structure.
# is it worth it to duplicate some of that functionality?
# or should IncludeDirective be modified to only allow one layer of options?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example:

# the root type *should* be assumed (it's currently not with JSON API
fields: :author
fields :author, :comments
fields :access_token # from the docs

docs: https://github.com/rails-api/active_model_serializers/blob/master/docs/general/rendering.md#fields
and: https://github.com/rails-api/active_model_serializers/blob/master/docs/general/fields.md

There is an inconsistency between APIs in the different adapters.

JSON API requires the root key in the fields specification, whereas the other adapters do not require the root.

So, maybe this PR should focus on unifying this between all adapters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, I've been thinking we should just name everything key. attribute key, relationship key, object key, collection key, and forget about json_key and have key make use of type and root options when available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be a great topic for a PR this week. Hmm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related #1857

While working on that I've discovered an architectural problem that will require some careful unraveling

In brief

serializer.attributes(fields) builds a hash from calling value (aka read_attribute_for_serialization ) on the Attribute objects with matching names

serializer.associations(include_directive) iterates over all the Reflection objects and rejects excluded and yields an Association object built jit by the Reflection

The Association is used by calling association.serializer.serializable_hash

As currently implemented, the reflection is evaluated (method is called) when the Association is built. This prevents lazy evaluation

I have a hack in that pr to delay evaluation until the serializer asks for it, which works when the serializer calls serializable_hash

However, the JSONAPI adapter iterates the attributes and associations directly and passes the association into a new Relationship which promptly evaluates the association before determining whether to include it. Complicating this is that there is an assumption in the code that we mist evaluate the association so that we can infer the serializer class so that we can instantiate it, but it is already too late.

The fundamental way that a serializer is told which fields and associations to evaluate is inconsistent and far to eager. The already-evaluated-association object permeates the code.... Should be much simpler

Also would be good to make Fields and similar apis consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also would be good to make Fields and similar apis consistent

I have a hack that brings fields functionality to the Attributes adapter (I use this for CSV generation)

module ActiveModelSerializers
  module Adapter
    class Attributes < Base
      def serializable_hash(options = nil)
        options = serialization_options(options)
        options[:fields] ||= instance_options[:fields]
        hash = serializer.serializable_hash(instance_options, options, self)
        fields = JSONAPI::IncludeDirective.new(options[:fields]).to_hash
        apply_fields_whitelist(hash, fields)
      end

      # this is a little backwards, but it's needed until AMS has a more unified interface
      def apply_fields_whitelist(hash, fields)
        return hash.map{ |e| apply_fields_whitelist(e, fields) } if hash.is_a?(Array)

        result = {}
        fields_keys = fields.keys
        hash.each do |k, v|
          next unless fields_keys.include?(k)
          if v.is_a?(Hash)
            result[k] = apply_fields_whitelist(v, fields[k])
          else
            result[k] = v
          end
        end

        result
      end
    end
  end
end

this sort of thing def needs to be unified.
As for unification timeline, I think I'll start this weekend, if not Monday, but I'd like to see #1857 merged, as well as this one, and the test refactor merged. :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, 0.10 is. 0.8 and 0.9 are still pretty clean. So sad.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it at least glutten free? 🍝 🍷

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.10 will become clean soon enough. :-)

The important thing is we have enough tests to accurately keep functionality as we refactor

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can return to a clean adapterless cache unaware serializer that can take in options to serialize its attributes and association... and I'd love to use just functions here, like in knuckles, we could rock this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and I'd love to use just functions here, like in knuckles

what do you mean by these two statements?
for first, do you mean more FP pure functions?

@NullVoxPopuli
Copy link
Contributor Author

In other news, I learned that multi-word types MUST use the same key-casing as how the key transform is configured, which I didn't think was very apparent, as there is no logging or anything. I'll add that to the docs.

@NullVoxPopuli
Copy link
Contributor Author

any objections to this?

@bf4
Copy link
Member

bf4 commented Aug 9, 2016

@NullVoxPopuli The PR description make it sound like it's adding a feature, but the actual is that it is adding tests. I've added a commit undoing the change in the FieldSet since is not a functional change, as far as I can tell.

While making that change, some of the discussion has gotten lost in the diff.

@@ -7,6 +7,7 @@ Breaking changes:
Features:

Fixes:
- [#1839](https://github.com/rails-api/active_model_serializers/pull/1839) `fields` option whitelists both attributes and relationships. (@NullVoxPopuli)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a fix, I don't think just tests? #1839 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, just tests

@NullVoxPopuli
Copy link
Contributor Author

PR description updated

@@ -9,6 +9,7 @@ Features:
Fixes:

Misc:
- [#1839](https://github.com/rails-api/active_model_serializers/pull/1839) `fields` tests demonstrating usage for both attributes and relationships. (@NullVoxPopuli)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bf4 changelog entry moved, and re-worded.

@NullVoxPopuli NullVoxPopuli changed the title fields should whitelist both attributes and relationships Add tests field fields option demonstrating usage on both attributes and relationships Aug 13, 2016
@NullVoxPopuli NullVoxPopuli changed the title Add tests field fields option demonstrating usage on both attributes and relationships Add tests for fields option demonstrating usage on both attributes and relationships Aug 13, 2016
@bf4 bf4 merged commit a319fef into rails-api:master Aug 17, 2016
@bf4 bf4 deleted the fields-also-whitelists-relationships branch August 17, 2016 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants