From 36871b1d981bdd9c62237989a2bbb641c046f001 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Sat, 16 Jan 2021 11:45:37 -0800 Subject: [PATCH 01/10] Links on a relationship can take a callable --- README.md | 11 +++++++++++ lib/fast_jsonapi/relationship.rb | 2 ++ spec/fixtures/movie.rb | 12 ++++++++++++ spec/integration/relationships_spec.rb | 13 +++++++++++++ 4 files changed, 38 insertions(+) diff --git a/README.md b/README.md index 6525bc7..6dca507 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,17 @@ This will create a `self` reference for the relationship, and a `related` link f } ``` +Relationship links can also be configured to be defined as a callable. + +```ruby + has_many :actors, links: -> (object, params) { + { + self: "https://movies.com/#{object.id}/relationships/actors", + next: "https://movies.com/#{object.id}/relationships/actors?page%5Bnumber%5D=2&page%5Bsize%5D=10" + } + } +``` + ### Meta Per Resource For every resource in the collection, you can include a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship. diff --git a/lib/fast_jsonapi/relationship.rb b/lib/fast_jsonapi/relationship.rb index 4a36932..ecb0e80 100644 --- a/lib/fast_jsonapi/relationship.rb +++ b/lib/fast_jsonapi/relationship.rb @@ -148,6 +148,8 @@ def fetch_id(record, params) def add_links_hash(record, params, output_hash) output_hash[key][:links] = if links.is_a?(Symbol) record.public_send(links) + elsif links.respond_to?(:call) + FastJsonapi.call_proc(links, record, params) else links.each_with_object({}) do |(key, method), hash| Link.new(key: key, method: method).serialize(record, params, hash) diff --git a/spec/fixtures/movie.rb b/spec/fixtures/movie.rb index f99af21..e42966a 100644 --- a/spec/fixtures/movie.rb +++ b/spec/fixtures/movie.rb @@ -125,3 +125,15 @@ class MovieSerializer < ::MovieSerializer end end end + +class CallableLinksMovieSerializer < ::MovieSerializer + has_many( + :first_two_actors, + id_method_name: :uid, + links: lambda do |record, params| + { some: record.id, fancy: 'here' } + end + ) do |record, params| + record.actors.take(2) + end +end diff --git a/spec/integration/relationships_spec.rb b/spec/integration/relationships_spec.rb index f0585a8..ab261fd 100644 --- a/spec/integration/relationships_spec.rb +++ b/spec/integration/relationships_spec.rb @@ -142,5 +142,18 @@ end end end + + context 'with a callable as relationship links' do + let(:serialized) do + CallableLinksMovieSerializer.new(movie, params).serializable_hash.as_json + end + + it do + expect(serialized['data']['relationships']['first_two_actors']) + .to have_link('some').with_value(movie.id) + expect(serialized['data']['relationships']['first_two_actors']) + .to have_link('fancy').with_value('here') + end + end end end From 5ec840dc0b00eb440954d1c837ec34a37d1f2cf1 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Sat, 16 Jan 2021 11:46:57 -0800 Subject: [PATCH 02/10] Allow filtering of attributes and relationships using a single call --- README.md | 47 +++++++++++++++++++ lib/fast_jsonapi/object_serializer.rb | 34 ++++++++++++++ lib/fast_jsonapi/serialization_core.rb | 32 +++++++++++++ spec/fixtures/actor.rb | 54 ++++++++++++++++++++++ spec/fixtures/movie.rb | 25 ++++++++++ spec/integration/attributes_fields_spec.rb | 39 ++++++++++++++++ spec/integration/relationships_spec.rb | 24 ++++++++++ 7 files changed, 255 insertions(+) diff --git a/README.md b/README.md index 6dca507..9135274 100644 --- a/README.md +++ b/README.md @@ -524,6 +524,29 @@ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? } serializer.serializable_hash ``` +Sometimes it might be more performant to reduce the number of attributes getting serialized in a single call rather than specifying and executing a conditional Proc for every attribute. For this situation `attributes_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all attributes defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the attributes to serialize. It will be further reduced by an eventually provided fieldset. + +```ruby +class MovieSerializer + include JSONAPI::Serializer + + attributes :name, :year, :release_year, :director + + attributes_filter do |all_attributes, record, params| + permit = params[:permitted_by_policy] + + case permit + when :all, nil + all_attributes + when :none, [] + [] + else + all_attributes.slice(*permit) + end + end +end +``` + ### Conditional Relationships Conditional relationships can be defined by passing a Proc to the `if` key. Return `true` if the relationship should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively. @@ -545,6 +568,30 @@ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? } serializer.serializable_hash ``` +Just like with attributes, it might sometimes be more performant to reduce the number of relationships getting serialized in a single call rather than specifying and executing a single conditional Proc for every relationship. For this situation `relationships_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all relationships defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the relationships to serialize. It will be further reduced by an eventually provided fieldset. + +```ruby +class MovieSerializer + include JSONAPI::Serializer + + has_many :actors + belongs_to :owner + + relationships_filter do |all_relationships, record, params| + permit = params[:permitted_by_policy] + + case permit + when :all, nil + all_relationships + when :none, [] + [] + else + all_relationships.slice(*permit) + end + end +end +``` + ### Specifying a Relationship Serializer In many cases, the relationship can automatically detect the serializer to use. diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index 04bd98d..b6ccb6f 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -119,7 +119,9 @@ def is_collection?(resource, force_is_collection = nil) def inherited(subclass) super(subclass) subclass.attributes_to_serialize = attributes_to_serialize.dup if attributes_to_serialize.present? + subclass.attributes_filter_method = attributes_filter_method.dup if attributes_filter_method.present? subclass.relationships_to_serialize = relationships_to_serialize.dup if relationships_to_serialize.present? + subclass.relationships_filter_method = relationships_filter_method.dup if relationships_filter_method.present? subclass.cachable_relationships_to_serialize = cachable_relationships_to_serialize.dup if cachable_relationships_to_serialize.present? subclass.uncachable_relationships_to_serialize = uncachable_relationships_to_serialize.dup if uncachable_relationships_to_serialize.present? subclass.transform_method = transform_method @@ -291,6 +293,38 @@ def create_relationship(base_key, relationship_type, options, block) ) end + ## + # Add an extra layer for attribute filtering to the serializer operating on the full list of defined attributes, + # but before any fieldset is applied. This is a more performant option than providing conditionals to every attribute. + # + # @param filter_method_name [Symbol, nil] + # The name of a class method used to filter the set of attributes. This method will receive the superset of attributes, + # the current record getting serialized and the serializer parameters passed along. + # + # @yieldparam filter_block [#call] + # If a block is provided instead of a method name, this is going to be called when building the attributes hash. + # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized + # and the serializer parameters. + def attributes_filter(filter_method_name = nil, &block) + self.attributes_filter_method = filter_method_name || block + end + + ## + # Add an extra layer for relationship filtering to the serializer operating on the full list of defined relationships, + # but before any fieldset is applied. This is a more performant option than providing conditionals to every relationship. + # + # @param filter_method_name [Symbol, nil] + # The name of a class method used to filter the set of relationships. This method will receive the superset of relationships, + # the current record getting serialized and the serializer parameters passed along. + # + # @yieldparam filter_block [#call] + # If a block is provided instead of a method name, this is going to be called when building the relationships hash. + # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized + # and the serializer parameters. + def relationships_filter(filter_method_name = nil, &block) + self.relationships_filter_method = filter_method_name || block + end + def compute_id_method_name(custom_id_method_name, id_method_name_from_relationship, polymorphic, serializer, block) if block.present? || serializer.is_a?(Proc) || polymorphic custom_id_method_name || :id diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 0cbdad0..2831880 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -13,7 +13,9 @@ module SerializationCore included do class << self attr_accessor :attributes_to_serialize, + :attributes_filter_method, :relationships_to_serialize, + :relationships_filter_method, :cachable_relationships_to_serialize, :uncachable_relationships_to_serialize, :transform_method, @@ -43,6 +45,7 @@ def links_hash(record, params = {}) def attributes_hash(record, fieldset = nil, params = {}) attributes = attributes_to_serialize + attributes = filter_list(attributes_filter_method, attributes, record, params) attributes = attributes.slice(*fieldset) if fieldset.present? attributes = {} if fieldset == [] @@ -51,8 +54,37 @@ def attributes_hash(record, fieldset = nil, params = {}) end end + ## + # Eventually filter a list of attributes or relationships using a configured filter method/block + # + # @param filter [Symbol, #call, nil] + # If a Symbol the name of the filter method to call on the serializer. + # If something callable, the result of that callable. + # + # @param superset [Hash] + # The attributers or relationships to filter + # + # @param record [Object] + # The current record to get serialized + # + # @param params [Hash] + # The params provided to the serializer + # + # @return [Hash] + # The eventually filtered set of attributes or relationships + def filter_list(filter, superset, record, params = {}) + return superset if filter.nil? + + if filter.respond_to?(:call) + filter.call(superset, record, params) + else + send(filter, superset, record, params) + end + end + def relationships_hash(record, relationships = nil, fieldset = nil, includes_list = nil, params = {}) relationships = relationships_to_serialize if relationships.nil? + relationships = filter_list(relationships_filter_method, relationships, record, params) relationships = relationships.slice(*fieldset) if fieldset.present? relationships = {} if fieldset == [] diff --git a/spec/fixtures/actor.rb b/spec/fixtures/actor.rb index f212de1..0ec57b1 100644 --- a/spec/fixtures/actor.rb +++ b/spec/fixtures/actor.rb @@ -55,6 +55,60 @@ class CamelCaseActorSerializer end end +class MethodFilteredActorSerializer < UserSerializer + set_type :actor + + attributes_filter :filtered_attributes_by_policy + + has_many( + :played_movies, + serializer: :movie, + links: :movie_urls, + if: ->(_object, params) { params[:conditionals_off].nil? } + ) do |object| + object.movies + end + + def self.filtered_attributes_by_policy(superset, record, params) + permit = params[:filter_attributes] + + case permit + when :all + superset + when nil, :none, [] + [] + else + superset.slice(*permit) + end + end +end + +class CallableFilteredActorSerializer < UserSerializer + set_type :actor + + attributes_filter do |superset, record, params| + permit = params[:filter_attributes] + + case permit + when :all + superset + when nil, :none, [] + [] + else + superset.slice(*permit) + end + end + + has_many( + :played_movies, + serializer: :movie, + links: :movie_urls, + if: ->(_object, params) { params[:conditionals_off].nil? } + ) do |object| + object.movies + end +end + class BadMovieSerializerActorSerializer < ActorSerializer has_many :played_movies, serializer: :bad, object_method_name: :movies end diff --git a/spec/fixtures/movie.rb b/spec/fixtures/movie.rb index e42966a..d963a03 100644 --- a/spec/fixtures/movie.rb +++ b/spec/fixtures/movie.rb @@ -126,6 +126,31 @@ class MovieSerializer < ::MovieSerializer end end +class MethodFilteredMovieSerializer < ::MovieSerializer + relationships_filter :filtered_by_something + + has_many( + :first_two_actors, + id_method_name: :uid + ) do |record, params| + record.actors.take(2) + end + + def self.filtered_by_something(superset, record, params) + return superset unless params[:limit_relationships] + + superset.slice(:actors, :creator) + end +end + +class CallableFilteredMovieSerializer < ::MovieSerializer + relationships_filter do |superset, record, params| + return superset unless params[:limit_relationships] + + superset.slice(:actors, :creator) + end +end + class CallableLinksMovieSerializer < ::MovieSerializer has_many( :first_two_actors, diff --git a/spec/integration/attributes_fields_spec.rb b/spec/integration/attributes_fields_spec.rb index d590aff..9319c5a 100644 --- a/spec/integration/attributes_fields_spec.rb +++ b/spec/integration/attributes_fields_spec.rb @@ -59,5 +59,44 @@ ) end end + + context 'with an attribute filter using a method permitting a few attributs only' do + let(:params) { { params: { filter_attributes: %i[last_name email] } } } + + let(:serialized) do + MethodFilteredActorSerializer.new(actor, params).serializable_hash.as_json + end + + it do + expect(serialized['data']) + .to have_jsonapi_attributes('last_name', 'email').exactly + end + end + + context 'with an attribute filter using a method permitting all' do + let(:params) { { params: { filter_attributes: :all } } } + + let(:serialized) do + MethodFilteredActorSerializer.new(actor, params).serializable_hash.as_json + end + + it do + expect(serialized['data']) + .to have_jsonapi_attributes('first_name', 'last_name', 'email').exactly + end + end + + context 'with an attribute filter using a block' do + let(:params) { { params: { filter_attributes: %i[last_name email] } } } + + let(:serialized) do + CallableFilteredActorSerializer.new(actor, params).serializable_hash.as_json + end + + it do + expect(serialized['data']) + .to have_jsonapi_attributes('last_name', 'email').exactly + end + end end end diff --git a/spec/integration/relationships_spec.rb b/spec/integration/relationships_spec.rb index ab261fd..1af5953 100644 --- a/spec/integration/relationships_spec.rb +++ b/spec/integration/relationships_spec.rb @@ -143,6 +143,30 @@ end end + context 'with a filter using a filter method' do + let(:params) { { params: { limit_relationships: true } } } + + let(:serialized) do + MethodFilteredMovieSerializer.new(movie, params).serializable_hash.as_json + end + + it do + expect(serialized.dig('data', 'relationships').keys).to match_array(%w[actors creator]) + end + end + + context 'with a filter using a filter block' do + let(:params) { { params: { limit_relationships: true } } } + + let(:serialized) do + CallableFilteredMovieSerializer.new(movie, params).serializable_hash.as_json + end + + it do + expect(serialized.dig('data', 'relationships').keys).to match_array(%w[actors creator]) + end + end + context 'with a callable as relationship links' do let(:serialized) do CallableLinksMovieSerializer.new(movie, params).serializable_hash.as_json From 5e174f4ca73e5da68c64c5fa1bc6d63739b7b287 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Fri, 5 Mar 2021 12:31:48 -0800 Subject: [PATCH 03/10] Update README.md Co-authored-by: Keith Gable --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9135274..9a6a287 100644 --- a/README.md +++ b/README.md @@ -568,7 +568,7 @@ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? } serializer.serializable_hash ``` -Just like with attributes, it might sometimes be more performant to reduce the number of relationships getting serialized in a single call rather than specifying and executing a single conditional Proc for every relationship. For this situation `relationships_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all relationships defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the relationships to serialize. It will be further reduced by an eventually provided fieldset. +Just like with attributes, it might sometimes be more performant to reduce the number of relationships getting serialized in a single call rather than specifying and executing a single conditional Proc for every relationship. For this situation, `relationships_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all relationships defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the relationships to serialize. It will be further reduced by an eventually provided fieldset. ```ruby class MovieSerializer From a5487fc342a0979111bfa6324a5d170ba03acd3a Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Fri, 5 Mar 2021 12:31:58 -0800 Subject: [PATCH 04/10] Update README.md Co-authored-by: Keith Gable --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a6a287..fa6e9fd 100644 --- a/README.md +++ b/README.md @@ -524,7 +524,7 @@ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? } serializer.serializable_hash ``` -Sometimes it might be more performant to reduce the number of attributes getting serialized in a single call rather than specifying and executing a conditional Proc for every attribute. For this situation `attributes_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all attributes defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the attributes to serialize. It will be further reduced by an eventually provided fieldset. +Sometimes it might be more performant to reduce the number of attributes getting serialized in a single call rather than specifying and executing a conditional Proc for every attribute. For this situation, `attributes_filter` can be used. It accepts both a method name representing a class method on the serializer class or a callable like a Proc. The class method or block provided receives three arguments. The first being the mapping of all attributes defined, the second is the object getting serialized and the last is the parameters passed to the serializer as the `params` option. The return value is then considered as starting point for the attributes to serialize. It will be further reduced by an eventually provided fieldset. ```ruby class MovieSerializer From 550c91d41a2085a9aec4ef5e17d91edc6caad478 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Fri, 5 Mar 2021 12:59:31 -0800 Subject: [PATCH 05/10] Raise an ArgumentError if both a method name an a block are provided to the attributes- or relationships-filter --- lib/fast_jsonapi/object_serializer.rb | 8 ++++++-- spec/integration/attributes_fields_spec.rb | 21 +++++++++++++++++++++ spec/integration/relationships_spec.rb | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index b6ccb6f..aee6120 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -301,11 +301,13 @@ def create_relationship(base_key, relationship_type, options, block) # The name of a class method used to filter the set of attributes. This method will receive the superset of attributes, # the current record getting serialized and the serializer parameters passed along. # - # @yieldparam filter_block [#call] + # @param block [#call] # If a block is provided instead of a method name, this is going to be called when building the attributes hash. # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized # and the serializer parameters. def attributes_filter(filter_method_name = nil, &block) + raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block_given? + self.attributes_filter_method = filter_method_name || block end @@ -317,11 +319,13 @@ def attributes_filter(filter_method_name = nil, &block) # The name of a class method used to filter the set of relationships. This method will receive the superset of relationships, # the current record getting serialized and the serializer parameters passed along. # - # @yieldparam filter_block [#call] + # @param block [#call] # If a block is provided instead of a method name, this is going to be called when building the relationships hash. # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized # and the serializer parameters. def relationships_filter(filter_method_name = nil, &block) + raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block_given? + self.relationships_filter_method = filter_method_name || block end diff --git a/spec/integration/attributes_fields_spec.rb b/spec/integration/attributes_fields_spec.rb index 9319c5a..249fbb0 100644 --- a/spec/integration/attributes_fields_spec.rb +++ b/spec/integration/attributes_fields_spec.rb @@ -98,5 +98,26 @@ .to have_jsonapi_attributes('last_name', 'email').exactly end end + + context 'with an attribute filter using both a method name and a block' do + let(:klass) do + Class.new do + include JSONAPI::Serializer + + set_id :uid + attributes :first_name, :last_name, :email + + attributes_filter :some_method do |superset, record, params| + [] + end + + def some_method + [] + end + end + end + + it { expect { klass }.to raise_error(ArgumentError, 'filter_method_name and block are mutually exclusive') } + end end end diff --git a/spec/integration/relationships_spec.rb b/spec/integration/relationships_spec.rb index 1af5953..b88bd85 100644 --- a/spec/integration/relationships_spec.rb +++ b/spec/integration/relationships_spec.rb @@ -167,6 +167,27 @@ end end + context 'with a relationships filter using both a method name and a block' do + let(:klass) do + Class.new do + include JSONAPI::Serializer + + set_id :uid + attributes :first_name, :last_name, :email + + relationships_filter :some_method do |superset, record, params| + [] + end + + def some_method + [] + end + end + end + + it { expect { klass }.to raise_error(ArgumentError, 'filter_method_name and block are mutually exclusive') } + end + context 'with a callable as relationship links' do let(:serialized) do CallableLinksMovieSerializer.new(movie, params).serializable_hash.as_json From 1bed4b61ee749a2ef65fbb44f10bd676cb5a9b43 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Fri, 5 Mar 2021 16:14:35 -0800 Subject: [PATCH 06/10] Fix typo --- lib/fast_jsonapi/serialization_core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 2831880..e913f8f 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -62,7 +62,7 @@ def attributes_hash(record, fieldset = nil, params = {}) # If something callable, the result of that callable. # # @param superset [Hash] - # The attributers or relationships to filter + # The attributes or relationships to filter # # @param record [Object] # The current record to get serialized From af3da5a8dd05c71c8bd548dcb59bd922b8cdf494 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Mon, 29 Mar 2021 15:48:15 -0700 Subject: [PATCH 07/10] Avoid a few Rubocop complaints --- spec/fixtures/actor.rb | 4 ++-- spec/fixtures/movie.rb | 10 +++++----- spec/integration/attributes_fields_spec.rb | 2 +- spec/integration/relationships_spec.rb | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/fixtures/actor.rb b/spec/fixtures/actor.rb index 0ec57b1..0d14b96 100644 --- a/spec/fixtures/actor.rb +++ b/spec/fixtures/actor.rb @@ -69,7 +69,7 @@ class MethodFilteredActorSerializer < UserSerializer object.movies end - def self.filtered_attributes_by_policy(superset, record, params) + def self.filtered_attributes_by_policy(superset, _record, params) permit = params[:filter_attributes] case permit @@ -86,7 +86,7 @@ def self.filtered_attributes_by_policy(superset, record, params) class CallableFilteredActorSerializer < UserSerializer set_type :actor - attributes_filter do |superset, record, params| + attributes_filter do |superset, _record, params| permit = params[:filter_attributes] case permit diff --git a/spec/fixtures/movie.rb b/spec/fixtures/movie.rb index d963a03..95384c0 100644 --- a/spec/fixtures/movie.rb +++ b/spec/fixtures/movie.rb @@ -132,11 +132,11 @@ class MethodFilteredMovieSerializer < ::MovieSerializer has_many( :first_two_actors, id_method_name: :uid - ) do |record, params| + ) do |record| record.actors.take(2) end - def self.filtered_by_something(superset, record, params) + def self.filtered_by_something(superset, _record, params) return superset unless params[:limit_relationships] superset.slice(:actors, :creator) @@ -144,7 +144,7 @@ def self.filtered_by_something(superset, record, params) end class CallableFilteredMovieSerializer < ::MovieSerializer - relationships_filter do |superset, record, params| + relationships_filter do |superset, _record, params| return superset unless params[:limit_relationships] superset.slice(:actors, :creator) @@ -155,10 +155,10 @@ class CallableLinksMovieSerializer < ::MovieSerializer has_many( :first_two_actors, id_method_name: :uid, - links: lambda do |record, params| + links: lambda do |record| { some: record.id, fancy: 'here' } end - ) do |record, params| + ) do |record| record.actors.take(2) end end diff --git a/spec/integration/attributes_fields_spec.rb b/spec/integration/attributes_fields_spec.rb index 249fbb0..f4e3f46 100644 --- a/spec/integration/attributes_fields_spec.rb +++ b/spec/integration/attributes_fields_spec.rb @@ -107,7 +107,7 @@ set_id :uid attributes :first_name, :last_name, :email - attributes_filter :some_method do |superset, record, params| + attributes_filter :some_method do |_superset, _record, _params| [] end diff --git a/spec/integration/relationships_spec.rb b/spec/integration/relationships_spec.rb index b88bd85..775d239 100644 --- a/spec/integration/relationships_spec.rb +++ b/spec/integration/relationships_spec.rb @@ -175,7 +175,7 @@ set_id :uid attributes :first_name, :last_name, :email - relationships_filter :some_method do |superset, record, params| + relationships_filter :some_method do |_superset, _record, _params| [] end From 4d75e0e0bfbdb260c7cb57401a8e72a3a5b21294 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Mon, 29 Mar 2021 15:52:19 -0700 Subject: [PATCH 08/10] Check the block directly instead of using block_given? --- lib/fast_jsonapi/object_serializer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index aee6120..a7326c2 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -306,7 +306,7 @@ def create_relationship(base_key, relationship_type, options, block) # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized # and the serializer parameters. def attributes_filter(filter_method_name = nil, &block) - raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block_given? + raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block self.attributes_filter_method = filter_method_name || block end @@ -324,7 +324,7 @@ def attributes_filter(filter_method_name = nil, &block) # The arguments to the block are the same as for the method: the superset of attributes, the record getting serialized # and the serializer parameters. def relationships_filter(filter_method_name = nil, &block) - raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block_given? + raise ArgumentError, 'filter_method_name and block are mutually exclusive' if filter_method_name && block self.relationships_filter_method = filter_method_name || block end From 20351f6e6ede531fff3c276be21f10f0d5d50095 Mon Sep 17 00:00:00 2001 From: Johannes Vetter Date: Mon, 29 Mar 2021 15:56:17 -0700 Subject: [PATCH 09/10] Apply another suggestion from Rubocop From b1655930c9ec9dd0b48dfb402f4b3ec08f0dd1c3 Mon Sep 17 00:00:00 2001 From: Tate Thurston Date: Fri, 14 Jun 2024 09:50:41 -0700 Subject: [PATCH 10/10] update revision number --- README.md | 18 ++++++++++++++++++ jsonapi-serializer.gemspec | 1 + lib/jsonapi/serializer/version.rb | 5 ++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa6e9fd..1a4881f 100644 --- a/README.md +++ b/README.md @@ -837,3 +837,21 @@ pull request creation processes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://contributor-covenant.org) code of conduct. + +### Publishing + +Releases are manual, performed locally on a developer's machine. Gems are published to Github Packages. A comprehesive outline of this process can be found here: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-rubygems-registry. + +1. Increment the `ART19_REVISION` in [lib/jsonapi/serializer/version.rb#L5](https://github.com/art19/jsonapi-serializer/blob/master/lib/jsonapi/serializer/version.rb#L5) + +2. Build the gem: + +``` +gem build jsonapi-serializer.gemspec +``` + +3. Publish your gem, replacing $VERSION with the gem version. You'll see the generated file after running `gem build` above, eg: 'jsonapi-serializer-2.2.0.1.gem'. + +``` +gem push --key github --host https://rubygems.pkg.github.com/art19 jsonapi-serializer-$VERSION.gem +``` diff --git a/jsonapi-serializer.gemspec b/jsonapi-serializer.gemspec index d8ccb4a..8be3df2 100644 --- a/jsonapi-serializer.gemspec +++ b/jsonapi-serializer.gemspec @@ -6,6 +6,7 @@ require 'jsonapi/serializer/version' Gem::Specification.new do |gem| gem.name = 'jsonapi-serializer' gem.version = JSONAPI::Serializer::VERSION + gem.metadata["allowed_push_host"] = 'https://rubygems.pkg.github.com/art19' gem.authors = ['JSON:API Serializer Community'] gem.email = '' diff --git a/lib/jsonapi/serializer/version.rb b/lib/jsonapi/serializer/version.rb index 667269e..3392831 100644 --- a/lib/jsonapi/serializer/version.rb +++ b/lib/jsonapi/serializer/version.rb @@ -1,5 +1,8 @@ module JSONAPI module Serializer - VERSION = '2.2.0'.freeze + # ART19 maintains a fork with patches applied on top of the upstream gem. + # We publish our fork with a revision number appended to the upstream version. + ART19_REVISION = '1'.freeze + VERSION = "2.2.0.#{ART19_REVISION}".freeze end end