active_facet 1.2.8
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/MIT-LICENSE.txt +21 -0
- data/README.md +206 -0
- data/Rakefile +29 -0
- data/lib/active_facet.rb +98 -0
- data/lib/active_facet/acts_as_active_facet.rb +102 -0
- data/lib/active_facet/config.rb +267 -0
- data/lib/active_facet/document_cache.rb +52 -0
- data/lib/active_facet/errors/attribute_error.rb +6 -0
- data/lib/active_facet/errors/configuration_error.rb +16 -0
- data/lib/active_facet/errors/lookup_error.rb +6 -0
- data/lib/active_facet/filter.rb +62 -0
- data/lib/active_facet/helper.rb +116 -0
- data/lib/active_facet/serializer/base.rb +297 -0
- data/lib/active_facet/serializer/facade.rb +223 -0
- data/lib/active_facet/version.rb +3 -0
- data/lib/rails/generators/active_facet/install/install_generator.rb +21 -0
- data/lib/rails/generators/active_facet/install/templates/active_facet.yml +18 -0
- data/lib/rails/generators/active_facet/install/templates/initializer.rb +85 -0
- metadata +317 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
#TODO --jdc rename field set to facet throughut project
|
2
|
+
|
3
|
+
# Field = Symbol representing a json attribute that corresponds to resource attributes or extensions
|
4
|
+
|
5
|
+
# Field Set = Nested, Mixed Collection of Fields, Aliases, and Relations (Strings, Symbols, Arrays and Hashes)
|
6
|
+
# e.g. [:a, {b: "c"}]
|
7
|
+
|
8
|
+
# Normalized Field Set = Field Set with all Strings converted to Symbols, Aliases dealiased, and Arrays converted to Hashes
|
9
|
+
|
10
|
+
# Field Set Alias = Symbol representing a Field Set
|
11
|
+
|
12
|
+
module ActiveFacet
|
13
|
+
class Config
|
14
|
+
|
15
|
+
#TODO --decouple this class completely from serializer by moving reflection to resource_manager
|
16
|
+
|
17
|
+
# Boolean: state
|
18
|
+
attr_reader :compiled
|
19
|
+
|
20
|
+
# Serializer::Base
|
21
|
+
attr_reader :serializer
|
22
|
+
|
23
|
+
# Hash: compiled field sets
|
24
|
+
attr_reader :normalized_field_sets
|
25
|
+
|
26
|
+
# Hash: keys are public API attribute names, values are resource attribute names
|
27
|
+
attr_reader :transforms_from, :transforms_to
|
28
|
+
|
29
|
+
# Hash: API attribute names requiring custom serialization
|
30
|
+
attr_reader :serializers
|
31
|
+
|
32
|
+
# Hash: keys are resource attribute names storing nested JSON, values are nested attribute names
|
33
|
+
attr_reader :namespaces
|
34
|
+
|
35
|
+
# Hash: keys are defined extension values
|
36
|
+
attr_reader :extensions
|
37
|
+
|
38
|
+
# Class: Resource Class to serialize
|
39
|
+
attr_accessor :resource_class
|
40
|
+
|
41
|
+
# Store Facet
|
42
|
+
# @param field_set_alias [Symbol]
|
43
|
+
# @param field_set_alias [Facet]
|
44
|
+
def alias_field_set(field_set_alias, field_set)
|
45
|
+
self.compiled = false
|
46
|
+
field_sets[field_set_alias] = field_set
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns Field to resource attribute map
|
50
|
+
# @param direction [Symbol]
|
51
|
+
# @return [Hash]
|
52
|
+
def transforms(direction = :from)
|
53
|
+
direction == :from ? transforms_from : transforms_to
|
54
|
+
end
|
55
|
+
|
56
|
+
# (Memoized) Normalizes all Field Set Aliases
|
57
|
+
# @param serializer [Serializer::Base]
|
58
|
+
# @return [Config]
|
59
|
+
def compile!(serializer)
|
60
|
+
self.serializer = serializer
|
61
|
+
self.normalized_field_sets = { all: {} }.with_indifferent_access
|
62
|
+
|
63
|
+
#aggregate all compiled field_sets into the all collection
|
64
|
+
normalized_field_sets[:all][:fields] = field_sets.inject({}) do |result, (field_set_alias, field_set)|
|
65
|
+
result = merge_field_sets(result, dealias_field_set!(field_set, field_set_alias)[:fields])
|
66
|
+
end
|
67
|
+
|
68
|
+
#filter all compiled field_sets into a corresponding attributes collection
|
69
|
+
normalized_field_sets.each do |field_set_alias, normalized_field_set|
|
70
|
+
normalized_field_set[:attributes] = normalized_field_set[:fields].reject { |field_set, nested_field_sets|
|
71
|
+
serializer.send :is_association?, field_set
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
self.compiled = true
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Merges all ancestor accessors into self
|
80
|
+
# @return [Config]
|
81
|
+
def merge!(config)
|
82
|
+
self.compiled = false
|
83
|
+
self.resource_class ||= config.resource_class
|
84
|
+
transforms_from.merge! config.transforms_from
|
85
|
+
transforms_to.merge! config.transforms_to
|
86
|
+
serializers.merge! config.serializers
|
87
|
+
namespaces.merge! config.namespaces
|
88
|
+
field_sets.merge! config.field_sets
|
89
|
+
extensions.merge! config.extensions
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Invokes block on a Field Set with recursive, depth first traversal
|
95
|
+
# @param field_set [Field Set] to traverse
|
96
|
+
# @param block [Block] to call for each field
|
97
|
+
# @return [Hash] injection of block results
|
98
|
+
def field_set_itterator(field_set)
|
99
|
+
raise ActiveFacet::Errors::ConfigurationError.new(ActiveFacet::Errors::ConfigurationError::COMPILED_ERROR_MSG) unless compiled
|
100
|
+
internal_field_set_itterator(dealias_field_set!(default_field_set(field_set))[:fields], Proc.new)
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
attr_accessor :field_sets
|
106
|
+
|
107
|
+
attr_writer :compiled, :serializer, :normalized_field_sets, :transforms_from, :transforms_to,
|
108
|
+
:serializers, :namespaces, :extensions
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
#TODO --jdc change Serializer::Base to convert all Strings to Symbols and remove indifferent_access
|
113
|
+
def initialize
|
114
|
+
self.compiled = false
|
115
|
+
self.transforms_from = {}.with_indifferent_access
|
116
|
+
self.transforms_to = {}.with_indifferent_access
|
117
|
+
self.serializers = {}.with_indifferent_access
|
118
|
+
self.namespaces = {}.with_indifferent_access
|
119
|
+
self.field_sets = {}.with_indifferent_access
|
120
|
+
self.extensions = {}.with_indifferent_access
|
121
|
+
end
|
122
|
+
|
123
|
+
# (Memoized) Convert all Field Set Aliases to their declarations and Normalize Field Set
|
124
|
+
# @param field_set [Symbol] to evaluate
|
125
|
+
# @param field_set_alias [String] key to associate the evaluated field set with
|
126
|
+
# @return [Normalized Field Set]
|
127
|
+
def dealias_field_set!(field_set, field_set_alias = nil)
|
128
|
+
field_set_alias ||= field_set.to_s.to_sym
|
129
|
+
normalized_field_sets[field_set_alias] ||= begin
|
130
|
+
{ fields: normalize_field_set(dealias_field_set field_set) }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Converts all Field Set Aliases in a Field Set into their declarations (see Serializer::Base DSL)
|
135
|
+
# Recursively evaluates all aliases embedded within declaration
|
136
|
+
# - Does not recursively evalute associations
|
137
|
+
# @param field_set [Symbol] to evaluate
|
138
|
+
# @return [Mixed]
|
139
|
+
def dealias_field_set(field_set)
|
140
|
+
case field_set
|
141
|
+
when :all
|
142
|
+
dealias_field_set serializer.exposed_aliases(:all, true, true)
|
143
|
+
when :all_attributes
|
144
|
+
dealias_field_set serializer.exposed_aliases
|
145
|
+
when Symbol, String
|
146
|
+
field_set = field_set.to_sym
|
147
|
+
aliased_field_set?(field_set) ? dealias_field_set(field_sets[field_set]) : field_set
|
148
|
+
when Array
|
149
|
+
field_set.map do |s|
|
150
|
+
dealias_field_set(s)
|
151
|
+
end
|
152
|
+
when Hash
|
153
|
+
field_set.inject({}) { |result, (k,v)|
|
154
|
+
v.blank? ? inject_field_set(result, dealias_field_set(k)) : result[k] = v #todo: symbolize
|
155
|
+
result
|
156
|
+
}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Converts Field Set into a Normalized Field Set that can be idempotently itterated
|
161
|
+
# @param field_set [Symbol] to normalize
|
162
|
+
# @return [Normalized Field Set]
|
163
|
+
def normalize_field_set(field_set)
|
164
|
+
case field_set
|
165
|
+
when nil
|
166
|
+
{}
|
167
|
+
when Symbol, String
|
168
|
+
{field_set.to_sym => nil}
|
169
|
+
when Array
|
170
|
+
field_set.flatten.compact.inject({}) do |result, s|
|
171
|
+
result = merge_field_sets(result, s)
|
172
|
+
end
|
173
|
+
when Hash
|
174
|
+
field_set.inject({}) { |result, (k,v)| result[k.to_sym] = v; result }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Adds :basic to a Field Set unless minimal is specified
|
179
|
+
# @param field_set [Field Set] field set to be serialized
|
180
|
+
# @return [Field Set]
|
181
|
+
def default_field_set(field_set)
|
182
|
+
minimal = detect_field_set(field_set, :minimal)
|
183
|
+
case field_set
|
184
|
+
when nil
|
185
|
+
:basic
|
186
|
+
when Symbol, String
|
187
|
+
minimal ? field_set.to_sym : [field_set.to_sym, :basic]
|
188
|
+
when Array
|
189
|
+
minimal ? field_set : field_set + [:basic]
|
190
|
+
when Hash
|
191
|
+
field_set[:basic] = nil unless minimal
|
192
|
+
field_set
|
193
|
+
else
|
194
|
+
raise ActiveFacet::Errors::ConfigurationError.new(ActiveFacet::Errors::ConfigurationError::FIELD_SET_ERROR_MSG)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Iterrates the first level of Field Set checking for key
|
199
|
+
# @param field_set [Field Set]
|
200
|
+
# @return [Boolean]
|
201
|
+
def detect_field_set(field_set, key)
|
202
|
+
case field_set
|
203
|
+
when nil
|
204
|
+
false
|
205
|
+
when Symbol
|
206
|
+
field_set == key
|
207
|
+
when String
|
208
|
+
field_set.to_sym == key
|
209
|
+
when Array
|
210
|
+
field_set.detect { |s| detect_field_set(s, key) }
|
211
|
+
when Hash
|
212
|
+
field_set.detect { |s, n| detect_field_set(s, key) }.try(:[], 0)
|
213
|
+
else
|
214
|
+
raise ActiveFacet::Errors::ConfigurationError.new(ActiveFacet::Errors::ConfigurationError::FIELD_SET_ERROR_MSG)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Invokes block on Fields in a Field Set with recursive, depth first traversal
|
219
|
+
# Skips fields already processed
|
220
|
+
# @param field_set [Field Set] to traverse
|
221
|
+
# @param block [Block] to call for each field_set
|
222
|
+
# @return [Hash] injection of block results
|
223
|
+
def internal_field_set_itterator(field_set, block)
|
224
|
+
field_set.each do |field, nested_field_set|
|
225
|
+
block.call(field, nested_field_set)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Adds a Field into a Normalized Field Set
|
230
|
+
# @param field_set [Normalized Field Set]
|
231
|
+
# @param key [Field Set]
|
232
|
+
# @return [Hash]
|
233
|
+
def inject_field_set(field_set, key)
|
234
|
+
case key
|
235
|
+
when Symbol, String
|
236
|
+
field_set[key.to_sym] = {}
|
237
|
+
when Hash
|
238
|
+
field_set.merge! key
|
239
|
+
when Array
|
240
|
+
key.each { |k| inject_field_set(field_set, k) }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Tells if the Field is a Field Set Alias
|
245
|
+
# @param field_set [Symbol] to evaluate
|
246
|
+
# @return [Boolean]
|
247
|
+
def aliased_field_set?(field_set)
|
248
|
+
return false unless field_sets.key? field_set
|
249
|
+
v = field_sets[field_set]
|
250
|
+
!v.is_a?(Symbol) || v != field_set
|
251
|
+
end
|
252
|
+
|
253
|
+
# Recursively merges two Field Sets
|
254
|
+
# @param a [Symbol] to merge
|
255
|
+
# @param b [Symbol] to merge
|
256
|
+
# @return [Field Set]
|
257
|
+
def merge_field_sets(a, b)
|
258
|
+
na = normalize_field_set(a)
|
259
|
+
nb = normalize_field_set(b)
|
260
|
+
nb.inject(na.dup) do |result, (field_set, nested_field_sets)|
|
261
|
+
result[field_set] = merge_field_sets(na[field_set], nested_field_sets)
|
262
|
+
result
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# A really dump cache interface that caches everything when enabled.
|
2
|
+
# Extend and conditionally cache documents.
|
3
|
+
module ActiveFacet
|
4
|
+
class DocumentCache
|
5
|
+
CACHE_PREFIX = 'af_doc_cache'
|
6
|
+
|
7
|
+
# Fetches a JSON document representing the facade
|
8
|
+
# @param facade [Object] to cache
|
9
|
+
# @param options [Hash] for Rails.cache.fetch
|
10
|
+
# @param &block [Proc] for cache miss
|
11
|
+
# @return [Object]
|
12
|
+
def self.fetch(facade, options = {})
|
13
|
+
return yield unless cacheable?(facade)
|
14
|
+
|
15
|
+
force = facade.opts[ActiveFacet.cache_force_key] || options[:force] || ActiveFacet::default_cache_options[:force]
|
16
|
+
cache_key = digest_key(facade)
|
17
|
+
if force || !(result = Rails.cache.fetch(cache_key))
|
18
|
+
result = yield
|
19
|
+
Rails.cache.write(cache_key, ::Oj.dump(result), ActiveFacet::default_cache_options.merge(options).merge(force: force))
|
20
|
+
result
|
21
|
+
else
|
22
|
+
::Oj.load(result)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Fetches a JSON document representing the association specified for the resource in the facade
|
27
|
+
# @param facade [Object] to cache
|
28
|
+
# @param options [Hash] for Rails.cache.fetch
|
29
|
+
# @param &block [Proc] for cache miss
|
30
|
+
# @return [Object]
|
31
|
+
def self.fetch_association(facade, association, options = {})
|
32
|
+
# override and implement
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Salts and hashes facade cache_key
|
39
|
+
# @param facade [Facade] to generate key for
|
40
|
+
# @return [String]
|
41
|
+
def self.digest_key(facade)
|
42
|
+
Digest::MD5.hexdigest(CACHE_PREFIX + facade.cache_key.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Tells if the resource to be serialized can be cached
|
46
|
+
# @param facade [Facade] to inspect
|
47
|
+
# @return [Boolean]
|
48
|
+
def self.cacheable?(facade)
|
49
|
+
ActiveFacet.cache_enabled && facade.opts[ActiveFacet.cache_bypass_key].blank?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveFacet
|
2
|
+
module Errors
|
3
|
+
class ConfigurationError < StandardError
|
4
|
+
|
5
|
+
RESOURCE_ERROR_MSG = 'unable to identify resource class'
|
6
|
+
STACK_ERROR_MSG = "self referencing attribute declaration"
|
7
|
+
ALL_ATTRIBUTES_ERROR_MSG = "publish name (:all_attributes) reserved"
|
8
|
+
ALL_FIELDS_ERROR_MSG = "publish name (:all) reserved"
|
9
|
+
COMPILED_ERROR_MSG = "field set configuration not compiled"
|
10
|
+
FIELD_SET_ERROR_MSG = "invalid field set"
|
11
|
+
ACTS_AS_ERROR_MSG = "filters can only be defined on acts_as_active_facet resources"
|
12
|
+
DUPLICATE_ACTS_AS_ERROR_MSG = "acts_as_active_facet_options already exists"
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Maps filters to resource classes without adding methods to the resource classes directly
|
2
|
+
module ActiveFacet
|
3
|
+
class Filter
|
4
|
+
|
5
|
+
# filters holds the resource/filter map for each resource
|
6
|
+
# registered_filters holds the resource/filter map for each resource and all superclasses
|
7
|
+
# global_filters queues a collection of filters that can be defined
|
8
|
+
# on any resource classes having called included and called acts_as_active_facet_options
|
9
|
+
cattr_accessor :filters, :registered_filters, :global_filters
|
10
|
+
self.filters, self.registered_filters, self.global_filters = {}, {}, {}
|
11
|
+
|
12
|
+
# Queues a filter to be applied to resource classes
|
13
|
+
# @param filter_name [Symbol]
|
14
|
+
# @param filter_method [Proc] method body code which implements the filter
|
15
|
+
# @return [Proc] for chaining
|
16
|
+
def self.register_global(filter_name, filter_method)
|
17
|
+
global_filters[filter_name.to_sym] = filter_method
|
18
|
+
end
|
19
|
+
|
20
|
+
# Tells that the receiver class implements a filter
|
21
|
+
# @param receiver [Class] resource class
|
22
|
+
# @param filter_name [Symbol]
|
23
|
+
# @param filter_method_name [Symbol] name of method defined on receiver instances which implments the filter
|
24
|
+
# @return [Class] for chaining
|
25
|
+
def self.register(receiver, filter_name, filter_method_name)
|
26
|
+
raise ActiveFacet::Errors::ConfigurationError.new(ActiveFacet::Errors::ConfigurationError::ACTS_AS_ERROR_MSG) unless filterable?(receiver)
|
27
|
+
receiver_filters = filters[receiver.name] ||= {}
|
28
|
+
receiver_filters[filter_name.to_sym] = filter_method_name.to_sym
|
29
|
+
receiver
|
30
|
+
end
|
31
|
+
|
32
|
+
# Register queued filters for given resource class
|
33
|
+
# @param receiver [Class] resource class
|
34
|
+
# @return [Class] for chaining
|
35
|
+
def self.apply_globals_to(receiver)
|
36
|
+
raise ActiveFacet::Errors::ConfigurationError.new(ActiveFacet::Errors::ConfigurationError::ACTS_AS_ERROR_MSG) unless filterable?(receiver)
|
37
|
+
global_filters.each do |filter_name, filter_method|
|
38
|
+
filter_method_name = receiver.acts_as_active_facet_options[:filter_method_name]
|
39
|
+
receiver.send(filter_method_name, filter_name, filter_method)
|
40
|
+
end
|
41
|
+
receiver
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the list of filters the resource class implements
|
45
|
+
# Memoized
|
46
|
+
# @param receiver [Class] resource class
|
47
|
+
# @return [Hash] all filters registered on this resource (and superclass)
|
48
|
+
def self.registered_filters_for(receiver)
|
49
|
+
registered_filters[receiver.name] ||= begin
|
50
|
+
receiver_filters = filters[receiver.name] ||= {}
|
51
|
+
receiver_filters.reverse_merge!(registered_filters_for(receiver.superclass)) if filterable?(receiver.superclass)
|
52
|
+
receiver_filters
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Tells if any filters can be registered for the given resource class
|
57
|
+
# @return [Boolean]
|
58
|
+
def self.filterable?(receiver)
|
59
|
+
receiver.ancestors.include? ActiveFacet::ActsAsActiveFacet
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Implements varius methods that bind serializers and resources together
|
2
|
+
module ActiveFacet
|
3
|
+
class Helper
|
4
|
+
# Memoizes resource_mapper results
|
5
|
+
cattr_accessor :memoized_resource_map
|
6
|
+
self.memoized_resource_map = {}
|
7
|
+
|
8
|
+
# Memoizes serializer_mapper results
|
9
|
+
cattr_accessor :memoized_serializers
|
10
|
+
self.memoized_serializers = {}
|
11
|
+
|
12
|
+
# Default resource mapping scheme, can be replaced with a custom implementation
|
13
|
+
# @param resource_class [Class]
|
14
|
+
# @return [Array] of Strings for use by filters and field configurations
|
15
|
+
def self.default_resource_mapper(resource_class)
|
16
|
+
map = []
|
17
|
+
until(resource_class.superclass == BasicObject) do
|
18
|
+
map << resource_class.name.tableize
|
19
|
+
resource_class = resource_class.superclass
|
20
|
+
end
|
21
|
+
map
|
22
|
+
end
|
23
|
+
|
24
|
+
# Holds reference to resource_mapper method (configurable)
|
25
|
+
cattr_accessor :resource_mapper
|
26
|
+
self.resource_mapper = method(:default_resource_mapper)
|
27
|
+
|
28
|
+
# Fetches the set of keys the resource_class might appear as for Filters and Fields
|
29
|
+
# Memoized
|
30
|
+
# @param resource_class [Object]
|
31
|
+
# @return [Array] of Strings
|
32
|
+
def self.resource_map(resource_class)
|
33
|
+
memoized_resource_map[resource_class] ||= begin
|
34
|
+
resource_mapper.call(resource_class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Default serializer mapping scheme, can be overrided with config
|
39
|
+
# Memoized
|
40
|
+
# @param resource_class [Class]
|
41
|
+
# @return [Serializer::Base | Class]
|
42
|
+
def self.default_serializer_mapper(resource_class, serializer, type, version, options)
|
43
|
+
key = [resource_class.name, serializer.to_s, type.to_s.camelcase, version].join(".")
|
44
|
+
return memoized_serializers[key] if memoized_serializers.key?(key)
|
45
|
+
memoized_serializers[key] = internal_serializer_mapper(resource_class, serializer, type, version, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Holds reference to serializer_mapper method (configurable)
|
49
|
+
cattr_accessor :serializer_mapper
|
50
|
+
self.serializer_mapper = method(:default_serializer_mapper)
|
51
|
+
|
52
|
+
# TODO --jdc implement recursive superclass/parentclass lookup
|
53
|
+
# Default serializer mapping scheme, can be overrided with config
|
54
|
+
# @param resource_class [Class]
|
55
|
+
# @return [Serializer::Base | Class]
|
56
|
+
def self.internal_serializer_mapper(resource_class, serializer, type, version, options)
|
57
|
+
case type
|
58
|
+
when :serializer
|
59
|
+
[
|
60
|
+
'V' + version.to_i.to_s + '::' + resource_class.name.camelcase + '::' + resource_class.name.camelcase + type.to_s.camelcase,
|
61
|
+
'V' + version.to_i.to_s + '::' + resource_class.name.camelcase + type.to_s.camelcase,
|
62
|
+
].each { |name|
|
63
|
+
klass = name.safe_constantize
|
64
|
+
return klass.new if klass.present?
|
65
|
+
}
|
66
|
+
else
|
67
|
+
[
|
68
|
+
'V' + version.to_i.to_s + '::' + resource_class.name.camelcase + '::' + serializer + type.to_s.camelcase,
|
69
|
+
'V' + version.to_i.to_s + '::' + serializer + type.to_s.camelcase,
|
70
|
+
].find { |name|
|
71
|
+
klass = name.safe_constantize
|
72
|
+
return klass if klass.present?
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Fetches the serializer registered for a resource_class
|
78
|
+
# @param resource_class [Object] to find serializer for
|
79
|
+
# @param options [Hash] context
|
80
|
+
# @return [Serializer::Base]
|
81
|
+
def self.serializer_for(resource_class, options)
|
82
|
+
fetch_serializer(resource_class, resource_class.name.demodulize.to_s.camelcase, :serializer, options)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Fetches the attribute serializer registered for the given resource_class
|
86
|
+
# @param resource_class [Object] to find attribute serializer class for
|
87
|
+
# @param attribute_class_name [String] to find attribute serializer class for
|
88
|
+
# @param options [Hash] context
|
89
|
+
# @return [Class]
|
90
|
+
def self.attribute_serializer_class_for(resource_class, attribute_name, options)
|
91
|
+
fetch_serializer(resource_class, attribute_name.to_s.camelcase, :attribute_serializer, options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Retrieves the first serializer successfully described by the parameters
|
95
|
+
# @param resource_class [Class] the class of the resource to serialize
|
96
|
+
# @param serializer [String] name of the base_class of the resource to serialize
|
97
|
+
# @param type [String] type of serializer to look for (attribute vs. resource)
|
98
|
+
# @param options [Hash] context
|
99
|
+
# @return [Serializer::Base | Class]
|
100
|
+
def self.fetch_serializer(resource_class, serializer, type, options)
|
101
|
+
version = extract_version_from_opts(options)
|
102
|
+
unless result = serializer_mapper.call(resource_class, serializer, type, version, options)
|
103
|
+
error_message = "Unable to locate serializer for:: " + [resource_class.name, serializer, type, version].to_s
|
104
|
+
Rails.logger.debug error_message
|
105
|
+
raise ActiveFacet::Errors::LookupError.new(error_message) if ActiveFacet.strict_lookups
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
# Safely extracts version from options hash
|
111
|
+
# @return [Numeric]
|
112
|
+
def self.extract_version_from_opts(options)
|
113
|
+
((options.try(:[], ActiveFacet.opts_key) || {})[ActiveFacet.version_key] || ActiveFacet.default_version).to_f
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|