Class: Praxis::Blueprint
- Inherits:
-
Object
show all
- Extended by:
- Finalizable
- Includes:
- Attributor::Dumpable, Attributor::Type
- Defined in:
- lib/praxis-blueprints/blueprint.rb
Constant Summary
collapse
- @@caching_enabled =
false
Class Attribute Summary collapse
Instance Attribute Summary collapse
Class Method Summary
collapse
-
._finalize! ⇒ Object
Internal finalize! logic.
-
.attributes(opts = {}, &block) ⇒ Object
-
.cache ⇒ Object
Fetch current blueprint cache, scoped by this class.
-
.cache=(cache) ⇒ Object
-
.caching_enabled=(caching_enabled) ⇒ Object
-
.caching_enabled? ⇒ Boolean
-
.check_option!(name, value) ⇒ Object
-
.define_attribute! ⇒ Object
-
.define_reader!(name) ⇒ Object
-
.define_readers! ⇒ Object
-
.describe(shallow = false, example: nil, **opts) ⇒ Object
-
.domain_model(klass = nil) ⇒ Object
-
.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts) ⇒ Object
(also: render)
-
.example(context = nil, **values) ⇒ Object
-
.family ⇒ Object
-
.generate_master_view! ⇒ Object
-
.inherited(klass) ⇒ Object
-
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options) ⇒ Object
(also: from)
-
.new(object, decorators = nil) ⇒ Object
Override default new behavior to support memoized creation through an IdentityMap.
-
.resolve_domain_model! ⇒ Object
-
.valid_type?(value) ⇒ Boolean
-
.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil) ⇒ Object
-
.view(name, **options, &block) ⇒ Object
Instance Method Summary
collapse
_finalize!, extended, finalizable, finalize!, finalized?, inherited
Constructor Details
#initialize(object, decorators = nil) ⇒ Blueprint
Returns a new instance of Blueprint.
279
280
281
282
283
284
285
286
287
288
289
290
|
# File 'lib/praxis-blueprints/blueprint.rb', line 279
def initialize(object, decorators = nil)
@object = object
@decorators = if decorators.is_a?(Hash) && decorators.any?
OpenStruct.new(decorators)
else
decorators
end
@validating = false
end
|
Class Attribute Details
.attribute ⇒ Object
Returns the value of attribute attribute.
30
31
32
|
# File 'lib/praxis-blueprints/blueprint.rb', line 30
def attribute
@attribute
end
|
.options ⇒ Object
Returns the value of attribute options.
31
32
33
|
# File 'lib/praxis-blueprints/blueprint.rb', line 31
def options
@options
end
|
.reference ⇒ Object
Returns the value of attribute reference.
32
33
34
|
# File 'lib/praxis-blueprints/blueprint.rb', line 32
def reference
@reference
end
|
.views ⇒ Object
Returns the value of attribute views.
29
30
31
|
# File 'lib/praxis-blueprints/blueprint.rb', line 29
def views
@views
end
|
Instance Attribute Details
#decorators ⇒ Object
Returns the value of attribute decorators.
26
27
28
|
# File 'lib/praxis-blueprints/blueprint.rb', line 26
def decorators
@decorators
end
|
#object ⇒ Object
Returns the value of attribute object.
25
26
27
|
# File 'lib/praxis-blueprints/blueprint.rb', line 25
def object
@object
end
|
#validating ⇒ Object
Returns the value of attribute validating.
24
25
26
|
# File 'lib/praxis-blueprints/blueprint.rb', line 24
def validating
@validating
end
|
Class Method Details
._finalize! ⇒ Object
216
217
218
219
220
221
222
223
224
225
|
# File 'lib/praxis-blueprints/blueprint.rb', line 216
def self._finalize!
if @block
self.define_attribute!
self.define_readers!
self.generate_master_view! unless self.view(:master)
self.resolve_domain_model!
end
super
end
|
.attributes(opts = {}, &block) ⇒ Object
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
# File 'lib/praxis-blueprints/blueprint.rb', line 88
def self.attributes(opts = {}, &block)
if block_given?
raise 'Redefining Blueprint attributes is not currently supported' if self.const_defined?(:Struct, false)
if opts.key?(:reference) && opts[:reference] != self.reference
raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
elsif self.reference
opts[:reference] = self.reference else
opts[:reference] = self
end
@options.merge!(opts)
@block = block
return @attribute
end
raise "@attribute not defined yet for #{self.name}" unless @attribute
@attribute.attributes
end
|
.cache ⇒ Object
Fetch current blueprint cache, scoped by this class
160
161
162
|
# File 'lib/praxis-blueprints/blueprint.rb', line 160
def self.cache
Thread.current[:praxis_blueprints_cache][self]
end
|
.cache=(cache) ⇒ Object
164
165
166
|
# File 'lib/praxis-blueprints/blueprint.rb', line 164
def self.cache=(cache)
Thread.current[:praxis_blueprints_cache] = cache
end
|
.caching_enabled=(caching_enabled) ⇒ Object
155
156
157
|
# File 'lib/praxis-blueprints/blueprint.rb', line 155
def self.caching_enabled=(caching_enabled)
@@caching_enabled = caching_enabled
end
|
.caching_enabled? ⇒ Boolean
151
152
153
|
# File 'lib/praxis-blueprints/blueprint.rb', line 151
def self.caching_enabled?
@@caching_enabled
end
|
.check_option!(name, value) ⇒ Object
116
117
118
119
120
121
122
123
124
|
# File 'lib/praxis-blueprints/blueprint.rb', line 116
def self.check_option!(name, value)
case name
when :identity
raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.is_a?(::Symbol)
return :ok
else
return Attributor::Struct.check_option!(name, value)
end
end
|
.define_attribute! ⇒ Object
233
234
235
236
237
238
|
# File 'lib/praxis-blueprints/blueprint.rb', line 233
def self.define_attribute!
@attribute = Attributor::Attribute.new(Attributor::Struct, @options, &@block)
@block = nil
@attribute.type.anonymous_type true
self.const_set(:Struct, @attribute.type)
end
|
.define_reader!(name) ⇒ Object
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
|
# File 'lib/praxis-blueprints/blueprint.rb', line 251
def self.define_reader!(name)
attribute = self.attributes[name]
define_method(name) do
if @decorators && @decorators.respond_to?(name)
@decorators.send(name)
else
value = @object.__send__(name)
return value if value.nil? || value.is_a?(attribute.type)
attribute.load(value)
end
end
end
|
.define_readers! ⇒ Object
240
241
242
243
244
245
246
247
248
249
|
# File 'lib/praxis-blueprints/blueprint.rb', line 240
def self.define_readers!
self.attributes.each do |name, _attribute|
name = name.to_sym
next if self.instance_methods.include? name
define_reader! name
end
end
|
.describe(shallow = false, example: nil, **opts) ⇒ Object
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
# File 'lib/praxis-blueprints/blueprint.rb', line 70
def self.describe(shallow = false, example: nil, **opts)
type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
example = example.object if example
description = self.attribute.type.describe(shallow, example: example, **opts).merge!(id: self.id, name: type_name)
description.delete :anonymous description[:anonymous] = @_anonymous unless @_anonymous.nil?
unless shallow
description[:views] = self.views.each_with_object({}) do |(view_name, view), hash|
hash[view_name] = view.describe
end
end
description
end
|
.domain_model(klass = nil) ⇒ Object
111
112
113
114
|
# File 'lib/praxis-blueprints/blueprint.rb', line 111
def self.domain_model(klass = nil)
return @domain_model if klass.nil?
@domain_model = klass
end
|
.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts) ⇒ Object
Also known as:
render
205
206
207
208
209
210
|
# File 'lib/praxis-blueprints/blueprint.rb', line 205
def self.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts)
object = self.load(object, context, **opts)
return nil if object.nil?
object.render(view: view, context: context, **opts)
end
|
.example(context = nil, **values) ⇒ Object
173
174
175
176
177
178
179
180
181
182
183
184
|
# File 'lib/praxis-blueprints/blueprint.rb', line 173
def self.example(context = nil, **values)
context = case context
when nil
["#{self.name}-#{values.object_id}"]
when ::String
[context]
else
context
end
self.new(self.attribute.example(context, values: values))
end
|
.family ⇒ Object
66
67
68
|
# File 'lib/praxis-blueprints/blueprint.rb', line 66
def self.family
'hash'
end
|
.generate_master_view! ⇒ Object
268
269
270
271
272
273
274
275
276
277
|
# File 'lib/praxis-blueprints/blueprint.rb', line 268
def self.generate_master_view!
attributes = self.attributes
view :master do
attributes.each do |name, _attr|
attribute name, view: :default
end
end
end
|
.inherited(klass) ⇒ Object
35
36
37
38
39
40
41
42
43
|
# File 'lib/praxis-blueprints/blueprint.rb', line 35
def self.inherited(klass)
super
klass.instance_eval do
@views = {}
@options = {}
@domain_model = Object
end
end
|
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options) ⇒ Object
Also known as:
from
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# File 'lib/praxis-blueprints/blueprint.rb', line 126
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options)
case value
when self
value
when nil, Hash, String
if (value = self.attribute.load(value, context, **options))
self.new(value)
end
else
if value.is_a?(self.domain_model) || value.is_a?(self::Struct)
self.new(value)
else
self.new(domain_model.new(value))
end
end
end
|
.new(object, decorators = nil) ⇒ Object
Override default new behavior to support memoized creation through an IdentityMap
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
# File 'lib/praxis-blueprints/blueprint.rb', line 46
def self.new(object, decorators = nil)
if @@caching_enabled && decorators.nil?
cache = if object.respond_to?(:identity_map) && object.identity_map
object.identity_map.blueprint_cache[self]
else
self.cache
end
return cache[object] ||= begin
blueprint = self.allocate
blueprint.send(:initialize, object, decorators)
blueprint
end
end
blueprint = self.allocate
blueprint.send(:initialize, object, decorators)
blueprint
end
|
.resolve_domain_model! ⇒ Object
227
228
229
230
231
|
# File 'lib/praxis-blueprints/blueprint.rb', line 227
def self.resolve_domain_model!
return unless self.domain_model.is_a?(String)
@domain_model = self.domain_model.constantize
end
|
.valid_type?(value) ⇒ Boolean
168
169
170
171
|
# File 'lib/praxis-blueprints/blueprint.rb', line 168
def self.valid_type?(value)
value.is_a?(self) || value.is_a?(self.attribute.type)
end
|
.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil) ⇒ Object
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/praxis-blueprints/blueprint.rb', line 186
def self.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil)
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
context = [context] if context.is_a? ::String
unless value.is_a?(self)
raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{self.name} for an object of type #{value.class.name}."
end
value.validate(context)
end
|
.view(name, **options, &block) ⇒ Object
197
198
199
200
201
202
203
|
# File 'lib/praxis-blueprints/blueprint.rb', line 197
def self.view(name, **options, &block)
if block_given?
return self.views[name] = View.new(name, self, **options, &block)
end
self.views[name]
end
|
Instance Method Details
#_cache_key ⇒ Object
By default we’ll use the object identity, to avoid rendering the same object twice Override, if there is a better way cache things up
294
295
296
|
# File 'lib/praxis-blueprints/blueprint.rb', line 294
def _cache_key
self.object
end
|
#_get_attr(name) ⇒ Object
generic semi-private getter used by Renderer
362
363
364
|
# File 'lib/praxis-blueprints/blueprint.rb', line 362
def _get_attr(name)
self.send(name)
end
|
#render(view_name = nil, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **opts) ⇒ Object
Also known as:
dump
Render the wrapped data with the given view
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
|
# File 'lib/praxis-blueprints/blueprint.rb', line 299
def render(view_name = nil, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **opts)
if !view_name.nil?
warn 'DEPRECATED: please do not pass the view name as the first parameter in Blueprint.render, pass through the view: named param instead.'
elsif opts.key?(:view)
view_name = opts[:view]
end
fields = opts[:fields]
view_name = :default if view_name.nil? && fields.nil?
if view_name
unless (view = self.class.views[view_name])
raise "view with name '#{view_name.inspect}' is not defined in #{self.class}"
end
return view.render(self, context: context, renderer: renderer)
end
if fields.is_a? Array
fields = fields.each_with_object({}) { |field, hash| hash[field] = true }
end
expanded_fields = FieldExpander.expand(self.class, fields)
renderer.render(self, expanded_fields, context: context)
end
|
#to_h ⇒ Object
329
330
331
|
# File 'lib/praxis-blueprints/blueprint.rb', line 329
def to_h
Attributor.recursive_to_h(@object)
end
|
#validate(context = Attributor::DEFAULT_ROOT_CONTEXT) ⇒ Object
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
|
# File 'lib/praxis-blueprints/blueprint.rb', line 333
def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
context = [context] if context.is_a? ::String
keys_with_values = []
raise 'validation conflict' if @validating
@validating = true
errors = []
self.class.attributes.each do |sub_attribute_name, sub_attribute|
sub_context = self.class.generate_subcontext(context, sub_attribute_name)
value = self.send(sub_attribute_name)
keys_with_values << sub_attribute_name unless value.nil?
if value.respond_to?(:validating) next if value.validating
end
errors.concat(sub_attribute.validate(value, sub_context))
end
self.class.attribute.type.requirements.each do |req|
validation_errors = req.validate(keys_with_values, context)
errors.concat(validation_errors) unless validation_errors.empty?
end
errors
ensure
@validating = false
end
|