rails_json_serializer 1.1.3 → 2.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87d8f10823e559a993218286bc85c0598e077f59e10a23ce0694f3cd41645100
4
- data.tar.gz: 8b3983a64085b1a7d2e75cfaf10455c93387059cf5e0adb9cd5fec1f19ce2c3f
3
+ metadata.gz: d4ed73e603bb8c02b8999399e76e1d3e359d502db69ede57a800229e1c9a12f9
4
+ data.tar.gz: 97cb1d7ac4a6b975d57f23406828783242121f0b3c63f57243e0ed125525733a
5
5
  SHA512:
6
- metadata.gz: b0dc86ef87ab82c921885e043c831c07f5e8d47ad3d497316c647522016e2497c07891c7a4b9d52f3b215b091e0c6f7872023365f6480ec0321242d4374a0d17
7
- data.tar.gz: 2aae2a124dad83d9c04d3088a5539a43f4e47f1b96d3239b9ca755e629a3dad8e9ced58f7c85e2f549c4b94f0d5be2781c751803a8a98b8a83094d75b4013f16
6
+ metadata.gz: ea0aa2817b2c46c27ff246d0b6e9df2bfbab06ad429d2e99a53f75bb258e81bb3cf9c5f27038992b0ff0037a270815fe9bde69524d3f7ed741bed7ca64f3138a
7
+ data.tar.gz: 721fefb41c3ed4892a0899f370eff95e2fdda0d77810137e379fcc1eed3241001c22bda2ebc232d321c99f365479f2757b959c2386febafba965b8d470281663
@@ -1,8 +1,6 @@
1
- require 'active_support/concern'
2
- require_relative 'serializer/application_serializer'
1
+ require_relative 'serializer/model_serializer'
3
2
  Dir[File.join(Rails.root, 'app', 'serializers', '**', '*.rb')].each {|file| require file }
4
3
  require_relative 'serializer/configuration'
5
- require_relative 'serializer/concern'
6
4
 
7
5
  module Serializer
8
6
  # config src: http://lizabinante.com/blog/creating-a-configurable-ruby-gem/
@@ -24,4 +22,4 @@ module Serializer
24
22
  end
25
23
 
26
24
  # include the extension
27
- ActiveRecord::Base.send(:include, Serializer::Concern)
25
+ # ActiveRecord::Base.send(:include, Serializer::Concern)
@@ -0,0 +1,202 @@
1
+ module ModelSerializer
2
+ # klazz is that class object that included this module
3
+ def self.included klass
4
+ # START CLASS EVAL
5
+ klass.class_eval do
6
+
7
+ # Rails 5 has autoloading issues with modules. They will show as not defined, when available.
8
+ if Rails.env.development?
9
+ begin
10
+ "#{klass.name}Serializer".constantize
11
+ rescue NameError => e
12
+ end
13
+ end
14
+
15
+ if self.const_defined?("#{klass.name}Serializer")
16
+ serializer_klass = "#{klass.name}Serializer".constantize
17
+ serializer_query_names = serializer_klass.public_instance_methods
18
+ if klass.superclass.const_defined?("SERIALIZER_QUERY_KEYS_CACHE")
19
+ self.const_set('SERIALIZER_QUERY_KEYS_CACHE', (serializer_query_names + klass.superclass::SERIALIZER_QUERY_KEYS_CACHE).uniq)
20
+ else
21
+ self.const_set('SERIALIZER_QUERY_KEYS_CACHE', serializer_query_names)
22
+ end
23
+ # Inject class methods, will have access to those queries on the class.
24
+ klass.send(:extend, serializer_klass)
25
+
26
+ # no need to define it if inheriting class has defined it OR has been manually overridden.
27
+ # CLASS METHODS
28
+ klass.send(:define_singleton_method, :serializer) do |opts = {}|
29
+ query = opts[:json_query_override].present? ? self.send(opts[:json_query_override], opts) : serializer_query(opts)
30
+ if Serializer.configuration.enable_includes && query[:include].present? && !opts[:skip_eager_loading]
31
+ includes(generate_includes_from_json_query(query)).as_json(query)
32
+ else
33
+ # Have to use 'all' gets stack level too deep otherwise. Not sure why.
34
+ all.as_json(query)
35
+ end
36
+ end
37
+
38
+ klass.send(:define_singleton_method, :generate_includes_from_json_query) do |options = {}, klass = nil|
39
+ query_filter = {}
40
+ klass = self if klass.nil?
41
+ if options[:include].present? && !options[:skip_eager_loading]
42
+ options[:include].each do |include_key, include_hash|
43
+ next if include_hash[:skip_eager_loading] == true
44
+ # Will 'next' if there is a scope that takes arguments, an instance-dependent scope.
45
+ # Can't eager load when assocation has a instance condition for it's associative scope.
46
+ # Might not be a real assocation
47
+ next if klass.reflect_on_association(include_key).nil?
48
+ next if klass.reflect_on_association(include_key).scope&.arity&.nonzero?
49
+ query_filter[include_key] = {}
50
+ next if include_hash.none?
51
+ query_filter[include_key] = generate_includes_from_json_query(include_hash, klass.reflect_on_association(include_key).klass)
52
+ end
53
+ end
54
+ # Does not include data, just eager-loads. Useful when methods need assocations, but you don't need association data.
55
+ if options[:eager_include].present?
56
+ options[:eager_include].each do |include_key|
57
+ # Will 'next' if there is a scope that takes arguments, an instance-dependent scope.
58
+ # Can't eager load when assocation has a instance condition for it's associative scope.
59
+ # Might not be a real assocation
60
+ next if klass.reflect_on_association(include_key).nil?
61
+ next if klass.reflect_on_association(include_key).scope&.arity&.nonzero?
62
+ query_filter[include_key] ||= {}
63
+ end
64
+ end
65
+ return query_filter
66
+ end
67
+
68
+ klass.send(:define_singleton_method, :as_json_associations_alias_fix) do |options, data, opts = {}|
69
+ if data
70
+ # Depth is almost purely for debugging purposes
71
+ opts[:depth] ||= 0
72
+ if options[:include].present?
73
+ options[:include].each do |include_key, include_hash|
74
+ # The includes doesn't have to have a hash attached. Skip it if it doesn't.
75
+ next if include_hash.nil?
76
+ data_key = include_key.to_s
77
+ if include_hash[:as].present?
78
+ if include_hash[:as].to_s == include_key.to_s
79
+ raise "Serializer: Cannot alias json query association to have the same as the original key; as: #{include_hash[:as].to_s}; original_key: #{include_key.to_s} on self: #{name}"
80
+ end
81
+ alias_name = include_hash[:as]
82
+ data[alias_name.to_s] = data[include_key.to_s]
83
+ data.delete(include_key.to_s)
84
+ data_key = alias_name.to_s
85
+ end
86
+ # At this point, the data could be an array of objects, with no as_json options.
87
+ if !data[data_key].is_a?(Array)
88
+ data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key], {depth: opts[:depth] + 1})
89
+ else
90
+ data[data_key].each_with_index do |value,i|
91
+ data[data_key][i] = as_json_associations_alias_fix(include_hash, value, {depth: opts[:depth] + 1})
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ return data
98
+ end
99
+
100
+ # no need to define it if inheriting class has defined it OR has been manually overridden.
101
+ # INSTANCE Methods
102
+
103
+
104
+ klass.send(:define_method, :as_json) do |options = {}|
105
+ # We don't need to run this custom `as_json` multiple times, if defined on inherited class.
106
+ if options[:ran_serialization]
107
+ return super(options)
108
+ end
109
+ options[:ran_serialization] = true
110
+ # Not caching records that don't have IDs.
111
+ if !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && !(options.key?(:cache_for) && options[:cache_for].nil?)
112
+ cache_key = "#{self.class.name}_____#{options[:cache_key]}___#{self.id}"
113
+ if Rails.cache.exist?(cache_key)
114
+ Rails.logger.info "Serializer: Cache reading #{cache_key}" if Serializer.configuration.debug
115
+ return Rails.cache.read(cache_key)
116
+ else
117
+ data = super(options)
118
+ data = self.class.as_json_associations_alias_fix(options, data)
119
+ begin
120
+ Rails.logger.info "Serializer: Caching #{cache_key} for #{(options[:cache_for] || Serializer.configuration.default_cache_time)} minutes." if Serializer.configuration.debug
121
+ Rails.cache.write(cache_key, data, expires_in: (options[:cache_for] || Serializer.configuration.default_cache_time).minute)
122
+ rescue Exception => e
123
+ Rails.logger.error "Serializer: Internal Server Error on #{self.class}#as_json ID: #{self.id} for cache key: #{cache_key}"
124
+ Rails.logger.error e.class
125
+ Rails.logger.error e.message
126
+ Rails.logger.error e.backtrace
127
+ end
128
+ return data
129
+ end
130
+ else
131
+ if Serializer.configuration.debug && !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && options.key?(:cache_for) && options[:cache_for].nil?
132
+ Rails.logger.info "Serializer: Caching #{cache_key} NOT caching due to `cache_for: nil`"
133
+ end
134
+ data = super(options)
135
+ data = self.class.as_json_associations_alias_fix(options, data)
136
+ return data
137
+ end
138
+ end
139
+
140
+ if !klass.method_defined?(:serializer)
141
+ klass.send(:define_method, :serializer) do |opts = {}|
142
+ query = opts[:json_query_override].present? ? self.class.send(opts[:json_query_override], opts) : self.class.serializer_query(opts)
143
+ if Serializer.configuration.enable_includes && query[:include].present? && self.class.column_names.include?('id') && self.id.present? && !opts[:skip_eager_loading] && self.respond_to?(:persisted?) && self.persisted?
144
+ # It's an extra SQL call, but most likely worth it to pre-load associations
145
+ self.class.includes(self.class.generate_includes_from_json_query(query)).find(self.id).as_json(query)
146
+ else
147
+ as_json(query)
148
+ end
149
+ end
150
+ end
151
+
152
+ # # SHOULD NOT BE OVERRIDDEN.
153
+ klass.send(:define_method, :clear_serializer_cache) do
154
+ if self.class.const_defined?("SERIALIZER_QUERY_KEYS_CACHE")
155
+ self.class::SERIALIZER_QUERY_KEYS_CACHE.each do |query_name|
156
+ cache_key = "#{self.class.name}_____#{query_name}___#{self.id}"
157
+ Rails.logger.info "Serializer: CLEARING CACHE KEY: #{cache_key}" if Serializer.configuration.debug
158
+ Rails.cache.delete(cache_key)
159
+ end
160
+ return true
161
+ else
162
+ # if Serializer.configuration.debug
163
+ Rails.logger.error(
164
+ """
165
+ ERROR. COULD NOT CLEAR SERIALIZER CACHE FOR: Class #{self.class.name}
166
+ Serializer: Class #{self.class.name} may not have the serializer module #{self.class.name}Serializer defined.
167
+ Nor was it defined on an inheriting class.
168
+ """
169
+ )
170
+ # end
171
+ return nil
172
+ end
173
+ end
174
+
175
+ serializer_query_names.each do |query_name|
176
+ serializer_name = query_name[/(?<name>.+)_query/, :name]
177
+ if serializer_name.nil?
178
+ Rails.logger.error "Serializer: #{serializer_klass.name} method #{query_name} does not end in '(.+)_query', as is expected of serializers" if Serializer.configuration.debug
179
+ next
180
+ end
181
+ if serializer_name == 'serializer'
182
+ # No longer necessary to add here. We've added them above.
183
+ # klass.send(:define_method, serializer_name) do |opts = {}|
184
+ # super({json_query_override: query_name}.merge(opts))
185
+ # end
186
+ # klass.send(:define_singleton_method, serializer_name) do |opts = {}|
187
+ # super({json_query_override: query_name}.merge(opts))
188
+ # end
189
+ else
190
+ klass.send(:define_method, serializer_name) do |opts = {}|
191
+ serializer({json_query_override: query_name}.merge(opts))
192
+ end
193
+ klass.send(:define_singleton_method, serializer_name) do |opts = {}|
194
+ serializer({json_query_override: query_name}.merge(opts))
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ # END CLASS EVAL
201
+ end
202
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_json_serializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - benjamin.dana.software.dev@gmail.com
@@ -115,9 +115,8 @@ extensions: []
115
115
  extra_rdoc_files: []
116
116
  files:
117
117
  - lib/serializer.rb
118
- - lib/serializer/application_serializer.rb
119
- - lib/serializer/concern.rb
120
118
  - lib/serializer/configuration.rb
119
+ - lib/serializer/model_serializer.rb
121
120
  homepage: https://github.com/danabr75/rails_json_serializer
122
121
  licenses:
123
122
  - LGPL-3.0-only
@@ -1,10 +0,0 @@
1
- module ApplicationSerializer
2
- def serializer_query opts = {}
3
- {
4
- include: {
5
- },
6
- methods: %w(),
7
- cache_key: __callee__,
8
- }
9
- end
10
- end
@@ -1,222 +0,0 @@
1
- module Serializer
2
- module Concern
3
- # ActiveSupport extend src: https://stackoverflow.com/questions/2328984/rails-extending-activerecordbase
4
- extend ActiveSupport::Concern
5
-
6
- # START MODEL INSTANCE METHODS
7
- def clear_serializer_cache
8
- if self.class.const_defined?("SERIALIZER_QUERY_KEYS_CACHE")
9
-
10
- # list_of_serializer_query_names = "#{self.class.name}::SERIALIZER_QUERY_KEYS_CACHE".constantize
11
- self.class.get_cumulatively_inherited_serializer_query_list.each do |query_name|
12
- cache_key = "#{self.class.name}_____#{query_name}___#{self.id}"
13
- Rails.logger.info "Serializer: CLEARING CACHE KEY: #{cache_key}" if Serializer.configuration.debug
14
- Rails.cache.delete(cache_key)
15
- end
16
- return true
17
- else
18
- # if Serializer.configuration.debug
19
- Rails.logger.error(
20
- """
21
- ERROR. COULD NOT CLEAR SERIALIZER CACHE FOR: Class #{self.class.name}
22
- Serializer: Class #{self.class.name} may not have the serializer module #{self.class.name}Serializer defined.
23
- Nor was it defined on an inheriting class.
24
- """
25
- )
26
- # end
27
- return nil
28
- end
29
- end
30
-
31
- def as_json options = {}
32
- # Not caching records that don't have IDs.
33
- if !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && !(options.key?(:cache_for) && options[:cache_for].nil?)
34
- cache_key = "#{self.class.name}_____#{options[:cache_key]}___#{self.id}"
35
- if Rails.cache.exist?(cache_key)
36
- Rails.logger.info "Serializer: Cache reading #{cache_key}" if Serializer.configuration.debug
37
- return Rails.cache.read(cache_key)
38
- else
39
- data = super(options)
40
- data = self.class.as_json_associations_alias_fix(options, data)
41
- begin
42
- Rails.logger.info "Serializer: Caching #{cache_key} for #{(options[:cache_for] || Serializer.configuration.default_cache_time)} minutes." if Serializer.configuration.debug
43
- Rails.cache.write(cache_key, data, expires_in: (options[:cache_for] || Serializer.configuration.default_cache_time).minute)
44
- rescue Exception => e
45
- Rails.logger.error "Serializer: Internal Server Error on #{self.class}#as_json ID: #{self.id} for cache key: #{cache_key}"
46
- Rails.logger.error e.class
47
- Rails.logger.error e.message
48
- Rails.logger.error e.backtrace
49
- end
50
- return data
51
- end
52
- else
53
- if Serializer.configuration.debug && !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && options.key?(:cache_for) && options[:cache_for].nil?
54
- Rails.logger.info "Serializer: Caching #{cache_key} NOT caching due to `cache_for: nil`"
55
- end
56
- data = super(options)
57
- data = self.class.as_json_associations_alias_fix(options, data)
58
- return data
59
- end
60
- end
61
-
62
- # Can override the query, using the options. ex: {json_query_override: :tiny_serializer_query}
63
- def serializer opts = {}
64
- query = opts[:json_query_override].present? ? self.class.send(opts[:json_query_override], opts) : self.class.serializer_query(opts)
65
- if Serializer.configuration.enable_includes && query[:include].present? && self.class.column_names.include?('id') && self.id.present? && !opts[:skip_eager_loading] && self.respond_to?(:persisted?) && self.persisted?
66
- # It's an extra SQL call, but most likely worth it to pre-load associations
67
- self.class.includes(self.class.generate_includes_from_json_query(query)).find(self.id).as_json(query)
68
- else
69
- as_json(query)
70
- end
71
- end
72
- # END MODEL INSTANCE METHODS
73
-
74
- class_methods do
75
- # START MODEL CLASS METHODS
76
- def inherited subclass
77
- if subclass.const_defined?("#{subclass.name}Serializer")
78
- serializer_klass = "#{subclass.name}Serializer".constantize
79
-
80
- if serializer_klass.class == Module
81
- if !serializer_klass.const_defined?("SerializerClassAndInstanceMethods")
82
- serializer_klass.const_set('SerializerClassAndInstanceMethods', Module.new {})
83
- end
84
- if !serializer_klass.const_defined?("SerializerClassMethods")
85
- serializer_klass.const_set('SerializerClassMethods', Module.new {})
86
- serializer_klass::SerializerClassMethods.send(:define_method, :get_cumulatively_inherited_serializer_query_list) do |opts = {}|
87
- if defined?(super)
88
- return (subclass::SERIALIZER_QUERY_KEYS_CACHE + superclass.get_cumulatively_inherited_serializer_query_list).uniq
89
- else
90
- return subclass::SERIALIZER_QUERY_KEYS_CACHE
91
- end
92
- end
93
- end
94
-
95
- serializer_query_names = serializer_klass.public_instance_methods
96
-
97
- serializer_query_names.each do |query_name|
98
- serializer_name = query_name[/(?<name>.+)_query/, :name]
99
- if serializer_name.nil?
100
- Rails.logger.info "Serializer: #{serializer_klass.name} method #{query_name} does not end in '(.+)_query', as is expected of serializers" if Serializer.configuration.debug
101
- next
102
- end
103
- # Skip if chosen to override it.
104
- next if serializer_klass.respond_to?(serializer_name)
105
- if serializer_name == 'serializer'
106
- serializer_klass::SerializerClassAndInstanceMethods.send(:define_method, serializer_name) do |opts = {}|
107
- super({json_query_override: query_name}.merge(opts))
108
- end
109
- else
110
- serializer_klass::SerializerClassAndInstanceMethods.send(:define_method, serializer_name) do |opts = {}|
111
- serializer({json_query_override: query_name}.merge(opts))
112
- end
113
- end
114
- end
115
- if serializer_query_names.any?
116
- # Inject instance methods
117
- subclass.send(:include, serializer_klass::SerializerClassAndInstanceMethods)
118
- # Inject class methods
119
- subclass.send(:extend, serializer_klass::SerializerClassAndInstanceMethods)
120
- # Inject class methods that has queries
121
- if Serializer.configuration.debug
122
- Rails.logger.info "Injecting queries: #{serializer_klass.public_instance_methods} into class: #{subclass}"
123
- end
124
- puts "Injecting queries: #{serializer_klass.public_instance_methods} into class: #{subclass}"
125
- subclass.send(:extend, serializer_klass)
126
- # Injecting the Serializer Methods as a namespaced class of the rails class, so we can have
127
- # access to the list of methods to clear their cache.
128
- # 'Class Name + Serializer' does not work with inheritence.
129
- subclass.const_set('SERIALIZER_QUERY_KEYS_CACHE', serializer_query_names)
130
- # Inject class methods
131
- subclass.send(:extend, serializer_klass::SerializerClassMethods)
132
-
133
- # Issue with inheritting classes caching the serializer data from queries in super classes.
134
- # Only on the rails server, not the console strangely.
135
- serializer_query_names.each do |query_name|
136
- cache_key_prefix = /#{subclass.name}_____#{query_name}___(\d+)/
137
- puts "Deleting cache here: Rails.cache.delete_matched(#{cache_key_prefix})"
138
- Rails.cache.delete_matched(cache_key_prefix)
139
- end
140
- end
141
- else
142
- Rails.logger.info "Serializer: #{serializer_klass.name} was not a Module as expected" if Serializer.configuration.debug
143
- end
144
- end
145
- super(subclass)
146
- end
147
-
148
- # Class defined clear serializer
149
- def clear_serializer_cache
150
- self.get_cumulatively_inherited_serializer_query_list.each do |query_name|
151
- cache_key_prefix = /#{self.name}_____#{query_name}___(\d+)/
152
- Rails.logger.info "Serializer: CLEARING CACHE KEY Prefix: #{cache_key_prefix}" if Serializer.configuration.debug
153
- Rails.cache.delete_matched(cache_key_prefix)
154
- end
155
- return true
156
- end
157
-
158
- # Can override the query, using the options. ex: {json_query_override: :tiny_children_serializer_query}
159
- def serializer opts = {}
160
- query = opts[:json_query_override].present? ? self.send(opts[:json_query_override], opts) : serializer_query(opts)
161
- if Serializer.configuration.enable_includes && query[:include].present? && !opts[:skip_eager_loading]
162
- includes(generate_includes_from_json_query(query)).as_json(query)
163
- else
164
- # Have to use 'all' gets stack level too deep otherwise. Not sure why.
165
- all.as_json(query)
166
- end
167
- end
168
-
169
- def as_json_associations_alias_fix options, data, opts = {}
170
- if data
171
- # Depth is almost purely for debugging purposes
172
- opts[:depth] ||= 0
173
- if options[:include].present?
174
- options[:include].each do |include_key, include_hash|
175
- # The includes doesn't have to have a hash attached. Skip it if it doesn't.
176
- next if include_hash.nil?
177
- data_key = include_key.to_s
178
- if include_hash[:as].present?
179
- if include_hash[:as].to_s == include_key.to_s
180
- raise "Serializer: Cannot alias json query association to have the same as the original key; as: #{include_hash[:as].to_s}; original_key: #{include_key.to_s} on self: #{name}"
181
- end
182
- alias_name = include_hash[:as]
183
- data[alias_name.to_s] = data[include_key.to_s]
184
- data.delete(include_key.to_s)
185
- data_key = alias_name.to_s
186
- end
187
- # At this point, the data could be an array of objects, with no as_json options.
188
- if !data[data_key].is_a?(Array)
189
- data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key], {depth: opts[:depth] + 1})
190
- else
191
- data[data_key].each_with_index do |value,i|
192
- data[data_key][i] = as_json_associations_alias_fix(include_hash, value, {depth: opts[:depth] + 1})
193
- end
194
- end
195
- end
196
- end
197
- end
198
- return data
199
- end
200
-
201
- def generate_includes_from_json_query options = {}, klass = nil
202
- query_filter = {}
203
- klass = self if klass.nil?
204
- if options[:include].present? && !options[:skip_eager_loading]
205
- options[:include].each do |include_key, include_hash|
206
- # Will 'next' if there is a scope that takes arguments, an instance-dependent scope.
207
- # Can't eager load when assocation has a instance condition for it's associative scope.
208
- # Might not be a real assocation
209
- next if klass.reflect_on_association(include_key).nil?
210
- next if klass.reflect_on_association(include_key).scope&.arity&.nonzero?
211
- query_filter[include_key] = {}
212
- next if include_hash.none?
213
- query_filter[include_key] = generate_includes_from_json_query(include_hash, klass.reflect_on_association(include_key).klass)
214
- end
215
- end
216
- return query_filter
217
- end
218
- # END MODEL CLASS METHODS
219
-
220
- end
221
- end
222
- end