rails_json_serializer 1.1.2 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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