rails_json_serializer 1.1.2 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9232f08e4be61f2f817b67e734253f02f4970625d32a3a48382170a11ffbc222
4
- data.tar.gz: 58052d52689ed5376c0849f4b478cd319fabebb78d5e95dac9ca21f976fe831e
3
+ metadata.gz: '090d6170d6135efa9fdc924bb164e456fff8beee34bd6a2378727b264c4eb5fd'
4
+ data.tar.gz: 9ec1d082a3d03788007c62bb6aa24be7f184609a3afbc1c6e603831b2b5a205c
5
5
  SHA512:
6
- metadata.gz: d8ef6a39473a49fbb46e828ee7428cf237426802aabe9d2277b3d887ab83c49acf7337305a4589851a7c942389101b45618f899ff3afa42ae5803d0f23057290
7
- data.tar.gz: a7a9a49810fde0ec0699d23aaadc24c80f9fd4bc277344646a92e28f4d654f63707c2b77b3ef1f533881f39b69a9882506efbb6e20ee4425e696e24836ee6ef5
6
+ metadata.gz: 9e0b530149381114f463fb36a023de5cdecc5896721dfa60ebc703349e7c3fe732fe0ec6680a99c9501d9663f530346f2e8c3ace238141f54df49095b77b1afe
7
+ data.tar.gz: cbec34221725aafd93682ca7bf00fd96e8513406f8d919492f46ffdad516bdc06c782ba0ea9e6c575dd0fef45c7f40f2d07efd05bdfaadb0fcee22651c9c58dd
@@ -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,197 @@
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
+ # Not caching records that don't have IDs.
106
+ if !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && !(options.key?(:cache_for) && options[:cache_for].nil?)
107
+ cache_key = "#{self.class.name}_____#{options[:cache_key]}___#{self.id}"
108
+ if Rails.cache.exist?(cache_key)
109
+ Rails.logger.info "Serializer: Cache reading #{cache_key}" if Serializer.configuration.debug
110
+ return Rails.cache.read(cache_key)
111
+ else
112
+ data = super(options)
113
+ data = self.class.as_json_associations_alias_fix(options, data)
114
+ begin
115
+ Rails.logger.info "Serializer: Caching #{cache_key} for #{(options[:cache_for] || Serializer.configuration.default_cache_time)} minutes." if Serializer.configuration.debug
116
+ Rails.cache.write(cache_key, data, expires_in: (options[:cache_for] || Serializer.configuration.default_cache_time).minute)
117
+ rescue Exception => e
118
+ Rails.logger.error "Serializer: Internal Server Error on #{self.class}#as_json ID: #{self.id} for cache key: #{cache_key}"
119
+ Rails.logger.error e.class
120
+ Rails.logger.error e.message
121
+ Rails.logger.error e.backtrace
122
+ end
123
+ return data
124
+ end
125
+ else
126
+ if Serializer.configuration.debug && !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && options.key?(:cache_for) && options[:cache_for].nil?
127
+ Rails.logger.info "Serializer: Caching #{cache_key} NOT caching due to `cache_for: nil`"
128
+ end
129
+ data = super(options)
130
+ data = self.class.as_json_associations_alias_fix(options, data)
131
+ return data
132
+ end
133
+ end
134
+
135
+ if !klass.method_defined?(:serializer)
136
+ klass.send(:define_method, :serializer) do |opts = {}|
137
+ query = opts[:json_query_override].present? ? self.class.send(opts[:json_query_override], opts) : self.class.serializer_query(opts)
138
+ 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?
139
+ # It's an extra SQL call, but most likely worth it to pre-load associations
140
+ self.class.includes(self.class.generate_includes_from_json_query(query)).find(self.id).as_json(query)
141
+ else
142
+ as_json(query)
143
+ end
144
+ end
145
+ end
146
+
147
+ # # SHOULD NOT BE OVERRIDDEN.
148
+ klass.send(:define_method, :clear_serializer_cache) do
149
+ if self.class.const_defined?("SERIALIZER_QUERY_KEYS_CACHE")
150
+ self.class::SERIALIZER_QUERY_KEYS_CACHE.each do |query_name|
151
+ cache_key = "#{self.class.name}_____#{query_name}___#{self.id}"
152
+ Rails.logger.info "Serializer: CLEARING CACHE KEY: #{cache_key}" if Serializer.configuration.debug
153
+ Rails.cache.delete(cache_key)
154
+ end
155
+ return true
156
+ else
157
+ # if Serializer.configuration.debug
158
+ Rails.logger.error(
159
+ """
160
+ ERROR. COULD NOT CLEAR SERIALIZER CACHE FOR: Class #{self.class.name}
161
+ Serializer: Class #{self.class.name} may not have the serializer module #{self.class.name}Serializer defined.
162
+ Nor was it defined on an inheriting class.
163
+ """
164
+ )
165
+ # end
166
+ return nil
167
+ end
168
+ end
169
+
170
+ serializer_query_names.each do |query_name|
171
+ serializer_name = query_name[/(?<name>.+)_query/, :name]
172
+ if serializer_name.nil?
173
+ Rails.logger.error "Serializer: #{serializer_klass.name} method #{query_name} does not end in '(.+)_query', as is expected of serializers" if Serializer.configuration.debug
174
+ next
175
+ end
176
+ if serializer_name == 'serializer'
177
+ # No longer necessary to add here. We've added them above.
178
+ # klass.send(:define_method, serializer_name) do |opts = {}|
179
+ # super({json_query_override: query_name}.merge(opts))
180
+ # end
181
+ # klass.send(:define_singleton_method, serializer_name) do |opts = {}|
182
+ # super({json_query_override: query_name}.merge(opts))
183
+ # end
184
+ else
185
+ klass.send(:define_method, serializer_name) do |opts = {}|
186
+ serializer({json_query_override: query_name}.merge(opts))
187
+ end
188
+ klass.send(:define_singleton_method, serializer_name) do |opts = {}|
189
+ serializer({json_query_override: query_name}.merge(opts))
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ # END CLASS EVAL
196
+ end
197
+ 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.2
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - benjamin.dana.software.dev@gmail.com
@@ -11,7 +11,7 @@ cert_chain: []
11
11
  date: 2020-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
@@ -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,199 +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
-
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
- subclass.send(:extend, serializer_klass)
122
- # Injecting the Serializer Methods as a namespaced class of the rails class, so we can have
123
- # access to the list of methods to clear their cache.
124
- # 'Class Name + Serializer' does not work with inheritence.
125
- subclass.const_set('SERIALIZER_QUERY_KEYS_CACHE', serializer_query_names)
126
- # Inject class methods
127
- subclass.send(:extend, serializer_klass::SerializerClassMethods)
128
- else
129
- Rails.logger.info "Serializer: #{serializer_klass.name} was not a Module as expected" if Serializer.configuration.debug
130
- end
131
- end
132
- super(subclass)
133
- end
134
-
135
- # Can override the query, using the options. ex: {json_query_override: :tiny_children_serializer_query}
136
- def serializer opts = {}
137
- query = opts[:json_query_override].present? ? self.send(opts[:json_query_override], opts) : serializer_query(opts)
138
- if Serializer.configuration.enable_includes && query[:include].present? && !opts[:skip_eager_loading]
139
- includes(generate_includes_from_json_query(query)).as_json(query)
140
- else
141
- # Have to use 'all' gets stack level too deep otherwise. Not sure why.
142
- all.as_json(query)
143
- end
144
- end
145
-
146
- def as_json_associations_alias_fix options, data, opts = {}
147
- if data
148
- # Depth is almost purely for debugging purposes
149
- opts[:depth] ||= 0
150
- if options[:include].present?
151
- options[:include].each do |include_key, include_hash|
152
- # The includes doesn't have to have a hash attached. Skip it if it doesn't.
153
- next if include_hash.nil?
154
- data_key = include_key.to_s
155
- if include_hash[:as].present?
156
- if include_hash[:as].to_s == include_key.to_s
157
- 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}"
158
- end
159
- alias_name = include_hash[:as]
160
- data[alias_name.to_s] = data[include_key.to_s]
161
- data.delete(include_key.to_s)
162
- data_key = alias_name.to_s
163
- end
164
- # At this point, the data could be an array of objects, with no as_json options.
165
- if !data[data_key].is_a?(Array)
166
- data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key], {depth: opts[:depth] + 1})
167
- else
168
- data[data_key].each_with_index do |value,i|
169
- data[data_key][i] = as_json_associations_alias_fix(include_hash, value, {depth: opts[:depth] + 1})
170
- end
171
- end
172
- end
173
- end
174
- end
175
- return data
176
- end
177
-
178
- def generate_includes_from_json_query options = {}, klass = nil
179
- query_filter = {}
180
- klass = self if klass.nil?
181
- if options[:include].present? && !options[:skip_eager_loading]
182
- options[:include].each do |include_key, include_hash|
183
- # Will 'next' if there is a scope that takes arguments, an instance-dependent scope.
184
- # Can't eager load when assocation has a instance condition for it's associative scope.
185
- # Might not be a real assocation
186
- next if klass.reflect_on_association(include_key).nil?
187
- next if klass.reflect_on_association(include_key).scope&.arity&.nonzero?
188
- query_filter[include_key] = {}
189
- next if include_hash.none?
190
- query_filter[include_key] = generate_includes_from_json_query(include_hash, klass.reflect_on_association(include_key).klass)
191
- end
192
- end
193
- return query_filter
194
- end
195
- # END MODEL CLASS METHODS
196
-
197
- end
198
- end
199
- end