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