praxis-blueprints 3.2 → 3.3
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 +4 -4
- data/.rubocop.yml +35 -0
- data/.travis.yml +5 -3
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -0
- data/Guardfile +13 -7
- data/Rakefile +4 -3
- data/lib/praxis-blueprints.rb +2 -1
- data/lib/praxis-blueprints/blueprint.rb +68 -89
- data/lib/praxis-blueprints/collection_view.rb +8 -11
- data/lib/praxis-blueprints/config_hash.rb +15 -12
- data/lib/praxis-blueprints/field_expander.rb +46 -52
- data/lib/praxis-blueprints/finalizable.rb +4 -8
- data/lib/praxis-blueprints/renderer.rb +29 -27
- data/lib/praxis-blueprints/version.rb +2 -1
- data/lib/praxis-blueprints/view.rb +23 -28
- data/praxis-blueprints.gemspec +35 -25
- data/spec/praxis-blueprints/blueprint_spec.rb +33 -57
- data/spec/praxis-blueprints/collection_view_spec.rb +6 -10
- data/spec/praxis-blueprints/config_hash_spec.rb +64 -0
- data/spec/praxis-blueprints/field_expander_spec.rb +30 -38
- data/spec/praxis-blueprints/renderer_spec.rb +57 -50
- data/spec/praxis-blueprints/view_spec.rb +8 -12
- data/spec/spec_helper.rb +11 -14
- data/spec/support/spec_blueprints.rb +6 -14
- metadata +44 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2baa109dab4f8585d61ee9d5317bc11039cbaa98
|
|
4
|
+
data.tar.gz: f2bd553cd4f075311155154b0c7fef9aad415b3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e52a91a7f9c78b9964650972b09c61a883035903f04e41374e82115d04b6643776f9ff05aebb281a007b7074913c9bfb086328ab6043418bd0df0ec8232ccfc8
|
|
7
|
+
data.tar.gz: af137419cbb73cceac9e4283a2558d758b0f4425ac009fe9243780328eb6df1091cdcee776147327fb646630a1832573bdbbd7e14b5a9cac8cb4e30d2f31fa5a
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.3
|
|
3
|
+
Style/Documentation:
|
|
4
|
+
Enabled: false
|
|
5
|
+
Metrics/MethodLength:
|
|
6
|
+
Enabled: false
|
|
7
|
+
Metrics/ClassLength:
|
|
8
|
+
Enabled: false
|
|
9
|
+
Metrics/LineLength:
|
|
10
|
+
Max: 200
|
|
11
|
+
Style/RedundantSelf:
|
|
12
|
+
Enabled: false
|
|
13
|
+
Style/ClassAndModuleChildren:
|
|
14
|
+
Enabled: false
|
|
15
|
+
Lint/Debugger:
|
|
16
|
+
Enabled: false
|
|
17
|
+
Metrics/AbcSize:
|
|
18
|
+
Enabled: false
|
|
19
|
+
Style/CaseEquality:
|
|
20
|
+
Enabled: false
|
|
21
|
+
Lint/UnusedMethodArgument:
|
|
22
|
+
AllowUnusedKeywordArguments: true
|
|
23
|
+
Style/FileName:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'lib/praxis-blueprints.rb'
|
|
26
|
+
Style/ClassVars:
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'lib/praxis-blueprints/blueprint.rb'
|
|
29
|
+
|
|
30
|
+
# Offense count: 5
|
|
31
|
+
Metrics/CyclomaticComplexity:
|
|
32
|
+
Max: 8
|
|
33
|
+
# Offense count: 5
|
|
34
|
+
Metrics/PerceivedComplexity:
|
|
35
|
+
Max: 12
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## next
|
|
4
4
|
|
|
5
|
+
## 3.3
|
|
6
|
+
|
|
7
|
+
* Include Attributor::Dumpable in Blueprint, so it renders (semi) correctly if
|
|
8
|
+
rendered with just `true` specified for fields.
|
|
9
|
+
* Fix bug rendering subobjects with nil values (manifested when `include_nil: true` there’s an explicit subsection of fields)
|
|
10
|
+
|
|
5
11
|
## 3.2
|
|
6
12
|
|
|
7
13
|
* Ensure we call `object.dump` in Renderer when fully dumping an instance (or array of instances) that have the Attributor::Dumpable module (i.e., when no subfields were selected)
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# Config file for Guard
|
|
2
3
|
# More info at https://github.com/guard/guard#readme
|
|
4
|
+
group :red_green_refactor, halt_on_fail: true do
|
|
5
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
|
6
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
7
|
+
watch(%r{^lib/praxis-blueprints/(.+)\.rb$}) { |m| "spec/praxis-blueprints/#{m[1]}_spec.rb" }
|
|
8
|
+
watch('spec/*.rb') { 'spec' }
|
|
9
|
+
watch('lib/praxis-blueprints.rb') { 'spec' }
|
|
10
|
+
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
|
|
11
|
+
end
|
|
3
12
|
|
|
4
|
-
guard :
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
watch('lib/praxis-blueprints.rb') { 'spec' }
|
|
9
|
-
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
|
|
13
|
+
guard :rubocop, cli: '--auto-correct --display-cop-names' do
|
|
14
|
+
watch(/.+\.rb$/)
|
|
15
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
|
16
|
+
end
|
|
10
17
|
end
|
|
11
|
-
|
data/Rakefile
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'bundler/setup'
|
|
2
3
|
|
|
3
|
-
require
|
|
4
|
+
require 'bundler/gem_tasks'
|
|
4
5
|
|
|
5
6
|
require 'rspec/core'
|
|
6
7
|
require 'rspec/core/rake_task'
|
|
7
8
|
require 'bundler/gem_tasks'
|
|
8
9
|
|
|
9
|
-
desc
|
|
10
|
+
desc 'Run RSpec code examples with simplecov'
|
|
10
11
|
RSpec::Core::RakeTask.new do |spec|
|
|
11
12
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
task :
|
|
15
|
+
task default: :spec
|
|
15
16
|
|
|
16
17
|
require 'yard'
|
|
17
18
|
YARD::Rake::YardocTask.new
|
data/lib/praxis-blueprints.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'json'
|
|
2
3
|
require 'yaml'
|
|
3
4
|
require 'logger'
|
|
4
5
|
|
|
5
6
|
require 'attributor'
|
|
6
7
|
|
|
7
|
-
require
|
|
8
|
+
require 'praxis-blueprints/version'
|
|
8
9
|
|
|
9
10
|
require 'praxis-blueprints/finalizable'
|
|
10
11
|
require 'praxis-blueprints/config_hash'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'ostruct'
|
|
2
3
|
|
|
3
4
|
# Blueprint ==
|
|
@@ -7,6 +8,8 @@ require 'ostruct'
|
|
|
7
8
|
module Praxis
|
|
8
9
|
class Blueprint
|
|
9
10
|
include Attributor::Type
|
|
11
|
+
include Attributor::Dumpable
|
|
12
|
+
|
|
10
13
|
extend Finalizable
|
|
11
14
|
|
|
12
15
|
if RUBY_ENGINE =~ /^jruby/
|
|
@@ -18,7 +21,6 @@ module Praxis
|
|
|
18
21
|
|
|
19
22
|
@@caching_enabled = false
|
|
20
23
|
|
|
21
|
-
|
|
22
24
|
attr_reader :validating
|
|
23
25
|
attr_accessor :object
|
|
24
26
|
attr_accessor :decorators
|
|
@@ -34,20 +36,20 @@ module Praxis
|
|
|
34
36
|
super
|
|
35
37
|
|
|
36
38
|
klass.instance_eval do
|
|
37
|
-
@views =
|
|
38
|
-
@options =
|
|
39
|
+
@views = {}
|
|
40
|
+
@options = {}
|
|
39
41
|
@domain_model = Object
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
# Override default new behavior to support memoized creation through an IdentityMap
|
|
44
|
-
def self.new(object, decorators=nil)
|
|
46
|
+
def self.new(object, decorators = nil)
|
|
45
47
|
if @@caching_enabled && decorators.nil?
|
|
46
48
|
cache = if object.respond_to?(:identity_map) && object.identity_map
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
object.identity_map.blueprint_cache[self]
|
|
50
|
+
else
|
|
51
|
+
self.cache
|
|
52
|
+
end
|
|
51
53
|
|
|
52
54
|
return cache[object] ||= begin
|
|
53
55
|
blueprint = self.allocate
|
|
@@ -65,14 +67,12 @@ module Praxis
|
|
|
65
67
|
'hash'
|
|
66
68
|
end
|
|
67
69
|
|
|
68
|
-
def self.describe(shallow=false,example: nil, **opts)
|
|
70
|
+
def self.describe(shallow = false, example: nil, **opts)
|
|
69
71
|
type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
|
|
70
72
|
|
|
71
|
-
if example
|
|
72
|
-
example = example.object
|
|
73
|
-
end
|
|
73
|
+
example = example.object if example
|
|
74
74
|
|
|
75
|
-
description = self.attribute.type.describe(shallow,example: example, **opts).merge!(id: self.id, name: type_name)
|
|
75
|
+
description = self.attribute.type.describe(shallow, example: example, **opts).merge!(id: self.id, name: type_name)
|
|
76
76
|
description.delete :anonymous # discard the Struct's view of anonymity, and use the Blueprint's one
|
|
77
77
|
description[:anonymous] = @_anonymous unless @_anonymous.nil?
|
|
78
78
|
|
|
@@ -85,37 +85,30 @@ module Praxis
|
|
|
85
85
|
description
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
def self.attributes(opts={}, &block)
|
|
88
|
+
def self.attributes(opts = {}, &block)
|
|
90
89
|
if block_given?
|
|
91
|
-
if self.const_defined?(:Struct, false)
|
|
92
|
-
raise "Redefining Blueprint attributes is not currently supported"
|
|
93
|
-
else
|
|
90
|
+
raise 'Redefining Blueprint attributes is not currently supported' if self.const_defined?(:Struct, false)
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
@options.merge!(opts)
|
|
104
|
-
@block = block
|
|
92
|
+
if opts.key?(:reference) && opts[:reference] != self.reference
|
|
93
|
+
raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
|
|
94
|
+
elsif self.reference
|
|
95
|
+
opts[:reference] = self.reference # pass the reference Class down
|
|
96
|
+
else
|
|
97
|
+
opts[:reference] = self
|
|
105
98
|
end
|
|
106
99
|
|
|
100
|
+
@options.merge!(opts)
|
|
101
|
+
@block = block
|
|
102
|
+
|
|
107
103
|
return @attribute
|
|
108
104
|
end
|
|
109
105
|
|
|
110
|
-
unless @attribute
|
|
111
|
-
raise "@attribute not defined yet for #{self.name}"
|
|
112
|
-
end
|
|
106
|
+
raise "@attribute not defined yet for #{self.name}" unless @attribute
|
|
113
107
|
|
|
114
108
|
@attribute.attributes
|
|
115
109
|
end
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
def self.domain_model(klass=nil)
|
|
111
|
+
def self.domain_model(klass = nil)
|
|
119
112
|
return @domain_model if klass.nil?
|
|
120
113
|
@domain_model = klass
|
|
121
114
|
end
|
|
@@ -123,26 +116,25 @@ module Praxis
|
|
|
123
116
|
def self.check_option!(name, value)
|
|
124
117
|
case name
|
|
125
118
|
when :identity
|
|
126
|
-
raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.
|
|
119
|
+
raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.is_a?(::Symbol)
|
|
127
120
|
return :ok
|
|
128
121
|
else
|
|
129
122
|
return Attributor::Struct.check_option!(name, value)
|
|
130
123
|
end
|
|
131
124
|
end
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
|
126
|
+
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
|
135
127
|
case value
|
|
136
128
|
when self
|
|
137
129
|
value
|
|
138
130
|
when nil, Hash, String
|
|
139
131
|
# Need to parse/deserialize first
|
|
140
132
|
# or apply default/recursive loading options if necessary
|
|
141
|
-
if (value = self.attribute.load(value,context, **options))
|
|
133
|
+
if (value = self.attribute.load(value, context, **options))
|
|
142
134
|
self.new(value)
|
|
143
135
|
end
|
|
144
136
|
else
|
|
145
|
-
if value.
|
|
137
|
+
if value.is_a?(self.domain_model) || value.is_a?(self::Struct)
|
|
146
138
|
# Wrap the value directly
|
|
147
139
|
self.new(value)
|
|
148
140
|
else
|
|
@@ -153,7 +145,7 @@ module Praxis
|
|
|
153
145
|
end
|
|
154
146
|
|
|
155
147
|
class << self
|
|
156
|
-
|
|
148
|
+
alias from load
|
|
157
149
|
end
|
|
158
150
|
|
|
159
151
|
def self.caching_enabled?
|
|
@@ -175,35 +167,33 @@ module Praxis
|
|
|
175
167
|
|
|
176
168
|
def self.valid_type?(value)
|
|
177
169
|
# FIXME: this should be more... ducklike
|
|
178
|
-
value.
|
|
170
|
+
value.is_a?(self) || value.is_a?(self.attribute.type)
|
|
179
171
|
end
|
|
180
172
|
|
|
181
|
-
def self.example(context=nil, **values)
|
|
173
|
+
def self.example(context = nil, **values)
|
|
182
174
|
context = case context
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
175
|
+
when nil
|
|
176
|
+
["#{self.name}-#{values.object_id}"]
|
|
177
|
+
when ::String
|
|
178
|
+
[context]
|
|
179
|
+
else
|
|
180
|
+
context
|
|
181
|
+
end
|
|
190
182
|
|
|
191
183
|
self.new(self.attribute.example(context, values: values))
|
|
192
184
|
end
|
|
193
185
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context == nil
|
|
186
|
+
def self.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil)
|
|
187
|
+
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
|
|
197
188
|
context = [context] if context.is_a? ::String
|
|
198
189
|
|
|
199
|
-
unless value.
|
|
190
|
+
unless value.is_a?(self)
|
|
200
191
|
raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{self.name} for an object of type #{value.class.name}."
|
|
201
192
|
end
|
|
202
193
|
|
|
203
194
|
value.validate(context)
|
|
204
195
|
end
|
|
205
196
|
|
|
206
|
-
|
|
207
197
|
def self.view(name, **options, &block)
|
|
208
198
|
if block_given?
|
|
209
199
|
return self.views[name] = View.new(name, self, **options, &block)
|
|
@@ -219,7 +209,7 @@ module Praxis
|
|
|
219
209
|
object.render(view: view, context: context, **opts)
|
|
220
210
|
end
|
|
221
211
|
class << self
|
|
222
|
-
|
|
212
|
+
alias render dump
|
|
223
213
|
end
|
|
224
214
|
|
|
225
215
|
# Internal finalize! logic
|
|
@@ -235,7 +225,7 @@ module Praxis
|
|
|
235
225
|
end
|
|
236
226
|
|
|
237
227
|
def self.resolve_domain_model!
|
|
238
|
-
return unless self.domain_model.
|
|
228
|
+
return unless self.domain_model.is_a?(String)
|
|
239
229
|
|
|
240
230
|
@domain_model = self.domain_model.constantize
|
|
241
231
|
end
|
|
@@ -248,7 +238,7 @@ module Praxis
|
|
|
248
238
|
end
|
|
249
239
|
|
|
250
240
|
def self.define_readers!
|
|
251
|
-
self.attributes.each do |name,
|
|
241
|
+
self.attributes.each do |name, _attribute|
|
|
252
242
|
name = name.to_sym
|
|
253
243
|
|
|
254
244
|
# Don't redefine existing methods
|
|
@@ -258,7 +248,6 @@ module Praxis
|
|
|
258
248
|
end
|
|
259
249
|
end
|
|
260
250
|
|
|
261
|
-
|
|
262
251
|
def self.define_reader!(name)
|
|
263
252
|
attribute = self.attributes[name]
|
|
264
253
|
# TODO: profile and optimize
|
|
@@ -270,18 +259,16 @@ module Praxis
|
|
|
270
259
|
@decorators.send(name)
|
|
271
260
|
else
|
|
272
261
|
value = @object.__send__(name)
|
|
273
|
-
return value if value.nil? || value.
|
|
262
|
+
return value if value.nil? || value.is_a?(attribute.type)
|
|
274
263
|
attribute.load(value)
|
|
275
264
|
end
|
|
276
265
|
end
|
|
277
266
|
end
|
|
278
267
|
|
|
279
|
-
|
|
280
|
-
|
|
281
268
|
def self.generate_master_view!
|
|
282
269
|
attributes = self.attributes
|
|
283
270
|
view :master do
|
|
284
|
-
attributes.each do |
|
|
271
|
+
attributes.each do |name, _attr|
|
|
285
272
|
# Note: we can freely pass master view for attributes that aren't blueprint/containers because
|
|
286
273
|
# their dump methods will ignore it (they always dump everything regardless)
|
|
287
274
|
attribute name, view: :default
|
|
@@ -289,33 +276,29 @@ module Praxis
|
|
|
289
276
|
end
|
|
290
277
|
end
|
|
291
278
|
|
|
292
|
-
|
|
293
|
-
def initialize(object, decorators=nil)
|
|
279
|
+
def initialize(object, decorators = nil)
|
|
294
280
|
# TODO: decide what sort of type checking (if any) we want to perform here.
|
|
295
281
|
@object = object
|
|
296
282
|
|
|
297
|
-
@decorators = if decorators.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
283
|
+
@decorators = if decorators.is_a?(Hash) && decorators.any?
|
|
284
|
+
OpenStruct.new(decorators)
|
|
285
|
+
else
|
|
286
|
+
decorators
|
|
287
|
+
end
|
|
302
288
|
|
|
303
289
|
@validating = false
|
|
304
290
|
end
|
|
305
291
|
|
|
306
|
-
|
|
307
292
|
# Render the wrapped data with the given view
|
|
308
|
-
def render(view_name=nil, context: Attributor::DEFAULT_ROOT_CONTEXT,renderer: Renderer.new, **opts)
|
|
309
|
-
if view_name
|
|
310
|
-
warn
|
|
293
|
+
def render(view_name = nil, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **opts)
|
|
294
|
+
if !view_name.nil?
|
|
295
|
+
warn 'DEPRECATED: please do not pass the view name as the first parameter in Blueprint.render, pass through the view: named param instead.'
|
|
311
296
|
elsif opts.key?(:view)
|
|
312
297
|
view_name = opts[:view]
|
|
313
298
|
end
|
|
314
299
|
|
|
315
300
|
fields = opts[:fields]
|
|
316
|
-
if view_name.nil? && fields.nil?
|
|
317
|
-
view_name = :default
|
|
318
|
-
end
|
|
301
|
+
view_name = :default if view_name.nil? && fields.nil?
|
|
319
302
|
|
|
320
303
|
if view_name
|
|
321
304
|
unless (view = self.class.views[view_name])
|
|
@@ -326,7 +309,7 @@ module Praxis
|
|
|
326
309
|
|
|
327
310
|
# Accept a simple array of fields, and transform it to a 1-level hash with true values
|
|
328
311
|
if fields.is_a? Array
|
|
329
|
-
fields = fields.each_with_object({}) {|field, hash| hash[field] = true }
|
|
312
|
+
fields = fields.each_with_object({}) { |field, hash| hash[field] = true }
|
|
330
313
|
end
|
|
331
314
|
|
|
332
315
|
# expand fields
|
|
@@ -334,33 +317,31 @@ module Praxis
|
|
|
334
317
|
|
|
335
318
|
renderer.render(self, expanded_fields, context: context)
|
|
336
319
|
end
|
|
337
|
-
|
|
338
|
-
|
|
320
|
+
alias to_hash render
|
|
321
|
+
alias dump render
|
|
339
322
|
|
|
340
|
-
def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
|
|
341
|
-
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context
|
|
323
|
+
def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
|
|
324
|
+
raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
|
|
342
325
|
context = [context] if context.is_a? ::String
|
|
343
326
|
keys_with_values = []
|
|
344
327
|
|
|
345
|
-
raise
|
|
328
|
+
raise 'validation conflict' if @validating
|
|
346
329
|
@validating = true
|
|
347
330
|
|
|
348
331
|
errors = []
|
|
349
332
|
self.class.attributes.each do |sub_attribute_name, sub_attribute|
|
|
350
|
-
sub_context = self.class.generate_subcontext(context,sub_attribute_name)
|
|
333
|
+
sub_context = self.class.generate_subcontext(context, sub_attribute_name)
|
|
351
334
|
value = self.send(sub_attribute_name)
|
|
352
|
-
unless value.nil?
|
|
353
|
-
keys_with_values << sub_attribute_name
|
|
354
|
-
end
|
|
335
|
+
keys_with_values << sub_attribute_name unless value.nil?
|
|
355
336
|
|
|
356
337
|
if value.respond_to?(:validating) # really, it's a thing with sub-attributes
|
|
357
338
|
next if value.validating
|
|
358
339
|
end
|
|
359
|
-
errors.
|
|
340
|
+
errors.concat(sub_attribute.validate(value, sub_context))
|
|
360
341
|
end
|
|
361
342
|
self.class.attribute.type.requirements.each do |req|
|
|
362
343
|
validation_errors = req.validate(keys_with_values, context)
|
|
363
|
-
errors.
|
|
344
|
+
errors.concat(validation_errors) unless validation_errors.empty?
|
|
364
345
|
end
|
|
365
346
|
errors
|
|
366
347
|
ensure
|
|
@@ -371,7 +352,5 @@ module Praxis
|
|
|
371
352
|
def _get_attr(name)
|
|
372
353
|
self.send(name)
|
|
373
354
|
end
|
|
374
|
-
|
|
375
355
|
end
|
|
376
|
-
|
|
377
356
|
end
|