lp-serializable 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ 2.5.0
@@ -2,4 +2,6 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.5.0
5
- before_install: gem install bundler -v 1.16.1
5
+ before_install: gem install bundler -v 1.16.3
6
+ ignore:
7
+ - spec/lib/object_serializer_performance.rb
@@ -1,12 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lp-serializable (0.1.0)
5
- fast_jsonapi
4
+ lp-serializable (1.0.0)
5
+ activesupport (>= 4.2)
6
+ fast_jsonapi (>= 1.3)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ actionpack (5.2.0)
12
+ actionview (= 5.2.0)
13
+ activesupport (= 5.2.0)
14
+ rack (~> 2.0)
15
+ rack-test (>= 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
18
+ actionview (5.2.0)
19
+ activesupport (= 5.2.0)
20
+ builder (~> 3.1)
21
+ erubi (~> 1.4)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
24
+ active_model_serializers (0.10.7)
25
+ actionpack (>= 4.1, < 6)
26
+ activemodel (>= 4.1, < 6)
27
+ case_transform (>= 0.2)
28
+ jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
29
+ activemodel (5.2.0)
30
+ activesupport (= 5.2.0)
31
+ activerecord (5.2.0)
32
+ activemodel (= 5.2.0)
33
+ activesupport (= 5.2.0)
34
+ arel (>= 9.0)
10
35
  activesupport (5.2.0)
11
36
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
37
  i18n (>= 0.7, < 2)
@@ -16,23 +41,58 @@ GEM
16
41
  bundler
17
42
  rake
18
43
  thor (>= 0.14.0)
44
+ arel (9.0.0)
45
+ benchmark-perf (0.2.1)
46
+ builder (3.2.3)
47
+ byebug (10.0.2)
48
+ case_transform (0.2)
49
+ activesupport
19
50
  coderay (1.1.2)
20
- concurrent-ruby (1.0.5)
51
+ concurrent-ruby (1.1.5)
52
+ crass (1.0.4)
21
53
  diff-lcs (1.3)
22
- fast_jsonapi (1.2)
54
+ erubi (1.8.0)
55
+ fast_jsonapi (1.5)
23
56
  activesupport (>= 4.2)
24
- i18n (1.0.1)
57
+ i18n (1.6.0)
25
58
  concurrent-ruby (~> 1.0)
59
+ jsonapi-deserializable (0.2.0)
60
+ jsonapi-rb (0.5.0)
61
+ jsonapi-deserializable (~> 0.2.0)
62
+ jsonapi-serializable (~> 0.3.0)
63
+ jsonapi-renderer (0.2.0)
64
+ jsonapi-serializable (0.3.0)
65
+ jsonapi-renderer (~> 0.2.0)
66
+ jsonapi-serializers (1.0.1)
67
+ activesupport
68
+ loofah (2.2.3)
69
+ crass (~> 1.0.2)
70
+ nokogiri (>= 1.5.9)
26
71
  method_source (0.9.0)
72
+ mini_portile2 (2.4.0)
27
73
  minitest (5.11.3)
74
+ nokogiri (1.10.2)
75
+ mini_portile2 (~> 2.4.0)
76
+ oj (3.6.3)
28
77
  pry (0.11.3)
29
78
  coderay (~> 1.1.0)
30
79
  method_source (~> 0.9.0)
80
+ rack (2.0.6)
81
+ rack-test (1.0.0)
82
+ rack (>= 1.0, < 3)
83
+ rails-dom-testing (2.0.3)
84
+ activesupport (>= 4.2.0)
85
+ nokogiri (>= 1.6)
86
+ rails-html-sanitizer (1.0.4)
87
+ loofah (~> 2.2, >= 2.2.2)
31
88
  rake (10.5.0)
32
89
  rspec (3.7.0)
33
90
  rspec-core (~> 3.7.0)
34
91
  rspec-expectations (~> 3.7.0)
35
92
  rspec-mocks (~> 3.7.0)
93
+ rspec-benchmark (0.3.0)
94
+ benchmark-perf (~> 0.2.0)
95
+ rspec (>= 3.0.0, < 4.0.0)
36
96
  rspec-core (3.7.1)
37
97
  rspec-support (~> 3.7.0)
38
98
  rspec-expectations (3.7.0)
@@ -42,6 +102,7 @@ GEM
42
102
  diff-lcs (>= 1.2.0, < 2.0)
43
103
  rspec-support (~> 3.7.0)
44
104
  rspec-support (3.7.1)
105
+ sqlite3 (1.3.13)
45
106
  thor (0.20.0)
46
107
  thread_safe (0.3.6)
47
108
  tzinfo (1.2.5)
@@ -51,12 +112,20 @@ PLATFORMS
51
112
  ruby
52
113
 
53
114
  DEPENDENCIES
115
+ active_model_serializers (~> 0.10.7)
116
+ activerecord (>= 4.2)
54
117
  appraisal
55
118
  bundler (~> 1.16)
119
+ byebug
120
+ jsonapi-rb (~> 0.5.0)
121
+ jsonapi-serializers (~> 1.0.0)
56
122
  lp-serializable!
123
+ oj (~> 3.3)
57
124
  pry
58
125
  rake (~> 10.0)
59
126
  rspec (~> 3.0)
127
+ rspec-benchmark (~> 0.3.0)
128
+ sqlite3 (~> 1.3)
60
129
 
61
130
  BUNDLED WITH
62
- 1.16.1
131
+ 1.16.3
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Lp::Serializable
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/lp/serializable`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ When serializing with [fast_jsonapi](https://github.com/Netflix/fast_jsonapi), data is structured per the json-api [specs](http://jsonapi.org/format/).
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ lp-serializable is a thin wrapper around fast_jsonapi serialization, producting AMS style output.
6
+
7
+ lp-serializable is intended to be used in Rails controllers.
6
8
 
7
9
  ## Installation
8
10
 
@@ -22,7 +24,170 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ **Controller Definition**
28
+
29
+ ```ruby
30
+ class ApplicationController < ActionController::Base
31
+ include Lp::Serializable
32
+ end
33
+
34
+ class MoviesController < ApplicationController
35
+ def index
36
+ movies = Movie.all
37
+ movies_hash = serializable_collection(movies, 'Movie')
38
+ render json: movies_hash
39
+ end
40
+
41
+ def show
42
+ movie = Movie.find(params[:id])
43
+ movie_hash = serializable(movie)
44
+ render json: movie_hash
45
+ end
46
+ end
47
+ ```
48
+
49
+ **Serializer Definition**
50
+
51
+ ```ruby
52
+ class MovieSerializer
53
+ include FastJsonapi::ObjectSerializer
54
+
55
+ attributes :name
56
+
57
+ attribute :year, if: Proc.new { |object| object.year.present? }
58
+
59
+ attribute :last_updated do |object|
60
+ object.updated_at
61
+ end
62
+
63
+ has_many :actors
64
+ belongs_to :owner
65
+ end
66
+
67
+ class ActorSerializer
68
+ include FastJsonapi::ObjectSerializer
69
+
70
+ attributes :id
71
+ end
72
+
73
+ class OwnerSerializer
74
+ include FastJsonapi::ObjectSerializer
75
+
76
+ attributes :id
77
+ end
78
+ ```
79
+
80
+ ## Object Serialization
81
+ **Sample Object**
82
+
83
+ ```ruby
84
+ movie = Movie.new
85
+ movie.id = 232
86
+ movie.name = 'test movie'
87
+ movie.actor_ids = [1, 2, 3]
88
+ movie.owner_id = 3
89
+ movie.movie_type_id = 1
90
+ movie
91
+ ```
92
+
93
+ **Return a hash**
94
+ ```ruby
95
+ hash = serializable(movie)
96
+ ```
97
+
98
+ **Output**
99
+
100
+ ```json
101
+ {
102
+ "data": {
103
+ "id": "3",
104
+ "type": "movie",
105
+ "name": "test movie",
106
+ "last_updated": "2019-04-26 18:55:46 UTC",
107
+ "actors": [
108
+ {
109
+ "id": "1",
110
+ "type": "actor"
111
+ },
112
+ {
113
+ "id": "2",
114
+ "type": "actor"
115
+ }
116
+ ],
117
+ "owner": {
118
+ "id": "3",
119
+ "type": "user"
120
+ }
121
+ }
122
+ }
123
+
124
+ ```
125
+
126
+ For more information on configuration, refer to [fast_jsonapi](https://github.com/Netflix/fast_jsonapi#customizable-options) documentation.
127
+
128
+ ## Deeply Nested Serialization Pattern
129
+
130
+ Fastjson API does not support serialization of deeply nested resources.
131
+
132
+ To get around this, extend `Lp::Serializable` in your serializers:
133
+
134
+ ```ruby
135
+ class MovieSerializer
136
+ include FastJsonapi::ObjectSerializer
137
+ extend Lp::Serializable
138
+
139
+ ...
140
+ end
141
+ ```
142
+
143
+ Define custom attributes for relationships, instead of defining them via fastjson_api:
144
+
145
+ ```ruby
146
+ class MovieSerializer
147
+ include FastJsonapi::ObjectSerializer
148
+ extend Lp::Serializable
149
+
150
+ attribute :actors do |object|
151
+ collection = object.actors
152
+ serializer = 'Actor'
153
+ serializable_collection(collection, serializer, nested: true)
154
+ end
155
+ end
156
+ ```
157
+
158
+ Attribute `:actors` will trigger `ActorSerializer` to serialize the actors collection. Consequently, any relationships defined in `ActorSerializer` via custom attributes and serialized with `serializable_` methods (using the `nested: true` option) will be appropriately nested.
159
+
160
+ ## Custom Serializer Class
161
+
162
+ Use `#serializable_class` to serialize with a custom class:
163
+
164
+ ```ruby
165
+ def show
166
+ movie = Movie.find(params[:id])
167
+ # Will serialize with FilmSerializer instead of MovieSerializer
168
+ movie_hash = serializable_class(movie, 'Film')
169
+ render json: movie_hash
170
+ end
171
+ ```
172
+
173
+ ## Options Support
174
+
175
+ Supported options include:
176
+
177
+ - `:fields` ([Sparse Fieldsets](https://github.com/Netflix/fast_jsonapi#sparse-fieldsets))
178
+ - `:params` ([Params](https://github.com/Netflix/fast_jsonapi#params))
179
+ - [Conditional Attributes](https://github.com/Netflix/fast_jsonapi#conditional-attributes)
180
+
181
+ Other options are "supported" but may yeild unexpected results, as Serializable's hash flattening prioritizes deeply nested data structures.
182
+
183
+ `:is_collection` is baked into Seriazable methods for accurate detection of collections or singular resources.
184
+
185
+ ## Aliases
186
+
187
+ - `serialize_and_flatten()` = `serializable()`
188
+ - `serialize_and_flatten_with_class_name()` = `serializable_class()`
189
+ - `serialize_and_flatten_collection()` = `serializable_collection()`
190
+
26
191
 
27
192
  ## Development
28
193
 
@@ -32,7 +197,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
197
 
33
198
  ## Contributing
34
199
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lp-serializable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
200
+ Bug reports and pull requests are welcome on GitHub at https://github.com/LaunchPadLab/lp-serializable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
201
 
37
202
  ## License
38
203
 
@@ -40,4 +205,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
40
205
 
41
206
  ## Code of Conduct
42
207
 
43
- Everyone interacting in the Lp::Serializable project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lp-serializable/blob/master/CODE_OF_CONDUCT.md).
208
+ Everyone interacting in the Lp::Serializable project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/LaunchPadLab/lp-serializable/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ # Usage:
6
+ # class Movie
7
+ # def to_json(payload)
8
+ # FastJsonapi::MultiToJson.to_json(payload)
9
+ # end
10
+ # end
11
+ module FastJsonapi
12
+ module MultiToJson
13
+ # Result object pattern is from https://johnnunemaker.com/resilience-in-ruby/
14
+ # e.g. https://github.com/github/github-ds/blob/fbda5389711edfb4c10b6c6bad19311dfcb1bac1/lib/github/result.rb
15
+ class Result
16
+ def initialize(*rescued_exceptions)
17
+ @rescued_exceptions = if rescued_exceptions.empty?
18
+ [StandardError]
19
+ else
20
+ rescued_exceptions
21
+ end
22
+
23
+ @value = yield
24
+ @error = nil
25
+ rescue *rescued_exceptions => e
26
+ @error = e
27
+ end
28
+
29
+ def ok?
30
+ @error.nil?
31
+ end
32
+
33
+ def value!
34
+ if ok?
35
+ @value
36
+ else
37
+ raise @error
38
+ end
39
+ end
40
+
41
+ def rescue
42
+ return self if ok?
43
+
44
+ Result.new(*@rescued_exceptions) { yield(@error) }
45
+ end
46
+ end
47
+
48
+ def self.logger(device=nil)
49
+ return @logger = Logger.new(device) if device
50
+ @logger ||= Logger.new(IO::NULL)
51
+ end
52
+
53
+ # Encoder-compatible with default MultiJSON adapters and defaults
54
+ def self.to_json_method
55
+ encode_method = String.new(%(def _fast_to_json(object)\n ))
56
+ encode_method << Result.new(LoadError) {
57
+ require 'oj'
58
+ %(::Oj.dump(object, mode: :compat, time_format: :ruby, use_to_json: true))
59
+ }.rescue {
60
+ require 'yajl'
61
+ %(::Yajl::Encoder.encode(object))
62
+ }.rescue {
63
+ require 'jrjackson' unless defined?(::JrJackson)
64
+ %(::JrJackson::Json.dump(object))
65
+ }.rescue {
66
+ require 'json'
67
+ %(JSON.fast_generate(object, create_additions: false, quirks_mode: true))
68
+ }.rescue {
69
+ require 'gson'
70
+ %(::Gson::Encoder.new({}).encode(object))
71
+ }.rescue {
72
+ require 'active_support/json/encoding'
73
+ %(::ActiveSupport::JSON.encode(object))
74
+ }.rescue {
75
+ warn "No JSON encoder found. Falling back to `object.to_json`"
76
+ %(object.to_json)
77
+ }.value!
78
+ encode_method << "\nend"
79
+ end
80
+
81
+ def self.to_json(object)
82
+ _fast_to_json(object)
83
+ rescue NameError
84
+ define_to_json(FastJsonapi::MultiToJson)
85
+ _fast_to_json(object)
86
+ end
87
+
88
+ def self.define_to_json(receiver)
89
+ cl = caller_locations[0]
90
+ method_body = to_json_method
91
+ logger.debug { "Defining #{receiver}._fast_to_json as #{method_body.inspect}" }
92
+ receiver.instance_eval method_body, cl.absolute_path, cl.lineno
93
+ end
94
+
95
+ def self.reset_to_json!
96
+ undef :_fast_to_json if method_defined?(:_fast_to_json)
97
+ logger.debug { "Undefining #{receiver}._fast_to_json" }
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object'
4
+ require 'active_support/concern'
5
+ require 'active_support/inflector'
6
+ require 'fast_jsonapi/serialization_core'
7
+ require 'fast_jsonapi/attribute'
8
+
9
+ module FastJsonapi
10
+ module ObjectSerializer
11
+ extend ActiveSupport::Concern
12
+ include SerializationCore
13
+
14
+ SERIALIZABLE_HASH_NOTIFICATION = 'render.fast_jsonapi.serializable_hash'
15
+ SERIALIZED_JSON_NOTIFICATION = 'render.fast_jsonapi.serialized_json'
16
+
17
+ included do
18
+ # Set record_type based on the name of the serializer class
19
+ set_type(reflected_record_type) if reflected_record_type
20
+ end
21
+
22
+ def initialize(resource, options = {})
23
+ process_options(options)
24
+
25
+ @resource = resource
26
+ end
27
+
28
+ def serializable_hash
29
+ return hash_for_collection if is_collection?(@resource)
30
+
31
+ hash_for_one_record
32
+ end
33
+ alias_method :to_hash, :serializable_hash
34
+
35
+ def hash_for_one_record
36
+ serializable_hash = { data: nil }
37
+ serializable_hash[:meta] = @meta if @meta.present?
38
+ serializable_hash[:links] = @links if @links.present?
39
+
40
+ return serializable_hash unless @resource
41
+
42
+ serializable_hash[:data] = self.class.record_hash(@resource, @params)
43
+ serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
44
+ serializable_hash
45
+ end
46
+
47
+ def hash_for_collection
48
+ serializable_hash = {}
49
+
50
+ data = []
51
+ included = []
52
+ @resource.each do |record|
53
+ data << self.class.record_hash(record, @params)
54
+ included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
55
+ end
56
+
57
+ serializable_hash[:data] = data
58
+ serializable_hash[:included] = included if @includes.present?
59
+ serializable_hash[:meta] = @meta if @meta.present?
60
+ serializable_hash[:links] = @links if @links.present?
61
+ serializable_hash
62
+ end
63
+
64
+ def serialized_json
65
+ self.class.to_json(serializable_hash)
66
+ end
67
+
68
+ private
69
+
70
+ def process_options(options)
71
+ return if options.blank?
72
+
73
+ @known_included_objects = {}
74
+ @meta = options[:meta]
75
+ @links = options[:links]
76
+ @params = options[:params] || {}
77
+ raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
78
+
79
+ if options[:include].present?
80
+ @includes = options[:include].delete_if(&:blank?).map(&:to_sym)
81
+ self.class.validate_includes!(@includes)
82
+ end
83
+ end
84
+
85
+ def is_collection?(resource)
86
+ resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
87
+ end
88
+
89
+ class_methods do
90
+
91
+ def inherited(subclass)
92
+ super(subclass)
93
+ subclass.attributes_to_serialize = attributes_to_serialize.dup if attributes_to_serialize.present?
94
+ subclass.relationships_to_serialize = relationships_to_serialize.dup if relationships_to_serialize.present?
95
+ subclass.cachable_relationships_to_serialize = cachable_relationships_to_serialize.dup if cachable_relationships_to_serialize.present?
96
+ subclass.uncachable_relationships_to_serialize = uncachable_relationships_to_serialize.dup if uncachable_relationships_to_serialize.present?
97
+ subclass.transform_method = transform_method
98
+ subclass.cache_length = cache_length
99
+ subclass.race_condition_ttl = race_condition_ttl
100
+ subclass.data_links = data_links
101
+ subclass.cached = cached
102
+ end
103
+
104
+ def reflected_record_type
105
+ return @reflected_record_type if defined?(@reflected_record_type)
106
+
107
+ @reflected_record_type ||= begin
108
+ if self.name.end_with?('Serializer')
109
+ self.name.split('::').last.chomp('Serializer').underscore.to_sym
110
+ end
111
+ end
112
+ end
113
+
114
+ def set_key_transform(transform_name)
115
+ mapping = {
116
+ camel: :camelize,
117
+ camel_lower: [:camelize, :lower],
118
+ dash: :dasherize,
119
+ underscore: :underscore
120
+ }
121
+ self.transform_method = mapping[transform_name.to_sym]
122
+ end
123
+
124
+ def run_key_transform(input)
125
+ if self.transform_method.present?
126
+ input.to_s.send(*@transform_method).to_sym
127
+ else
128
+ input.to_sym
129
+ end
130
+ end
131
+
132
+ def use_hyphen
133
+ warn('DEPRECATION WARNING: use_hyphen is deprecated and will be removed from fast_jsonapi 2.0 use (set_key_transform :dash) instead')
134
+ set_key_transform :dash
135
+ end
136
+
137
+ def set_type(type_name)
138
+ self.record_type = run_key_transform(type_name)
139
+ end
140
+
141
+ def set_id(id_name)
142
+ self.record_id = id_name
143
+ end
144
+
145
+ def cache_options(cache_options)
146
+ self.cached = cache_options[:enabled] || false
147
+ self.cache_length = cache_options[:cache_length] || 5.minutes
148
+ self.race_condition_ttl = cache_options[:race_condition_ttl] || 5.seconds
149
+ end
150
+
151
+ def attributes(*attributes_list, &block)
152
+ attributes_list = attributes_list.first if attributes_list.first.class.is_a?(Array)
153
+ options = attributes_list.last.is_a?(Hash) ? attributes_list.pop : {}
154
+ self.attributes_to_serialize = {} if self.attributes_to_serialize.nil?
155
+
156
+ attributes_list.each do |attr_name|
157
+ method_name = attr_name
158
+ key = run_key_transform(method_name)
159
+ attributes_to_serialize[key] = Attribute.new(
160
+ key: key,
161
+ method: block || method_name,
162
+ options: options
163
+ )
164
+ end
165
+ end
166
+
167
+ alias_method :attribute, :attributes
168
+
169
+ def add_relationship(name, relationship)
170
+ self.relationships_to_serialize = {} if relationships_to_serialize.nil?
171
+ self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
172
+ self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
173
+
174
+ if !relationship[:cached]
175
+ self.uncachable_relationships_to_serialize[name] = relationship
176
+ else
177
+ self.cachable_relationships_to_serialize[name] = relationship
178
+ end
179
+ self.relationships_to_serialize[name] = relationship
180
+ end
181
+
182
+ def has_many(relationship_name, options = {}, &block)
183
+ name = relationship_name.to_sym
184
+ hash = create_relationship_hash(relationship_name, :has_many, options, block)
185
+ add_relationship(name, hash)
186
+ end
187
+
188
+ def has_one(relationship_name, options = {}, &block)
189
+ name = relationship_name.to_sym
190
+ hash = create_relationship_hash(relationship_name, :has_one, options, block)
191
+ add_relationship(name, hash)
192
+ end
193
+
194
+ def belongs_to(relationship_name, options = {}, &block)
195
+ name = relationship_name.to_sym
196
+ hash = create_relationship_hash(relationship_name, :belongs_to, options, block)
197
+ add_relationship(name, hash)
198
+ end
199
+
200
+ def create_relationship_hash(base_key, relationship_type, options, block)
201
+ name = base_key.to_sym
202
+ if relationship_type == :has_many
203
+ base_serialization_key = base_key.to_s.singularize
204
+ base_key_sym = base_serialization_key.to_sym
205
+ id_postfix = '_ids'
206
+ else
207
+ base_serialization_key = base_key
208
+ base_key_sym = name
209
+ id_postfix = '_id'
210
+ end
211
+ {
212
+ key: options[:key] || run_key_transform(base_key),
213
+ name: name,
214
+ id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
215
+ record_type: options[:record_type] || run_key_transform(base_key_sym),
216
+ object_method_name: options[:object_method_name] || name,
217
+ object_block: block,
218
+ serializer: compute_serializer_name(options[:serializer] || base_key_sym),
219
+ relationship_type: relationship_type,
220
+ cached: options[:cached] || false,
221
+ polymorphic: fetch_polymorphic_option(options)
222
+ }
223
+ end
224
+
225
+ def compute_serializer_name(serializer_key)
226
+ return serializer_key unless serializer_key.is_a? Symbol
227
+ namespace = self.name.gsub(/()?\w+Serializer$/, '')
228
+ serializer_name = serializer_key.to_s.classify + 'Serializer'
229
+ (namespace + serializer_name).to_sym
230
+ end
231
+
232
+ def fetch_polymorphic_option(options)
233
+ option = options[:polymorphic]
234
+ return false unless option.present?
235
+ return option if option.respond_to? :keys
236
+ {}
237
+ end
238
+
239
+ def link(link_name, link_method_name = nil, &block)
240
+ self.data_links = {} if self.data_links.nil?
241
+ link_method_name = link_name if link_method_name.nil?
242
+ key = run_key_transform(link_name)
243
+ self.data_links[key] = block || link_method_name
244
+ end
245
+
246
+ def validate_includes!(includes)
247
+ return if includes.blank?
248
+
249
+ includes.detect do |include_item|
250
+ klass = self
251
+ parse_include_item(include_item).each do |parsed_include|
252
+ relationship_to_include = klass.relationships_to_serialize[parsed_include]
253
+ raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
254
+ raise NotImplementedError if relationship_to_include[:polymorphic].is_a?(Hash)
255
+ klass = relationship_to_include[:serializer].to_s.constantize
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end