Code Monkey home page Code Monkey logo

fast_jsonapi's Issues

Don't require a method called `cache_key` on the model

Given the following model:

class Foo
  attr_accessor :id, :name
  def initialize(opts = {})
    @id = opts[:id]
    @name = opts[:name]
  end
end

and the following serializer:

module Api
  module V1
    class FooSerializer
      include FastJsonapi::ObjectSerializer
      set_type :foo
      cache_options enabled: true, cache_length: 12.hours
      attributes :id, :name
    end
  end
end

an error is raised when called with:

module Api
  module V1
    class FoosController < ApiController
      def show
        foo = Foo.new(id: 1 , name: 'foo')
        render json: FooSerializer.new(foo).serialized_json, status: :ok
      end
  end
end

#=> undefined method `cache_key' for #<Foo:0x00007f94d880b500>

we need to add documentation that this is required for caching or we need to not require it, and I would prefer the later. If cache_key is not defined we should assume a cache_key of the object name and id (in this case "foo:#{object.id}").

Proper separation between the serializer class vs. the serializer instance

This issue is an extraction of the conversations in #49 and #67.

Right now, it is difficult to differentiate between what functionality belongs on the class and what belongs on the instance. Currently they both kinda act as singletons. It poses potential problems, currently and most notably when trying to implement AMS-style custom attribute methods, e.g.:

class MovieSerializer
  include FastJsonapi::ObjectSerializer

  attributes :name, :year, :name_with_year

  def name_with_year
    "#{object.name} (#{object.year})"
  end
end

In what context is name_with_year executed? What is its scope? How is the object property available within this method? And how do we avoid the performance pitfalls of AMS, that instantiates new serializers for every object being serialized?

I believe an ideal solution would be the following:

  • The serializer class is responsible for containing the definition of the serializer: attributes, relationships, key transformation, etc. Basically, anything built into the serializer DSL would be stored at the class.

  • The serializer instance is responsible for serializing a single object at one time. Its job is to take an object as an input and output the object's serialized representation. This way, additional methods defined on the serializer (custom attributes, custom relationships, etc.) will naturally have an object within the context of the serialization process.

  • The serializer class will have methods to serialize one object or a collection of objects. The class will then create the necessary instance(s). In most cases, developers will be calling class methods to perform the serialization, and they will not be instantiating serializers themselves.

  • We can achieve performance levels comparable to today by reusing the serializer instances within the serialization process, as opposed to generating new instances for every object. Prior to each iteration, we simply set the new object and clear any instance variables that custom methods may have set. From the developer's point of view, it will feel like the serializer is new each time, but without the performance cost.

Examples:

class MovieSerializer
  include FastJsonapi::ObjectSerializer

  attributes :name, :year, :name_with_year

  def name_with_year
    "#{object.name} (#{object.year})"
  end
end


MovieSerializer.to_hash(movie, options)
MovieSerializer.to_hash([ movie1, movie2 ], options)

MovieSerializer.to_json(movie, options)
MovieSerializer.to_json([ movie1, movie2 ], options)

Making some decisions along these lines will help set us up for success as we continue to build support for the entire JSON:API 1.0 spec.

jbuilder support

I was wondering how can I use fast_jsonapi with jbuilder. I Probably need new adapter in multijson,
problem is that multijson is fast_jsonapi dependency. It's somehow possible?

Different underlying models used in benchmarks skew results

The benchmarks use underlying models that are fundamentally different. The models used by fast_jsonapi in the benchmarks are plain Ruby classes, whereas the models used in the ams parts of the benchmarks inherit from ActiveModelSerializers::Model.

This difference skews the benchmarks significantly in favor of fast_jsonapi, after updating the models for the fast_jsonapi parts of the benchmark to also inherit from ActiveModelSerializers::Model several of the benchmark specs fail:

Failures:

  1) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 25 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed faster by 14.87 times
     # ./spec/lib/object_serializer_performance_spec.rb:105:in `block (4 levels) in <top (required)>'

  2) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 250 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed faster by 14.78 times
     # ./spec/lib/object_serializer_performance_spec.rb:105:in `block (4 levels) in <top (required)>'

  3) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 1000 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed by the same time
     # ./spec/lib/object_serializer_performance_spec.rb:105:in `block (4 levels) in <top (required)>'

Classes used in benchmarks:

AMS benchmarks:

class AMSActor < ActiveModelSerializers::Model
  attr_accessor :id, :name, :email
end

fast_jsonapi benchmarks:

class Actor
  attr_accessor :id, :name, :email
end

Support for CamelCase attributes

We're looking at json serializers specifically for serializing a PORO.

One of the core requirements for serialization is that the snake_cased attribute keys be transformed into CamelCased ones to support the json standards being followed by our organization and also some others like Google, w3resource, schema.org, etc.

Will such a request be considered for this gem? It can be a special configuration item. If yes, I am open to putting a tech design of sorts

Is it compatible with a project running Rails 4.2.3, Ruby 2.2.1 and Mongoid 5.1.0

I get the following error on bundle update

Bundler could not find compatible versions for gem "activesupport":
  In snapshot (Gemfile.lock):
    activesupport (= 4.2.3)

  In Gemfile:
    fast_jsonapi was resolved to 1.0.17, which depends on
      activesupport (~> 5.0)

    rails (= 4.2.3) was resolved to 4.2.3, which depends on
      activesupport (= 4.2.3)

Include nested of nested association

It would be great to allow to include nested of nested associations... example:

class Post < ApplicationRecord
  has_many :post_tags, dependent: :destroy
  has_many :tags, through: :post_tags
  belongs_to :author, touch: true
end

class Author < ApplicationRecord
  has_many :posts, dependent: :nullify
  has_one :profile, dependent: :destroy
end

class Profile < ApplicationRecord
  belongs_to :author, touch: true
end

In Post serializer, to include author's profile something like:
PostSerializer.new @post, {include: [:author, :'author.profile', :tags]}
could be very useful.

Custom attributes

Will it be possible to add custom attributes to serializers that will delegate to a function within the serializer class. E.g.:

attributes :fullname

def fullname
    "#{object.firstname} #{object.lastname}"
end

Are you guys willing to accept a pull request implementing such a feature?

association and polymorphic

Hey, a brief example :

class User < ApplicationRecord
  has_many  :reports, as: :reportable, inverse_of: :reportable
end

class Report < ApplicationRecord
  belongs_to :reportable, polymorphic: true
end

class Group < ApplicationRecord
  has_many  :reports, as: :reportable, inverse_of: :reportable
end

serializer :

class ReportSerializer 
  include FastJsonapi::ObjectSerializer

  has_one :reportable
end

output : (the type of data not relative to reportable_type)

:relationships=> {
  :reportable=> {
    :data=> {
      :id=>"bd729369-6ece-454c-8269-437d1e2ed474", 
      :type=>:reportable
    }
  }
}

type is reportable, not user or group and Fast_JSONAPI fetch a ReportableSerializer not UserSerializer ou GroupSerializer

Implementation of polymorphic association is a great feature 👍

How to override the id attribute ?

According to the JSON API specifications there must be an id field. But I do not want to expose the id column of my DB and I have a friendly_id column (which is unique as well).

How do I instruct fast_jsonapi to use my friendly_id column instead ?

Benchmarks vs jsonapi-rb?

Jsonapi-rb was written by @beauby, who also used to work on AMS (i did too). It's also way faster than AMS.

I saw the medium blog post, and i think more details about the benchmark woulld be good, too.
For example, in my own benchmarks, with 200 records with 2 included relationships each,. I found that jsonapi-rb is 5 to 6 times faster than AMS. I can share the benchmarrk files with you if it'd help.

I have some doubts about the speed increase mentioned in the medium article, which is why i bring this up.

I could do my own benchmarks, but i'm in the car right now. :)

Thanks for supporting/making-more-known the jsonapi spec. :D

Benchmarks are misleading. I get only 2.5x AMS, and only 1.05x jsonapi-rb

I just did my own benchmarks, and I'm suuuuper open to others checking my work.
But this is what I got (using default settings):

1 User, 2 Posts, 2 Comments per Post. equiv: GET /api/users/:id?include=posts

iterations per second
Comparison:
  fast_jsonapi eager:      547.9 i/s
  jsonapi-rb   eager:      521.4 i/s - 1.05x  (± 0.01) slower
  ams          eager:      219.0 i/s - 2.50x  (± 0.02) slower
                   with 95.0% confidence
memory allocation
Comparison:
  fast_jsonapi eager:      96234 allocated
  jsonapi-rb   eager:     109874 allocated - 1.14x more
  ams          eager:     249662 allocated - 2.59x more

that memory allocation is pretty good though. :-D

Here is the repo where I made the benchmark:
https://github.com/NullVoxPopuli/ruby-jsonapi-benchmarks

Something that needs to be benchmarked is nested includes performance...
AMS and jsonapi-rb both support nested includes. (and this is where jsonapi-rb beats the pants of AMS). :-)

For example:
1 User, 20 Posts, 20 Comments per Post - including 'posts.comments'

Comparison:
  jsonapi-rb   eager:       22.6 i/s
  ams          eager:        4.6 i/s - 4.91x  (± 0.04) slower
                   with 95.0% confidence

Comparison:
  jsonapi-rb   eager:    3979876 allocated
  ams          eager:   12287098 allocated - 3.09x more

dynamic keys

It's possible to serialize object/hash with dynamic keys?

[Question] `src` in attributes

Hey,

Thanks for this gem !

I haven't see this yet in documentation but do you plan to add the src when you create an object e.g. :

HTTP/1.1 201 Created
Location: http://example.com/photos/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/vnd.api+json

{
  "data": {
    "type": "photos",
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "attributes": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    },
    "links": {
      "self": "http://example.com/photos/550e8400-e29b-41d4-a716-446655440000"
    }
  }
}

Same for links e.g.:

 "links": {
    "self": "http://example.com/articles/1"
}

Make `belongs_to` an alias of `has_one`

Looking at the code, there's no real difference for an association being belongs_to and has_one. Instead, is a source for problems (two places with the same code are two places you can forget to place a fix.. have you noticed the default serializer is obtained differently?). Both do the same thing, and both cause the same consequences in the serialization core.

Also, the jsonapi doesn't care about the direction of a 1-to-1 association.

How about:

  • Removing the belongs_to method in object_serializer.rb
  • Adding belongs_to as an alias of has_one, so it is backwards-compatible.

Unclear if :relationships can be hydrated

Hi!

Dunno if this is more of a concern of http://jsonapi.org/, but:

it is unclear whether the items contained in :relationships can/should have richer keys than just id and type.

"relationships":{"foos":{"data":[{"id":"2","type":"foo"}]},"bars":{"data":[{"id":"8","type":"bar"}]}}

When one sees this generated json, may wonder "can I have more fields there?"

I am aware that there's included, but ideally one would avoid generating bloated representations. Particularly for a performance-sensitive lib :)

Cheers - Victor

Error with namespaced relations with inclusions

An error is perform when includes an associations has_one

class Cluster::LocationSerializer 
  include FastJsonapi::ObjectSerializer
  attributes :begin_at, :end_at

  has_one :workspace, serializer: Cluster::WorkspaceSerializer
  has_one :user, serializer: Cluster::UserSerializer
end
class Cluster::UserSerializer 
  include FastJsonapi::ObjectSerializer
  attributes :uid, :login, :nickname, :avatar_url
end
class Cluster::WorkspaceSerializer 
  include FastJsonapi::ObjectSerializer
  attributes :identifier, :campus_id
end

call:

active_locations = Location.active.includes(:workspace, :user).references(:workspace, :user)
Cluster::LocationSerializer.new(active_locations, include: [:workspace, :user]).serializable_hash

Error :

NameError - uninitialized constant Cluster::Cluster

No support for 'links' key, which optionally contains 'self', 'next', 'last' and 'related'

Part of the JSON API is to support links but it doesn't appear like there's any way to achieve this right now.

Sample output of this:

{
  "links": {
    "self": "http://example.com/articles",
    "next": "http://example.com/articles?page[offset]=2",
    "last": "http://example.com/articles?page[offset]=10"
  },
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    }
  }]
}

Deserialization Support

When working with AMS, I find it very useful to be able to deserialize my incoming Json API requests. After looking through the library, it looks like this is not an option. Am I missing something, or is there a reason that deserialization is left out?

This library looks great! I'm using this on my next project, will just need AMS in tandem to deserialize for now. Thanks!

Customize JSON Target-Structure

Hi there,

First thanks for your this awesome gem. :)

Is it somehow possible to customize the json structure from #serialized_json in order to have a different structure?

{
  "data": {
...
    "relationships": {
    ...
    }
  }
}

So for example I want to get rid of 'data' and would like to rename relationships to a customname and so on.

Possibly skewing of benchmark results

I noticed 2 issues with how benchmarks are set up.

  1. build_ams_movies builds owner, movie_type and actor objects while build_movies only sets up ids and builds objects just in time when serializing the movies array (and not building owner object at all). It looks like AMS serialization will use more memory and trigger more GC (be slower) because of the way it is set up. Both test setups should initialize same amout of objects/object types
  2. AMS is not set up to use OJ even though it supports it. comparing performance of serializable_hash of both gems or using OJ with both gems should be fairer comparison.

could these 2 issue cause some problems with "fairness" of the benchmark?

has_many relationship serialization

Hello everyone!

I recently started using fast_jsonapi in my rails API and I have to say that the performance improvement is amazing!

I just have a suggestion to make. At the moment, in order to serialise "has_many" relationships, the gem is looking for relationship_model_name_ids and queries on that. With active record serializers, it would be enough to have a method to fetch the relationship data, named after the rails convention.

e.g.

Actor has_many :movies could have a method movies and the relationship data would be serialised.

I mention this because I have some models that are Elasticsearch models and I was not storing the relationship ids, instead I had a method to fetch the relationship data ( I was caching the data as well ) and after I switched to fast_jsonapi the relationship was empty! :)

For now, I store the ids in the mode, but I think It's an easy addition for the gem, if it makes sense to you as well.

Serializer Inheritance doesn't copy attributes

It's not clear that the following won't work as might be expected:

class UserSerializer
  include FastJsonapi::ObjectSerializer

  attribute :first_name
  attribute :last_name
end
class FullUserSerializer < UserSerializer

  attribute :birthday
  attribute :email
end

Workaround:

class UserSerializer
  include FastJsonapi::ObjectSerializer

  def self.inherited(subclass)
    subclass.attributes_to_serialize ||= {}
    subclass.attributes_to_serialize.merge!(attributes_to_serialize)
  end

  attribute :first_name
  attribute :last_name
end

Selecting attributes in associations

Hello!
Prompt, I can not understand, whether there is a possibility to select necessary attributes for the specified association?

class MovieSerializer
  include FastJsonapi::ObjectSerializer
  set_type :movie
  attributes :name
  belongs_to :owner, attributes: [:name]
end
{
  "data": {
    "id": "232",
    "type": "movie",
    "attributes": {
      "name": "test movie"
    },
    "relationships": {
      "owner": {
        "data": {
          "id": "3",
          "type": "user",
          "name": "Owner name example"
        }
      }
    }
  }
}

Is it possible to do this?

relationship type isn't plural when the resource type is.

example:

"fast_jsonapi"
{
        :data => {
                   :id => "1",
                 :type => :users,
           :attributes => {
            :first_name => "Diana",
             :last_name => "Prince",
              :birthday => -0982-02-05 03:36:03 UTC,
            :created_at => 2018-02-14 02:51:25 UTC,
            :updated_at => 2018-02-14 02:51:25 UTC
        },
        :relationships => {
            :posts => {
                :data => [
                    [0] {
                          :id => "1",
                        :type => :post
                    },
                    [1] {
                          :id => "2",
                        :type => :post
                    }
                ]
            }
        }
    },
    :included => [
        [0] {
                       :id => "1",
                     :type => :posts,
               :attributes => {
                     :title => "Some Post",
                      :body => "awesome content",
                :created_at => 2018-02-14 02:51:25 UTC,
                :updated_at => 2018-02-14 02:51:25 UTC
            },
            :relationships => {
                    :user => {
                    :data => {
                          :id => "1",
                        :type => :user
                    }
                },
                :comments => {
                    :data => [
                        [0] {
                              :id => "1",
                            :type => :comment
                        },
                        [1] {
                              :id => "2",
                            :type => :comment
                        }
                    ]
                }
            }
        },
        [1] {
                       :id => "2",
                     :type => :posts,
               :attributes => {
                     :title => "Some Post",
                      :body => "awesome content",
                :created_at => 2018-02-14 02:51:25 UTC,
                :updated_at => 2018-02-14 02:51:25 UTC
            },
            :relationships => {
                    :user => {
                    :data => {
                          :id => "1",
                        :type => :user
                    }
                },
                :comments => {
                    :data => [
                        [0] {
                              :id => "3",
                            :type => :comment
                        },
                        [1] {
                              :id => "4",
                            :type => :comment
                        }
                    ]
                }
            }
        }
    ]
}

I'm working on a reproduction repo. will post results soon.

Polymorphic associations and Serializer DSL

After documenting #64, I felt a little odd about the polymorphic option I added to the has_many | belongs_to | has_one methods:

class ProjectSerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :owner, polymorphic: { User => :user, Administrator => :administrator }
end

I think the polymorphic option describes an ORM feature (specially when working with relational databases) rather than an object serialization aspect.

Actually, I think the jsonapi spec doesn't even care if an association is polymorphic or not, as long as the associated object has the type key in it, it's all good. I think this should be reflected in the serializer DSL.

So, how about replacing the polymorphic option with being able to use the record_type option with either a symbol (as it is right now) or the Hash object polymorphic receives now?

class ProjectSerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :owner, record_type: { User => :user, Administrator => :administrator }
end

As a matter of fact, since we're memoizing the associated object => record_type with #64 when the Hash isn't provided (Thanks @christophersansone for the tip), I think we can make pretty much the same (performance wise) to not need to specify a record_type at all:

class ProjectSerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :owner # The auto-detect fallback + memoization added in #64 would kick in
end

(You could still define a Hash for record_type, but that would give just a very marginal performance gain when serializing 1 object, I think)

Your thoughts @shishirmk @christophersansone @AtomGrafiks ?

Caching: race condition ttl

Rails.cache.fetch might need a race_condition_ttl option?

Setting :race_condition_ttl is very useful in situations where a cache entry is used very frequently and is under heavy load. If a cache expires and due to heavy load several different processes will try to read data natively and then they all will try to write to cache. To avoid that case the first process to find an expired cache entry will bump the cache expiration time by the value set in :race_condition_ttl. Yes, this process is extending the time for a stale value by another few seconds. Because of extended life of the previous cache, other processes will continue to use slightly stale data for a just a bit longer. In the meantime that first process will go ahead and will write into cache the new value. After that all the processes will start getting the new value. The key is to keep :race_condition_ttl small.

Merge/Inheritance of Serializers

Is there any possibility to use attribute definitions of one Serializer in another?
Like i have:

class SimpleDataSerializer
attributes :a, :b, :c
end

class FullDataSerializer
attributes :a, :b, :c, :d, :e
end

#Scopes
scope :simple
...
scope full, -> do
merge(x.simple).select(additionalData)
end

for many Objects. so it would be nice to include simple in full Serializer and avoid double writing work. Especially to avoid dopple work when attributes in simple change.

Don't require id

Currently, if a class that is being serialized does not have an attribute id an error undefined method 'id' is thrown. IMO we should not have an opinion about what attributes are in the class at all. This will allow for non-ActiveRecord classes to also be serialized. At the very least, however, we should document that this is a required field.

Question: why juwelier

The gemspec of juwelier is a mess. What are the advantages of it and running rake gemspec every time vs the regular Bundler gemspec?

Uninitialized Constant on Namespaced Controller

My serializer

module Api
  module V4
    class EmployeeSerializer
      include FastJsonapi::ObjectSerializer
      ...
    end
  end
end

My Controller

module Api
  module V4
    class EmployeesController < ApiController
      ...
      def index
        ...
        ...
        render json: Api::V4::EmployeeSerializer.new(employees), status: :ok
      end
    end
  end
end

got this:

uninitialized constant Api::V4::EmployeeSerializer::FastJsonapi

but it's working well if calling it from console.

was looked at this: #16
but it's different. CMIIW

Skylight Integration Suggestions

As one of the lead developers on Skylight it's awesome to see you integrating it!

This is probably a bit counter to our own self-interest, but I think it would probably make a bit more sense if, instead of using Skylight::Helpers you just use ActiveSupport::Notifications. You could then set up a Skylight::Normalizer to consume the events if Skylight was present or other libraries could use them as well. We haven't really flushed out a public API for adding Normalizers so this is definitely something I'd be happy to discuss with you if you're interested.

Serialization structure

Hi,

We works with AMS, and we have many options to customize the serialization output.
Actually the serialization object with fast_jsonapi are imposed.

{
  "data": {
    "id": "232",
    "type": "movie",
    "attributes": {
      "name": "test movie",
      "year": null
    },
   ...
}

So, do you have any ideas if you wants add more options to changes structure of data, example if I want build :

GET /movies/:id

{
   "movie": {
     "id": "232",
     "name": "test movie",
     "year": null
   }
}

GET /movies

{
   "movies": [
    {
     "id": "232",
     "name": "test movie",
     "year": null
    },
    {
     "id": "233",
     "name": "another test movie",
     "year": null
    }
  ],
  "meta": { ... }
}

Thanks

has_one serialization fails for nil association

Attempt to serialize model with has_one association fails if association is nil:

NoMethodError (undefined method 'id' for nil:NilClass)

(fast_jsonapi-1.0.16/lib/extensions/has_one.rb:12)

If associated model exists, it gets serialized correctly.

Example:

class User < ApplicationRecord
  has_one :profile
end
 
class Profile < ApplicationRecord
   belongs_to :user
end

class UserSerializer
  include FastJsonapi::ObjectSerializer
  attributes :id, :name
  has_one :profile
end

UserSerializer.new(User.create(name: "John")).serialized_json
NoMethodError (undefined method 'id' for nil:NilClass)

After OJ is enabled for AMS, the speed test randomly failed

According to this PR #36, OJ is enabled for AMS

Now causing the benchmark spec randomly failed, looks like the speed factor is not quite consistent since it's random. I might look into this later anyway.

  1) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x should serialize 1 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed faster by 22.81 times
     # ./spec/lib/object_serializer_performance_spec.rb:77:in `block (4 levels) in <top (required)>'

  2) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 25 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed faster by 19.94 times
     # ./spec/lib/object_serializer_performance_spec.rb:102:in `block (4 levels) in <top (required)>'

  3) FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 250 records atleast 25 times faster than AMS
     Failure/Error: expect { our_serializer.serialized_json }.to perform_faster_than { ams_serializer.to_json }.at_least(speed_factor).times
       expected given block to perform faster than comparison block by at_least 25 times, but performed faster by 19.44 times
     # ./spec/lib/object_serializer_performance_spec.rb:102:in `block (4 levels) in <top (required)>'

Finished in 25.31 seconds (files took 0.74305 seconds to load)
49 examples, 3 failures

rspec './spec/lib/object_serializer_performance_spec.rb[1:2:1]' # FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x should serialize 1 records atleast 25 times faster than AMS
rspec './spec/lib/object_serializer_performance_spec.rb[1:3:2]' # FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 25 records atleast 25 times faster than AMS
rspec './spec/lib/object_serializer_performance_spec.rb[1:3:3]' # FastJsonapi::ObjectSerializer when comparing with AMS 0.10.x and with includes and meta should serialize 250 records atleast 25 times faster than AMS

Question on benchmark results

I'm kind of courious where exactly or what makes fast_jsonapi 25x faster than AMS. Could you provide me some information on this?

It is clear that AMS probably generates some overhead due to a variety of features such as
custom attributes, adapters, conditionals and others. Eventhough the benchmark tests are only using the non-default adapter. E.g.: Only by removing this line of code and using the default adapter the execution times of AWS are dropping almost by 50%

ActiveModel::Serializer.config.adapter = :json_api

Serializer Records Time
AMS serializer 1 1.25 ms
Fast serializer 1 0.14 ms

Serializer Records Time
AMS serializer 25 7.98 ms
Fast serializer 25 0.57 ms

Serializer Records Time
AMS serializer 250 73.46 ms
Fast serializer 250 2.64 ms

Serializer Records Time
AMS serializer 1000 322.16 ms
Fast serializer 1000 15.85 ms

Serializer Records Time
AMS serializer 1 0.55 ms
Fast serializer 1 0.13 ms

Serializer Records Time
AMS serializer 25 5.49 ms
Fast serializer 25 0.55 ms

Serializer Records Time
AMS serializer 250 63.45 ms
Fast serializer 250 4.77 ms

Serializer Records Time
AMS serializer 1000 282.82 ms
Fast serializer 1000 32.25 ms

Allow custom method or scope for includes

I think this suggestion is distinct from #47, but it's similar and could possibly be developed at the same time, if others agree and would find this valuable. Alternatively, if there is already a solution to this that would be even better 😄

Scenario is as follows. This is for boxing/UFC fights where there are two athletes (fighters) in each bout, and many bouts in an event.

Serializers:

class FighterSerializer
  include FastJsonapi::ObjectSerializer
  attributes :first_name, :last_name
  has_many :fighter_bouts
end
class FighterBoutSerializer
  include FastJsonapi::ObjectSerializer
  attributes :date_of_event, :name_of_event, :result
end

Models:

class Fighter < ActiveRecord::Base
  has_many  :fighter_bouts

  def custom_method_with_scoping_and_eager_loading
    fighter_bouts.includes(<nested relationships>).where(<scoping of records>).order(<detailed ordering>)
  end
end
class FighterBout < ActiveRecord::Base
  belongs_to :fighter
  belongs_to :bout
end
class Bout < ActiveRecord::Base
  has_many    :fighter_bouts
  belongs_to  :event
  belongs_to  :weight_class
end

What I would like to be able to do is have an endpoint for a Fighter which is a compound document with included fighter_bouts. Such as:

fighter = Fighter.find(1)
opts = {include: [:fighter_bouts]}
payload = FighterSerializer.new(fighter, opts).serialized_json
render json: payload

However, I want to use the custom method defined on the Fighter class, because it will eager load everything needed from a series of nested associations. Data from these nested associations will be "flattened" and presented as attributes on the included fighter_bouts. For example, the event name will be returned as an attribute of a fighter_bout, even though in actuality this value is 2 associations away in the database.

Therefore I was thinking it would be very useful to be able to either pass an additional method name or value along with the options hash being given to the serializer. Or, allow for a custom-named "association method" in the serializer. Extremely roughly conceptualized as:

opts = {include: [:fighter_bouts_custom]}
payload = FighterSerializer.new(fighter, opts).serialized_json
class FighterSerializer
  include FastJsonapi::ObjectSerializer
  attributes :first_name, :last_name
  has_many :fighter_bouts

  def fighter_bouts_custom
    <reference to custom method on the Fighter class>
  end
end

Thanks for reading!

Allow includes to be strings

Having a Series serializer with has_many :episodes

SeriesSerializer.new(series, include: ['episodes'])

Throws an ArgumentError.

I would expect it to work, since includes are often pulled from a controller's params, which are always strings.

Having .to_sym the strings would also be a memory issue in Ruby before 2.2.

How to integrate with DynamoDB using aws-record

As mentioned in title, how can I integrate this with DynamoDB using aws-record gem?

For example, I have this Project model

class Project
  include Aws::Record
  include ActiveModel::Validations

  string_attr :id, hash_key: true
  string_attr :state
  string_attr :title
  string_attr :description
  datetime_attr :start_date
  string_attr :status
  string_attr :company
  string_attr :created_by
end

and then I have this Company model

class Company
  include Aws::Record
  include ActiveModel::Validations

  string_attr :id, hash_key: true
  string_attr :name
end

and then I have the serializer for Project

class ProjectSerializer
  include FastJsonapi::ObjectSerializer

  set_type :project
  attributes :id, :state, :title,
             :description, :start_date, :status,
             :company, :created_by

  has_one :company
end

it return me error as follow:

{"message":"undefined method `company_id' for #\u003cProject:0x00007fbbeac09dd0\u003e\nDid you mean?  company\n               company=\n               company_was"}

I have no idea how to fix this. Please help.

Oj Runtime Dependency

Would you accept a PR for removing Oj as a dependency? Since this is a C gem, it prevents non-MRI VMs from using this gem. With MultiJson, just having Oj loaded in the host app is enough to make to_json use it.

Shame

insolent lie

Active Model Serializer serialized 250 records in 138.71 ms
Fast JSON API serialized 250 records in 3.01 ms

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.