cascading_classes 0.3.0 → 0.6.0
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.
- data/README.md +959 -140
- data/Rakefile +9 -2
- data/lib/cascading_classes.rb +254 -46
- data/lib/cascading_classes/cascading_classes.rb +194 -40
- data/lib/cascading_classes/dsl.rb +137 -59
- data/lib/cascading_classes/parse_options.rb +71 -0
- data/lib/cascading_classes/types.rb +229 -0
- data/spec/basics/basic_spec.rb +230 -0
- data/spec/basics/block_spec.rb +52 -0
- data/spec/basics/container_spec.rb +201 -0
- data/spec/basics/inherit_spec.rb +339 -0
- data/spec/basics/proc_spec.rb +343 -0
- data/spec/class_helper_methods/parents_for_spec.rb +36 -0
- data/spec/custom_classes/hash_like_spec.rb +144 -0
- data/spec/helper_spec.rb +34 -2
- data/spec/instances/basics.rb +239 -0
- data/spec/preset_classes/array_spec.rb +214 -0
- data/spec/preset_classes/hash_spec.rb +210 -0
- data/spec/preset_classes/strings.rb +190 -0
- data/spec/preset_classes/undefined.rb +56 -0
- data/spec/usage/block_spec.rb +59 -0
- data/spec/usage/proc_spec.rb +60 -0
- data/todo +6 -0
- metadata +35 -14
- data/spec/alternative_syntax_spec.rb +0 -173
- data/spec/extended_spec.rb +0 -315
- data/spec/included_spec.rb +0 -103
@@ -0,0 +1,339 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
# todo: update --> when you do :inherit => false this means NO INHERITANCE
|
8
|
+
|
9
|
+
describe "inherit" do
|
10
|
+
before do
|
11
|
+
A = Class.new{extend CC}
|
12
|
+
B = Class.new(A)
|
13
|
+
C = Class.new(B)
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
Object.send :remove_const, :A
|
18
|
+
Object.send :remove_const, :B
|
19
|
+
Object.send :remove_const, :C
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "defaults" do
|
23
|
+
before do
|
24
|
+
@props = A.cascade do
|
25
|
+
location :default => "south side"
|
26
|
+
score :default => 32.18
|
27
|
+
num :default => 12
|
28
|
+
weather :default => :sunny
|
29
|
+
available :default => false
|
30
|
+
mood :default => Proc.new{|me| me.weather == :sunny ? "content" : "depressed"}
|
31
|
+
color :default => Proc.new{|me| me.mood == "content" ? "rosy" : "blue"}
|
32
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
33
|
+
groceries :default => [:ice, :lemons, :splenda]
|
34
|
+
ingredients :default => Set.new([:sugar, :butter, :flour, :eggs])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "descendents" do
|
39
|
+
it "inherits symbols strings, fixnums, floats, and bools (by default)" do
|
40
|
+
@props[:location][:inherit].must_equal true
|
41
|
+
@props[:weather][:inherit].must_equal true
|
42
|
+
@props[:num][:inherit].must_equal true
|
43
|
+
@props[:score][:inherit].must_equal true
|
44
|
+
@props[:available][:inherit].must_equal true
|
45
|
+
|
46
|
+
B.location.must_equal "south side"
|
47
|
+
C.location.must_equal "south side"
|
48
|
+
|
49
|
+
B.weather.must_equal :sunny
|
50
|
+
C.weather.must_equal :sunny
|
51
|
+
|
52
|
+
B.num.must_equal 12
|
53
|
+
C.num.must_equal 12
|
54
|
+
|
55
|
+
B.score.must_equal 32.18
|
56
|
+
C.score.must_equal 32.18
|
57
|
+
|
58
|
+
B.available.must_equal false
|
59
|
+
C.available.must_equal false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't inherit containers like hashes, arrays, and sets (by default)" do
|
63
|
+
@props[:name][:inherit].must_equal false
|
64
|
+
@props[:groceries][:inherit].must_equal false
|
65
|
+
|
66
|
+
B.name.must_equal Hash.new
|
67
|
+
C.name.must_equal Hash.new
|
68
|
+
|
69
|
+
B.groceries.must_equal Array.new
|
70
|
+
C.groceries.must_equal Array.new
|
71
|
+
|
72
|
+
B.ingredients.must_equal Set.new
|
73
|
+
C.ingredients.must_equal Set.new
|
74
|
+
end
|
75
|
+
|
76
|
+
it "inherits properties of type ':Proc'" do
|
77
|
+
@props[:mood][:inherit].must_equal true
|
78
|
+
|
79
|
+
A.weather = :sunny
|
80
|
+
B.weather = :rainy
|
81
|
+
|
82
|
+
C.weather.must_equal :rainy
|
83
|
+
|
84
|
+
A.mood.must_equal "content"
|
85
|
+
B.mood.must_equal "depressed"
|
86
|
+
C.mood.must_equal "depressed"
|
87
|
+
|
88
|
+
C.weather = :sunny
|
89
|
+
C.mood.must_equal "content"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "syntax" do
|
95
|
+
describe "overriding the default 'inherit' age" do
|
96
|
+
describe "manual override" do
|
97
|
+
before do
|
98
|
+
A.cascade do
|
99
|
+
location :default => "south side"
|
100
|
+
score :default => 32.18
|
101
|
+
num :default => 12
|
102
|
+
weather :default => :sunny
|
103
|
+
available :default => false
|
104
|
+
mood :default => Proc.new{|me| me.weather == :sunny ? "content" : "depressed"}
|
105
|
+
color :default => Proc.new{|me| me.mood == "content" ? "rosy" : "blue"}
|
106
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
107
|
+
groceries :default => [:ice, :lemons, :splenda]
|
108
|
+
ingredients :default => Set.new([:sugar, :butter, :flour, :eggs])
|
109
|
+
end
|
110
|
+
|
111
|
+
B.color = Proc.new{|me| me.mood == "content" ? "#{me}: rosy" : "#{me}: blue"}
|
112
|
+
end
|
113
|
+
|
114
|
+
it "allows 'false', 'nil' and '0' to force no inheritance" do
|
115
|
+
B.location(false).must_equal ""
|
116
|
+
C.location(false).must_equal ""
|
117
|
+
|
118
|
+
B.score(nil).must_equal nil
|
119
|
+
C.score(nil).must_equal nil
|
120
|
+
|
121
|
+
B.weather(0).must_equal nil
|
122
|
+
C.weather(0).must_equal nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "allows 'true', '-1' to force full, inifinte max inheritance" do
|
126
|
+
B.num(true).must_equal 12
|
127
|
+
C.num(true).must_equal 12
|
128
|
+
|
129
|
+
B.groceries(-1).must_equal [:ice, :lemons, :splenda]
|
130
|
+
C.groceries(-1).must_equal [:ice, :lemons, :splenda]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "allows '<fixnum>' value to indicate max inheritance age" do
|
134
|
+
B.score(0).must_equal nil
|
135
|
+
B.score(1).must_equal 32.18
|
136
|
+
B.score(2).must_equal 32.18
|
137
|
+
C.score(1).must_equal nil
|
138
|
+
C.score(2).must_equal 32.18
|
139
|
+
|
140
|
+
B.name(0).must_equal Hash.new
|
141
|
+
B.name(1).must_equal({:first => 'jon', :last => 'smith'})
|
142
|
+
C.name(0).must_equal Hash.new
|
143
|
+
C.name(1).must_equal Hash.new
|
144
|
+
C.name(2).must_equal({:first => 'jon', :last => 'smith'})
|
145
|
+
end
|
146
|
+
|
147
|
+
it "allows ':inherit => false' and ':inherit => nil' syntax" do
|
148
|
+
B.location(:inherit => false).must_equal ""
|
149
|
+
c_location = C.location :inherit => false
|
150
|
+
c_location.must_equal ""
|
151
|
+
|
152
|
+
B.weather(:inherit => nil).must_equal nil
|
153
|
+
c_weather = C.weather :inherit => nil
|
154
|
+
c_weather.must_equal nil
|
155
|
+
end
|
156
|
+
|
157
|
+
it "allows ':inhert, false' and ':inherit, nil' syntax" do
|
158
|
+
B.num(:inherit, false).must_equal nil
|
159
|
+
c_num = C.num :inherit, false
|
160
|
+
c_num.must_equal nil
|
161
|
+
|
162
|
+
B.score(:inherit, nil).must_equal nil
|
163
|
+
c_score = C.score :inherit, nil
|
164
|
+
c_score.must_equal nil
|
165
|
+
end
|
166
|
+
|
167
|
+
it "allows ':inherit => true' and ':inherit => -1' syntax" do
|
168
|
+
B.name(:inherit => true).must_equal({:first => 'jon', :last => 'smith'})
|
169
|
+
c_name = C.name :inherit => true
|
170
|
+
c_name.must_equal({:first => 'jon', :last => 'smith'})
|
171
|
+
|
172
|
+
B.groceries(:inherit => -1).must_equal [:ice, :lemons, :splenda]
|
173
|
+
c_groceries = C.groceries :inherit => -1
|
174
|
+
c_groceries.must_equal [:ice, :lemons, :splenda]
|
175
|
+
end
|
176
|
+
|
177
|
+
it "allows ':inherit, true' and ':inherit, -1' syntax" do
|
178
|
+
B.ingredients(:inherit, true).must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
179
|
+
c_ingredients = C.ingredients :inherit, true
|
180
|
+
c_ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
181
|
+
|
182
|
+
B.name(:inherit, -1).must_equal({:first => 'jon', :last => 'smith'})
|
183
|
+
c_name = C.name :inherit, -1
|
184
|
+
c_name.must_equal({:first => 'jon', :last => 'smith'})
|
185
|
+
end
|
186
|
+
|
187
|
+
it "allows ':inherit => <n>' (n: fixnum) syntax" do
|
188
|
+
B.location(:inherit => 0).must_equal ''
|
189
|
+
B.location(:inherit => 1).must_equal "south side"
|
190
|
+
|
191
|
+
c_loc_0 = C.location :inherit => 0
|
192
|
+
c_loc_1 = C.location :inherit => 1
|
193
|
+
c_loc_2 = C.location :inherit => 2
|
194
|
+
|
195
|
+
c_loc_0.must_equal ''
|
196
|
+
c_loc_1.must_equal ''
|
197
|
+
c_loc_2.must_equal "south side"
|
198
|
+
|
199
|
+
B.groceries(:inherit => 0).must_equal []
|
200
|
+
B.groceries(:inherit => 1).must_equal [:ice, :lemons, :splenda]
|
201
|
+
|
202
|
+
c_groc_0 = C.groceries :inherit => 0
|
203
|
+
c_groc_1 = C.groceries :inherit => 1
|
204
|
+
c_groc_2 = C.groceries :inherit => 2
|
205
|
+
|
206
|
+
c_groc_0.must_equal []
|
207
|
+
c_groc_1.must_equal []
|
208
|
+
c_groc_2.must_equal [:ice, :lemons, :splenda]
|
209
|
+
end
|
210
|
+
|
211
|
+
it "allows ':inherit, <n>' (n: fixnum) syntax" do
|
212
|
+
B.score(:inherit, 0).must_equal nil
|
213
|
+
B.score(:inherit, 1).must_equal 32.18
|
214
|
+
|
215
|
+
c_score_0 = C.score :inherit, 0
|
216
|
+
c_score_1 = C.score :inherit, 1
|
217
|
+
c_score_2 = C.score :inherit, 2
|
218
|
+
|
219
|
+
c_score_0.must_equal nil
|
220
|
+
c_score_1.must_equal nil
|
221
|
+
c_score_2.must_equal 32.18
|
222
|
+
|
223
|
+
B.ingredients(:inherit, 0).must_equal Set.new([])
|
224
|
+
B.ingredients(:inherit, 1).must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
225
|
+
|
226
|
+
c_ingr_0 = C.ingredients :inherit, 0
|
227
|
+
c_ingr_1 = C.ingredients :inherit, 1
|
228
|
+
c_ingr_2 = C.ingredients :inherit, 2
|
229
|
+
|
230
|
+
c_ingr_0.must_equal Set.new([])
|
231
|
+
c_ingr_1.must_equal Set.new([])
|
232
|
+
c_ingr_2.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "property-wide inherit age override" do
|
237
|
+
before do
|
238
|
+
@props = A.cascade do
|
239
|
+
location :default => "south side", :inherit => false
|
240
|
+
score :default => 32.18, :inherit => false
|
241
|
+
num :default => 12, :inherit => false
|
242
|
+
weather :default => :sunny, :inherit => false
|
243
|
+
available :default => false, :inherit => false
|
244
|
+
mood :default => Proc.new{|me| me.weather == :sunny ? "content" : "depressed"}, :inherit => false
|
245
|
+
color :default => Proc.new{|me| me.mood == "content" ? "rosy" : "blue"}, :inherit => false
|
246
|
+
name :default => {:first => 'jon', :last => 'smith'}, :inherit => false
|
247
|
+
groceries :default => [:ice, :lemons, :splenda], :inherit => false
|
248
|
+
ingredients :default => Set.new([:sugar, :butter, :flour, :eggs]), :inherit => false
|
249
|
+
end
|
250
|
+
|
251
|
+
B.color = Proc.new{|me| me.mood == "content" ? "#{me}: rosy" : "#{me}: blue"}
|
252
|
+
end
|
253
|
+
|
254
|
+
it "all should not inherit" do
|
255
|
+
@props[:location][:inherit].must_equal false
|
256
|
+
@props[:score][:inherit].must_equal false
|
257
|
+
@props[:num][:inherit].must_equal false
|
258
|
+
@props[:weather][:inherit].must_equal false
|
259
|
+
@props[:available][:inherit].must_equal false
|
260
|
+
@props[:mood][:inherit].must_equal false
|
261
|
+
@props[:color][:inherit].must_equal false
|
262
|
+
@props[:groceries][:inherit].must_equal false
|
263
|
+
@props[:ingredients][:inherit].must_equal false
|
264
|
+
|
265
|
+
A.location.must_equal "south side"
|
266
|
+
B.location.must_equal ''
|
267
|
+
C.location.must_equal ''
|
268
|
+
|
269
|
+
A.score.must_equal 32.18
|
270
|
+
B.score.must_equal nil
|
271
|
+
C.score.must_equal nil
|
272
|
+
|
273
|
+
A.num.must_equal 12
|
274
|
+
B.num.must_equal nil
|
275
|
+
C.num.must_equal nil
|
276
|
+
|
277
|
+
A.weather.must_equal :sunny
|
278
|
+
B.weather.must_equal nil
|
279
|
+
C.weather.must_equal nil
|
280
|
+
|
281
|
+
A.available.must_equal false
|
282
|
+
B.available.must_equal nil
|
283
|
+
C.available.must_equal nil
|
284
|
+
|
285
|
+
A.mood.must_equal "content"
|
286
|
+
B.mood.must_equal "depressed"
|
287
|
+
C.mood.must_equal "depressed"
|
288
|
+
|
289
|
+
A.color.must_equal "rosy"
|
290
|
+
B.color.must_equal "B: blue"
|
291
|
+
C.color.must_equal "blue"
|
292
|
+
|
293
|
+
A.name.must_equal({:first => 'jon', :last => 'smith'})
|
294
|
+
B.name.must_equal Hash.new
|
295
|
+
C.name.must_equal Hash.new
|
296
|
+
|
297
|
+
A.groceries.must_equal [:ice, :lemons, :splenda]
|
298
|
+
B.groceries.must_equal Array.new
|
299
|
+
C.groceries.must_equal Array.new
|
300
|
+
|
301
|
+
A.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
302
|
+
B.ingredients.must_equal Set.new
|
303
|
+
C.ingredients.must_equal Set.new
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "manual inheritance age override" do
|
307
|
+
it "allows 'true' and '-1' syntax" do
|
308
|
+
B.location(true).must_equal "south side"
|
309
|
+
C.location(true).must_equal "south side"
|
310
|
+
|
311
|
+
B.score(-1).must_equal 32.18
|
312
|
+
C.score(-1).must_equal 32.18
|
313
|
+
|
314
|
+
B.ingredients(true).must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
315
|
+
C.ingredients(true).must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
316
|
+
end
|
317
|
+
|
318
|
+
it "allows <fixnum> (n > 0) syntax to indicate max inheritance age" do
|
319
|
+
B.weather(0).must_equal nil
|
320
|
+
B.weather(1).must_equal :sunny
|
321
|
+
B.weather(2).must_equal :sunny
|
322
|
+
C.weather(0).must_equal nil
|
323
|
+
C.weather(1).must_equal nil
|
324
|
+
C.weather(2).must_equal :sunny
|
325
|
+
C.weather(3).must_equal :sunny
|
326
|
+
|
327
|
+
B.groceries(0).must_equal Array.new
|
328
|
+
B.groceries(1).must_equal [:ice, :lemons, :splenda]
|
329
|
+
B.groceries(2).must_equal [:ice, :lemons, :splenda]
|
330
|
+
C.groceries(0).must_equal Array.new
|
331
|
+
C.groceries(1).must_equal Array.new
|
332
|
+
C.groceries(2).must_equal Array.new [:ice, :lemons, :splenda]
|
333
|
+
C.groceries(3).must_equal Array.new [:ice, :lemons, :splenda]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
# known error: defining a proc property with do ... end syntax
|
8
|
+
# A.cascade do
|
9
|
+
# mood :default => Proc.new do |me|
|
10
|
+
# me.weather == :sunny ? "content" : "depressed"
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
|
14
|
+
|
15
|
+
describe "proc properties" do
|
16
|
+
before do
|
17
|
+
A = Class.new{include CC}
|
18
|
+
B = Class.new(A)
|
19
|
+
C = Class.new(B)
|
20
|
+
|
21
|
+
@props = A.cascade do
|
22
|
+
weather :default => :sunny
|
23
|
+
mood :default => Proc.new{|me|
|
24
|
+
me.weather == :sunny ? "content" : "depressed"
|
25
|
+
}
|
26
|
+
color :default => Proc.new{|me|
|
27
|
+
me.mood == "content" ? "rosy" : "blue"
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
Object.send :remove_const, :A
|
34
|
+
Object.send :remove_const, :B
|
35
|
+
Object.send :remove_const, :C
|
36
|
+
end
|
37
|
+
|
38
|
+
it "inherits by default" do
|
39
|
+
@props[:weather][:inherit].must_equal true # sanity
|
40
|
+
@props[:mood][:inherit].must_equal true
|
41
|
+
@props[:color][:inherit].must_equal true
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "a property of type :Proc" do
|
45
|
+
it "evaluates the proc on each invocation of the property" do
|
46
|
+
A.weather.must_equal :sunny
|
47
|
+
A.mood.must_equal "content"
|
48
|
+
A.color.must_equal "rosy"
|
49
|
+
|
50
|
+
A.weather = :rainy
|
51
|
+
A.mood.must_equal "depressed"
|
52
|
+
A.color.must_equal "blue"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "has second parameter that is a list of parents" do
|
56
|
+
A.mood = Proc.new{|me, parents| parents}
|
57
|
+
|
58
|
+
A.mood.must_equal []
|
59
|
+
B.mood.must_equal [A]
|
60
|
+
C.mood.must_equal [B, A]
|
61
|
+
|
62
|
+
A.new.mood.must_equal [A]
|
63
|
+
B.new.mood.must_equal [B, A]
|
64
|
+
C.new.mood.must_equal [C, B, A]
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "blank descendents" do
|
68
|
+
it "inherits from its nearest non-blank ancestor" do
|
69
|
+
B.mood_is_blank?.must_equal true
|
70
|
+
C.mood_is_blank?.must_equal true
|
71
|
+
|
72
|
+
A.weather = :sunny
|
73
|
+
B.mood.must_equal "content"
|
74
|
+
C.mood.must_equal "content"
|
75
|
+
B.color.must_equal "rosy"
|
76
|
+
C.color.must_equal "rosy"
|
77
|
+
|
78
|
+
A.weather = :rainy
|
79
|
+
|
80
|
+
B.mood.must_equal "depressed"
|
81
|
+
C.mood.must_equal "depressed"
|
82
|
+
B.color.must_equal "blue"
|
83
|
+
C.color.must_equal "blue"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "uses its own proc if not blank" do
|
87
|
+
B.mood = Proc.new{ "angry" }
|
88
|
+
|
89
|
+
B.mood.must_equal "angry"
|
90
|
+
B.color.must_equal "blue"
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "the parent redefines the proc property" do
|
94
|
+
before do
|
95
|
+
A.color = Proc.new{|me| me.mood == "content" ? "#{me}: rosy" : "#{me}: blue"}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "cascades down to descendents whose property is blank, just as before" do
|
99
|
+
A.weather = :sunny
|
100
|
+
B.weather = :rainy
|
101
|
+
|
102
|
+
A.color.must_equal "A: rosy"
|
103
|
+
B.color.must_equal "B: blue"
|
104
|
+
C.color.must_equal "C: blue"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "a descendent changes its proc property" do
|
109
|
+
before do
|
110
|
+
B.mood = Proc.new{ "angry" }
|
111
|
+
B.color = Proc.new{|me, parents| me.mood == "content" ? "#{me}: rosy" : "#{me}: blue"}
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "descendents of the descendent" do
|
115
|
+
it "inherits the new proc by default" do
|
116
|
+
A.weather = :sunny
|
117
|
+
|
118
|
+
A.mood.must_equal "content"
|
119
|
+
B.mood.must_equal "angry"
|
120
|
+
C.mood.must_equal "angry"
|
121
|
+
|
122
|
+
A.color.must_equal "rosy"
|
123
|
+
B.color.must_equal "B: blue"
|
124
|
+
C.color.must_equal "C: blue"
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "descendents can any inheritance, using the default instead" do
|
128
|
+
it "accepts ':default' syntax" do
|
129
|
+
A.weather = :sunny
|
130
|
+
|
131
|
+
A.mood.must_equal "content"
|
132
|
+
B.mood.must_equal "angry"
|
133
|
+
C.mood(:default).must_equal "content"
|
134
|
+
end
|
135
|
+
|
136
|
+
it "accepts ':inherit => false' syntax" do
|
137
|
+
A.weather = :sunny
|
138
|
+
|
139
|
+
A.mood.must_equal "content"
|
140
|
+
B.mood.must_equal "angry"
|
141
|
+
C.mood(:inherit => false).must_equal "content"
|
142
|
+
|
143
|
+
A.color.must_equal "rosy"
|
144
|
+
B.color.must_equal "B: blue"
|
145
|
+
C.color(:inherit => false).must_equal "blue" # ***** take note: C.mood = "depressed" *****
|
146
|
+
end
|
147
|
+
|
148
|
+
it "accepts ':inherit, false' syntax" do
|
149
|
+
A.weather = :sunny
|
150
|
+
|
151
|
+
A.mood.must_equal "content"
|
152
|
+
B.mood.must_equal "angry"
|
153
|
+
C.mood(:inherit, false).must_equal "content"
|
154
|
+
|
155
|
+
A.color.must_equal "rosy"
|
156
|
+
B.color.must_equal "B: blue"
|
157
|
+
C.color(:inherit, false).must_equal "blue"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "a property of type :Proc with property-level inheritance override" do
|
166
|
+
before do
|
167
|
+
@props = A.cascade do
|
168
|
+
weather :default => :sunny
|
169
|
+
mood :default => Proc.new{|me| me.weather == :sunny ? "content" : "depressed"}, :inherit => false
|
170
|
+
color :default => Proc.new{|me| me.mood == "content" ? "#{me}: rosy" : "#{me}: blue"}, :inherit => false
|
171
|
+
end
|
172
|
+
|
173
|
+
B.mood = Proc.new{ "angry" }
|
174
|
+
end
|
175
|
+
|
176
|
+
it "does not inherit" do
|
177
|
+
@props[:weather][:inherit].must_equal true # sanity
|
178
|
+
@props[:mood][:inherit].must_equal false
|
179
|
+
@props[:color][:inherit].must_equal false
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "descendents" do
|
183
|
+
it "propogates the initial proc value, but not any descendents' proc" do
|
184
|
+
B.color_is_blank?.must_equal true
|
185
|
+
C.mood_is_blank?.must_equal true
|
186
|
+
|
187
|
+
A.weather.must_equal :sunny # sanity
|
188
|
+
B.weather.must_equal :sunny # sanity
|
189
|
+
C.weather.must_equal :sunny
|
190
|
+
|
191
|
+
A.mood.must_equal "content" # sanity
|
192
|
+
B.mood.must_equal "angry" # sanity
|
193
|
+
C.mood.must_equal "content"
|
194
|
+
|
195
|
+
A.color.must_equal "A: rosy" # sanity
|
196
|
+
B.color.must_equal "B: blue" # sanity
|
197
|
+
C.color.must_equal "C: rosy"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "an inherited property not of type :Proc" do
|
203
|
+
before do
|
204
|
+
@props = A.cascade do
|
205
|
+
ingredients :default => Set.new([:sugar, :butter, :flour, :eggs]), :inherit => true
|
206
|
+
sweet_tooth :default => false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it "inherits the property" do
|
211
|
+
@props[:ingredients][:inherit].must_equal true
|
212
|
+
@props[:sweet_tooth][:inherit].must_equal true
|
213
|
+
|
214
|
+
A.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
215
|
+
B.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
216
|
+
C.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
217
|
+
|
218
|
+
A.sweet_tooth.must_equal false
|
219
|
+
B.sweet_tooth.must_equal false
|
220
|
+
C.sweet_tooth.must_equal false
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "a descendent redefines the property with a proc" do
|
224
|
+
before do
|
225
|
+
class D < C; end
|
226
|
+
|
227
|
+
B.cascade do
|
228
|
+
diabetic :default => false
|
229
|
+
end
|
230
|
+
|
231
|
+
B.ingredients = Proc.new do |me, parents|
|
232
|
+
me.diabetic ? parents.first.ingredients.dup.delete(:sugar) << :splenda :
|
233
|
+
parents.first.ingredients.dup.delete(:splenda) << :sugar
|
234
|
+
end
|
235
|
+
|
236
|
+
B.sweet_tooth = Proc.new{|me, parents| me.ingredients.include? :sugar}
|
237
|
+
end
|
238
|
+
|
239
|
+
after do
|
240
|
+
Object.send :remove_const, :D
|
241
|
+
end
|
242
|
+
|
243
|
+
it "further descendents inherit the proc" do
|
244
|
+
C.diabetic = true
|
245
|
+
D.diabetic = false
|
246
|
+
|
247
|
+
B.diabetic.must_equal false
|
248
|
+
C.diabetic.must_equal true
|
249
|
+
D.diabetic.must_equal false
|
250
|
+
|
251
|
+
B.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
252
|
+
C.ingredients.must_equal Set.new([:splenda, :butter, :flour, :eggs])
|
253
|
+
D.ingredients.must_equal Set.new([:sugar, :butter, :flour, :eggs])
|
254
|
+
|
255
|
+
A.sweet_tooth.must_equal false
|
256
|
+
B.sweet_tooth.must_equal true
|
257
|
+
C.sweet_tooth.must_equal false
|
258
|
+
D.sweet_tooth.must_equal true
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "descendents redefine the property, but not to a proc" do
|
262
|
+
before do
|
263
|
+
C.ingredients = Set.new([:equal, :butter, :flour, :eggs])
|
264
|
+
C.sweet_tooth = false
|
265
|
+
end
|
266
|
+
|
267
|
+
it "descendents reflect the new 'static' settings" do
|
268
|
+
C.ingredients.must_equal Set.new([:equal, :butter, :flour, :eggs])
|
269
|
+
D.ingredients.must_equal Set.new([:equal, :butter, :flour, :eggs])
|
270
|
+
|
271
|
+
C.sweet_tooth.must_equal false
|
272
|
+
D.sweet_tooth.must_equal false
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "syntax" do
|
279
|
+
it "takes two params: self, parent classes" do
|
280
|
+
who_am_i, which_parents = nil, nil
|
281
|
+
|
282
|
+
B.color = Proc.new{|me, parents|
|
283
|
+
who_am_i = me
|
284
|
+
which_parents = parents
|
285
|
+
"green"
|
286
|
+
}
|
287
|
+
|
288
|
+
B.color.must_equal("green")
|
289
|
+
who_am_i.must_equal(B)
|
290
|
+
which_parents.must_equal([A])
|
291
|
+
end
|
292
|
+
|
293
|
+
it "evaluates to the property value" do
|
294
|
+
B.color = Proc.new{ "green" }
|
295
|
+
B.color.must_equal "green"
|
296
|
+
|
297
|
+
C.color = Proc.new{|me, parents|
|
298
|
+
"#{me} #{parents.first.color.capitalize}"
|
299
|
+
}
|
300
|
+
C.color.must_equal "C Green"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "examples" do
|
306
|
+
before do
|
307
|
+
class A; extend CC; end
|
308
|
+
class B < A; end
|
309
|
+
class C < B; end
|
310
|
+
end
|
311
|
+
|
312
|
+
after do
|
313
|
+
Object.send :remove_const, :A
|
314
|
+
Object.send :remove_const, :B
|
315
|
+
Object.send :remove_const, :C
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "merge: hash property" do
|
319
|
+
before do
|
320
|
+
A.cascade do
|
321
|
+
opts :default => {:color => "black", :music => ["Rolling Stones"]}
|
322
|
+
merged_opts :proc => true
|
323
|
+
end
|
324
|
+
|
325
|
+
A.merged_opts = Proc.new do |me, parents|
|
326
|
+
parents.reverse.inject({}){|r, child|
|
327
|
+
r.merge(child.opts)
|
328
|
+
}.merge(me.opts)
|
329
|
+
end
|
330
|
+
|
331
|
+
B.opts[:music] = ["Arcade Fire"]
|
332
|
+
C.opts[:color] = "blond"
|
333
|
+
end
|
334
|
+
|
335
|
+
it "works" do
|
336
|
+
A.merged_opts.must_equal({:color => "black", :music => ["Rolling Stones"]})
|
337
|
+
B.merged_opts.must_equal({:color => "black", :music => ["Arcade Fire"]})
|
338
|
+
C.merged_opts.must_equal({:color => "blond", :music => ["Arcade Fire"]})
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
|