rails_json_serializer 1.0.0
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 +7 -0
- data/lib/serializer.rb +27 -0
- data/lib/serializer/application_serializer.rb +10 -0
- data/lib/serializer/concern.rb +172 -0
- data/lib/serializer/configuration.rb +12 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b1a107c67b6ae1ccb5ad9abcd8f13b1255e46b07d48726c44b89e2e41186a2bb
|
4
|
+
data.tar.gz: be454318e55f60ca236b4fbb1ecb9a68fe144e636e13deb43587f94385f7c0cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7af60e8a27c62b336d31ec574aa5b3321ec7d73e3097c90d4027763f71148dc587d5113c9ae36576ea8869f4908d40145997c657ce2d5f4321fa9695df56128b
|
7
|
+
data.tar.gz: 4b3e8f9aebd58d53de0dc13688fdfb3d0b8f4ffdf0015d688e8329fc0d50b3dc8d81cc69dda3e16e51c08fbc82fb35fd2755c984cd8de53f9ea0c59650c8cd7c
|
data/lib/serializer.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'serializer/application_serializer'
|
3
|
+
Dir[File.join(Rails.root, 'app', 'serializers', '**', '*.rb')].each {|file| require file }
|
4
|
+
require 'serializer/configuration'
|
5
|
+
require 'serializer/concern'
|
6
|
+
|
7
|
+
module Serializer
|
8
|
+
# config src: http://lizabinante.com/blog/creating-a-configurable-ruby-gem/
|
9
|
+
class << self
|
10
|
+
attr_accessor :configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset
|
18
|
+
@configuration = Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configure
|
22
|
+
yield(configuration)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# include the extension
|
27
|
+
ActiveRecord::Base.send(:include, Serializer::Concern)
|
@@ -0,0 +1,172 @@
|
|
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?("#{self.class.name}Serializer")
|
9
|
+
serializer_klass = "#{self.class.name}Serializer".constantize
|
10
|
+
serializer_klass.public_instance_methods.each do |query_name|
|
11
|
+
cache_key = "#{self.class.name}_____#{query_name}___#{self.id}"
|
12
|
+
Rails.logger.info "Serializer: CLEARING CACHE KEY: #{cache_key}" if Serializer.configuration.debug
|
13
|
+
Rails.cache.delete(cache_key)
|
14
|
+
end
|
15
|
+
return true
|
16
|
+
else
|
17
|
+
Rails.logger.info "Serializer: Class #{self.class.name} does not have the serializer module #{self.class.name}Serializer defined." if Serializer.configuration.debug
|
18
|
+
return nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_json options = {}
|
23
|
+
# Not caching records that don't have IDs.
|
24
|
+
if !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && !(options.key?(:cache_for) && options[:cache_for].nil?)
|
25
|
+
cache_key = "#{self.class.name}_____#{options[:cache_key]}___#{self.id}"
|
26
|
+
if Rails.cache.exist?(cache_key)
|
27
|
+
Rails.logger.info "Serializer: Cache reading #{cache_key}" if Serializer.configuration.debug
|
28
|
+
return Rails.cache.read(cache_key)
|
29
|
+
else
|
30
|
+
data = super(options)
|
31
|
+
data = self.class.as_json_associations_alias_fix(options, data)
|
32
|
+
begin
|
33
|
+
Rails.logger.info "Serializer: Caching #{cache_key} for #{(options[:cache_for] || Serializer.configuration.default_cache_time)} minutes." if Serializer.configuration.debug
|
34
|
+
Rails.cache.write(cache_key, data, expires_in: (options[:cache_for] || Serializer.configuration.default_cache_time).minute)
|
35
|
+
rescue Exception => e
|
36
|
+
Rails.logger.error "Serializer: Internal Server Error on #{self.class}#as_json ID: #{self.id} for cache key: #{cache_key}"
|
37
|
+
Rails.logger.error e.class
|
38
|
+
Rails.logger.error e.message
|
39
|
+
Rails.logger.error e.backtrace
|
40
|
+
end
|
41
|
+
return data
|
42
|
+
end
|
43
|
+
else
|
44
|
+
if Serializer.configuration.debug && !Serializer.configuration.disable_model_caching && self.id && options[:cache_key].present? && options.key?(:cache_for) && options[:cache_for].nil?
|
45
|
+
Rails.logger.info "Serializer: Caching #{cache_key} NOT caching due to `cache_for: nil`"
|
46
|
+
end
|
47
|
+
data = super(options)
|
48
|
+
data = self.class.as_json_associations_alias_fix(options, data)
|
49
|
+
return data
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Can override the query, using the options. ex: {json_query_override: :tiny_serializer_query}
|
54
|
+
def serializer opts = {}
|
55
|
+
query = opts[:json_query_override].present? ? self.class.send(opts[:json_query_override], opts) : self.class.serializer_query(opts)
|
56
|
+
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?
|
57
|
+
# It's an extra SQL call, but most likely worth it to pre-load associations
|
58
|
+
self.class.includes(self.class.generate_includes_from_json_query(query)).find(self.id).as_json(query)
|
59
|
+
else
|
60
|
+
as_json(query)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
# END MODEL INSTANCE METHODS
|
64
|
+
|
65
|
+
class_methods do
|
66
|
+
# START MODEL CLASS METHODS
|
67
|
+
def inherited subclass
|
68
|
+
if subclass.const_defined?("#{subclass.name}Serializer")
|
69
|
+
serializer_klass = "#{subclass.name}Serializer".constantize
|
70
|
+
|
71
|
+
if serializer_klass.class == Module
|
72
|
+
if !serializer_klass.const_defined?("SerializerMethods")
|
73
|
+
serializer_klass.const_set('SerializerMethods', Module.new {})
|
74
|
+
end
|
75
|
+
|
76
|
+
serializer_klass.public_instance_methods.each do |query_name|
|
77
|
+
serializer_name = query_name[/(?<name>.+)_query/, :name]
|
78
|
+
if serializer_name.nil?
|
79
|
+
Rails.logger.info "Serializer: #{serializer_klass.name} method #{query_name} does not end in '(.+)_query', as is expected of serializers" if Serializer.configuration.debug
|
80
|
+
next
|
81
|
+
end
|
82
|
+
# Skip if chosen to override it.
|
83
|
+
next if serializer_klass.respond_to?(serializer_name)
|
84
|
+
if serializer_name == 'serializer'
|
85
|
+
serializer_klass::SerializerMethods.send(:define_method, serializer_name) do |opts = {}|
|
86
|
+
super({json_query_override: query_name}.merge(opts))
|
87
|
+
end
|
88
|
+
else
|
89
|
+
serializer_klass::SerializerMethods.send(:define_method, serializer_name) do |opts = {}|
|
90
|
+
serializer({json_query_override: query_name}.merge(opts))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Inject instance methods
|
96
|
+
subclass.send(:include, serializer_klass::SerializerMethods)
|
97
|
+
# Inject class methods
|
98
|
+
subclass.send(:extend, serializer_klass::SerializerMethods)
|
99
|
+
# Inject class methods that has queries
|
100
|
+
subclass.send(:extend, serializer_klass)
|
101
|
+
else
|
102
|
+
Rails.logger.info "Serializer: #{serializer_klass.name} was not a Module as expected" if Serializer.configuration.debug
|
103
|
+
end
|
104
|
+
end
|
105
|
+
super(subclass)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Can override the query, using the options. ex: {json_query_override: :tiny_children_serializer_query}
|
109
|
+
def serializer opts = {}
|
110
|
+
query = opts[:json_query_override].present? ? self.send(opts[:json_query_override], opts) : serializer_query(opts)
|
111
|
+
if Serializer.configuration.enable_includes && query[:include].present? && !opts[:skip_eager_loading]
|
112
|
+
includes(generate_includes_from_json_query(query)).as_json(query)
|
113
|
+
else
|
114
|
+
# Have to use 'all' gets stack level too deep otherwise. Not sure why.
|
115
|
+
all.as_json(query)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def as_json_associations_alias_fix options, data, opts = {}
|
120
|
+
if data
|
121
|
+
# Depth is almost purely for debugging purposes
|
122
|
+
opts[:depth] ||= 0
|
123
|
+
if options[:include].present?
|
124
|
+
options[:include].each do |include_key, include_hash|
|
125
|
+
# The includes doesn't have to have a hash attached. Skip it if it doesn't.
|
126
|
+
next if include_hash.nil?
|
127
|
+
data_key = include_key.to_s
|
128
|
+
if include_hash[:as].present?
|
129
|
+
if include_hash[:as].to_s == include_key.to_s
|
130
|
+
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}"
|
131
|
+
end
|
132
|
+
alias_name = include_hash[:as]
|
133
|
+
data[alias_name.to_s] = data[include_key.to_s]
|
134
|
+
data.delete(include_key.to_s)
|
135
|
+
data_key = alias_name.to_s
|
136
|
+
end
|
137
|
+
# At this point, the data could be an array of objects, with no as_json options.
|
138
|
+
if !data[data_key].is_a?(Array)
|
139
|
+
data[data_key] = as_json_associations_alias_fix(include_hash, data[data_key], {depth: opts[:depth] + 1})
|
140
|
+
else
|
141
|
+
data[data_key].each_with_index do |value,i|
|
142
|
+
data[data_key][i] = as_json_associations_alias_fix(include_hash, value, {depth: opts[:depth] + 1})
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
return data
|
149
|
+
end
|
150
|
+
|
151
|
+
def generate_includes_from_json_query options = {}, klass = nil
|
152
|
+
query_filter = {}
|
153
|
+
klass = self if klass.nil?
|
154
|
+
if options[:include].present? && !options[:skip_eager_loading]
|
155
|
+
options[:include].each do |include_key, include_hash|
|
156
|
+
# Will 'next' if there is a scope that takes arguments, an instance-dependent scope.
|
157
|
+
# Can't eager load when assocation has a instance condition for it's associative scope.
|
158
|
+
# Might not be a real assocation
|
159
|
+
next if klass.reflect_on_association(include_key).nil?
|
160
|
+
next if klass.reflect_on_association(include_key).scope&.arity&.nonzero?
|
161
|
+
query_filter[include_key] = {}
|
162
|
+
next if include_hash.none?
|
163
|
+
query_filter[include_key] = generate_includes_from_json_query(include_hash, klass.reflect_on_association(include_key).klass)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
return query_filter
|
167
|
+
end
|
168
|
+
# END MODEL CLASS METHODS
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Serializer
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :enable_includes, :default_cache_time, :disable_model_caching, :debug
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@enable_includes = true
|
7
|
+
@default_cache_time = 360
|
8
|
+
@disable_model_caching = false
|
9
|
+
@debug = false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_json_serializer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- benjamin.dana.software.dev@gmail.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/serializer.rb
|
34
|
+
- lib/serializer/application_serializer.rb
|
35
|
+
- lib/serializer/concern.rb
|
36
|
+
- lib/serializer/configuration.rb
|
37
|
+
homepage: https://github.com/danabr75/rails_json_serializer
|
38
|
+
licenses:
|
39
|
+
- GNU
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '2.4'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubygems_version: 3.1.2
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: An ActiveRecord JSON Serializer with supported caching and eager-loading
|
60
|
+
test_files: []
|