Module: ModelSerializer
- Defined in:
- lib/serializer/model_serializer.rb
Class Method Summary collapse
-
.included(klass) ⇒ Object
klazz is that class object that included this module.
Class Method Details
.included(klass) ⇒ Object
klazz is that class object that included this module
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/serializer/model_serializer.rb', line 3 def self.included klass # START CLASS EVAL klass.class_eval do # Rails 5 has autoloading issues with modules. They will show as not defined, when available. if Rails.env.development? begin "#{klass.name}Serializer".constantize rescue NameError => e end end if self.const_defined?("#{klass.name}Serializer") serializer_klass = "#{klass.name}Serializer".constantize serializer_query_names = serializer_klass.public_instance_methods if klass.superclass.const_defined?("SERIALIZER_QUERY_KEYS_CACHE") self.const_set('SERIALIZER_QUERY_KEYS_CACHE', (serializer_query_names + klass.superclass::SERIALIZER_QUERY_KEYS_CACHE).uniq) else self.const_set('SERIALIZER_QUERY_KEYS_CACHE', serializer_query_names) end # Inject class methods, will have access to those queries on the class. klass.send(:extend, serializer_klass) # no need to define it if inheriting class has defined it OR has been manually overridden. # CLASS METHODS klass.send(:define_singleton_method, :serializer) do |opts = {}| query = opts[:json_query_override].present? ? self.send(opts[:json_query_override], opts) : serializer_query(opts) if Serializer.configuration.enable_includes && query[:include].present? && !opts[:skip_eager_loading] includes(generate_includes_from_json_query(query)).as_json(query) else # Have to use 'all' gets stack level too deep otherwise. Not sure why. all.as_json(query) end end klass.send(:define_singleton_method, :generate_includes_from_json_query) do | = {}, klass = nil| query_filter = {} klass = self if klass.nil? if [:include].present? && ![:skip_eager_loading] [:include].each do |include_key, include_hash| next if include_hash[:skip_eager_loading] == true # Will 'next' if there is a scope that takes arguments, an instance-dependent scope. # Can't eager load when assocation has a instance condition for it's associative scope. # Might not be a real assocation next if klass.reflect_on_association(include_key).nil? next if klass.reflect_on_association(include_key).scope&.arity&.nonzero? query_filter[include_key] = {} next if include_hash.none? query_filter[include_key] = generate_includes_from_json_query(include_hash, klass.reflect_on_association(include_key).klass) end end # Does not include data, just eager-loads. Useful when methods need assocations, but you don't need association data. if [:eager_include].present? [:eager_include].each do |include_key| # Will 'next' if there is a scope that takes arguments, an instance-dependent scope. # Can't eager load when assocation has a instance condition for it's associative scope. # Might not be a real assocation next if klass.reflect_on_association(include_key).nil? next if klass.reflect_on_association(include_key).scope&.arity&.nonzero? query_filter[include_key] ||= {} end end return query_filter end klass.send(:define_singleton_method, :as_json_associations_alias_fix) do |, data, opts = {}| if data # Depth is almost purely for debugging purposes opts[:depth] ||= 0 if [:include].present? [:include].each do |include_key, include_hash| # The includes doesn't have to have a hash attached. Skip it if it doesn't. next if include_hash.nil? data_key = include_key.to_s if include_hash[:as].present? if include_hash[:as].to_s == include_key.to_s 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}" end alias_name = include_hash[:as] data[alias_name.to_s] = data[include_key.to_s] data.delete(include_key.to_s) data_key = alias_name.to_s end # At this point, the data could be an array of objects, with no as_json options. if !data[data_key].is_a?(Array) data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key], {depth: opts[:depth] + 1}) else data[data_key].each_with_index do |value,i| data[data_key][i] = as_json_associations_alias_fix(include_hash, value, {depth: opts[:depth] + 1}) end end end end end return data end # no need to define it if inheriting class has defined it OR has been manually overridden. # INSTANCE Methods klass.send(:define_method, :as_json) do | = {}| # We don't need to run this custom `as_json` multiple times, if defined on inherited class. if [:ran_serialization] return super() end [:ran_serialization] = true # Not caching records that don't have IDs. if !Serializer.configuration.disable_model_caching && self.id && [:cache_key].present? && !(.key?(:cache_for) && [:cache_for].nil?) cache_key = "#{self.class.name}_____#{[:cache_key]}___#{self.id}" if Rails.cache.exist?(cache_key) Rails.logger.info "Serializer: Cache reading #{cache_key}" if Serializer.configuration.debug return Rails.cache.read(cache_key) else data = super() data = self.class.as_json_associations_alias_fix(, data) begin Rails.logger.info "Serializer: Caching #{cache_key} for #{([:cache_for] || Serializer.configuration.default_cache_time)} minutes." if Serializer.configuration.debug Rails.cache.write(cache_key, data, expires_in: ([:cache_for] || Serializer.configuration.default_cache_time).minute) rescue Exception => e Rails.logger.error "Serializer: Internal Server Error on #{self.class}#as_json ID: #{self.id} for cache key: #{cache_key}" Rails.logger.error e.class Rails.logger.error e. Rails.logger.error e.backtrace end return data end else if Serializer.configuration.debug && !Serializer.configuration.disable_model_caching && self.id && [:cache_key].present? && .key?(:cache_for) && [:cache_for].nil? Rails.logger.info "Serializer: Caching #{cache_key} NOT caching due to `cache_for: nil`" end data = super() data = self.class.as_json_associations_alias_fix(, data) return data end end if !klass.method_defined?(:serializer) klass.send(:define_method, :serializer) do |opts = {}| query = opts[:json_query_override].present? ? self.class.send(opts[:json_query_override], opts) : self.class.serializer_query(opts) 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? # It's an extra SQL call, but most likely worth it to pre-load associations self.class.includes(self.class.generate_includes_from_json_query(query)).find(self.id).as_json(query) else as_json(query) end end end # # SHOULD NOT BE OVERRIDDEN. klass.send(:define_method, :clear_serializer_cache) do if self.class.const_defined?("SERIALIZER_QUERY_KEYS_CACHE") self.class::SERIALIZER_QUERY_KEYS_CACHE.each do |query_name| cache_key = "#{self.class.name}_____#{query_name}___#{self.id}" Rails.logger.info "Serializer: CLEARING CACHE KEY: #{cache_key}" if Serializer.configuration.debug Rails.cache.delete(cache_key) end return true else # if Serializer.configuration.debug Rails.logger.error( """ ERROR. COULD NOT CLEAR SERIALIZER CACHE FOR: Class #{self.class.name} Serializer: Class #{self.class.name} may not have the serializer module #{self.class.name}Serializer defined. Nor was it defined on an inheriting class. """ ) # end return nil end end serializer_query_names.each do |query_name| serializer_name = query_name[/(?<name>.+)_query/, :name] if serializer_name.nil? Rails.logger.error "Serializer: #{serializer_klass.name} method #{query_name} does not end in '(.+)_query', as is expected of serializers" if Serializer.configuration.debug next end if serializer_name == 'serializer' # No longer necessary to add here. We've added them above. # klass.send(:define_method, serializer_name) do |opts = {}| # super({json_query_override: query_name}.merge(opts)) # end # klass.send(:define_singleton_method, serializer_name) do |opts = {}| # super({json_query_override: query_name}.merge(opts)) # end else klass.send(:define_method, serializer_name) do |opts = {}| serializer({json_query_override: query_name}.merge(opts)) end klass.send(:define_singleton_method, serializer_name) do |opts = {}| serializer({json_query_override: query_name}.merge(opts)) end end end end end # END CLASS EVAL end |