smart_properties 1.8.0 → 1.8.1
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/lib/smart_properties.rb +2 -1
- data/spec/acceptance_checking_spec.rb +99 -0
- data/spec/base_spec.rb +85 -0
- data/spec/configuration_error_spec.rb +17 -0
- data/spec/conversion_spec.rb +30 -0
- data/spec/default_values_spec.rb +51 -0
- data/spec/inheritance_spec.rb +136 -0
- data/spec/required_values_spec.rb +104 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/dummy_class.rb +9 -0
- metadata +18 -4
- data/spec/smart_properties_spec.rb +0 -794
@@ -1,794 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe SmartProperties do
|
4
|
-
described_module = self.described_class
|
5
|
-
|
6
|
-
context 'when extending a subclass' do
|
7
|
-
let(:klass) do
|
8
|
-
Class.new do
|
9
|
-
attr_reader :a, :b
|
10
|
-
|
11
|
-
def initialize(a = 13, b = nil)
|
12
|
-
@a = a
|
13
|
-
@b = b
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
let(:subklass) do
|
19
|
-
Class.new(klass) do
|
20
|
-
include described_module
|
21
|
-
property :title
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when creating new instances' do
|
26
|
-
it 'should call the super-class constructor' do
|
27
|
-
instance = subklass.new
|
28
|
-
expect(instance.a).to eq(13)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should handle keyword arguments' do
|
32
|
-
instance = subklass.new(:title => 'Lorem ipsum')
|
33
|
-
expect(instance.title).to eq('Lorem ipsum')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'when creating new instances with additional positional arguments' do
|
38
|
-
it 'it should forward those arguments to super-class constructor' do
|
39
|
-
instance = subklass.new(1, 2)
|
40
|
-
expect(instance.a).to eq(1)
|
41
|
-
expect(instance.b).to eq(2)
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should forward arrays as they are to the super-class constructor' do
|
45
|
-
instance = subklass.new([1, 2])
|
46
|
-
expect(instance.a).to eq([1, 2])
|
47
|
-
expect(instance.b).to eq(nil)
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'should handle keyword arguments' do
|
51
|
-
instance = subklass.new(1, :title => 'Lorem ipsum')
|
52
|
-
expect(instance.a).to eq(1)
|
53
|
-
expect(instance.b).to eq(nil)
|
54
|
-
expect(instance.title).to eq('Lorem ipsum')
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'should handle blocks' do
|
58
|
-
instance = subklass.new(1) { |i| i.title = 'Lorem ipsum' }
|
59
|
-
expect(instance.a).to eq(1)
|
60
|
-
expect(instance.b).to eq(nil)
|
61
|
-
expect(instance.title).to eq('Lorem ipsum')
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context "when extending an other class" do
|
67
|
-
subject(:klass) do
|
68
|
-
Class.new.tap do |c|
|
69
|
-
c.send(:include, described_class)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should add a .property method" do
|
74
|
-
expect(klass.respond_to?(:property, true)).to be_truthy
|
75
|
-
end
|
76
|
-
|
77
|
-
context "and defining a property with invalid configuration options" do
|
78
|
-
it "should raise an error reporting one invalid option when one invalid option was given" do
|
79
|
-
expect {
|
80
|
-
klass.tap do |c|
|
81
|
-
c.instance_eval do
|
82
|
-
property :title, :invalid_option => 'boom'
|
83
|
-
end
|
84
|
-
end
|
85
|
-
}.to raise_error(SmartProperties::ConfigurationError, "SmartProperties do not support the following configuration options: invalid_option.")
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should raise an error reporting three invalid options when three invalid options were given" do
|
89
|
-
expect {
|
90
|
-
klass.tap do |c|
|
91
|
-
c.instance_eval do
|
92
|
-
property :title, :invalid_option_1 => 'boom', :invalid_option_2 => 'boom', :invalid_option_3 => 'boom'
|
93
|
-
end
|
94
|
-
end
|
95
|
-
}.to raise_error(SmartProperties::ConfigurationError, "SmartProperties do not support the following configuration options: invalid_option_1, invalid_option_2, invalid_option_3.")
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
context "when used to build a class that has a property called :title" do
|
101
|
-
subject(:klass) do
|
102
|
-
title = Object.new.tap do |o|
|
103
|
-
def o.to_title; 'chunky'; end
|
104
|
-
end
|
105
|
-
|
106
|
-
klass = Class.new.tap do |c|
|
107
|
-
c.send(:include, described_class)
|
108
|
-
c.instance_eval do
|
109
|
-
def name; "TestDummy"; end
|
110
|
-
|
111
|
-
property :title, :accepts => String,
|
112
|
-
:converts => :to_title,
|
113
|
-
:required => true,
|
114
|
-
:default => title
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
klass
|
119
|
-
end
|
120
|
-
|
121
|
-
let(:superklass) { klass }
|
122
|
-
|
123
|
-
it { is_expected.to have_smart_property(:title) }
|
124
|
-
|
125
|
-
context "instances of this class" do
|
126
|
-
subject(:instance) { klass.new }
|
127
|
-
|
128
|
-
it { is_expected.to respond_to(:title) }
|
129
|
-
it { is_expected.to respond_to(:title=) }
|
130
|
-
|
131
|
-
it "should have 'chunky' as default value for title when accessed using the #title method" do
|
132
|
-
expect(instance.title).to eq('chunky')
|
133
|
-
end
|
134
|
-
|
135
|
-
it "should have 'chunky' as default value for title when accessed using the #[] method" do
|
136
|
-
expect(instance[:title]).to eq('chunky')
|
137
|
-
end
|
138
|
-
|
139
|
-
it "should convert all values that are assigned to title into strings when using the #title= method" do
|
140
|
-
instance.title = double(:to_title => 'bacon')
|
141
|
-
expect(instance.title).to eq('bacon')
|
142
|
-
end
|
143
|
-
|
144
|
-
it "should convert all values that are assigned to title into strings when using the #[]= method" do
|
145
|
-
instance[:title] = double(:to_title => 'bacon')
|
146
|
-
expect(instance.title).to eq('bacon')
|
147
|
-
end
|
148
|
-
|
149
|
-
it "should not allow to set nil as title" do
|
150
|
-
expect { instance.title = nil }.to raise_error(SmartProperties::MissingValueError, "TestDummy requires the property title to be set") {|error|
|
151
|
-
expect(error.to_hash[:title]).to eq('must be set')
|
152
|
-
}
|
153
|
-
end
|
154
|
-
|
155
|
-
it "should not allow to set objects as title that do not respond to #to_title" do
|
156
|
-
expect { instance.title = Object.new }.to raise_error(NoMethodError, /undefined method `to_title'/)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should not influence other instances that have been initialized with different attributes" do
|
160
|
-
other_instance = klass.new :title => double(:to_title => 'Lorem ipsum')
|
161
|
-
|
162
|
-
expect(instance.title).to eq('chunky')
|
163
|
-
expect(other_instance.title).to eq('Lorem ipsum')
|
164
|
-
end
|
165
|
-
|
166
|
-
context "when initialized with a block" do
|
167
|
-
subject(:instance) do
|
168
|
-
klass.new do |c|
|
169
|
-
c.title = double(:to_title => 'bacon')
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
it "should have the title specified in the block" do
|
174
|
-
expect(instance.title).to eq('bacon')
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
context "when subclassed" do
|
180
|
-
subject(:subklass) { Class.new(superklass) }
|
181
|
-
|
182
|
-
it { is_expected.to have_smart_property(:title) }
|
183
|
-
|
184
|
-
context "instances of this subclass" do
|
185
|
-
subject(:instance) { subklass.new }
|
186
|
-
|
187
|
-
it { is_expected.to respond_to(:title) }
|
188
|
-
it { is_expected.to respond_to(:title=) }
|
189
|
-
end
|
190
|
-
|
191
|
-
context "instances of this subclass that have been intialized from a set of attributes" do
|
192
|
-
subject(:instance) { subklass.new :title => double(:to_title => 'Message') }
|
193
|
-
|
194
|
-
it "should have the correct title" do
|
195
|
-
expect(instance.title).to eq('Message')
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
context "when subclassed and extended with a property called text" do
|
201
|
-
subject(:subklass) do
|
202
|
-
Class.new(superklass).tap do |c|
|
203
|
-
c.instance_eval do
|
204
|
-
property :text
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
it { is_expected.to have_smart_property(:title) }
|
210
|
-
it { is_expected.to have_smart_property(:text) }
|
211
|
-
|
212
|
-
context "instances of this subclass" do
|
213
|
-
subject(:instance) { subklass.new }
|
214
|
-
|
215
|
-
it { is_expected.to respond_to(:title) }
|
216
|
-
it { is_expected.to respond_to(:title=) }
|
217
|
-
it { is_expected.to respond_to(:text) }
|
218
|
-
it { is_expected.to respond_to(:text=) }
|
219
|
-
end
|
220
|
-
|
221
|
-
context "instances of the super class" do
|
222
|
-
subject(:instance) { superklass.new }
|
223
|
-
|
224
|
-
it { is_expected.not_to respond_to(:text) }
|
225
|
-
it { is_expected.not_to respond_to(:text=) }
|
226
|
-
end
|
227
|
-
|
228
|
-
context "instances of this subclass" do
|
229
|
-
context "when initialized with a set of attributes" do
|
230
|
-
subject(:instance) { subklass.new :title => double(:to_title => 'Message'), :text => "Hello" }
|
231
|
-
|
232
|
-
context "when properties are accessed using the dedicated instance methods" do
|
233
|
-
it("should have the correct title") { expect(instance.title).to eq('Message') }
|
234
|
-
it("should have the correct text") { expect(instance.text).to eq('Hello') }
|
235
|
-
end
|
236
|
-
|
237
|
-
context "when properties are accessed using the index methods" do
|
238
|
-
it("should have the correct title") { expect(instance[:title]).to eq('Message') }
|
239
|
-
it("should have the correct text") { expect(instance[:text]).to eq('Hello') }
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
context "when initialized with a block" do
|
244
|
-
subject(:instance) do
|
245
|
-
subklass.new do |c|
|
246
|
-
c.title = double(:to_title => 'Message')
|
247
|
-
c.text = "Hello"
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
it("should have the correct title") { expect(instance.title).to eq('Message') }
|
252
|
-
it("should have the correct text") { expect(instance.text).to eq('Hello') }
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
context "when extended with a :type property at runtime" do
|
258
|
-
before do
|
259
|
-
superklass.tap do |c|
|
260
|
-
c.instance_eval do
|
261
|
-
property :type, :converts => :to_sym
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
it { is_expected.to have_smart_property(:title) }
|
267
|
-
it { is_expected.to have_smart_property(:type) }
|
268
|
-
|
269
|
-
context "instances of this class" do
|
270
|
-
subject(:instance) { superklass.new :title => double(:to_title => 'Lorem ipsum') }
|
271
|
-
|
272
|
-
it { is_expected.to respond_to(:type) }
|
273
|
-
it { is_expected.to respond_to(:type=) }
|
274
|
-
end
|
275
|
-
|
276
|
-
context "when subclassing this class" do
|
277
|
-
subject(:subclass) { Class.new(superklass) }
|
278
|
-
|
279
|
-
context "instances of this class" do
|
280
|
-
subject(:instance) { subclass.new :title => double(:to_title => 'Lorem ipsum') }
|
281
|
-
|
282
|
-
it { is_expected.to respond_to :title }
|
283
|
-
it { is_expected.to respond_to :title= }
|
284
|
-
it { is_expected.to respond_to :type }
|
285
|
-
it { is_expected.to respond_to :type= }
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
context "when used to build a class that has a property called :title that uses a lambda statement for conversion" do
|
292
|
-
subject(:klass) do
|
293
|
-
Class.new.tap do |c|
|
294
|
-
c.send(:include, described_class)
|
295
|
-
c.instance_eval do
|
296
|
-
property :title, :converts => lambda { |t| "<title>#{t.to_s}</title>"}
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
context "instances of this class" do
|
302
|
-
subject(:instance) { klass.new }
|
303
|
-
|
304
|
-
it "should convert the property title as specified the lambda statement" do
|
305
|
-
instance.title = "Lorem ipsum"
|
306
|
-
expect(instance.title).to eq("<title>Lorem ipsum</title>")
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
context "when used to build a class that has a property called :title that uses an object that responds to #to_proc for conversion" do
|
312
|
-
subject(:klass) do
|
313
|
-
converter = Object.new.tap do |o|
|
314
|
-
def o.to_proc
|
315
|
-
lambda { |t| "<title>#{t.to_s}</title>"}
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
Class.new.tap do |c|
|
320
|
-
c.send(:include, described_class)
|
321
|
-
c.instance_eval do
|
322
|
-
property :title, :converts => converter
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
context "instances of this class" do
|
328
|
-
subject(:instance) { klass.new }
|
329
|
-
|
330
|
-
it "should convert the property title as specified the lambda statement" do
|
331
|
-
instance.title = "Lorem ipsum"
|
332
|
-
expect(instance.title).to eq("<title>Lorem ipsum</title>")
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
context "when used to build a class that has a property called :visible which uses an array of valid values for acceptance checking" do
|
338
|
-
subject(:klass) do
|
339
|
-
Class.new.tap do |c|
|
340
|
-
def c.name; "TestDummy"; end
|
341
|
-
|
342
|
-
c.send(:include, described_class)
|
343
|
-
|
344
|
-
c.instance_eval do
|
345
|
-
property :visible, :accepts => [true, false]
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
context "instances of this class" do
|
351
|
-
subject(:instance) { klass.new }
|
352
|
-
|
353
|
-
context "when properties are accessed using the dedicated instance methods" do
|
354
|
-
it "should allow to set true as value for visible" do
|
355
|
-
expect { instance.visible = true }.to_not raise_error
|
356
|
-
end
|
357
|
-
|
358
|
-
it "should allow to set false as value for visible" do
|
359
|
-
expect { instance.visible = false }.to_not raise_error
|
360
|
-
end
|
361
|
-
|
362
|
-
it "should not allow to set :maybe as value for visible" do
|
363
|
-
expect { instance.visible = :maybe }.to raise_error(SmartProperties::InvalidValueError, "TestDummy does not accept :maybe as value for the property visible") {|error|
|
364
|
-
expect(error.to_hash[:visible]).to eq('does not accept :maybe as value')
|
365
|
-
}
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
context "when properties are accessed using the index methods" do
|
370
|
-
it "should allow to set true as value for visible" do
|
371
|
-
expect { instance[:visible] = true }.to_not raise_error
|
372
|
-
end
|
373
|
-
|
374
|
-
it "should allow to set false as value for visible" do
|
375
|
-
expect { instance[:visible] = false }.to_not raise_error
|
376
|
-
end
|
377
|
-
|
378
|
-
it "should not allow to set :maybe as value for visible" do
|
379
|
-
expect { instance[:visible] = :maybe }.to raise_error(SmartProperties::InvalidValueError, "TestDummy does not accept :maybe as value for the property visible") {|error|
|
380
|
-
expect(error.to_hash[:visible]).to eq('does not accept :maybe as value')
|
381
|
-
}
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
context "when used to build a class that has a property called :title that can either be a String or a Symbol" do
|
388
|
-
subject(:klass) do
|
389
|
-
Class.new.tap do |c|
|
390
|
-
c.send(:include, described_class)
|
391
|
-
c.instance_eval do
|
392
|
-
property :title, accepts: [String, Symbol]
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
context "intance of this class" do
|
398
|
-
subject(:instance) { klass.new }
|
399
|
-
|
400
|
-
context "when properties are accessed using the dedicated instance methods" do
|
401
|
-
it "should accept a String as title" do
|
402
|
-
expect { subject.title = "Test" }.to_not raise_error
|
403
|
-
end
|
404
|
-
|
405
|
-
it "should accept a Symbol as title" do
|
406
|
-
expect { subject.title = :test }.to_not raise_error
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
context "when properties are accessed using the index methods" do
|
411
|
-
it "should accept a String as title" do
|
412
|
-
expect { subject[:title] = "Test" }.to_not raise_error
|
413
|
-
end
|
414
|
-
|
415
|
-
it "should accept a Symbol as title" do
|
416
|
-
expect { subject[:title] = :test }.to_not raise_error
|
417
|
-
end
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
context 'when used to build a class that has a property called :license_plate which uses a lambda statement for accpetance checking' do
|
423
|
-
subject(:klass) do
|
424
|
-
Class.new.tap do |c|
|
425
|
-
def c.name; 'TestDummy'; end
|
426
|
-
|
427
|
-
c.send(:include, described_class)
|
428
|
-
|
429
|
-
c.instance_eval do
|
430
|
-
property :license_plate, :accepts => lambda { |v| /\w{1,2} \w{1,2} \d{1,4}/.match(v) }
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
context 'instances of this class' do
|
436
|
-
subject(:instance) { klass.new }
|
437
|
-
|
438
|
-
context "when properties are accessed using the dedicated instance methods" do
|
439
|
-
it 'should not a accept "invalid" as value for license_plate' do
|
440
|
-
expect { instance.license_plate = "invalid" }.to raise_error(SmartProperties::InvalidValueError, 'TestDummy does not accept "invalid" as value for the property license_plate') {|error|
|
441
|
-
expect(error.to_hash[:license_plate]).to eq('does not accept "invalid" as value')
|
442
|
-
}
|
443
|
-
end
|
444
|
-
|
445
|
-
it 'should accept "NE RD 1337" as license plate' do
|
446
|
-
expect { instance.license_plate = "NE RD 1337" }.to_not raise_error
|
447
|
-
end
|
448
|
-
end
|
449
|
-
|
450
|
-
context "when properties are accessed using the index methods" do
|
451
|
-
it 'should not a accept "invalid" as value for license_plate' do
|
452
|
-
expect { instance[:license_plate] = "invalid" }.to raise_error(ArgumentError, 'TestDummy does not accept "invalid" as value for the property license_plate')
|
453
|
-
end
|
454
|
-
|
455
|
-
it 'should accept "NE RD 1337" as license plate' do
|
456
|
-
expect { instance[:license_plate] = "NE RD 1337" }.to_not raise_error
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
end
|
461
|
-
|
462
|
-
context 'when used to build a class that has a property called :text whose getter is overriden' do
|
463
|
-
subject(:klass) do
|
464
|
-
Class.new.tap do |c|
|
465
|
-
c.send(:include, described_class)
|
466
|
-
|
467
|
-
c.instance_eval do
|
468
|
-
property :text, :default => 'Hello'
|
469
|
-
end
|
470
|
-
|
471
|
-
c.class_eval do
|
472
|
-
def text
|
473
|
-
"<em>#{super}</em>"
|
474
|
-
end
|
475
|
-
end
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
context "instances of this class" do
|
480
|
-
subject(:instance) { klass.new }
|
481
|
-
|
482
|
-
context "when properties are accessed using the dedicated instance methods" do
|
483
|
-
it "should return the accepted value for the property called :text" do
|
484
|
-
expect(instance.text).to eq('<em>Hello</em>')
|
485
|
-
end
|
486
|
-
end
|
487
|
-
|
488
|
-
context "when properties are accessed using the index methods" do
|
489
|
-
it "should return the accepted value for the property called :text" do
|
490
|
-
expect(instance[:text]).to eq('<em>Hello</em>')
|
491
|
-
end
|
492
|
-
end
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
context 'when used to build a class that has a required property with no default called :text whose getter is overriden' do
|
497
|
-
subject(:klass) do
|
498
|
-
Class.new.tap do |c|
|
499
|
-
c.send(:include, described_class)
|
500
|
-
|
501
|
-
c.instance_eval do
|
502
|
-
property :text, required: true
|
503
|
-
end
|
504
|
-
|
505
|
-
c.class_eval do
|
506
|
-
def text
|
507
|
-
"<em>#{super}</em>"
|
508
|
-
end
|
509
|
-
end
|
510
|
-
end
|
511
|
-
end
|
512
|
-
|
513
|
-
it "should raise an error during initilization if no value for :text has been specified" do
|
514
|
-
expect { klass.new }.to raise_error(SmartProperties::InitializationError)
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
context 'when used to build a class that has a property called :id whose default value is a lambda statement for retrieving the object_id' do
|
519
|
-
subject(:klass) do
|
520
|
-
Class.new.tap do |c|
|
521
|
-
c.send(:include, described_class)
|
522
|
-
|
523
|
-
c.instance_eval do
|
524
|
-
property :id, :default => lambda { object_id }
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
context "instances of this class" do
|
530
|
-
it "should evaluate the lambda in their own context and thus return a different value for each instance" do
|
531
|
-
first_instance = klass.new
|
532
|
-
second_instance = klass.new
|
533
|
-
|
534
|
-
expect(first_instance.id).to_not eq(second_instance.id)
|
535
|
-
end
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
context 'when used to build a class that has a property called :boom whose default value is a lambda statement that raises an exception' do
|
540
|
-
subject(:klass) do
|
541
|
-
Class.new.tap do |c|
|
542
|
-
c.send(:include, described_class)
|
543
|
-
|
544
|
-
c.instance_eval do
|
545
|
-
property :boom, :default => lambda { raise 'Boom!' }
|
546
|
-
end
|
547
|
-
end
|
548
|
-
end
|
549
|
-
|
550
|
-
context "instances of this class" do
|
551
|
-
it "should raise during initialization if no other value for :boom has been provided" do
|
552
|
-
expect { klass.new }.to raise_error(RuntimeError, 'Boom!')
|
553
|
-
end
|
554
|
-
|
555
|
-
it "should not evaluate the lambda expression and thus not raise during initialization if a different value for :boom has been provided as a parameter" do
|
556
|
-
expect { klass.new(boom: 'Everything is just fine!') }.not_to raise_error
|
557
|
-
end
|
558
|
-
|
559
|
-
it "should not evaluate the lambda expression and thus not raise during initialization if a different value for :boom has been provided in an initilization block" do
|
560
|
-
expect { klass.new { |inst| inst.boom = 'Everything is just fine!' } }.not_to raise_error
|
561
|
-
end
|
562
|
-
end
|
563
|
-
end
|
564
|
-
|
565
|
-
context 'when used to build a class that is then subclassed and later extended at runtime' do
|
566
|
-
let!(:klass) do
|
567
|
-
Class.new.tap do |c|
|
568
|
-
c.send(:include, described_class)
|
569
|
-
c.send(:property, :title)
|
570
|
-
end
|
571
|
-
end
|
572
|
-
|
573
|
-
let!(:subklass) do
|
574
|
-
Class.new(klass).tap do |c|
|
575
|
-
c.send(:property, :body)
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
before do
|
580
|
-
klass.tap do |c|
|
581
|
-
c.send(:property, :priority)
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
context "the class" do
|
586
|
-
subject { klass }
|
587
|
-
|
588
|
-
it { is_expected.to have_smart_property(:title) }
|
589
|
-
it { is_expected.to have_smart_property(:priority) }
|
590
|
-
end
|
591
|
-
|
592
|
-
context 'the subclass' do
|
593
|
-
subject { subklass }
|
594
|
-
|
595
|
-
it { is_expected.to have_smart_property(:title) }
|
596
|
-
it { is_expected.to have_smart_property(:body) }
|
597
|
-
it { is_expected.to have_smart_property(:priority) }
|
598
|
-
|
599
|
-
it "should be initializable using a block" do
|
600
|
-
configuration_instructions = lambda do |s|
|
601
|
-
s.title = "Lorem Ipsum"
|
602
|
-
s.priority = :low
|
603
|
-
s.body = "Lorem ipsum dolor sit amet."
|
604
|
-
end
|
605
|
-
|
606
|
-
expect { subklass.new(&configuration_instructions) }.to_not raise_error
|
607
|
-
end
|
608
|
-
|
609
|
-
it "should be initializable using a hash of attributes" do
|
610
|
-
attributes = {
|
611
|
-
:title => "Lorem Ipsum",
|
612
|
-
:priority => :low,
|
613
|
-
:body => "Lorem ipsum dolor sit amet."
|
614
|
-
}
|
615
|
-
|
616
|
-
expect { subklass.new(attributes) }.to_not raise_error
|
617
|
-
end
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
context "when building a class that has a property which is not required and has a default" do
|
622
|
-
subject(:klass) do
|
623
|
-
Class.new.tap do |c|
|
624
|
-
c.send(:include, described_class)
|
625
|
-
c.send(:property, :title, :default => 'Lorem Ipsum')
|
626
|
-
end
|
627
|
-
end
|
628
|
-
|
629
|
-
context 'instances of that class' do
|
630
|
-
context 'when created with a set of attributes that explicitly contains nil for the title' do
|
631
|
-
subject(:instance) { klass.new :title => nil }
|
632
|
-
|
633
|
-
it "should have no title" do
|
634
|
-
expect(instance.title).to be_nil
|
635
|
-
end
|
636
|
-
end
|
637
|
-
|
638
|
-
context 'when created without any arguments' do
|
639
|
-
subject(:instance) { klass.new }
|
640
|
-
|
641
|
-
context "when properties are accessed using the index methods" do
|
642
|
-
it "should have the default title" do
|
643
|
-
expect(instance.title).to eq('Lorem Ipsum')
|
644
|
-
end
|
645
|
-
end
|
646
|
-
|
647
|
-
context "when properties are accessed using the index methods" do
|
648
|
-
it "should have the default title" do
|
649
|
-
expect(instance[:title]).to eq('Lorem Ipsum')
|
650
|
-
end
|
651
|
-
end
|
652
|
-
end
|
653
|
-
|
654
|
-
context 'when created with an empty block' do
|
655
|
-
subject(:instance) { klass.new {} }
|
656
|
-
|
657
|
-
context "when properties are accessed using the index methods" do
|
658
|
-
it "should have the default title" do
|
659
|
-
expect(instance.title).to eq('Lorem Ipsum')
|
660
|
-
end
|
661
|
-
end
|
662
|
-
|
663
|
-
context "when properties are accessed using the index methods" do
|
664
|
-
it "should have the default title" do
|
665
|
-
expect(instance[:title]).to eq('Lorem Ipsum')
|
666
|
-
end
|
667
|
-
end
|
668
|
-
end
|
669
|
-
end
|
670
|
-
end
|
671
|
-
|
672
|
-
context "when building a class that has a property which is required and has no default" do
|
673
|
-
subject(:klass) do
|
674
|
-
Class.new.tap do |c|
|
675
|
-
c.send(:include, described_class)
|
676
|
-
c.send(:property, :title, :required => true)
|
677
|
-
|
678
|
-
def c.name; "Dummy"; end
|
679
|
-
end
|
680
|
-
end
|
681
|
-
|
682
|
-
context 'instances of that class' do
|
683
|
-
context 'when created with a set of attributes that contains a title' do
|
684
|
-
subject(:instance) { klass.new :title => 'Lorem Ipsum' }
|
685
|
-
|
686
|
-
it "should have the correct title" do
|
687
|
-
expect(instance.title).to eq('Lorem Ipsum')
|
688
|
-
end
|
689
|
-
end
|
690
|
-
|
691
|
-
context 'when created with an block specifying that property' do
|
692
|
-
subject(:instance) { klass.new { |i| i.title = 'Lorem Ipsum' } }
|
693
|
-
|
694
|
-
it "should have the default title" do
|
695
|
-
expect(instance.title).to eq('Lorem Ipsum')
|
696
|
-
end
|
697
|
-
end
|
698
|
-
|
699
|
-
context "when created with no arguments" do
|
700
|
-
it "should raise an error stating that required properties are missing" do
|
701
|
-
expect { klass.new }.to raise_error(SmartProperties::InitializationError, "Dummy requires the following properties to be set: title") {|error|
|
702
|
-
expect(error.to_hash[:title]).to eq('must be set')
|
703
|
-
}
|
704
|
-
end
|
705
|
-
end
|
706
|
-
end
|
707
|
-
end
|
708
|
-
|
709
|
-
context "when building a class that has a property which is required depending on the value of another property" do
|
710
|
-
subject(:klass) do
|
711
|
-
described_class = self.described_class
|
712
|
-
|
713
|
-
Class.new do
|
714
|
-
include described_class
|
715
|
-
property :name, :required => lambda { not anonymous }
|
716
|
-
property :anonymous, accepts: [true, false], default: true
|
717
|
-
def self.name; "Dummy"; end
|
718
|
-
end
|
719
|
-
end
|
720
|
-
|
721
|
-
context "when created with no arguments" do
|
722
|
-
it "should not raise an error" do
|
723
|
-
expect { klass.new }.to_not raise_error
|
724
|
-
end
|
725
|
-
end
|
726
|
-
|
727
|
-
context "when created with no name and anonymous being set to false" do
|
728
|
-
it "should raise an error indicating that a required property was not specified" do
|
729
|
-
expect { klass.new anonymous: false }.to raise_error(SmartProperties::InitializationError, "Dummy requires the following properties to be set: name") {|error|
|
730
|
-
expect(error.to_hash[:name]).to eq("must be set")
|
731
|
-
}
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
context "when created with a name and anonymous being set to false" do
|
736
|
-
it "should not raise an error" do
|
737
|
-
expect { klass.new name: "John Doe", anonymous: false }.to_not raise_error
|
738
|
-
end
|
739
|
-
end
|
740
|
-
end
|
741
|
-
|
742
|
-
context "when building a class that has a property which is required and has false as default" do
|
743
|
-
subject(:klass) do
|
744
|
-
Class.new.tap do |c|
|
745
|
-
c.send(:include, described_class)
|
746
|
-
c.send(:property, :flag, :required => true, :default => false)
|
747
|
-
|
748
|
-
def c.name; "Dummy"; end
|
749
|
-
end
|
750
|
-
end
|
751
|
-
|
752
|
-
context 'instances of that class' do
|
753
|
-
context 'when created with a set of attributes that explicitly contains nil for the title' do
|
754
|
-
subject(:instance) { klass.new :flag => true }
|
755
|
-
|
756
|
-
it "should have no title" do
|
757
|
-
expect(instance.flag).to be_truthy
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
|
-
context 'when created with an block specifying that property' do
|
762
|
-
subject(:instance) { klass.new { |i| i.flag = true } }
|
763
|
-
|
764
|
-
context "when properties are accessed using the dedicated instance methods" do
|
765
|
-
it "should have the default title" do
|
766
|
-
expect(instance.flag).to be(true)
|
767
|
-
end
|
768
|
-
end
|
769
|
-
|
770
|
-
context "when properties are accessed using the index methods" do
|
771
|
-
it "should have the default title" do
|
772
|
-
expect(instance[:flag]).to be(true)
|
773
|
-
end
|
774
|
-
end
|
775
|
-
end
|
776
|
-
|
777
|
-
context "when created with no arguments" do
|
778
|
-
subject(:instance) { klass.new }
|
779
|
-
|
780
|
-
context "when properties are accessed using the dedicated instance methods" do
|
781
|
-
it "should have false as default flag" do
|
782
|
-
expect(instance.flag).to be(false)
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
|
-
context "when properties are accessed using the index methods" do
|
787
|
-
it "should have false as default flag" do
|
788
|
-
expect(instance[:flag]).to be(false)
|
789
|
-
end
|
790
|
-
end
|
791
|
-
end
|
792
|
-
end
|
793
|
-
end
|
794
|
-
end
|