From ee76e0c69b31528a223c57830ed178969b29b80b Mon Sep 17 00:00:00 2001 From: Shishir Kakaraddi Date: Sat, 3 Nov 2018 12:11:56 -0700 Subject: [PATCH 01/30] bump up version to 1.5 --- lib/fast_jsonapi/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/version.rb b/lib/fast_jsonapi/version.rb index c9c4c7ab..12442c14 100644 --- a/lib/fast_jsonapi/version.rb +++ b/lib/fast_jsonapi/version.rb @@ -1,3 +1,3 @@ module FastJsonapi - VERSION = "1.4" + VERSION = "1.5" end From a160d6746f2f35e157b42cd6d797a74bed1405f0 Mon Sep 17 00:00:00 2001 From: Manoj M J Date: Thu, 15 Nov 2018 13:40:18 +0530 Subject: [PATCH 02/30] Fix Documentation of Meta Per Resource Some part of the documentation for this got removed during last merge, so fixing it. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 53c8bfa7..77cbf3bf 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,12 @@ This will create a `self` reference for the relationship, and a `related` link f ### 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. + + ```ruby +class MovieSerializer + include FastJsonapi::ObjectSerializer + meta do |movie| { years_since_release: Date.current.year - movie.year From e0228dacdca7599cf4c7a83cc37d28568eef9a09 Mon Sep 17 00:00:00 2001 From: Manoj M J Date: Wed, 14 Nov 2018 19:58:14 +0530 Subject: [PATCH 03/30] Add documentation on how to use helper methods in serializers --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index 77cbf3bf..0ef40239 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Fast JSON API serialized 250 records in 3.01 ms * [Conditional Attributes](#conditional-attributes) * [Conditional Relationships](#conditional-relationships) * [Sparse Fieldsets](#sparse-fieldsets) + * [Using helper methods](#using-helper-methods) * [Contributing](#contributing) @@ -449,6 +450,68 @@ serializer = MovieSerializer.new(movie, { fields: { movie: [:name] } }) serializer.serializable_hash ``` +### Using helper methods + +You can mix-in code from another ruby module into your serializer class to reuse functions across your app. + +Since a serializer is evaluated in a the context of a `class` rather than an `instance` of a class, you need to make sure that your methods act as `class` methods when mixed in. + + +##### Using ActiveSupport::Concern + +``` ruby + +module AvatarHelper + extend ActiveSupport::Concern + + class_methods do + def avatar_url(user) + user.image.url + end + end +end + +class UserSerializer + include FastJsonapi::ObjectSerializer + + include AvatarHelper # mixes in your helper method as class method + + set_type :user + + attributes :name, :email + + attribute :avatar do |user| + avatar_url(user) + end +end + +``` + +##### Using Plain Old Ruby + +``` ruby +module AvatarHelper + def avatar_url(user) + user.image.url + end +end + +class UserSerializer + include FastJsonapi::ObjectSerializer + + extend AvatarHelper # mixes in your helper method as class method + + set_type :user + + attributes :name, :email + + attribute :avatar do |user| + avatar_url(user) + end +end + +``` + ### Customizable Options Option | Purpose | Example From 4b1c2fbae0df429b56ed3584121019dbd045bcf7 Mon Sep 17 00:00:00 2001 From: David Pickart Date: Mon, 10 Jun 2019 14:51:12 -0500 Subject: [PATCH 04/30] Include `data` key when lazy-loaded relationships are specified with `includes` --- lib/fast_jsonapi/object_serializer.rb | 4 ++-- lib/fast_jsonapi/relationship.rb | 4 ++-- lib/fast_jsonapi/serialization_core.rb | 17 +++++++++-------- ...bject_serializer_relationship_links_spec.rb | 18 ++++++++++++++++++ spec/lib/serialization_core_spec.rb | 2 +- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index c161fb6b..30212c03 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -43,7 +43,7 @@ def hash_for_one_record return serializable_hash unless @resource - serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @params) + serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @includes, @params) serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present? serializable_hash end @@ -55,7 +55,7 @@ def hash_for_collection included = [] fieldset = @fieldsets[self.class.record_type.to_sym] @resource.each do |record| - data << self.class.record_hash(record, fieldset, @params) + data << self.class.record_hash(record, fieldset, @includes, @params) included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present? end diff --git a/lib/fast_jsonapi/relationship.rb b/lib/fast_jsonapi/relationship.rb index 7a038de7..da5487cd 100644 --- a/lib/fast_jsonapi/relationship.rb +++ b/lib/fast_jsonapi/relationship.rb @@ -34,12 +34,12 @@ def initialize( @lazy_load_data = lazy_load_data end - def serialize(record, serialization_params, output_hash) + def serialize(record, included, serialization_params, output_hash) if include_relationship?(record, serialization_params) empty_case = relationship_type == :has_many ? [] : nil output_hash[key] = {} - unless lazy_load_data + unless (lazy_load_data && !included) output_hash[key][:data] = ids_hash_from_record_and_relationship(record, serialization_params) || empty_case end add_links_hash(record, serialization_params, output_hash) if links.present? diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 845aee75..298dd0c0 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -51,13 +51,14 @@ def attributes_hash(record, fieldset = nil, params = {}) end end - def relationships_hash(record, relationships = nil, fieldset = nil, params = {}) + def relationships_hash(record, relationships = nil, fieldset = nil, includes_list = nil, params = {}) relationships = relationships_to_serialize if relationships.nil? relationships = relationships.slice(*fieldset) if fieldset.present? relationships = {} if fieldset == [] - relationships.each_with_object({}) do |(_k, relationship), hash| - relationship.serialize(record, params, hash) + relationships.each_with_object({}) do |(key, relationship), hash| + included = includes_list.present? && includes_list.include?(key) + relationship.serialize(record, included, params, hash) end end @@ -65,23 +66,23 @@ def meta_hash(record, params = {}) meta_to_serialize.call(record, params) end - def record_hash(record, fieldset, params = {}) + def record_hash(record, fieldset, includes_list, params = {}) if cached record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do temp_hash = id_hash(id_from_record(record), record_type, true) temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present? temp_hash[:relationships] = {} - temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present? + temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, includes_list, params) if cachable_relationships_to_serialize.present? temp_hash[:links] = links_hash(record, params) if data_links.present? temp_hash end - record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, params)) if uncachable_relationships_to_serialize.present? + record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, includes_list, params)) if uncachable_relationships_to_serialize.present? record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present? record_hash else record_hash = id_hash(id_from_record(record), record_type, true) record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present? - record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present? + record_hash[:relationships] = relationships_hash(record, nil, fieldset, includes_list, params) if relationships_to_serialize.present? record_hash[:links] = links_hash(record, params) if data_links.present? record_hash[:meta] = meta_hash(record, params) if meta_to_serialize.present? record_hash @@ -149,7 +150,7 @@ def get_included_records(record, includes_list, known_included_objects, fieldset known_included_objects[code] = inc_obj - included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], params) + included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], includes_list, params) end end end diff --git a/spec/lib/object_serializer_relationship_links_spec.rb b/spec/lib/object_serializer_relationship_links_spec.rb index 8c2f2725..66281308 100644 --- a/spec/lib/object_serializer_relationship_links_spec.rb +++ b/spec/lib/object_serializer_relationship_links_spec.rb @@ -67,5 +67,23 @@ class LazyLoadingMovieSerializer < MovieSerializer expect(actor_hash).not_to have_key(:data) end end + + context "included lazy loaded relationships" do + before(:context) do + class LazyLoadingMovieSerializer < MovieSerializer + has_many :actors, lazy_load_data: true, links: { + related: :actors_relationship_url + } + end + end + + let(:serializer) { LazyLoadingMovieSerializer.new(movie, include: [:actors]) } + let(:actor_hash) { hash[:data][:relationships][:actors] } + + it "includes the :data key" do + expect(actor_hash).to be_present + expect(actor_hash).to have_key(:data) + end + end end end diff --git a/spec/lib/serialization_core_spec.rb b/spec/lib/serialization_core_spec.rb index adf33dfc..0d8d0117 100644 --- a/spec/lib/serialization_core_spec.rb +++ b/spec/lib/serialization_core_spec.rb @@ -52,7 +52,7 @@ end it 'returns correct hash when record_hash is called' do - record_hash = MovieSerializer.send(:record_hash, movie, nil) + record_hash = MovieSerializer.send(:record_hash, movie, nil, nil) expect(record_hash[:id]).to eq movie.id.to_s expect(record_hash[:type]).to eq MovieSerializer.record_type expect(record_hash).to have_key(:attributes) if MovieSerializer.attributes_to_serialize.present? From 3df917f4073bc3752c2078a84099bc85d5b4f8b9 Mon Sep 17 00:00:00 2001 From: Srinivas R Date: Tue, 11 Jun 2019 14:59:22 -0700 Subject: [PATCH 05/30] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 0ef40239..df86d288 100644 --- a/README.md +++ b/README.md @@ -579,9 +579,3 @@ To run tests only performance tests: ```bash rspec spec --tag performance:true ``` - -### We're Hiring! - -Join the Netflix Studio Engineering team and help us build gems like this! - -* [Senior Ruby Engineer](https://jobs.netflix.com/jobs/864893) From d64b1b5f4f193b37aa108b1bf555cb7d934bfb07 Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Fri, 4 Oct 2019 14:42:12 +0200 Subject: [PATCH 06/30] Test against Ruby 2.6 and fix Travis (#3) * Test against Ruby 2.6 * Enforce bundler 1.17.3 --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 00b98f79..7025fd1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,12 @@ rvm: - 2.3.6 - 2.4.3 - 2.5.0 + - 2.6 + +before_install: + - "travis_retry gem update --system 2.7.9" + - "travis_retry gem install bundler -v '1.17.3'" +install: BUNDLER_VERSION=1.17.3 bundle install --path=vendor/bundle --retry=3 --jobs=3 + script: - bundle exec rspec From 1373eb436c90d4e26d6b1d3777ac2b8a7233c5ae Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:09:25 +0200 Subject: [PATCH 07/30] Compute remaining_items once --- lib/fast_jsonapi/serialization_core.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 845aee75..924b5dab 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -119,6 +119,8 @@ def get_included_records(record, includes_list, known_included_objects, fieldset includes_list.sort.each_with_object([]) do |include_item, included_records| items = parse_include_item(include_item) + remaining_items = remaining_items(items) + items.each do |item| next unless relationships_to_serialize && relationships_to_serialize[item] relationship_item = relationships_to_serialize[item] @@ -139,8 +141,8 @@ def get_included_records(record, includes_list, known_included_objects, fieldset serializer = self.compute_serializer_name(inc_obj.class.name.demodulize.to_sym).to_s.constantize end - if remaining_items(items) - serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params) + if remaining_items.present? + serializer_records = serializer.get_included_records(inc_obj, remaining_items, known_included_objects, fieldsets, params) included_records.concat(serializer_records) unless serializer_records.empty? end From 2b6c81692fb8a9d78bfd315ebc4b283d34ebe3f2 Mon Sep 17 00:00:00 2001 From: Jo Potts Date: Fri, 4 Oct 2019 17:39:34 +0100 Subject: [PATCH 08/30] Allow relationship links to be declared as object method (#2) * Allow relationship links to be declared as object method * Relationship links note added to readme --- README.md | 6 +++++ lib/fast_jsonapi/relationship.rb | 8 +++++-- ...ject_serializer_relationship_links_spec.rb | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f36a22a3..ff42cac9 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,12 @@ class MovieSerializer end ``` +Relationship links can also be configured to be defined as a method on the object. + +```ruby + has_many :actors, links: :actor_relationship_links +``` + This will create a `self` reference for the relationship, and a `related` link for loading the actors relationship later. NB: This will not automatically disable loading the data in the relationship, you'll need to do that using the `lazy_load_data` option: ```ruby diff --git a/lib/fast_jsonapi/relationship.rb b/lib/fast_jsonapi/relationship.rb index 7a038de7..f8a9bfd7 100644 --- a/lib/fast_jsonapi/relationship.rb +++ b/lib/fast_jsonapi/relationship.rb @@ -104,8 +104,12 @@ def fetch_id(record, params) end def add_links_hash(record, params, output_hash) - output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash| - Link.new(key: key, method: method).serialize(record, params, hash)\ + if links.is_a?(Symbol) + output_hash[key][:links] = record.public_send(links) + else + output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash| + Link.new(key: key, method: method).serialize(record, params, hash)\ + end end end diff --git a/spec/lib/object_serializer_relationship_links_spec.rb b/spec/lib/object_serializer_relationship_links_spec.rb index 8c2f2725..e0d48483 100644 --- a/spec/lib/object_serializer_relationship_links_spec.rb +++ b/spec/lib/object_serializer_relationship_links_spec.rb @@ -67,5 +67,28 @@ class LazyLoadingMovieSerializer < MovieSerializer expect(actor_hash).not_to have_key(:data) end end + + context "relationship links defined by a method on the object" do + before(:context) do + class Movie + def relationship_links + { self: "http://movies.com/#{id}/relationships/actors" } + end + end + + class LinksPassingMovieSerializer < MovieSerializer + has_many :actors, links: :relationship_links + end + end + + let(:serializer) { LinksPassingMovieSerializer.new(movie) } + let(:links) { hash[:data][:relationships][:actors][:links] } + let(:relationship_url) { "http://movies.com/#{movie.id}/relationships/actors" } + + it "generates relationship links in the object" do + expect(links).to be_present + expect(links[:self]).to eq(relationship_url) + end + end end end From 9ec89d4cf5c4c3c825ee737ccc6af2c79f2bd9ac Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:11:56 +0200 Subject: [PATCH 09/30] Map split include_item in-place --- lib/fast_jsonapi/serialization_core.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 924b5dab..55994a0b 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -102,7 +102,8 @@ def to_json(payload) def parse_include_item(include_item) return [include_item.to_sym] unless include_item.to_s.include?('.') - include_item.to_s.split('.').map { |item| item.to_sym } + + include_item.to_s.split('.').map!(&:to_sym) end def remaining_items(items) From dce1faf1e2a16950f102bc27e004a4bacf4d6304 Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:09:55 +0200 Subject: [PATCH 10/30] Move transforms mapping to constant --- lib/fast_jsonapi/object_serializer.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index c161fb6b..c6861cdb 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -17,6 +17,12 @@ module ObjectSerializer SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash' SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json' + TRANSFORMS_MAPPING = { + camel: :camelize, + camel_lower: [:camelize, :lower], + dash: :dasherize, + underscore: :underscore + }.freeze included do # Set record_type based on the name of the serializer class @@ -137,13 +143,7 @@ def reflected_record_type end def set_key_transform(transform_name) - mapping = { - camel: :camelize, - camel_lower: [:camelize, :lower], - dash: :dasherize, - underscore: :underscore - } - self.transform_method = mapping[transform_name.to_sym] + self.transform_method = TRANSFORMS_MAPPING[transform_name.to_sym] # ensure that the record type is correctly transformed if record_type From 0dc332dc325a1d985aa3b7c8aa2ab13fde5589bd Mon Sep 17 00:00:00 2001 From: Kevin Pheasey Date: Fri, 4 Oct 2019 12:46:53 -0400 Subject: [PATCH 11/30] Create CHANGELOG.md --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..65b8b371 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added +- Allow relationship links to be delcared as a method ([#2](https://github.com/fast-jsonapi/fast_jsonapi/pull/2)) +- Test against Ruby 2.6 ([#1](https://github.com/fast-jsonapi/fast_jsonapi/pull/1)) +### Changed +- Optimize SerializationCore.get_included_records calculates remaining_items only once ([#4](https://github.com/fast-jsonapi/fast_jsonapi/pull/4)) +- Optimize SerializtionCore.parse_include_item by mapping in place ([#5](https://github.com/fast-jsonapi/fast_jsonapi/pull/5)) +- Define ObjectSerializer.set_key_transform mapping as a constant ([#7](https://github.com/fast-jsonapi/fast_jsonapi/pull/7)) + +[Unreleased]: https://github.com/fast-jsonapi/fast_jsonapi/compare/dev...HEAD From 9e83c1e0a5a70c134f50fb7c79ca4db469ceb45d Mon Sep 17 00:00:00 2001 From: "Jessie A. Young" Date: Thu, 21 Mar 2019 15:55:36 -0700 Subject: [PATCH 12/30] Highlight that this is for JSON:API spec only * The link was already there but I skipped over it on my first read. This update makes the fact more prominent. * I was testing to see if we wanted to move from AM Serializers to fast_jsonapi but our API is not written according to the JSON:API spec so, after converting one serializer over, I learned that this would not work for me. * This update might save someone in my position the ~30 mins or so it takes to bundle and write a serializer in the future. :) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index df86d288..a8b87998 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ A lightning fast [JSON:API](http://jsonapi.org/) serializer for Ruby Objects. +Note: this gem deals only with implementing the JSON:API spec. If your API +responses are not formatted according to the JSON:API spec, this library will +not work for you. + # Performance Comparison We compare serialization times with Active Model Serializer as part of RSpec performance tests included on this library. We want to ensure that with every change on this library, serialization time is at least `25 times` faster than Active Model Serializers on up to current benchmark of 1000 records. Please read the [performance document](https://github.com/Netflix/fast_jsonapi/blob/master/performance_methodology.md) for any questions related to methodology. From f0142d948c62b2552a92907597224a34d2471525 Mon Sep 17 00:00:00 2001 From: Csaba Apagyi Date: Mon, 4 Mar 2019 13:18:03 +0100 Subject: [PATCH 13/30] Fix formatting of `set_id` example in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8b87998..3c4f846a 100644 --- a/README.md +++ b/README.md @@ -522,7 +522,7 @@ Option | Purpose | Example ------------ | ------------- | ------------- set_type | Type name of Object | ```set_type :movie ``` key | Key of Object | ```belongs_to :owner, key: :user ``` -set_id | ID of Object | ```set_id :owner_id ``` or ```set_id { |record| "#{record.name.downcase}-#{record.id}" }``` +set_id | ID of Object | ```set_id :owner_id ``` or ```set_id { \|record\| "#{record.name.downcase}-#{record.id}" }``` cache_options | Hash to enable caching and set cache length | ```cache_options enabled: true, cache_length: 12.hours, race_condition_ttl: 10.seconds``` id_method_name | Set custom method name to get ID of an object (If block is provided for the relationship, `id_method_name` is invoked on the return value of the block instead of the resource object) | ```has_many :locations, id_method_name: :place_ids ``` object_method_name | Set custom method name to get related objects | ```has_many :locations, object_method_name: :places ``` From 209c925723fd14689399def9774f1ee5ecfbd485 Mon Sep 17 00:00:00 2001 From: Charalampos Aristomenopoulos Date: Fri, 22 Feb 2019 12:08:36 +0200 Subject: [PATCH 14/30] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c4f846a..6a056dc7 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ json_string = MovieSerializer.new([movie, movie], options).serialized_json You can use `is_collection` option to have better control over collection serialization. -If this option is not provided or `nil` autedetect logic is used to try understand +If this option is not provided or `nil` autodetect logic is used to try understand if provided resource is a single object or collection. Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but From 021db276050defe00a3d36b5911d0426ec5491f8 Mon Sep 17 00:00:00 2001 From: Maxime Orefice Date: Mon, 18 Feb 2019 19:32:30 -0500 Subject: [PATCH 15/30] Update Readme Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a056dc7..5c5c77b0 100644 --- a/README.md +++ b/README.md @@ -545,7 +545,7 @@ Skylight relies on `ActiveSupport::Notifications` to track these two core method require 'fast_jsonapi/instrumentation' ``` -The two instrumented notifcations are supplied by these two constants: +The two instrumented notifications are supplied by these two constants: * `FastJsonapi::ObjectSerializer::SERIALIZABLE_HASH_NOTIFICATION` * `FastJsonapi::ObjectSerializer::SERIALIZED_JSON_NOTIFICATION` From 1a407c00304595693374e03f5dc2aa197e1ed9e6 Mon Sep 17 00:00:00 2001 From: Danil Pismenny Date: Fri, 21 Dec 2018 18:41:55 +0300 Subject: [PATCH 16/30] [#365] Support frozen array in option --- lib/fast_jsonapi/object_serializer.rb | 2 +- spec/lib/object_serializer_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index b8a24183..e767135d 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -86,7 +86,7 @@ def process_options(options) raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash) if options[:include].present? - @includes = options[:include].delete_if(&:blank?).map(&:to_sym) + @includes = options[:include].reject(&:blank?).map(&:to_sym) self.class.validate_includes!(@includes) end end diff --git a/spec/lib/object_serializer_spec.rb b/spec/lib/object_serializer_spec.rb index ef755ccc..74bb2f41 100644 --- a/spec/lib/object_serializer_spec.rb +++ b/spec/lib/object_serializer_spec.rb @@ -473,6 +473,16 @@ class BlahSerializer options[:include] = [:actors] expect(serializable_hash['included']).to be_blank end + + end + end + + context 'when include has frozen array' do + let(:options) { { include: [:actors].freeze }} + let(:json) { MovieOptionalRelationshipSerializer.new(movie, options).serialized_json } + + it 'does not raise and error' do + expect(json['included']).to_not be_blank end end From 21ae4aaa0a0e707d11643b9da67a022f49dcbc3e Mon Sep 17 00:00:00 2001 From: Matt Eddy Date: Mon, 8 Oct 2018 11:45:46 -0700 Subject: [PATCH 17/30] Allow fieldsets to specify no attributes/relationships --- lib/fast_jsonapi/serialization_core.rb | 3 +++ spec/lib/object_serializer_fields_spec.rb | 33 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 200af9b3..845aee75 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -44,6 +44,8 @@ def links_hash(record, params = {}) def attributes_hash(record, fieldset = nil, params = {}) attributes = attributes_to_serialize attributes = attributes.slice(*fieldset) if fieldset.present? + attributes = {} if fieldset == [] + attributes.each_with_object({}) do |(_k, attribute), hash| attribute.serialize(record, params, hash) end @@ -52,6 +54,7 @@ def attributes_hash(record, fieldset = nil, params = {}) def relationships_hash(record, relationships = nil, fieldset = nil, params = {}) relationships = relationships_to_serialize if relationships.nil? relationships = relationships.slice(*fieldset) if fieldset.present? + relationships = {} if fieldset == [] relationships.each_with_object({}) do |(_k, relationship), hash| relationship.serialize(record, params, hash) diff --git a/spec/lib/object_serializer_fields_spec.rb b/spec/lib/object_serializer_fields_spec.rb index 913ba83b..697f209d 100644 --- a/spec/lib/object_serializer_fields_spec.rb +++ b/spec/lib/object_serializer_fields_spec.rb @@ -22,6 +22,18 @@ expect(hash[:data][:relationships].keys.sort).to eq %i[actors advertising_campaign] end + it 'returns no fields when none are specified' do + hash = MovieSerializer.new(movie, fields: { movie: [] }).serializable_hash + + expect(hash[:data][:attributes].keys).to eq [] + end + + it 'returns no relationships when none are specified' do + hash = MovieSerializer.new(movie, fields: { movie: [] }).serializable_hash + + expect(hash[:data][:relationships].keys).to eq [] + end + it 'only returns specified fields for included relationships' do hash = MovieSerializer.new(movie, fields: fields, include: %i[actors]).serializable_hash @@ -45,4 +57,25 @@ expect(hash[:included][3][:relationships].keys.sort).to eq %i[movie] end + + context 'with no included fields specified' do + let(:fields) do + { + movie: %i[name actors advertising_campaign], + actor: [] + } + end + + it 'returns no fields for included relationships when none are specified' do + hash = MovieSerializer.new(movie, fields: fields, include: %i[actors advertising_campaign]).serializable_hash + + expect(hash[:included][2][:attributes].keys).to eq [] + end + + it 'returns no relationships when none are specified' do + hash = MovieSerializer.new(movie, fields: fields, include: %i[actors advertising_campaign]).serializable_hash + + expect(hash[:included][2][:relationships].keys).to eq [] + end + end end From 8d8e5c305912a32a5e0a077950783e4c4d1c04e3 Mon Sep 17 00:00:00 2001 From: Daniel Illi Date: Thu, 22 Nov 2018 10:58:42 +0100 Subject: [PATCH 18/30] Fix error on defining anonymous serializer class, fixes #353 --- lib/fast_jsonapi/object_serializer.rb | 2 +- spec/lib/object_serializer_spec.rb | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index e767135d..d142a04a 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -130,7 +130,7 @@ def reflected_record_type return @reflected_record_type if defined?(@reflected_record_type) @reflected_record_type ||= begin - if self.name.end_with?('Serializer') + if self.name && self.name.end_with?('Serializer') self.name.split('::').last.chomp('Serializer').underscore.to_sym end end diff --git a/spec/lib/object_serializer_spec.rb b/spec/lib/object_serializer_spec.rb index 74bb2f41..c86ce7c9 100644 --- a/spec/lib/object_serializer_spec.rb +++ b/spec/lib/object_serializer_spec.rb @@ -314,6 +314,15 @@ class BlahBlahSerializer expect(BlahBlahSerializer.record_type).to be :blah_blah end + it 'should set default_type for a namespaced serializer' do + module V1 + class BlahSerializer + include FastJsonapi::ObjectSerializer + end + end + expect(V1::BlahSerializer.record_type).to be :blah + end + it 'shouldnt set default_type for a serializer that doesnt follow convention' do class BlahBlahSerializerBuilder include FastJsonapi::ObjectSerializer @@ -321,13 +330,11 @@ class BlahBlahSerializerBuilder expect(BlahBlahSerializerBuilder.record_type).to be_nil end - it 'should set default_type for a namespaced serializer' do - module V1 - class BlahSerializer - include FastJsonapi::ObjectSerializer - end + it 'shouldnt set default_type for an anonymous serializer' do + serializer_class = Class.new do + include FastJsonapi::ObjectSerializer end - expect(V1::BlahSerializer.record_type).to be :blah + expect(serializer_class.record_type).to be_nil end end From 267b706366f5b6cf8e05efebda09d5c426ec6bc9 Mon Sep 17 00:00:00 2001 From: Daniel Duke Date: Fri, 4 Jan 2019 09:12:17 -0800 Subject: [PATCH 19/30] validate all include items instead of just the first --- lib/fast_jsonapi/object_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index d142a04a..c161fb6b 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -299,7 +299,7 @@ def link(link_name, link_method_name = nil, &block) def validate_includes!(includes) return if includes.blank? - includes.detect do |include_item| + includes.each do |include_item| klass = self parse_include_item(include_item).each do |parsed_include| relationships_to_serialize = klass.relationships_to_serialize || {} From 5767664c8a7890a3bdb2e80d0437e8c042e0985f Mon Sep 17 00:00:00 2001 From: Daniel Duke Date: Fri, 4 Jan 2019 09:12:58 -0800 Subject: [PATCH 20/30] add specs for multiple include options --- spec/lib/object_serializer_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/lib/object_serializer_spec.rb b/spec/lib/object_serializer_spec.rb index c86ce7c9..42db8f00 100644 --- a/spec/lib/object_serializer_spec.rb +++ b/spec/lib/object_serializer_spec.rb @@ -142,12 +142,25 @@ expect { MovieSerializer.new([movie, movie], options).serializable_hash }.to raise_error(ArgumentError) end + it 'returns errors when serializing with non-existent and existent includes keys' do + options = {} + options[:meta] = { total: 2 } + options[:include] = [:actors, :blah_blah] + expect { MovieSerializer.new([movie, movie], options).serializable_hash }.to raise_error(ArgumentError) + end + it 'does not throw an error with non-empty string array includes key' do options = {} options[:include] = ['actors'] expect { MovieSerializer.new(movie, options) }.not_to raise_error end + it 'does not throw an error with non-empty string array includes keys' do + options = {} + options[:include] = ['actors', 'owner'] + expect { MovieSerializer.new(movie, options) }.not_to raise_error + end + it 'returns keys when serializing with empty string/nil array includes key' do options = {} options[:meta] = { total: 2 } From b9a86a002acabfde9fcea76c5586924e2f5fc3c4 Mon Sep 17 00:00:00 2001 From: iwsksky Date: Fri, 21 Dec 2018 03:44:42 +0900 Subject: [PATCH 21/30] pass array of unique movies to serializer --- README.md | 4 ++-- .../as_notifications_negative_spec.rb | 3 ++- .../instrumentation/as_notifications_spec.rb | 3 ++- spec/lib/object_serializer_caching_spec.rb | 3 ++- .../lib/object_serializer_class_methods_spec.rb | 6 +++--- spec/lib/object_serializer_spec.rb | 17 +++++++++++------ 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5c5c77b0..ec817c4d 100644 --- a/README.md +++ b/README.md @@ -308,7 +308,7 @@ options[:links] = { prev: '...' } options[:include] = [:actors, :'actors.agency', :'actors.agency.state'] -MovieSerializer.new([movie, movie], options).serialized_json +MovieSerializer.new(movies, options).serialized_json ``` ### Collection Serialization @@ -320,7 +320,7 @@ options[:links] = { next: '...', prev: '...' } -hash = MovieSerializer.new([movie, movie], options).serializable_hash +hash = MovieSerializer.new(movies, options).serializable_hash json_string = MovieSerializer.new([movie, movie], options).serialized_json ``` diff --git a/spec/lib/instrumentation/as_notifications_negative_spec.rb b/spec/lib/instrumentation/as_notifications_negative_spec.rb index 6912ed49..de144dea 100644 --- a/spec/lib/instrumentation/as_notifications_negative_spec.rb +++ b/spec/lib/instrumentation/as_notifications_negative_spec.rb @@ -10,7 +10,8 @@ options[:meta] = { total: 2 } options[:include] = [:actors] - @serializer = MovieSerializer.new([movie, movie], options) + movies = build_movies(2) + @serializer = MovieSerializer.new(movies, options) end context 'serializable_hash' do diff --git a/spec/lib/instrumentation/as_notifications_spec.rb b/spec/lib/instrumentation/as_notifications_spec.rb index f7769ffe..e4993bb6 100644 --- a/spec/lib/instrumentation/as_notifications_spec.rb +++ b/spec/lib/instrumentation/as_notifications_spec.rb @@ -24,7 +24,8 @@ options[:meta] = { total: 2 } options[:include] = [:actors] - @serializer = MovieSerializer.new([movie, movie], options) + movies = build_movies(2) + @serializer = MovieSerializer.new(movies, options) end context 'serializable_hash' do diff --git a/spec/lib/object_serializer_caching_spec.rb b/spec/lib/object_serializer_caching_spec.rb index 8e455d49..2f58cd16 100644 --- a/spec/lib/object_serializer_caching_spec.rb +++ b/spec/lib/object_serializer_caching_spec.rb @@ -16,7 +16,8 @@ options[:links] = { self: 'self' } options[:include] = [:actors] - serializable_hash = CachingMovieSerializer.new([movie, movie], options).serializable_hash + movies = build_movies(2) + serializable_hash = CachingMovieSerializer.new(movies, options).serializable_hash expect(serializable_hash[:data].length).to eq 2 expect(serializable_hash[:data][0][:relationships].length).to eq 3 diff --git a/spec/lib/object_serializer_class_methods_spec.rb b/spec/lib/object_serializer_class_methods_spec.rb index 205f6452..6fe4718b 100644 --- a/spec/lib/object_serializer_class_methods_spec.rb +++ b/spec/lib/object_serializer_class_methods_spec.rb @@ -213,7 +213,7 @@ end context 'when an array of records is given' do - let(:resource) { [movie, movie] } + let(:resource) { build_movies(2) } it 'returns correct hash which id equals owner_id' do expect(serializable_hash[:data][0][:id].to_i).to eq movie.owner_id @@ -240,7 +240,7 @@ end context 'when an array of records is given' do - let(:resource) { [movie, movie] } + let(:resource) { build_movies(2) } it 'returns correct hash which id equals movie-id' do expect(serializable_hash[:data][0][:id]).to eq "movie-#{movie.owner_id}" @@ -403,7 +403,7 @@ def year_since_release_calculator(release_year) end describe '#key_transform' do - subject(:hash) { movie_serializer_class.new([movie, movie], include: [:movie_type]).serializable_hash } + subject(:hash) { movie_serializer_class.new(build_movies(2), include: [:movie_type]).serializable_hash } let(:movie_serializer_class) { "#{key_transform}_movie_serializer".classify.constantize } diff --git a/spec/lib/object_serializer_spec.rb b/spec/lib/object_serializer_spec.rb index 42db8f00..dc846e6c 100644 --- a/spec/lib/object_serializer_spec.rb +++ b/spec/lib/object_serializer_spec.rb @@ -10,7 +10,8 @@ options[:meta] = { total: 2 } options[:links] = { self: 'self' } options[:include] = [:actors] - serializable_hash = MovieSerializer.new([movie, movie], options).serializable_hash + movies = build_movies(2) + serializable_hash = MovieSerializer.new(movies, options).serializable_hash expect(serializable_hash[:data].length).to eq 2 expect(serializable_hash[:data][0][:relationships].length).to eq 4 @@ -58,7 +59,8 @@ it 'returns correct number of records when serialized_json is called for an array' do options = {} options[:meta] = { total: 2 } - json = MovieSerializer.new([movie, movie], options).serialized_json + movies = build_movies(2) + json = MovieSerializer.new(movies, options).serialized_json serializable_hash = JSON.parse(json) expect(serializable_hash['data'].length).to eq 2 expect(serializable_hash['meta']).to be_instance_of(Hash) @@ -124,7 +126,8 @@ end it 'returns multiple records' do - json_hash = MovieSerializer.new([movie, movie]).as_json + movies = build_movies(2) + json_hash = MovieSerializer.new(movies).as_json expect(json_hash['data'].length).to eq 2 end @@ -139,7 +142,8 @@ options = {} options[:meta] = { total: 2 } options[:include] = [:blah_blah] - expect { MovieSerializer.new([movie, movie], options).serializable_hash }.to raise_error(ArgumentError) + movies = build_movies(2) + expect { MovieSerializer.new(movies, options).serializable_hash }.to raise_error(ArgumentError) end it 'returns errors when serializing with non-existent and existent includes keys' do @@ -165,9 +169,10 @@ options = {} options[:meta] = { total: 2 } options[:include] = [''] - expect(MovieSerializer.new([movie, movie], options).serializable_hash.keys).to eq [:data, :meta] + movies = build_movies(2) + expect(MovieSerializer.new(movies, options).serializable_hash.keys).to eq [:data, :meta] options[:include] = [nil] - expect(MovieSerializer.new([movie, movie], options).serializable_hash.keys).to eq [:data, :meta] + expect(MovieSerializer.new(movies, options).serializable_hash.keys).to eq [:data, :meta] end end From e68dbee80660b8fd548f2b0ed6bacf2e25cd6e76 Mon Sep 17 00:00:00 2001 From: iwsksky Date: Tue, 22 Jan 2019 01:30:21 +0900 Subject: [PATCH 22/30] update document/use let statement --- README.md | 13 ++++++++++++- spec/lib/object_serializer_spec.rb | 7 ++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ec817c4d..461cf2bf 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,17 @@ movie.actor_ids = [1, 2, 3] movie.owner_id = 3 movie.movie_type_id = 1 movie + +movies = + 2.times.map do |i| + m = Movie.new + m.id = i + 1 + m.name = "test movie #{i}" + m.actor_ids = [1, 2, 3] + m.owner_id = 3 + m.movie_type_id = 1 + m + end ``` ### Object Serialization @@ -321,7 +332,7 @@ options[:links] = { prev: '...' } hash = MovieSerializer.new(movies, options).serializable_hash -json_string = MovieSerializer.new([movie, movie], options).serialized_json +json_string = MovieSerializer.new(movies, options).serialized_json ``` #### Control Over Collection Serialization diff --git a/spec/lib/object_serializer_spec.rb b/spec/lib/object_serializer_spec.rb index dc846e6c..724795c7 100644 --- a/spec/lib/object_serializer_spec.rb +++ b/spec/lib/object_serializer_spec.rb @@ -4,13 +4,14 @@ include_context 'movie class' include_context 'group class' + let(:movies) { build_movies(2) } + context 'when testing instance methods of object serializer' do it 'returns correct hash when serializable_hash is called' do options = {} options[:meta] = { total: 2 } options[:links] = { self: 'self' } options[:include] = [:actors] - movies = build_movies(2) serializable_hash = MovieSerializer.new(movies, options).serializable_hash expect(serializable_hash[:data].length).to eq 2 @@ -59,7 +60,6 @@ it 'returns correct number of records when serialized_json is called for an array' do options = {} options[:meta] = { total: 2 } - movies = build_movies(2) json = MovieSerializer.new(movies, options).serialized_json serializable_hash = JSON.parse(json) expect(serializable_hash['data'].length).to eq 2 @@ -126,7 +126,6 @@ end it 'returns multiple records' do - movies = build_movies(2) json_hash = MovieSerializer.new(movies).as_json expect(json_hash['data'].length).to eq 2 end @@ -142,7 +141,6 @@ options = {} options[:meta] = { total: 2 } options[:include] = [:blah_blah] - movies = build_movies(2) expect { MovieSerializer.new(movies, options).serializable_hash }.to raise_error(ArgumentError) end @@ -169,7 +167,6 @@ options = {} options[:meta] = { total: 2 } options[:include] = [''] - movies = build_movies(2) expect(MovieSerializer.new(movies, options).serializable_hash.keys).to eq [:data, :meta] options[:include] = [nil] expect(MovieSerializer.new(movies, options).serializable_hash.keys).to eq [:data, :meta] From b24af1f912f171b37c3919c0ddb9d4da8bc4ca7e Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Fri, 4 Oct 2019 14:42:12 +0200 Subject: [PATCH 23/30] Test against Ruby 2.6 and fix Travis (#3) * Test against Ruby 2.6 * Enforce bundler 1.17.3 --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 00b98f79..7025fd1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,12 @@ rvm: - 2.3.6 - 2.4.3 - 2.5.0 + - 2.6 + +before_install: + - "travis_retry gem update --system 2.7.9" + - "travis_retry gem install bundler -v '1.17.3'" +install: BUNDLER_VERSION=1.17.3 bundle install --path=vendor/bundle --retry=3 --jobs=3 + script: - bundle exec rspec From f04abfd2fe5d0f2040a65678041c5f815dc50d70 Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:09:25 +0200 Subject: [PATCH 24/30] Compute remaining_items once --- lib/fast_jsonapi/serialization_core.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 845aee75..924b5dab 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -119,6 +119,8 @@ def get_included_records(record, includes_list, known_included_objects, fieldset includes_list.sort.each_with_object([]) do |include_item, included_records| items = parse_include_item(include_item) + remaining_items = remaining_items(items) + items.each do |item| next unless relationships_to_serialize && relationships_to_serialize[item] relationship_item = relationships_to_serialize[item] @@ -139,8 +141,8 @@ def get_included_records(record, includes_list, known_included_objects, fieldset serializer = self.compute_serializer_name(inc_obj.class.name.demodulize.to_sym).to_s.constantize end - if remaining_items(items) - serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params) + if remaining_items.present? + serializer_records = serializer.get_included_records(inc_obj, remaining_items, known_included_objects, fieldsets, params) included_records.concat(serializer_records) unless serializer_records.empty? end From 83e99b2923081b343ca80fe32506c0f60ed320f3 Mon Sep 17 00:00:00 2001 From: Jo Potts Date: Fri, 4 Oct 2019 17:39:34 +0100 Subject: [PATCH 25/30] Allow relationship links to be declared as object method (#2) * Allow relationship links to be declared as object method * Relationship links note added to readme --- README.md | 6 +++++ lib/fast_jsonapi/relationship.rb | 8 +++++-- ...ject_serializer_relationship_links_spec.rb | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 461cf2bf..b18d0558 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,12 @@ class MovieSerializer end ``` +Relationship links can also be configured to be defined as a method on the object. + +```ruby + has_many :actors, links: :actor_relationship_links +``` + This will create a `self` reference for the relationship, and a `related` link for loading the actors relationship later. NB: This will not automatically disable loading the data in the relationship, you'll need to do that using the `lazy_load_data` option: ```ruby diff --git a/lib/fast_jsonapi/relationship.rb b/lib/fast_jsonapi/relationship.rb index 7a038de7..f8a9bfd7 100644 --- a/lib/fast_jsonapi/relationship.rb +++ b/lib/fast_jsonapi/relationship.rb @@ -104,8 +104,12 @@ def fetch_id(record, params) end def add_links_hash(record, params, output_hash) - output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash| - Link.new(key: key, method: method).serialize(record, params, hash)\ + if links.is_a?(Symbol) + output_hash[key][:links] = record.public_send(links) + else + output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash| + Link.new(key: key, method: method).serialize(record, params, hash)\ + end end end diff --git a/spec/lib/object_serializer_relationship_links_spec.rb b/spec/lib/object_serializer_relationship_links_spec.rb index 8c2f2725..e0d48483 100644 --- a/spec/lib/object_serializer_relationship_links_spec.rb +++ b/spec/lib/object_serializer_relationship_links_spec.rb @@ -67,5 +67,28 @@ class LazyLoadingMovieSerializer < MovieSerializer expect(actor_hash).not_to have_key(:data) end end + + context "relationship links defined by a method on the object" do + before(:context) do + class Movie + def relationship_links + { self: "http://movies.com/#{id}/relationships/actors" } + end + end + + class LinksPassingMovieSerializer < MovieSerializer + has_many :actors, links: :relationship_links + end + end + + let(:serializer) { LinksPassingMovieSerializer.new(movie) } + let(:links) { hash[:data][:relationships][:actors][:links] } + let(:relationship_url) { "http://movies.com/#{movie.id}/relationships/actors" } + + it "generates relationship links in the object" do + expect(links).to be_present + expect(links[:self]).to eq(relationship_url) + end + end end end From fd17386b51da9cb04f059b919700263999049993 Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:11:56 +0200 Subject: [PATCH 26/30] Map split include_item in-place --- lib/fast_jsonapi/serialization_core.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 924b5dab..55994a0b 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -102,7 +102,8 @@ def to_json(payload) def parse_include_item(include_item) return [include_item.to_sym] unless include_item.to_s.include?('.') - include_item.to_s.split('.').map { |item| item.to_sym } + + include_item.to_s.split('.').map!(&:to_sym) end def remaining_items(items) From e4c65a2567da81029a090c190cd5b5f55db94f9d Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:09:55 +0200 Subject: [PATCH 27/30] Move transforms mapping to constant --- lib/fast_jsonapi/object_serializer.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index c161fb6b..c6861cdb 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -17,6 +17,12 @@ module ObjectSerializer SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash' SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json' + TRANSFORMS_MAPPING = { + camel: :camelize, + camel_lower: [:camelize, :lower], + dash: :dasherize, + underscore: :underscore + }.freeze included do # Set record_type based on the name of the serializer class @@ -137,13 +143,7 @@ def reflected_record_type end def set_key_transform(transform_name) - mapping = { - camel: :camelize, - camel_lower: [:camelize, :lower], - dash: :dasherize, - underscore: :underscore - } - self.transform_method = mapping[transform_name.to_sym] + self.transform_method = TRANSFORMS_MAPPING[transform_name.to_sym] # ensure that the record type is correctly transformed if record_type From 44a896dda5145c2eef59255c2d6d35796e2e305e Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:12:31 +0200 Subject: [PATCH 28/30] Take items of original array instead of dup and delete --- lib/fast_jsonapi/serialization_core.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/fast_jsonapi/serialization_core.rb b/lib/fast_jsonapi/serialization_core.rb index 55994a0b..7dd1b76c 100644 --- a/lib/fast_jsonapi/serialization_core.rb +++ b/lib/fast_jsonapi/serialization_core.rb @@ -109,9 +109,7 @@ def parse_include_item(include_item) def remaining_items(items) return unless items.size > 1 - items_copy = items.dup - items_copy.delete_at(0) - [items_copy.join('.').to_sym] + [items[1..-1].join('.').to_sym] end # includes handler From f2a1934b766b58b0b689a64185d1934e51a6a1b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Rybka Date: Thu, 2 May 2019 15:10:52 +0200 Subject: [PATCH 29/30] Use each_with_object instead of Hash[map] --- lib/fast_jsonapi/object_serializer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/fast_jsonapi/object_serializer.rb b/lib/fast_jsonapi/object_serializer.rb index c6861cdb..c9476e94 100644 --- a/lib/fast_jsonapi/object_serializer.rb +++ b/lib/fast_jsonapi/object_serializer.rb @@ -99,9 +99,9 @@ def process_options(options) def deep_symbolize(collection) if collection.is_a? Hash - Hash[collection.map do |k, v| - [k.to_sym, deep_symbolize(v)] - end] + collection.each_with_object({}) do |(k, v), hsh| + hsh[k.to_sym] = deep_symbolize(v) + end elsif collection.is_a? Array collection.map { |i| deep_symbolize(i) } else From 37206ddf0b6df04f94663cd7ddb81d66c041dcd7 Mon Sep 17 00:00:00 2001 From: Kevin Pheasey Date: Fri, 4 Oct 2019 19:45:43 -0400 Subject: [PATCH 30/30] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65b8b371..97809b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,5 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optimize SerializationCore.get_included_records calculates remaining_items only once ([#4](https://github.com/fast-jsonapi/fast_jsonapi/pull/4)) - Optimize SerializtionCore.parse_include_item by mapping in place ([#5](https://github.com/fast-jsonapi/fast_jsonapi/pull/5)) - Define ObjectSerializer.set_key_transform mapping as a constant ([#7](https://github.com/fast-jsonapi/fast_jsonapi/pull/7)) +- Optimize SerializtionCore.remaining_items by taking from original array ([#9](https://github.com/fast-jsonapi/fast_jsonapi/pull/9)) +- Optimize ObjectSerializer.deep_symbolize by using each_with_object instead of Hash[map] ([#6](https://github.com/fast-jsonapi/fast_jsonapi/pull/6)) [Unreleased]: https://github.com/fast-jsonapi/fast_jsonapi/compare/dev...HEAD