jsonapi_serializer 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
 - data/CHANGELOG.md +9 -0
 - data/README.md +37 -12
 - data/bin/benchmark +9 -1
 - data/jsonapi_serializer.gemspec +1 -0
 - data/lib/jsonapi_serializer/base.rb +42 -39
 - data/lib/jsonapi_serializer/dsl/common.rb +13 -21
 - data/lib/jsonapi_serializer/dsl/polymorphic.rb +2 -2
 - data/lib/jsonapi_serializer/polymorphic.rb +6 -6
 - data/lib/jsonapi_serializer/utils.rb +6 -0
 - data/lib/jsonapi_serializer/version.rb +1 -1
 - data/perf/models.rb +1 -1
 - metadata +18 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 54a1f6c12504a34ad1658cc5324b324d374e510018db28b152bf9f043d26fc3e
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 201ed0fc8df08e5c4abc74fcd18493f9e5711d0612f397c9d369b11b8fd08c58
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: fd85b7ace1691b5916781df14b6074d77502b3eb8b387b86875f4bdf587d39e3ccd3eba6d9ce0d218489e5f0f94d7ff8911beada569cf4f5dbd15a99fe3e668c
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: fdc211f59a28ded7c07cbf56109673047285e09a3650d62d66f09fed509e8a3b996e944f47f6278bd67db67d99db0fbe59bb6d1a8a6c5e672bb434ae31f9b663
         
     | 
    
        data/CHANGELOG.md
    ADDED
    
    | 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ### 0.1.1
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            - Support for lambda as a source of relationships (`from` parameter)
         
     | 
| 
      
 4 
     | 
    
         
            +
            - Instance level attributes for fields and includes are removed in favor of initialization-time variables distribution (No public API changed)
         
     | 
| 
      
 5 
     | 
    
         
            +
            - Fixed bug when `key_transform` didn't have an effect on serialized attributes and relationships.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ### 0.1.0
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            Initial release
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -82,8 +82,8 @@ JsonapiSerializer.set_type_namespace_separator :ignore 
     | 
|
| 
       82 
82 
     | 
    
         | 
| 
       83 
83 
     | 
    
         
             
            # The default option is "_"
         
     | 
| 
       84 
84 
     | 
    
         
             
            # Bear in mind that only " " (space), "_" and "-" or any combination of these
         
     | 
| 
       85 
     | 
    
         
            -
            # are allowed as per json-api spec  
     | 
| 
       86 
     | 
    
         
            -
            #  
     | 
| 
      
 85 
     | 
    
         
            +
            # are allowed as per json-api spec, but in practice you can use other symbols if you
         
     | 
| 
      
 86 
     | 
    
         
            +
            # make sure to escape them while using in urls.
         
     | 
| 
       87 
87 
     | 
    
         
             
            ```
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
       89 
89 
     | 
    
         
             
            ### Attributes configuration
         
     | 
| 
         @@ -118,13 +118,17 @@ In this example, serializer will access `record.name` and `record.year` to fill 
     | 
|
| 
       118 
118 
     | 
    
         | 
| 
       119 
119 
     | 
    
         
             
            ### Relationships configuration
         
     | 
| 
       120 
120 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
            In order to define relationships, you can use `belongs_to` and `has_many` DSL methods. They accept options `serializer` and `from`. By default, serializer will try to guess relationship serializer by the name of relation. In case of `:director` relation, it would try to use `DirectorSerializer` and crash since it's not defined. Use `serializer` parameter to set serializer explicitly. Option `from` is used to point at the attribute of the model that actually returns the relation object(s) if it's different.
         
     | 
| 
      
 121 
     | 
    
         
            +
            In order to define relationships, you can use `belongs_to` and `has_many` DSL methods. They accept options `serializer` and `from`. By default, serializer will try to guess relationship serializer by the name of relation. In case of `:director` relation, it would try to use `DirectorSerializer` and crash since it's not defined. Use `serializer` parameter to set serializer explicitly. Option `from` is used to point at the attribute of the model that actually returns the relation object(s) if it's different. You can also supply lambda, if the relation is accessible in some non-trivial way.
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
123 
     | 
    
         
             
            ```ruby
         
     | 
| 
       124 
124 
     | 
    
         
             
            class MovieSerializer
         
     | 
| 
       125 
125 
     | 
    
         
             
              include JsonapiSerializer::Base
         
     | 
| 
       126 
126 
     | 
    
         
             
              belongs_to :director, serializer: PersonSerializer
         
     | 
| 
       127 
127 
     | 
    
         
             
              has_many :actors, from: :cast
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
              # or if you want to introduce some logic in building relationships
         
     | 
| 
      
 130 
     | 
    
         
            +
              has_many :male_actors, from: lambda { |movie| movie.cast.where(sex: "m") }
         
     | 
| 
      
 131 
     | 
    
         
            +
              has_many :female_actors, from: lambda { |movie| movie.cast.where(sex: "f") }
         
     | 
| 
       128 
132 
     | 
    
         
             
            end
         
     | 
| 
       129 
133 
     | 
    
         
             
            ```
         
     | 
| 
       130 
134 
     | 
    
         | 
| 
         @@ -137,7 +141,7 @@ From the perspective of serializer, there is no distinction between `belongs_to` 
     | 
|
| 
       137 
141 
     | 
    
         | 
| 
       138 
142 
     | 
    
         
             
            There are two kinds of polymorphism that `jsonapi_serializer` supports. First is polymorphic model (STI models in ActiveRecord), where most attributes are shared, but children have different types. Ultimately it is still one kind of entity: think of `Vehicle` base class inherited by `Car`, `Truck` and `Motorcycle`. Second kind is polymorphic relationship, where one relationship can contain entirely different models. Let's say you have `Post` and `Product`, and both can have comments, hence from the perspective of individual comment it belongs to `Commentable`. Even though `Post` and `Model` can share some attributes, their serializers will be used mostly along from comments.
         
     | 
| 
       139 
143 
     | 
    
         | 
| 
       140 
     | 
    
         
            -
            These types of serializers share most of the implementation  
     | 
| 
      
 144 
     | 
    
         
            +
            These types of serializers share most of the implementation and both rely on `resolver`, which is implicitly defined as a lambda, that applies `JsonapiSerializer.type_transform` to the record class name.
         
     | 
| 
       141 
145 
     | 
    
         | 
| 
       142 
146 
     | 
    
         
             
            #### Polymorphic Models
         
     | 
| 
       143 
147 
     | 
    
         | 
| 
         @@ -209,6 +213,8 @@ Once serializers are defined, you can instantiate them with several options. Cur 
     | 
|
| 
       209 
213 
     | 
    
         | 
| 
       210 
214 
     | 
    
         
             
            `fields` must be a hash, where keys represent record types and values are list of attributes and relationships of the corresponding type that will be present in serialized object. If some type is missing, that means all attributes and relationships defined in serializer will be serialized. In case of `polymorphic` serializer, you can supply shared fields under polymorphic type. **_There is a caveat, though: if you define a fieldset for a parent polymorphic class and omit fieldsets for subclasses it will be considered that you did not want any of attributes and relationships defined in subclass to be serialized._** It works the same fashion for polymorphic relationships, so if you want only `title` from `Post` and `name` from `Product`, you can supply `{commentable: ["title", "name"]}` as a `fields` parameter for `CommentableSerializer`.
         
     | 
| 
       211 
215 
     | 
    
         | 
| 
      
 216 
     | 
    
         
            +
            `fields` must have attributes as seen by API consumer. For example, if you have `key_transform` set to `:camelize`, then fields will be expected as `{"movie" => ["movieTitle", "releaseYear"]}`, you can use symbols or strings, they will be normallized on serializer instantiation.
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
       212 
218 
     | 
    
         
             
            `include` defines an arbitrary depth tree of included relationships in a similar way as ActiveRecord's `includes`. Bear in mind that `fields` has precedence, which means that if some relationship is missing in fields, it will not be included either.  
         
     | 
| 
       213 
219 
     | 
    
         | 
| 
       214 
220 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -236,23 +242,42 @@ serializer.serialazable_hash(movies, meta: meta) 
     | 
|
| 
       236 
242 
     | 
    
         
             
            serializer.serialized_json(movies, meta: meta)
         
     | 
| 
       237 
243 
     | 
    
         
             
            ```
         
     | 
| 
       238 
244 
     | 
    
         | 
| 
      
 245 
     | 
    
         
            +
            ### Utils
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
            `JsonapiSerializer` provides some convenience methods for converting `fields` and `include` from query parameters into the form accepted by the serializer.
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
            #### Fields converter
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 252 
     | 
    
         
            +
            JsonapiSerializer.convert_fields({"articles" => "title,body", "people" => "name"})
         
     | 
| 
      
 253 
     | 
    
         
            +
            # {articles: [:title, :body], people: [:name]}
         
     | 
| 
      
 254 
     | 
    
         
            +
            ```
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
            #### Include converter
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 259 
     | 
    
         
            +
            JsonapiSerializer.convert_include("author,comments.author,comments.theme")
         
     | 
| 
      
 260 
     | 
    
         
            +
            # {author: {}, comments: {author: {}, theme: {}}}
         
     | 
| 
      
 261 
     | 
    
         
            +
            end
         
     | 
| 
      
 262 
     | 
    
         
            +
            ```
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
       239 
264 
     | 
    
         
             
            ## Performance
         
     | 
| 
       240 
265 
     | 
    
         | 
| 
       241 
266 
     | 
    
         
             
            By running `bin/benchmark` you can launch performance test locally, however numbers are fluctuating widely. The example output is as follows:
         
     | 
| 
       242 
267 
     | 
    
         | 
| 
       243 
268 
     | 
    
         
             
            ### Base case
         
     | 
| 
       244 
269 
     | 
    
         | 
| 
       245 
     | 
    
         
            -
            | 
     | 
| 
       246 
     | 
    
         
            -
            |  
     | 
| 
       247 
     | 
    
         
            -
            | 
     | 
| 
       248 
     | 
    
         
            -
            | 
     | 
| 
      
 270 
     | 
    
         
            +
            |         Adapters         |    10 hash/json (ms)     |    100 hash/json (ms)    |   1000 hash/json (ms)    |   10000 hash/json (ms)   |
         
     | 
| 
      
 271 
     | 
    
         
            +
            | ------------------------ |:------------------------:|:------------------------:|:------------------------:|:------------------------:|
         
     | 
| 
      
 272 
     | 
    
         
            +
            |  JsonapiSerializerTest   |       0.39 / 1.17        |       1.32 / 1.75        |      11.26 / 16.55       |     118.13 / 179.28      |
         
     | 
| 
      
 273 
     | 
    
         
            +
            |     FastJsonapiTest      |       0.16 / 0.19        |       1.12 / 1.60        |      10.71 / 16.02       |     104.76 / 160.39      |
         
     | 
| 
       249 
274 
     | 
    
         | 
| 
       250 
275 
     | 
    
         
             
            ### With includes
         
     | 
| 
       251 
276 
     | 
    
         | 
| 
       252 
     | 
    
         
            -
            | 
     | 
| 
       253 
     | 
    
         
            -
            |  
     | 
| 
       254 
     | 
    
         
            -
            | 
     | 
| 
       255 
     | 
    
         
            -
            | 
     | 
| 
      
 277 
     | 
    
         
            +
            |         Adapters         |    10 hash/json (ms)     |    100 hash/json (ms)    |   1000 hash/json (ms)    |   10000 hash/json (ms)   |
         
     | 
| 
      
 278 
     | 
    
         
            +
            | ------------------------ |:------------------------:|:------------------------:|:------------------------:|:------------------------:|
         
     | 
| 
      
 279 
     | 
    
         
            +
            |  JsonapiSerializerTest   |       0.48 / 0.44        |       1.72 / 2.47        |      13.04 / 17.71       |     125.47 / 179.12      |
         
     | 
| 
      
 280 
     | 
    
         
            +
            |     FastJsonapiTest      |       0.27 / 0.26        |       1.84 / 2.11        |      13.64 / 17.85       |     141.91 / 222.25      |
         
     | 
| 
       256 
281 
     | 
    
         | 
| 
       257 
282 
     | 
    
         
             
            Performance tests do not include any advanced features, such as fieldsets, nested includes or polymorphic serializers, and were mostly intended to make sure that adding these features did not make serializer slower (or at least significantly slower), but there are models prepared to extend these tests. PRs are welcome.
         
     | 
| 
       258 
283 
     | 
    
         | 
    
        data/bin/benchmark
    CHANGED
    
    | 
         @@ -2,16 +2,21 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "bundler/setup"
         
     | 
| 
       4 
4 
     | 
    
         
             
            require "jsonapi_serializer"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "ruby-prof"
         
     | 
| 
       5 
6 
     | 
    
         
             
            require "./perf/runner"
         
     | 
| 
       6 
7 
     | 
    
         
             
            require "./perf/jsonapi_serializer_test"
         
     | 
| 
       7 
8 
     | 
    
         
             
            require "./perf/fast_jsonapi_test"
         
     | 
| 
       8 
9 
     | 
    
         
             
            GC.disable
         
     | 
| 
      
 10 
     | 
    
         
            +
            # RubyProf.measure_mode = RubyProf::WALL_TIME
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
12 
     | 
    
         
             
            runner = Runner.new(10_000)
         
     | 
| 
       11 
13 
     | 
    
         
             
            runner.set_modules(JsonapiSerializerTest, FastJsonapiTest)
         
     | 
| 
       12 
14 
     | 
    
         
             
            runner.set_tests(:base, :with_included)
         
     | 
| 
       13 
15 
     | 
    
         
             
            runner.set_takes(10, 100, 1000, 10_000)
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            # result = RubyProf.profile do
         
     | 
| 
      
 18 
     | 
    
         
            +
              runner.run
         
     | 
| 
      
 19 
     | 
    
         
            +
            # end
         
     | 
| 
       15 
20 
     | 
    
         | 
| 
       16 
21 
     | 
    
         
             
            print "\n"
         
     | 
| 
       17 
22 
     | 
    
         
             
            print "### Base case\n"
         
     | 
| 
         @@ -21,3 +26,6 @@ print "\n" 
     | 
|
| 
       21 
26 
     | 
    
         
             
            print "### With includes\n"
         
     | 
| 
       22 
27 
     | 
    
         
             
            print "\n"
         
     | 
| 
       23 
28 
     | 
    
         
             
            runner.print_table(:with_included)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            # printer = RubyProf::GraphPrinter.new(result)
         
     | 
| 
      
 31 
     | 
    
         
            +
            # printer.print(STDOUT, {})
         
     | 
    
        data/jsonapi_serializer.gemspec
    CHANGED
    
    | 
         @@ -26,5 +26,6 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       26 
26 
     | 
    
         
             
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
       27 
27 
     | 
    
         
             
              spec.add_development_dependency "rspec", "~> 3.0"
         
     | 
| 
       28 
28 
     | 
    
         
             
              spec.add_development_dependency "ffaker", "~> 2.8.1"
         
     | 
| 
      
 29 
     | 
    
         
            +
              spec.add_development_dependency "ruby-prof"
         
     | 
| 
       29 
30 
     | 
    
         
             
              spec.add_development_dependency "fast_jsonapi", "~> 1.0"
         
     | 
| 
       30 
31 
     | 
    
         
             
            end
         
     | 
| 
         @@ -15,12 +15,15 @@ module JsonapiSerializer::Base 
     | 
|
| 
       15 
15 
     | 
    
         
             
                super(opts)
         
     | 
| 
       16 
16 
     | 
    
         
             
                @id = self.class.meta_id
         
     | 
| 
       17 
17 
     | 
    
         
             
                unless opts[:id_only]
         
     | 
| 
       18 
     | 
    
         
            -
                   
     | 
| 
       19 
     | 
    
         
            -
                   
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                   
     | 
| 
      
 18 
     | 
    
         
            +
                  fields = normalize_fields(opts.fetch(:fields, {}))
         
     | 
| 
      
 19 
     | 
    
         
            +
                  if opts[:poly_fields].present?
         
     | 
| 
      
 20 
     | 
    
         
            +
                    fields[@type] = fields.fetch(@type, []) + opts[:poly_fields]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  includes = normalize_includes(opts.fetch(:include, {}))
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  prepare_attributes(fields)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  prepare_relationships(fields, includes)
         
     | 
| 
       24 
27 
     | 
    
         
             
                end
         
     | 
| 
       25 
28 
     | 
    
         
             
              end
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
         @@ -35,19 +38,15 @@ module JsonapiSerializer::Base 
     | 
|
| 
       35 
38 
     | 
    
         
             
              end
         
     | 
| 
       36 
39 
     | 
    
         | 
| 
       37 
40 
     | 
    
         
             
              def relationships_hash(record, context = {})
         
     | 
| 
       38 
     | 
    
         
            -
                @relationships.each_with_object({}) do |(key,  
     | 
| 
       39 
     | 
    
         
            -
                  if  
     | 
| 
       40 
     | 
    
         
            -
                    if  
     | 
| 
       41 
     | 
    
         
            -
                       
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                        id = serializer.id_hash(item)
         
     | 
| 
       44 
     | 
    
         
            -
                        hash[key][:data] << id
         
     | 
| 
       45 
     | 
    
         
            -
                        add_included(serializer, item, id, context) if @includes.has_key?(key)
         
     | 
| 
      
 41 
     | 
    
         
            +
                @relationships.each_with_object({}) do |(key, from, serializer, included), hash|
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if relation = from.call(record)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    if relation.respond_to?(:map)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      relation_ids = relation.map do |item|
         
     | 
| 
      
 45 
     | 
    
         
            +
                        process_relation(item, serializer, context, included)
         
     | 
| 
       46 
46 
     | 
    
         
             
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
                      hash[key] = {data: relation_ids}
         
     | 
| 
       47 
48 
     | 
    
         
             
                    else
         
     | 
| 
       48 
     | 
    
         
            -
                       
     | 
| 
       49 
     | 
    
         
            -
                      hash[key] = {data: id}
         
     | 
| 
       50 
     | 
    
         
            -
                      add_included(serializer, rel, id, context) if @includes.has_key?(key)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      hash[key] = {data: process_relation(relation, serializer, context, included)}
         
     | 
| 
       51 
50 
     | 
    
         
             
                    end
         
     | 
| 
       52 
51 
     | 
    
         
             
                  else
         
     | 
| 
       53 
52 
     | 
    
         
             
                    hash[key] = {data: nil}
         
     | 
| 
         @@ -66,34 +65,37 @@ module JsonapiSerializer::Base 
     | 
|
| 
       66 
65 
     | 
    
         
             
              end
         
     | 
| 
       67 
66 
     | 
    
         | 
| 
       68 
67 
     | 
    
         
             
              private
         
     | 
| 
       69 
     | 
    
         
            -
              def  
     | 
| 
       70 
     | 
    
         
            -
                @ 
     | 
| 
       71 
     | 
    
         
            -
                 
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                   
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
              def prepare_attributes
         
     | 
| 
       78 
     | 
    
         
            -
                key_intersect(@fields[@type], self.class.meta_attributes.keys).each do |key|
         
     | 
| 
       79 
     | 
    
         
            -
                  @attributes << [key, self.class.meta_attributes[key]]
         
     | 
| 
      
 68 
     | 
    
         
            +
              def prepare_attributes(all_fields)
         
     | 
| 
      
 69 
     | 
    
         
            +
                @attributes = []
         
     | 
| 
      
 70 
     | 
    
         
            +
                fields = all_fields[@type]
         
     | 
| 
      
 71 
     | 
    
         
            +
                self.class.meta_attributes.each do |attribute, getter|
         
     | 
| 
      
 72 
     | 
    
         
            +
                  key = JsonapiSerializer.key_transform(attribute)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  if fields.nil? || fields.include?(key)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    @attributes << [key, getter]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
       80 
76 
     | 
    
         
             
                end
         
     | 
| 
       81 
77 
     | 
    
         
             
              end
         
     | 
| 
       82 
78 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
              def prepare_relationships
         
     | 
| 
       84 
     | 
    
         
            -
                 
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                     
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
      
 79 
     | 
    
         
            +
              def prepare_relationships(all_fields, includes)
         
     | 
| 
      
 80 
     | 
    
         
            +
                @relationships = []
         
     | 
| 
      
 81 
     | 
    
         
            +
                relations = all_fields[@type]
         
     | 
| 
      
 82 
     | 
    
         
            +
                self.class.meta_relationships.each do |relation, cfg|
         
     | 
| 
      
 83 
     | 
    
         
            +
                  key = JsonapiSerializer.key_transform(relation)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  if relations.nil? || relations.include?(key)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    included = includes.has_key?(relation)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    serializer = cfg[:serializer].to_s.constantize.new(
         
     | 
| 
      
 87 
     | 
    
         
            +
                      fields: all_fields,
         
     | 
| 
      
 88 
     | 
    
         
            +
                      include: includes.fetch(relation, {}),
         
     | 
| 
      
 89 
     | 
    
         
            +
                      id_only: !included
         
     | 
| 
      
 90 
     | 
    
         
            +
                    )
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @relationships << [relation, cfg[:from], serializer, included]
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
       92 
93 
     | 
    
         
             
                end
         
     | 
| 
       93 
94 
     | 
    
         
             
              end
         
     | 
| 
       94 
95 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
              def  
     | 
| 
       96 
     | 
    
         
            -
                 
     | 
| 
      
 96 
     | 
    
         
            +
              def process_relation(item, serializer, context, included)
         
     | 
| 
      
 97 
     | 
    
         
            +
                id = serializer.id_hash(item)
         
     | 
| 
      
 98 
     | 
    
         
            +
                if included && (context[:tracker][id[:type]] ||= Set.new).add?(id[:id]).present?
         
     | 
| 
       97 
99 
     | 
    
         
             
                  attributes = serializer.attributes_hash(item)
         
     | 
| 
       98 
100 
     | 
    
         
             
                  relationships = serializer.relationships_hash(item, context)
         
     | 
| 
       99 
101 
     | 
    
         
             
                  inc = id.clone
         
     | 
| 
         @@ -101,5 +103,6 @@ module JsonapiSerializer::Base 
     | 
|
| 
       101 
103 
     | 
    
         
             
                  inc[:relationships] = relationships if relationships.present?
         
     | 
| 
       102 
104 
     | 
    
         
             
                  context[:included] << inc
         
     | 
| 
       103 
105 
     | 
    
         
             
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                id
         
     | 
| 
       104 
107 
     | 
    
         
             
              end
         
     | 
| 
       105 
108 
     | 
    
         
             
            end
         
     | 
| 
         @@ -11,6 +11,9 @@ module JsonapiSerializer::DSL 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  @meta_relationships = {}
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  class << self
         
     | 
| 
      
 14 
     | 
    
         
            +
                    alias_method :has_many, :relationship
         
     | 
| 
      
 15 
     | 
    
         
            +
                    alias_method :belongs_to, :relationship
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       14 
17 
     | 
    
         
             
                    attr_reader :meta_type, :meta_id, :meta_attributes, :meta_relationships
         
     | 
| 
       15 
18 
     | 
    
         
             
                  end
         
     | 
| 
       16 
19 
     | 
    
         
             
                end
         
     | 
| 
         @@ -35,7 +38,7 @@ module JsonapiSerializer::DSL 
     | 
|
| 
       35 
38 
     | 
    
         
             
                    attrs.each do |attr|
         
     | 
| 
       36 
39 
     | 
    
         
             
                      case attr
         
     | 
| 
       37 
40 
     | 
    
         
             
                      when Symbol, String
         
     | 
| 
       38 
     | 
    
         
            -
                        @meta_attributes[attr.to_sym] = lambda { |obj| obj.public_send(attr 
     | 
| 
      
 41 
     | 
    
         
            +
                        @meta_attributes[attr.to_sym] = lambda { |obj| obj.public_send(attr) }
         
     | 
| 
       39 
42 
     | 
    
         
             
                      when Hash
         
     | 
| 
       40 
43 
     | 
    
         
             
                        attr.each do |key, val|
         
     | 
| 
       41 
44 
     | 
    
         
             
                          @meta_attributes[key] = lambda { |obj| obj.public_send(val) }
         
     | 
| 
         @@ -45,32 +48,21 @@ module JsonapiSerializer::DSL 
     | 
|
| 
       45 
48 
     | 
    
         
             
                  end
         
     | 
| 
       46 
49 
     | 
    
         | 
| 
       47 
50 
     | 
    
         
             
                  def attribute(attr, &block)
         
     | 
| 
       48 
     | 
    
         
            -
                    @meta_attributes[attr.to_sym] = block
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @meta_attributes[attr.to_sym] = block_given? ? block : lambda { |obj| obj.public_send(attr) }
         
     | 
| 
       49 
52 
     | 
    
         
             
                  end
         
     | 
| 
       50 
53 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                  def  
     | 
| 
       52 
     | 
    
         
            -
                    @meta_relationships[name] = {
         
     | 
| 
       53 
     | 
    
         
            -
                       
     | 
| 
       54 
     | 
    
         
            -
                      from 
     | 
| 
       55 
     | 
    
         
            -
                      serializer: opts[:serializer] || guess_serializer(name.to_s.singularize)
         
     | 
| 
       56 
     | 
    
         
            -
                    }
         
     | 
| 
       57 
     | 
    
         
            -
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def relationship(name, opts = {})
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @meta_relationships[name.to_sym] = {}.tap do |relationship|
         
     | 
| 
      
 56 
     | 
    
         
            +
                      from = opts.fetch(:from, name)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      relationship[:from] = from.respond_to?(:call) ? from : lambda { |r| r.public_send(from) }
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                      from: opts.fetch(:from, name),
         
     | 
| 
       63 
     | 
    
         
            -
                      serializer: opts[:serializer] || guess_serializer(name.to_s)
         
     | 
| 
       64 
     | 
    
         
            -
                    }
         
     | 
| 
      
 59 
     | 
    
         
            +
                      serializer = opts[:serializer]
         
     | 
| 
      
 60 
     | 
    
         
            +
                      relationship[:serializer] = serializer ? serializer.to_s : "#{name.to_s.singularize.classify}Serializer"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
       65 
62 
     | 
    
         
             
                  end
         
     | 
| 
       66 
63 
     | 
    
         | 
| 
       67 
64 
     | 
    
         
             
                  def inherited(subclass)
         
     | 
| 
       68 
     | 
    
         
            -
                    raise "You attempted to inherit  
     | 
| 
       69 
     | 
    
         
            -
                  end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                  private
         
     | 
| 
       72 
     | 
    
         
            -
                  def guess_serializer(name)
         
     | 
| 
       73 
     | 
    
         
            -
                    "#{name.classify}Serializer".constantize
         
     | 
| 
      
 65 
     | 
    
         
            +
                    raise "You attempted to inherit from #{self.name}, if you want to create Polymorphic serializer, include JsonapiSerializer::Polymorphic"
         
     | 
| 
       74 
66 
     | 
    
         
             
                  end
         
     | 
| 
       75 
67 
     | 
    
         
             
                end
         
     | 
| 
       76 
68 
     | 
    
         
             
              end
         
     | 
| 
         @@ -25,7 +25,7 @@ module JsonapiSerializer::DSL 
     | 
|
| 
       25 
25 
     | 
    
         
             
                  end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                  def polymorphic_for(*serializers)
         
     | 
| 
       28 
     | 
    
         
            -
                    @meta_poly += serializers
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @meta_poly += serializers.map(&:to_s)
         
     | 
| 
       29 
29 
     | 
    
         
             
                  end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                  def inherited(subclass)
         
     | 
| 
         @@ -37,7 +37,7 @@ module JsonapiSerializer::DSL 
     | 
|
| 
       37 
37 
     | 
    
         
             
                      @meta_id = parent.meta_id
         
     | 
| 
       38 
38 
     | 
    
         
             
                      @meta_inherited = true
         
     | 
| 
       39 
39 
     | 
    
         
             
                    end
         
     | 
| 
       40 
     | 
    
         
            -
                    @meta_poly << subclass
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @meta_poly << subclass.to_s
         
     | 
| 
       41 
41 
     | 
    
         
             
                  end
         
     | 
| 
       42 
42 
     | 
    
         
             
                end
         
     | 
| 
       43 
43 
     | 
    
         
             
              end
         
     | 
| 
         @@ -15,14 +15,10 @@ module JsonapiSerializer::Polymorphic 
     | 
|
| 
       15 
15 
     | 
    
         
             
              def initialize(opts = {})
         
     | 
| 
       16 
16 
     | 
    
         
             
                super(opts)
         
     | 
| 
       17 
17 
     | 
    
         
             
                unless self.class.meta_inherited
         
     | 
| 
       18 
     | 
    
         
            -
                  unless self.class.meta_resolver.respond_to? :call
         
     | 
| 
       19 
     | 
    
         
            -
                    raise "Polymorphic serializer must implement a block resolving an object into type."
         
     | 
| 
       20 
     | 
    
         
            -
                  end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
18 
     | 
    
         
             
                  poly_fields = [*opts.dig(:fields, @type)].map { |f| JsonapiSerializer.key_transform(f) }
         
     | 
| 
       23 
19 
     | 
    
         
             
                  if self.class.meta_poly.present?
         
     | 
| 
       24 
20 
     | 
    
         
             
                    @poly = self.class.meta_poly.each_with_object({}) do |poly_class, hash|
         
     | 
| 
       25 
     | 
    
         
            -
                      serializer = poly_class.new(opts.merge poly_fields: poly_fields)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      serializer = poly_class.constantize.new(opts.merge poly_fields: poly_fields)
         
     | 
| 
       26 
22 
     | 
    
         
             
                      hash[serializer.type] = serializer
         
     | 
| 
       27 
23 
     | 
    
         
             
                    end
         
     | 
| 
       28 
24 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -57,6 +53,10 @@ module JsonapiSerializer::Polymorphic 
     | 
|
| 
       57 
53 
     | 
    
         | 
| 
       58 
54 
     | 
    
         
             
              private
         
     | 
| 
       59 
55 
     | 
    
         
             
              def serializer_for(record)
         
     | 
| 
       60 
     | 
    
         
            -
                @poly[self.class.meta_resolver.call(record)] 
     | 
| 
      
 56 
     | 
    
         
            +
                if serializer = @poly[self.class.meta_resolver.call(record)]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  serializer
         
     | 
| 
      
 58 
     | 
    
         
            +
                else
         
     | 
| 
      
 59 
     | 
    
         
            +
                 raise "Could not resolve serializer for #{record} associated with #{self.class.name}"
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
       61 
61 
     | 
    
         
             
              end
         
     | 
| 
       62 
62 
     | 
    
         
             
            end
         
     | 
| 
         @@ -22,6 +22,12 @@ module JsonapiSerializer::Utils 
     | 
|
| 
       22 
22 
     | 
    
         
             
                end
         
     | 
| 
       23 
23 
     | 
    
         
             
              end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
              def normalize_fields(fields)
         
     | 
| 
      
 26 
     | 
    
         
            +
                fields.each_with_object({}) do |(type, attributes), hash|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  hash[type.to_sym] = [*attributes].map(&:to_sym)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       25 
31 
     | 
    
         
             
              def apply_splat(item, &block)
         
     | 
| 
       26 
32 
     | 
    
         
             
                if item.is_a? Array
         
     | 
| 
       27 
33 
     | 
    
         
             
                  item.map { |i| block.call(i) }
         
     | 
    
        data/perf/models.rb
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'ffaker'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class Book
         
     | 
| 
       4 
     | 
    
         
            -
              attr_accessor :id, :name, :isbn, :year, :author, :author_id, :characters, :character_ids 
     | 
| 
      
 4 
     | 
    
         
            +
              attr_accessor :id, :name, :isbn, :year, :author, :author_id, :characters, :character_ids
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
              @characters = []
         
     | 
| 
       7 
7 
     | 
    
         
             
              @character_ids = []
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: jsonapi_serializer
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Ivan Yurov
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2018-02- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2018-02-27 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: activesupport
         
     | 
| 
         @@ -108,6 +108,20 @@ dependencies: 
     | 
|
| 
       108 
108 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       109 
109 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       110 
110 
     | 
    
         
             
                    version: 2.8.1
         
     | 
| 
      
 111 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 112 
     | 
    
         
            +
              name: ruby-prof
         
     | 
| 
      
 113 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 114 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 116 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 118 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 119 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 120 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 121 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 122 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 123 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 124 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       111 
125 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       112 
126 
     | 
    
         
             
              name: fast_jsonapi
         
     | 
| 
       113 
127 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -132,6 +146,7 @@ files: 
     | 
|
| 
       132 
146 
     | 
    
         
             
            - ".gitignore"
         
     | 
| 
       133 
147 
     | 
    
         
             
            - ".rspec"
         
     | 
| 
       134 
148 
     | 
    
         
             
            - ".travis.yml"
         
     | 
| 
      
 149 
     | 
    
         
            +
            - CHANGELOG.md
         
     | 
| 
       135 
150 
     | 
    
         
             
            - CODE_OF_CONDUCT.md
         
     | 
| 
       136 
151 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       137 
152 
     | 
    
         
             
            - LICENSE.txt
         
     | 
| 
         @@ -174,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       174 
189 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       175 
190 
     | 
    
         
             
            requirements: []
         
     | 
| 
       176 
191 
     | 
    
         
             
            rubyforge_project: 
         
     | 
| 
       177 
     | 
    
         
            -
            rubygems_version: 2. 
     | 
| 
      
 192 
     | 
    
         
            +
            rubygems_version: 2.7.6
         
     | 
| 
       178 
193 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       179 
194 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       180 
195 
     | 
    
         
             
            summary: Alternative JSONApi serializer
         
     |