Class: Praxis::Blueprint
- Inherits:
-
Object
show all
- Extended by:
- Finalizable
- Includes:
- Attributor::Type
- Defined in:
- lib/praxis-blueprints/blueprint.rb
Constant Summary
collapse
- CIRCULAR_REFERENCE_MARKER =
'...'.freeze
- @@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_blueprint_reader!(name) ⇒ Object
-
.define_direct_reader!(name) ⇒ Object
-
.define_reader!(name) ⇒ Object
-
.define_readers! ⇒ Object
-
.describe(shallow = false) ⇒ Object
-
.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts) ⇒ Object
-
.example(context = nil, **values) ⇒ Object
-
.generate_master_view! ⇒ Object
-
.inherited(klass) ⇒ Object
-
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options) ⇒ Object
-
.new(object, decorators = nil) ⇒ Object
Override default new behavior to support memoized creation through an IdentityMap.
-
.valid_type?(value) ⇒ Boolean
-
.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil) ⇒ Object
-
.view(name, &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.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
# File 'lib/praxis-blueprints/blueprint.rb', line 188
def initialize(object, decorators=nil)
@object = object
@decorators = if decorators.kind_of?(Hash) && decorators.any?
OpenStruct.new(decorators)
else
decorators
end
@rendered_views = {}
@validating = false
@active_renders = []
end
|
Class Attribute Details
.attribute ⇒ Object
Returns the value of attribute attribute.
19
20
21
|
# File 'lib/praxis-blueprints/blueprint.rb', line 19
def attribute
@attribute
end
|
.options ⇒ Object
Returns the value of attribute options.
19
20
21
|
# File 'lib/praxis-blueprints/blueprint.rb', line 19
def options
@options
end
|
.reference ⇒ Object
Returns the value of attribute reference.
20
21
22
|
# File 'lib/praxis-blueprints/blueprint.rb', line 20
def reference
@reference
end
|
.views ⇒ Object
Returns the value of attribute views.
19
20
21
|
# File 'lib/praxis-blueprints/blueprint.rb', line 19
def views
@views
end
|
Instance Attribute Details
#active_renders ⇒ Object
Returns the value of attribute active_renders.
16
17
18
|
# File 'lib/praxis-blueprints/blueprint.rb', line 16
def active_renders
@active_renders
end
|
#decorators ⇒ Object
Returns the value of attribute decorators.
15
16
17
|
# File 'lib/praxis-blueprints/blueprint.rb', line 15
def decorators
@decorators
end
|
#object ⇒ Object
Returns the value of attribute object.
15
16
17
|
# File 'lib/praxis-blueprints/blueprint.rb', line 15
def object
@object
end
|
#validating ⇒ Object
Returns the value of attribute validating.
16
17
18
|
# File 'lib/praxis-blueprints/blueprint.rb', line 16
def validating
@validating
end
|
Class Method Details
._finalize! ⇒ Object
230
231
232
233
234
235
236
237
238
|
# File 'lib/praxis-blueprints/blueprint.rb', line 230
def self._finalize!
if @block
self.define_attribute!
self.define_readers!
self.generate_master_view! unless self.view(:master)
end
super
end
|
.attributes(opts = {}, &block) ⇒ Object
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
|
# File 'lib/praxis-blueprints/blueprint.rb', line 71
def self.attributes(opts={}, &block)
if block_given?
if self.const_defined?(:Struct, false)
raise "Redefining Blueprint attributes is not currently supported"
else
if opts.has_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 = opts
@block = block
end
return @attribute
end
unless @attribute
raise "@attribute not defined yet for #{self.name}"
end
@attribute.attributes
end
|
.cache ⇒ Object
Fetch current blueprint cache, scoped by this class
134
135
136
|
# File 'lib/praxis-blueprints/blueprint.rb', line 134
def self.cache
Thread.current[:praxis_blueprints_cache][self]
end
|
.cache=(cache) ⇒ Object
138
139
140
|
# File 'lib/praxis-blueprints/blueprint.rb', line 138
def self.cache=(cache)
Thread.current[:praxis_blueprints_cache] = cache
end
|
.caching_enabled=(caching_enabled) ⇒ Object
129
130
131
|
# File 'lib/praxis-blueprints/blueprint.rb', line 129
def self.caching_enabled=(caching_enabled)
@@caching_enabled = caching_enabled
end
|
.caching_enabled? ⇒ Boolean
125
126
127
|
# File 'lib/praxis-blueprints/blueprint.rb', line 125
def self.caching_enabled?
@@caching_enabled
end
|
.check_option!(name, value) ⇒ Object
100
101
102
103
104
105
106
107
108
|
# File 'lib/praxis-blueprints/blueprint.rb', line 100
def self.check_option!(name, value)
case name
when :identity
raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.kind_of?(::Symbol)
return :ok
else
return Attributor::Struct.check_option!(name, value)
end
end
|
.define_attribute! ⇒ Object
240
241
242
243
244
|
# File 'lib/praxis-blueprints/blueprint.rb', line 240
def self.define_attribute!
@attribute = Attributor::Attribute.new(Attributor::Struct, @options, &@block)
@block = nil
self.const_set(:Struct, @attribute.type)
end
|
.define_blueprint_reader!(name) ⇒ Object
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
# File 'lib/praxis-blueprints/blueprint.rb', line 267
def self.define_blueprint_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.kind_of?(attribute.type)
attribute.type.new(value)
end
end
end
|
.define_direct_reader!(name) ⇒ Object
282
283
284
285
286
287
288
289
290
291
292
|
# File 'lib/praxis-blueprints/blueprint.rb', line 282
def self.define_direct_reader!(name)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
if @decorators && @decorators.respond_to?(:#{name})
@decorators.#{name}
else
@object.#{name}
end
end
RUBY
end
|
.define_reader!(name) ⇒ Object
258
259
260
261
262
263
264
265
|
# File 'lib/praxis-blueprints/blueprint.rb', line 258
def self.define_reader!(name)
attribute = self.attributes[name]
if attribute.type < Praxis::Blueprint
define_blueprint_reader!(name)
else
define_direct_reader!(name)
end
end
|
.define_readers! ⇒ Object
246
247
248
249
250
251
252
253
254
255
|
# File 'lib/praxis-blueprints/blueprint.rb', line 246
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) ⇒ Object
56
57
58
59
60
61
62
63
64
65
66
67
68
|
# File 'lib/praxis-blueprints/blueprint.rb', line 56
def self.describe(shallow=false)
type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
description = self.attribute.type.describe(shallow).merge!(name: type_name)
unless shallow
description[:views] = self.views.each_with_object({}) do |(view_name, view), hash|
hash[view_name] = view.describe
end
end
description
end
|
.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts) ⇒ Object
182
183
184
185
|
# File 'lib/praxis-blueprints/blueprint.rb', line 182
def self.dump(object, view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT, **opts)
object = self.load(object, context)
object.render(view, context: context)
end
|
.example(context = nil, **values) ⇒ Object
147
148
149
150
151
152
153
154
155
156
157
158
|
# File 'lib/praxis-blueprints/blueprint.rb', line 147
def self.example(context=nil, **values)
context = case context
when nil
["#{self.name}-#{values.object_id.to_s}"]
when ::String
[context]
else
context
end
self.new(self.attribute.example(context, values: values))
end
|
.generate_master_view! ⇒ Object
294
295
296
297
298
299
300
301
302
303
|
# File 'lib/praxis-blueprints/blueprint.rb', line 294
def self.generate_master_view!
attributes = self.attributes
view :master do
attributes.each do | name, attr |
attribute name, view: :master
end
end
end
|
.inherited(klass) ⇒ Object
23
24
25
26
27
28
29
30
|
# File 'lib/praxis-blueprints/blueprint.rb', line 23
def self.inherited(klass)
super
klass.instance_eval do
@views = Hash.new
@options = Hash.new
end
end
|
.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options) ⇒ Object
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/praxis-blueprints/blueprint.rb', line 111
def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
case value
when nil, self
value
when Hash, String
self.new(self.attribute.load(value,context, **options))
else
self.new(value)
end
end
|
.new(object, decorators = nil) ⇒ Object
Override default new behavior to support memoized creation through an IdentityMap
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
# File 'lib/praxis-blueprints/blueprint.rb', line 33
def self.new(object, decorators=nil)
if @@caching_enabled && decorators.nil?
key = object
cache = if object.respond_to?(:identity_map)
object.identity_map.blueprint_cache[self]
else
self.cache
end
return cache[key] ||= begin
blueprint = self.allocate
blueprint.send(:initialize, object, decorators)
blueprint
end
end
blueprint = self.allocate
blueprint.send(:initialize, object, decorators)
blueprint
end
|
.valid_type?(value) ⇒ Boolean
142
143
144
145
|
# File 'lib/praxis-blueprints/blueprint.rb', line 142
def self.valid_type?(value)
value.kind_of?(self) || value.kind_of?(self.attribute.type)
end
|
.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil) ⇒ Object
161
162
163
164
165
166
167
168
169
170
171
|
# File 'lib/praxis-blueprints/blueprint.rb', line 161
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.kind_of?(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, &block) ⇒ Object
174
175
176
177
178
179
180
|
# File 'lib/praxis-blueprints/blueprint.rb', line 174
def self.view(name, &block)
if block_given?
return self.views[name] = View.new(name, self, &block)
end
self.views[name]
end
|
Instance Method Details
#dump(view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT) ⇒ Object
225
226
227
|
# File 'lib/praxis-blueprints/blueprint.rb', line 225
def dump(view: :default, context: Attributor::DEFAULT_ROOT_CONTEXT)
self.render(view, context: context)
end
|
#render(view_name = :default, context: Attributor::DEFAULT_ROOT_CONTEXT) ⇒ Object
Also known as:
to_hash
Render the wrapped data with the given view
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# File 'lib/praxis-blueprints/blueprint.rb', line 207
def render(view_name=:default, context: Attributor::DEFAULT_ROOT_CONTEXT)
unless (view = self.class.views[view_name])
raise "view with name '#{view_name.inspect}' is not defined in #{self.class}"
end
return @rendered_views[view_name] if @rendered_views.has_key? view_name
return CIRCULAR_REFERENCE_MARKER if @active_renders.include?(view_name)
@active_renders << view_name
@rendered_views[view_name] = view.dump(self, context: context)
ensure
@active_renders.delete view_name
end
|
#validate(context = Attributor::DEFAULT_ROOT_CONTEXT) ⇒ Object
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
# File 'lib/praxis-blueprints/blueprint.rb', line 306
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
raise "validation conflict" if @validating
@validating = true
self.class.attributes.each_with_object(Array.new) do |(sub_attribute_name, sub_attribute), errors|
sub_context = self.class.generate_subcontext(context,sub_attribute_name)
value = self.send(sub_attribute_name)
if value.respond_to?(:validating) next if value.validating
end
errors.push *sub_attribute.validate(value, sub_context)
end
ensure
@validating = false
end
|