bound 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 290d76f9ebce3f1b1b815ecbaac8f7b0320e7a7c
4
- data.tar.gz: ea47e2f076c07eeaa1e45e14add1c5fc6683289b
3
+ metadata.gz: cb1d1bdcd67442312ce8e48fa04a1e202aca15a2
4
+ data.tar.gz: 1a506e8c642e84938a4f32ae8f97cf2ed579b05b
5
5
  SHA512:
6
- metadata.gz: d42bb29fd3b7f5285e8278a12a23eb85ed4fd25960445f4d9cebb6f6c66e01e9fd76aa23f229c039004add07d6409e79bc41d21e8734a4454fae78ce7b016d7d
7
- data.tar.gz: 269d4c1f5a82337aca2b520e82d614e50aae40183363c157dc30c06d56cfbb12c6bf980f991d0a09eed5010333221d49f89598c7ff68e289f388fdcc674857b7
6
+ metadata.gz: fa1024912a23354e5bb1ec42ee3518b6b2de5e5e8229eb2182c93f8b69783dc5dfc16e03d0f5da3d6f525ed4472b028f5787952848cc04795728786223df3cf3
7
+ data.tar.gz: a27f0003a78e6ad4fbc69362cb6da84707c6b5f5887e900cec0eccf0f23879b1bdf6cc393e4b7313c4e7644827e1faee1c42745bcbc0eb455e89f58c1534df5a
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ prof__*
@@ -2,8 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
- - jruby-18mode # JRuby in 1.8 mode
6
- - jruby-19mode # JRuby in 1.9 mode
7
- - rbx
8
5
  - 1.8.7
9
6
  - ree
7
+ - jruby-18mode # JRuby in 1.8 mode
8
+ - jruby-19mode # JRuby in 1.9 mode
data/Rakefile CHANGED
@@ -8,6 +8,5 @@ task :default => :spec
8
8
  Rake::TestTask.new(:spec) do |test|
9
9
  test.test_files = FileList["spec/**/*_spec.rb"]
10
10
  test.libs << "spec"
11
- test.warning = true
12
11
  test.verbose = true
13
12
  end
@@ -2,10 +2,102 @@ $: << 'lib'
2
2
  require 'bound'
3
3
  require 'benchmark'
4
4
 
5
- TestBoundary = Bound.required(:abc, :def, :ged)
5
+ if ENV['PROFILE']
6
+ require 'perftools'
7
+ def start_perf(name)
8
+ @_perf_name_ = 'prof__' + name
9
+ PerfTools::CpuProfiler.start @_perf_name_
10
+ end
11
+
12
+ def finish_perf
13
+ PerfTools::CpuProfiler.stop
14
+ system "pprof.rb --pdf #@_perf_name_ > #{@_perf_name_}.pdf"
15
+ system "rm -f ./#{@_perf_name_} ./#{@_perf_name_}.symbols"
16
+ ensure
17
+ @_perf_name_ = nil
18
+ end
19
+ else
20
+ def start_perf(*);end
21
+ def finish_perf(*);end
22
+ end
23
+
24
+
25
+
26
+ TestBoundary = Bound.required(
27
+ :foo,
28
+ :bar => [Bound.required(:abc)],
29
+ :baz => Bound.required(:gonzo)
30
+ )
31
+
32
+ StructBoundary = Struct.new(:foo, :bar, :baz)
33
+ BarStructBoundary = Struct.new(:abc)
34
+ BazStructBoundary = Struct.new(:gonzo)
35
+
36
+ StaticBoundClass = Class.new do
37
+ def self.initialize_unvalidated
38
+ class_eval <<-EOR
39
+ def initialize(target, overwrite = nil)
40
+ @t, @o = target, overwrite
41
+ end
42
+ EOR
43
+ end
44
+
45
+ def self.define_delegate(attr, prefix = '')
46
+ class_eval <<-EOR
47
+ def #{prefix}#{attr}
48
+ return @o[:#{attr}] if @o && @o.key?(:#{attr})
49
+ @t.kind_of?(Hash)? @t[:#{attr}] : @t.#{attr}
50
+ end
51
+ EOR
52
+ end
53
+
54
+ def self.define_nested_delegate(attr, nested_class)
55
+ define_delegate attr, 'get_'
56
+ if nested_class.kind_of? Array
57
+ nested_class = nested_class.first
58
+ class_eval <<-EOR
59
+ def #{attr}
60
+ @#{attr} ||= get_#{attr}.map{|t| #{nested_class}.new t}
61
+ end
62
+ private :get_#{attr}
63
+ EOR
64
+ else
65
+ class_eval <<-EOR
66
+ def #{attr}
67
+ @#{attr} ||= #{nested_class}.new(get_#{attr})
68
+ end
69
+ private :get_#{attr}
70
+ EOR
71
+ end
72
+ end
73
+ end
74
+
75
+ BarStaticBoundary = Class.new(StaticBoundClass) do
76
+ initialize_unvalidated
77
+
78
+ define_delegate :abc
79
+ end
80
+
81
+ BazStaticBoundary = Class.new(StaticBoundClass) do
82
+ initialize_unvalidated
83
+
84
+ define_delegate :gonzo
85
+ end
86
+
87
+ StaticBoundary = Class.new(StaticBoundClass) do
88
+ initialize_unvalidated
89
+
90
+ define_delegate :foo
91
+ define_nested_delegate :bar, [BarStaticBoundary]
92
+ define_nested_delegate :baz, BazStaticBoundary
93
+ end
6
94
 
7
- ManualBoundary = Provider = Class.new do
8
- attr_accessor :abc, :def, :ged
95
+ def assert_correctness(bound)
96
+ raise('foo is wrong') unless bound.foo == 'NOPE'
97
+ raise('bar size is wrong') unless bound.bar.size == 2
98
+ raise('bar[0] is wrong') unless bound.bar[0].abc == 'TRUE'
99
+ raise('bar[1] is wrong') unless bound.bar[1].abc == 'FALSE'
100
+ raise('baz.gonzo is wrong') unless bound.baz.gonzo == 22
9
101
  end
10
102
 
11
103
  def bench(key, &block)
@@ -17,36 +109,119 @@ def bench(key, &block)
17
109
  result
18
110
  end
19
111
 
20
- providers = 10_000.times.map do |i|
21
- provider = Provider.new
22
- provider.abc = "abc#{i}"
23
- provider.def = "def#{i}"
24
- provider.ged = "ged#{i}"
25
- provider
112
+ Provider = Class.new do
113
+ attr_accessor :foo, :bar, :baz
114
+ BarProvider = Class.new do
115
+ attr_accessor :abc
116
+ end
117
+ BazProvider = Class.new do
118
+ attr_accessor :gonzo
119
+ end
120
+ end
121
+ provider_objects = 100_000.times.map do |i|
122
+ Provider.new.tap do |p|
123
+ p.foo = 'YES'
124
+ p.bar = [
125
+ BarProvider.new.tap do |brp|
126
+ brp.abc = 'TRUE'
127
+ end,
128
+ BarProvider.new.tap do |brp|
129
+ brp.abc = 'FALSE'
130
+ end
131
+ ]
132
+ p.baz = BazProvider.new.tap do |bzp|
133
+ bzp.gonzo = 22
134
+ end
135
+ end
136
+ end
137
+
138
+ provider_hashes = 100_000.times.map do |i|
139
+ {
140
+ :foo => 'YES',
141
+ :bar => [{:abc => 'TRUE'}, {:abc => 'FALSE'}],
142
+ :baz => {:gonzo => 22}
143
+ }
144
+ end
145
+
146
+ overwrite = {:foo => 'NOPE'}
147
+
148
+
149
+ start_perf 'bound.objt'
150
+ bench ' bound w/ objt' do
151
+ provider_objects.each do |provider|
152
+ result = TestBoundary.new(provider, overwrite)
153
+ assert_correctness result
154
+ end
155
+ end
156
+ finish_perf
157
+
158
+ start_perf 'bound.hash'
159
+ bench ' bound w/ hash' do
160
+ provider_hashes.each do |provider|
161
+ result = TestBoundary.new(provider, overwrite)
162
+ assert_correctness result
163
+ end
164
+ end
165
+ finish_perf
166
+
167
+ Bound.disable_validation
168
+
169
+ start_perf 'bound.noval.objt'
170
+ bench 'bound noval w/ objt' do
171
+ provider_objects.each do |provider|
172
+ result = TestBoundary.new(provider, overwrite)
173
+ assert_correctness result
174
+ end
175
+ end
176
+ finish_perf
177
+
178
+ start_perf 'bound.noval.hash'
179
+ bench 'bound noval w/ hash' do
180
+ provider_hashes.each do |provider|
181
+ result = TestBoundary.new(provider, overwrite)
182
+ assert_correctness result
183
+ end
184
+ end
185
+ finish_perf
186
+
187
+ bench 'staticbound w/ objt' do
188
+ provider_objects.each do |provider|
189
+ result = StaticBoundary.new(provider, overwrite)
190
+ assert_correctness result
191
+ end
26
192
  end
27
193
 
28
- bench 'bound w/ objt' do
29
- providers.map do |provider|
30
- TestBoundary.new(provider)
194
+ bench 'staticbound w/ hash' do
195
+ provider_hashes.each do |provider|
196
+ result = StaticBoundary.new(provider, overwrite)
197
+ assert_correctness result
31
198
  end
32
199
  end
33
200
 
34
- bench 'bound w/ hash' do
35
- providers.map do |provider|
36
- TestBoundary.new({
37
- :abc => provider.abc,
38
- :def => provider.def,
39
- :ged => provider.ged
40
- })
201
+ bench 'structbound w/ objt' do
202
+ provider_objects.each do |provider|
203
+ result = StructBoundary.new(
204
+ overwrite[:foo],
205
+ [
206
+ BarStructBoundary.new(provider.bar[0].abc),
207
+ BarStructBoundary.new(provider.bar[1].abc),
208
+ ],
209
+ BazStructBoundary.new(provider.baz.gonzo)
210
+ )
211
+ assert_correctness result
41
212
  end
42
213
  end
43
214
 
44
- bench 'plain' do
45
- providers.map do |provider|
46
- test = ManualBoundary.new
47
- test.abc = provider.abc
48
- test.def = provider.def
49
- test.ged = provider.ged
50
- test
215
+ bench 'structbound w/ hash' do
216
+ provider_hashes.map do |provider|
217
+ result = StructBoundary.new(
218
+ overwrite[:foo],
219
+ [
220
+ BarStructBoundary.new(provider[:bar][0][:abc]),
221
+ BarStructBoundary.new(provider[:bar][1][:abc]),
222
+ ],
223
+ BazStructBoundary.new(provider[:baz][:gonzo])
224
+ )
225
+ assert_correctness result
51
226
  end
52
227
  end
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
22
  spec.add_development_dependency "minitest", "~> 5.0.7"
23
+ spec.add_development_dependency "perftools.rb"
23
24
 
24
25
  spec.add_development_dependency "simplecov"
25
26
  end
@@ -14,292 +14,300 @@ class Bound
14
14
  new_bound_class.required(*args)
15
15
  end
16
16
 
17
+ def self.validate?
18
+ !@validation_disabled
19
+ end
20
+
21
+ def self.disable_validation
22
+ @validation_disabled = true
23
+ StaticBoundClass.define_initializer_without_validation
24
+ end
25
+
17
26
  private
18
27
 
19
28
  def self.new_bound_class
20
- Class.new(BoundClass) do
21
- initialize_values
29
+ Class.new(StaticBoundClass) do
22
30
  end
23
31
  end
24
32
 
25
- class BoundClass
26
- class Attribute
27
- attr_reader :name, :value
28
- attr_accessor :nested_class
29
-
30
- def initialize(name)
31
- @name = name
32
- @assigned = false
33
- end
34
-
35
- def assign(value)
36
- @assigned = true
37
- if nested_class
38
- @value = assign_nested(value)
39
- else
40
- @value = value
41
- end
42
- end
43
-
44
- def assign_nested(value)
45
- nested_attribute = NestedAttribute.new(nested_class)
46
- nested_attribute.resolve(value)
47
- end
48
-
49
- def call_on(object)
50
- Caller.call(object, @name)
51
- end
33
+ class BoundValidator
34
+ attr_accessor :attributes, :optional_attributes, :nested_array_attributes
52
35
 
53
- def valid?
54
- !required? || is_assigned?
55
- end
56
-
57
- def required?
58
- false
59
- end
36
+ def initialize(target, overwrite)
37
+ @target = target
38
+ @overwrite = overwrite
39
+ end
60
40
 
61
- def is_assigned?
62
- !!@assigned
41
+ def validate!
42
+ ensure_all_attributes_are_known!
43
+ attributes.each do |attribute|
44
+ ensure_present! attribute
63
45
  end
64
-
65
- def inspect
66
- @value.inspect
46
+ nested_array_attributes.each do |nested_array_attribute|
47
+ ensure_array! nested_array_attribute
67
48
  end
68
49
  end
69
50
 
70
- class RequiredAttribute < Attribute
71
- def required?; true; end
72
- end
51
+ private
73
52
 
74
- class NestedAttribute
75
- def initialize(bound_definition)
76
- if bound_definition.kind_of?(Array)
77
- @assigner = ArrayAssigner.new(bound_definition)
78
- else
79
- @assigner = ValueAssigner.new(bound_definition)
53
+ def ensure_all_attributes_are_known!
54
+ (overwritten_attrs + target_attrs).each do |attr|
55
+ unless (attributes + optional_attributes).include? attr
56
+ message = "Unknown attribute: #{attr}"
57
+ raise ArgumentError, message
80
58
  end
81
59
  end
60
+ end
82
61
 
83
- def resolve(bound_arguments)
84
- @assigner.resolve(bound_arguments)
85
- end
86
-
87
- class ArrayAssigner
88
- def initialize(definitions)
89
- @bound_class = definitions.first
90
- end
91
-
92
- def resolve(arguments_list)
93
- raise ArgumentError.new("Expected #{arguments_list.inspect} to be an array") unless arguments_list.kind_of? Array
94
- arguments_list.map do |arguments|
95
- @bound_class.new(arguments)
96
- end
97
- end
62
+ def ensure_present!(attribute)
63
+ if !overwritten?(attribute) && !target_has?(attribute)
64
+ message = "Missing attribute: #{attribute}"
65
+ raise ArgumentError, message
98
66
  end
67
+ end
99
68
 
100
- class ValueAssigner
101
- def initialize(definition)
102
- @bound_class = definition
69
+ def ensure_array!(attribute)
70
+ message = "Expected %s to be an array"
71
+ if overwritten?(attribute)
72
+ unless val = overwritten(attribute).kind_of?(Array)
73
+ raise(ArgumentError, message % val.inspect)
103
74
  end
104
-
105
- def resolve(arguments)
106
- @bound_class.new(arguments)
75
+ elsif target_has?(attribute)
76
+ unless val = target(attribute).kind_of?(Array)
77
+ raise(ArgumentError, message % val.inspect)
107
78
  end
79
+ else
108
80
  end
109
81
  end
110
82
 
111
-
112
- class << self
113
- attr_accessor :attrs, :nested_attr_classes
114
-
115
- def initialize_values
116
- self.attrs = {}
117
- self.nested_attr_classes = {}
83
+ def overwritten_attrs
84
+ if @overwrite
85
+ @overwrite.keys
86
+ else
87
+ []
118
88
  end
89
+ end
119
90
 
120
- def optional(*attributes)
121
- if attributes.last.kind_of? Hash
122
- nested_attributes = attributes.pop
123
- else
124
- nested_attributes = {}
125
- end
126
-
127
- set_attributes :optional, attributes, nested_attributes
128
-
129
- self
91
+ def target_attrs
92
+ if @target && @target.kind_of?(Hash)
93
+ @target.keys
94
+ else
95
+ []
130
96
  end
97
+ end
131
98
 
132
- def required(*attributes)
133
- if attributes.last.kind_of? Hash
134
- nested_attributes = attributes.pop
135
- else
136
- nested_attributes = {}
137
- end
99
+ def overwritten?(attr)
100
+ @overwrite && @overwrite.key?(attr)
101
+ end
138
102
 
139
- set_attributes :required, attributes, nested_attributes
103
+ def overwritten(attr)
104
+ @overwrite && @overwrite[attr]
105
+ end
140
106
 
141
- self
142
- end
107
+ def target_has?(attr)
108
+ @target &&
109
+ @target.kind_of?(Hash)?@target.key?(attr):@target.respond_to?(attr)
110
+ end
143
111
 
144
- private
112
+ def target(attr)
113
+ @target &&
114
+ @target.kind_of?(Hash)?@target[attr]:@target.send(attr)
115
+ end
116
+ end
145
117
 
146
- def set_attributes(flag, attributes, nested_attributes = {})
147
- is_optional = flag == :optional
118
+ class StaticBoundClass
119
+ def ==(other)
120
+ false unless other
121
+ true
122
+ end
148
123
 
149
- if attributes.any? { |a| !a.kind_of? Symbol }
150
- raise ArgumentError.new("Invalid list of attributes: #{attributes.inspect}")
151
- end
124
+ def validate!
125
+ end
152
126
 
153
- attribute_class = if is_optional
154
- Attribute
155
- else
156
- RequiredAttribute
157
- end
127
+ def self.define_initializer_without_validation
128
+ define_initializer(nil)
129
+ end
158
130
 
159
- attributes.each do |attribute|
160
- self.attrs[attribute] = attribute_class
131
+ def self.define_initializer(after_init = 'validate!')
132
+ code = <<-EOR
133
+ def initialize(target = nil, overwrite = nil)
134
+ @t, @o = target, overwrite
135
+ %s
161
136
  end
137
+ EOR
138
+ if after_init
139
+ code = code % " #{after_init}"
140
+ else
141
+ code = code % ''
142
+ end
162
143
 
163
- define_attribute_accessors attributes
144
+ class_eval code
145
+ end
164
146
 
165
- if nested_attributes.any?
166
- set_attributes flag, nested_attributes.keys
167
- nested_attributes.each do |attribute_name, nested_class|
168
- self.nested_attr_classes[attribute_name] = nested_class
169
- end
170
- end
147
+ def self.define_attributes(*attributes)
148
+ if attributes.last.kind_of? Hash
149
+ nested_attributes = attributes.pop
150
+ else
151
+ nested_attributes = {}
171
152
  end
172
153
 
173
- def define_attribute_accessors(attributes)
174
- define_attribute_readers attributes
175
- define_attribute_writers attributes
154
+ if nested_attributes.keys.any? { |a| !a.kind_of? Symbol }
155
+ message = "Invalid list of attributes: #{nested_attributes.inspect}"
156
+ raise ArgumentError, message
176
157
  end
177
158
 
178
- def define_attribute_readers(attributes)
179
- attributes.each do |attribute|
180
- define_method attribute do
181
- get_attribute(attribute).value
182
- end
183
- end
159
+ if attributes.any? { |a| !a.kind_of? Symbol }
160
+ message = "Invalid list of attributes: #{attributes.inspect}"
161
+ raise ArgumentError, message
184
162
  end
185
163
 
186
- def define_attribute_writers(attributes)
187
- attributes.each do |attribute|
188
- define_method :"#{attribute}=" do |value|
189
- get_attribute(attribute).assign value
190
- end
191
- end
164
+ nested_attributes.each do |attribute, nested_class|
165
+ define_nested_delegate attribute, nested_class
166
+ define_equality attribute
192
167
  end
193
- end
194
-
195
- def initialize(seed = nil, overwrite = nil)
196
- @attributes = {}
197
- raise('Overwrite with object') if overwrite && !overwrite.kind_of?(Hash)
198
- seed_with overwrite if overwrite
199
- seed_with seed if seed
200
- validate!
201
- end
202
-
203
- def method_missing(meth, *args, &blk)
204
- attribute = meth.to_s.gsub(/=$/, '')
205
- raise ArgumentError.new("Unknown attribute: #{self.class}##{attribute}")
206
- end
207
168
 
208
- def get_attributes
209
- self.class.attrs.keys.map do |attribute_name|
210
- get_attribute(attribute_name)
169
+ attributes.each do |attribute|
170
+ define_delegate attribute
171
+ define_equality attribute
211
172
  end
212
173
  end
213
174
 
214
- def has_attribute?(attr)
215
- self.class.attrs.keys.include? attr
175
+ def self.define_validator
176
+ attributes = (@attributes || []).map do |attr|
177
+ ":#{attr.to_s}"
178
+ end.join(',')
179
+ optional_attributes = (@optional_attributes || []).map do |attr|
180
+ ":#{attr.to_s}"
181
+ end.join(',')
182
+ nested_array_attributes = (@nested_array_attributes || []).map do |attr|
183
+ ":#{attr.to_s}"
184
+ end.join(',')
185
+ code = <<-EOR
186
+ def validate!
187
+ v = Bound::BoundValidator.new(@t, @o)
188
+ v.attributes = [#{attributes}]
189
+ v.optional_attributes = [#{optional_attributes}]
190
+ v.nested_array_attributes = [#{nested_array_attributes}]
191
+ v.validate!
192
+ end
193
+ private :validate!
194
+ EOR
195
+ class_eval code
216
196
  end
217
197
 
218
- def __attributes__
219
- puts "BoundClass#__attributes__ is deprecated: use get_attributes"
220
- get_attributes.map(&:name)
198
+ def self.set_required_attributes(attributes, nested_array_attributes)
199
+ @attributes ||= []
200
+ @attributes += attributes
201
+ @attributes += nested_array_attributes
202
+ @nested_array_attributes ||= []
203
+ @nested_array_attributes += nested_array_attributes
204
+ define_validator
221
205
  end
222
206
 
223
- def get_attribute(attribute_name)
224
- return @attributes[attribute_name] if @attributes.has_key? attribute_name
225
-
226
- attribute_class = self.class.attrs[attribute_name]
227
- return nil if attribute_class.nil?
228
-
229
- nested_class = self.class.nested_attr_classes[attribute_name]
230
-
231
- attribute = attribute_class.new(attribute_name)
232
- attribute.nested_class = nested_class
233
-
234
- @attributes[attribute_name] = attribute
207
+ def self.set_optional_attributes(attributes, nested_array_attributes)
208
+ @optional_attributes ||= []
209
+ @optional_attributes += attributes
210
+ @optional_attributes += nested_array_attributes
211
+ @nested_array_attributes ||= []
212
+ @nested_array_attributes += nested_array_attributes
213
+ define_validator
235
214
  end
236
215
 
237
- def ==(other)
238
- return false unless other
239
-
240
- get_attributes.all? do |attribute|
241
- attribute.value == Caller.call(other, attribute.name)
242
- end
216
+ def self.define_equality(attr)
217
+ @equality ||= []
218
+ @equality << attr
219
+ code = <<-EOR
220
+ def==(other)
221
+ return false unless other
222
+ %w{#{@equality.join(' ')}}.all? do |attr|
223
+ other.respond_to?(attr) &&
224
+ other.send(attr) == send(attr)
225
+ end
226
+ end
227
+ EOR
228
+ class_eval code
243
229
  end
244
230
 
245
- private
246
-
247
- def validate!
248
- get_attributes.each do |attribute|
249
- raise ArgumentError.new("Missing attribute: #{self.class}##{attribute.name}") unless attribute.valid?
250
- end
231
+ def self.define_delegate(attr, prefix = '')
232
+ code = <<-EOR
233
+ def #{prefix}#{attr}
234
+ return @o[:#{attr}] if @o && @o.key?(:#{attr})
235
+ return @t.kind_of?(Hash)? @t[:#{attr}] : @t.#{attr} if @t
236
+ nil
237
+ end
238
+ EOR
239
+ class_eval code
251
240
  end
252
241
 
253
- def seed_with(seed)
254
- case seed
255
- when Hash
256
- seeder = HashSeeder.new(self)
242
+ def self.define_nested_delegate(attr, nested_class)
243
+ define_delegate attr, 'get_'
244
+ code = <<-EOR
245
+ class << self
246
+ def get_#{attr}_class
247
+ @#{attr}_class
248
+ end
249
+ def set_#{attr}_class(arg)
250
+ @#{attr}_class = arg
251
+ end
252
+ private :set_#{attr}_class
253
+ end
254
+ EOR
255
+
256
+ if nested_class.kind_of? Array
257
+ nested_class = nested_class.first
258
+ code += <<-EOR
259
+ def #{attr}
260
+ return @#{attr} if defined? @#{attr}
261
+ return [] unless val = get_#{attr}
262
+ @#{attr} ||= val.map{|t| self.class.get_#{attr}_class.new t}
263
+ end
264
+ private :get_#{attr}
265
+ EOR
257
266
  else
258
- seeder = ObjectSeeder.new(self)
267
+ code += <<-EOR
268
+ def #{attr}
269
+ return @#{attr} if defined? @#{attr}
270
+ return nil unless val = get_#{attr}
271
+ @#{attr} ||= self.class.get_#{attr}_class.new(val)
272
+ end
273
+ private :get_#{attr}
274
+ EOR
259
275
  end
260
-
261
- seeder.seed(seed)
262
- end
263
- end
264
-
265
- class HashSeeder
266
- def initialize(receiver)
267
- @receiver = receiver
276
+ class_eval code
277
+ self.send :"set_#{attr}_class", nested_class
268
278
  end
269
279
 
270
- def seed(hash)
271
- hash.each do |key, value|
272
- attribute = @receiver.get_attribute(key)
273
- next if attribute && attribute.is_assigned?
280
+ def self.required(*attributes)
281
+ self.define_attributes(*attributes)
274
282
 
275
- method = "#{key}="
276
- @receiver.send method, value
283
+ array_attributes = []
284
+ if attributes.last.kind_of? Hash
285
+ attributes.pop.each do |attr, nested_class|
286
+ array_attributes << attr if nested_class.kind_of? Array
287
+ attributes << attr
288
+ end
277
289
  end
278
- end
279
- end
280
290
 
281
- class ObjectSeeder
282
- def initialize(receiver)
283
- @receiver = receiver
291
+ self.set_required_attributes(attributes, array_attributes)
292
+ self
284
293
  end
285
294
 
286
- def seed(object)
287
- @receiver.get_attributes.each do |attribute|
288
- next if attribute.is_assigned?
295
+ def self.optional(*attributes)
296
+ self.define_attributes(*attributes)
289
297
 
290
- begin
291
- value = attribute.call_on(object)
292
- assign_to_receiver attribute, value
293
- rescue NoMethodError
298
+ array_attributes = []
299
+ if attributes.last.kind_of? Hash
300
+ attributes.pop.each do |attr, nested_class|
301
+ array_attributes << attr if nested_class.kind_of? Array
302
+ attributes << attr
294
303
  end
295
304
  end
296
- end
297
305
 
298
- private
299
- def assign_to_receiver(attribute, value)
300
- method = "#{attribute.name}="
301
- @receiver.send(method, value)
306
+ self.set_optional_attributes(attributes, array_attributes)
307
+ self
302
308
  end
303
309
 
310
+ define_initializer
304
311
  end
312
+
305
313
  end
@@ -1,3 +1,3 @@
1
1
  class Bound
2
- VERSION = "1.1.1"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -15,15 +15,6 @@ describe Bound do
15
15
  end
16
16
  end
17
17
 
18
- it 'checks if attribute exists' do
19
- [hash, object].each do |subject|
20
- user = User.new(subject)
21
-
22
- assert user.has_attribute?(:name)
23
- assert user.has_attribute?(:age)
24
- end
25
- end
26
-
27
18
  it 'fails if attribute is missing' do
28
19
  hash.delete :age
29
20
 
@@ -55,14 +46,6 @@ describe Bound do
55
46
  assert_match(/unknown.+gender/i, exception.message)
56
47
  end
57
48
 
58
- it 'exposes an attributes method' do
59
- user = User.new(hash)
60
-
61
- assert_equal 2, user.get_attributes.size
62
- assert_includes user.get_attributes.map(&:name), :name
63
- assert_includes user.get_attributes.map(&:name), :age
64
- end
65
-
66
49
  describe 'equality' do
67
50
  let(:user) { User.new(hash) }
68
51
 
@@ -145,14 +128,6 @@ describe Bound do
145
128
  UserWithoutAge.new(subject)
146
129
  end
147
130
  end
148
-
149
- it 'are also included in attributes' do
150
- user = UserWithoutAge.new(hash)
151
-
152
- assert_equal 2, user.get_attributes.size
153
- assert_includes user.get_attributes.map(&:name), :name
154
- assert_includes user.get_attributes.map(&:name), :age
155
- end
156
131
  end
157
132
 
158
133
  describe 'optional nested attributes' do
@@ -183,14 +158,6 @@ describe Bound do
183
158
  UserWithProfile.new(subject)
184
159
  end
185
160
  end
186
-
187
- it 'are also included in attributes' do
188
- user = UserWithProfile.new(hash)
189
-
190
- assert_equal 2, user.get_attributes.size
191
- assert_includes user.get_attributes.map(&:name), :id
192
- assert_includes user.get_attributes.map(&:name), :profile
193
- end
194
161
  end
195
162
 
196
163
  describe 'no attributes' do
@@ -273,14 +240,6 @@ describe Bound do
273
240
  end
274
241
 
275
242
  end
276
-
277
- it 'are also included in attributes' do
278
- user = BloggingUser.new(hash)
279
-
280
- assert_equal 2, user.get_attributes.size
281
- assert_includes user.get_attributes.map(&:name), :name
282
- assert_includes user.get_attributes.map(&:name), :posts
283
- end
284
243
  end
285
244
 
286
245
  describe 'allows optional as constructor' do
@@ -301,20 +260,6 @@ describe Bound do
301
260
  end
302
261
  end
303
262
 
304
- describe '__attributes__' do
305
- DeprecatedUser = Bound.required(:name)
306
-
307
- it 'is deprecated' do
308
- user = DeprecatedUser.new(:name => 'foo')
309
-
310
- deprecation_warning, _ = capture_io do
311
- user.__attributes__
312
- end
313
-
314
- assert_match(/deprecated/, deprecation_warning)
315
- end
316
- end
317
-
318
263
  describe 'questionmark suffix' do
319
264
  WonderingUser = Bound.required(:asked?)
320
265
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bound
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakob Holderbaum
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-08 00:00:00.000000000 Z
12
+ date: 2014-05-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 5.0.7
56
+ - !ruby/object:Gem::Dependency
57
+ name: perftools.rb
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: simplecov
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
125
  version: '0'
112
126
  requirements: []
113
127
  rubyforge_project:
114
- rubygems_version: 2.5.2
128
+ rubygems_version: 2.2.2
115
129
  signing_key:
116
130
  specification_version: 4
117
131
  summary: Implements a nice helper for fast boundary definitions