cascading_classes 0.3.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,230 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "basics" do
|
8
|
+
before do
|
9
|
+
A = Class.new{extend CC}
|
10
|
+
B = Class.new(A)
|
11
|
+
C = Class.new(B)
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
Object.send :remove_const, :A
|
16
|
+
Object.send :remove_const, :B
|
17
|
+
Object.send :remove_const, :C
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "properties with no type" do
|
21
|
+
before do
|
22
|
+
@props = A.cascade do
|
23
|
+
wings
|
24
|
+
features
|
25
|
+
blind
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has unknown type of :Object" do
|
30
|
+
@props[:wings][:type].must_equal :undefined_type
|
31
|
+
@props[:features][:type].must_equal :undefined_type
|
32
|
+
@props[:blind][:type].must_equal :undefined_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "whole slew of properties" do
|
37
|
+
before do
|
38
|
+
@props = A.cascade do
|
39
|
+
color :default => "red"
|
40
|
+
score :default => 45.9
|
41
|
+
rank :default => 18
|
42
|
+
style :default => :spotted
|
43
|
+
available :default => true
|
44
|
+
locations :default => [:south, :north]
|
45
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
46
|
+
classified :default => Proc.new{|me| me.score > 40 }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "parent class" do
|
51
|
+
it "has set with the default value" do
|
52
|
+
A.color.must_equal "red"
|
53
|
+
A.score.must_equal 45.9
|
54
|
+
A.rank.must_equal 18
|
55
|
+
A.style.must_equal :spotted
|
56
|
+
A.available.must_equal true
|
57
|
+
A.locations.must_equal [:south, :north]
|
58
|
+
A.name.must_equal({:first => 'jon', :last => 'smith'})
|
59
|
+
A.classified.must_equal true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "descendents" do
|
64
|
+
it "has access to the 'default' value" do
|
65
|
+
A.color(:default).must_equal "red"
|
66
|
+
A.color = "blue"
|
67
|
+
A.color(:default).must_equal "blue"
|
68
|
+
|
69
|
+
B.color = "white"
|
70
|
+
B.color.must_equal "white"
|
71
|
+
B.color(:default).must_equal "red"
|
72
|
+
C.color(:default).must_equal "red"
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "non-container-type properties" do
|
76
|
+
it "inherits non-blank values from first non-blank ancestor" do
|
77
|
+
@props[:color][:inherit].must_equal true
|
78
|
+
@props[:score][:inherit].must_equal true
|
79
|
+
@props[:rank][:inherit].must_equal true
|
80
|
+
@props[:style][:inherit].must_equal true
|
81
|
+
@props[:available][:inherit].must_equal true
|
82
|
+
|
83
|
+
B.color.must_equal "red"
|
84
|
+
B.score.must_equal 45.9
|
85
|
+
B.rank.must_equal 18
|
86
|
+
B.style.must_equal :spotted
|
87
|
+
B.available.must_equal true
|
88
|
+
|
89
|
+
C.color.must_equal "red"
|
90
|
+
C.score.must_equal 45.9
|
91
|
+
C.rank.must_equal 18
|
92
|
+
C.style.must_equal :spotted
|
93
|
+
C.available.must_equal true
|
94
|
+
|
95
|
+
B.score = 48.2
|
96
|
+
B.style = :rugged
|
97
|
+
B.available = false
|
98
|
+
|
99
|
+
B.score.must_equal 48.2
|
100
|
+
C.score.must_equal 48.2
|
101
|
+
|
102
|
+
B.style.must_equal :rugged
|
103
|
+
C.style.must_equal :rugged
|
104
|
+
|
105
|
+
B.available.must_equal false
|
106
|
+
C.available.must_equal false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "container-type properties" do
|
111
|
+
it "does not inherit ancestor properties" do
|
112
|
+
@props[:locations][:inherit].must_equal false
|
113
|
+
@props[:name][:inherit].must_equal false
|
114
|
+
|
115
|
+
B.locations.must_equal Array.new
|
116
|
+
C.locations.must_equal Array.new
|
117
|
+
B.name.must_equal Hash.new
|
118
|
+
C.name.must_equal Hash.new
|
119
|
+
|
120
|
+
B.locations = [:east, :west]
|
121
|
+
B.name = {:first => 'adam', :last => 'phillips'}
|
122
|
+
|
123
|
+
B.locations.must_equal [:east, :west]
|
124
|
+
C.locations.must_equal Array.new
|
125
|
+
B.name.must_equal({:first => 'adam', :last => 'phillips'})
|
126
|
+
C.name.must_equal Hash.new
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "proc properties" do
|
131
|
+
it "inherits by default" do
|
132
|
+
@props[:classified][:inherit].must_equal true
|
133
|
+
end
|
134
|
+
|
135
|
+
it "evaluates the proc in context of each descendent" do
|
136
|
+
A.score = 54.2
|
137
|
+
B.score = 30.1
|
138
|
+
C.score = 45.2
|
139
|
+
|
140
|
+
A.classified.must_equal true
|
141
|
+
B.classified.must_equal false
|
142
|
+
C.classified.must_equal true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "property block parameter" do
|
148
|
+
before do
|
149
|
+
B.color = "blue"
|
150
|
+
C.color = "purple"
|
151
|
+
end
|
152
|
+
|
153
|
+
it "expects two parameters: property_name, ancestors" do
|
154
|
+
A.color{|color_a, parents|
|
155
|
+
color_a.must_equal "red"
|
156
|
+
parents.must_equal []
|
157
|
+
}
|
158
|
+
|
159
|
+
B.color{|color_b, parents|
|
160
|
+
color_b.must_equal "blue"
|
161
|
+
parents.must_equal [A]
|
162
|
+
}
|
163
|
+
|
164
|
+
C.color{|color_c, parents|
|
165
|
+
color_c.must_equal "purple"
|
166
|
+
parents.must_equal [B, A]
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
it "returns last expression of block" do
|
171
|
+
A.color{|my_color| my_color.reverse}.must_equal "der"
|
172
|
+
B.color{|my_color| my_color.capitalize}.must_equal "Blue"
|
173
|
+
C.color{|my_color| my_color * 2}.must_equal "purplepurple"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "descendents" do
|
178
|
+
describe "properties not of type :Proc but whose descendents set it to one" do
|
179
|
+
before do
|
180
|
+
B.color = Proc.new{|me, parents| parents.first.color.capitalize}
|
181
|
+
B.rank = Proc.new{ Time.now.sec }
|
182
|
+
end
|
183
|
+
|
184
|
+
it "descendents whose property is blank, inherit (invoke) the proc" do
|
185
|
+
B.color.must_equal "Red"
|
186
|
+
|
187
|
+
sec = Time.now.sec
|
188
|
+
B.rank.must_equal sec
|
189
|
+
C.rank.must_equal sec
|
190
|
+
|
191
|
+
sleep(2)
|
192
|
+
|
193
|
+
sec = Time.now.sec
|
194
|
+
B.rank.must_equal sec
|
195
|
+
C.rank.must_equal sec
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "to_hash" do
|
203
|
+
before do
|
204
|
+
A = Class.new{extend CC}
|
205
|
+
B = Class.new(A)
|
206
|
+
C = Class.new(B)
|
207
|
+
|
208
|
+
A.cascade do
|
209
|
+
color :default => "red"
|
210
|
+
name :default => {:first => 'jon', :last => 'mackey'}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
after do
|
215
|
+
Object.send :remove_const, :A
|
216
|
+
Object.send :remove_const, :B
|
217
|
+
Object.send :remove_const, :C
|
218
|
+
end
|
219
|
+
|
220
|
+
it "returns hash of property name/values" do
|
221
|
+
A.to_hash.must_equal({:color => "red",
|
222
|
+
:name => {:first => 'jon',
|
223
|
+
:last => 'mackey'}
|
224
|
+
})
|
225
|
+
end
|
226
|
+
|
227
|
+
it "applies default getter logic: non-container-types inherit, container-types don't" do
|
228
|
+
B.to_hash.must_equal({:color => "red", :name => {}})
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "block given" do
|
8
|
+
before do
|
9
|
+
A = Class.new{include CC}
|
10
|
+
B = Class.new(A)
|
11
|
+
C = Class.new(B)
|
12
|
+
|
13
|
+
A.cascade do
|
14
|
+
color :default => "red"
|
15
|
+
score :default => 34.5
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
Object.send :remove_const, :A
|
21
|
+
Object.send :remove_const, :B
|
22
|
+
Object.send :remove_const, :C
|
23
|
+
end
|
24
|
+
|
25
|
+
it "takes two params: value of self, parent classes" do
|
26
|
+
A.score{|my_score, parents| my_score}.must_equal A.score
|
27
|
+
|
28
|
+
B.score = 28.2
|
29
|
+
B.score{|my_score, parents| my_score}.must_equal B.score
|
30
|
+
|
31
|
+
A.new.score{|my_score| my_score}.must_equal A.new.score
|
32
|
+
B.new.score{|my_score| my_score}.must_equal B.new.score
|
33
|
+
|
34
|
+
A.score{|my_score, parents| parents}.must_equal []
|
35
|
+
B.score{|my_score, parents| parents}.must_equal [A]
|
36
|
+
|
37
|
+
A.new.score{|my_score, parents| parents}.must_equal [A]
|
38
|
+
B.new.score{|my_score, parents| parents}.must_equal [B, A]
|
39
|
+
|
40
|
+
=begin
|
41
|
+
what_score, which_parents = nil, nil
|
42
|
+
|
43
|
+
B.score{|my_score, parents|
|
44
|
+
what_score = my_score
|
45
|
+
which_parents = parents
|
46
|
+
}
|
47
|
+
|
48
|
+
what_score.must_equal(B.score)
|
49
|
+
which_parents.must_equal([A])
|
50
|
+
=end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "container types: typical" do
|
8
|
+
before do
|
9
|
+
A = Class.new{extend CC}
|
10
|
+
B = Class.new(A)
|
11
|
+
C = Class.new(B)
|
12
|
+
|
13
|
+
@props = A.cascade do
|
14
|
+
urls :default => ["www.google.com"]
|
15
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
Object.send :remove_const, :A
|
21
|
+
Object.send :remove_const, :B
|
22
|
+
Object.send :remove_const, :C
|
23
|
+
end
|
24
|
+
|
25
|
+
it "does not inherit by default" do
|
26
|
+
@props[:urls][:inherit].must_equal false
|
27
|
+
@props[:name][:inherit].must_equal false
|
28
|
+
|
29
|
+
A.urls.must_equal ["www.google.com"]
|
30
|
+
B.urls.must_equal Array.new
|
31
|
+
C.urls.must_equal Array.new
|
32
|
+
|
33
|
+
A.name.must_equal({:first => 'jon', :last => 'smith'})
|
34
|
+
B.name.must_equal Hash.new
|
35
|
+
C.name.must_equal Hash.new
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "descendents can update their own container properties" do
|
39
|
+
before do
|
40
|
+
B.urls << "www.bing.com" << "www.example.com"
|
41
|
+
|
42
|
+
C.urls = ["c.homepage.org"]
|
43
|
+
C.urls << "example.org"
|
44
|
+
|
45
|
+
B.name[:first] = 'charlie'
|
46
|
+
C.name = {:first => 'tim', :last => 'emerson'}
|
47
|
+
end
|
48
|
+
|
49
|
+
it "works" do
|
50
|
+
B.urls.must_equal ["www.bing.com", "www.example.com"]
|
51
|
+
C.urls.must_equal ["c.homepage.org", "example.org"]
|
52
|
+
|
53
|
+
B.name.must_equal({:first => 'charlie'})
|
54
|
+
C.name.must_equal({:first => 'tim', :last => 'emerson'})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "manual override: allowing inheritance" do
|
59
|
+
it "accepts ':inherit' syntax" do
|
60
|
+
B.urls(:inherit).must_equal ["www.google.com"]
|
61
|
+
C.urls(:inherit).must_equal ["www.google.com"]
|
62
|
+
|
63
|
+
B.name(:inherit).must_equal({:first => 'jon', :last => 'smith'})
|
64
|
+
C.name(:inherit).must_equal({:first => 'jon', :last => 'smith'})
|
65
|
+
end
|
66
|
+
|
67
|
+
it "accepts ':inherit => <val>' syntax" do
|
68
|
+
B.urls(:inherit => 0).must_equal []
|
69
|
+
B.urls(:inherit => false).must_equal []
|
70
|
+
B.urls(:inherit => nil).must_equal []
|
71
|
+
B.urls(:inherit => 1).must_equal ["www.google.com"]
|
72
|
+
B.urls(:inherit => true).must_equal ["www.google.com"]
|
73
|
+
B.urls(:inherit => -1).must_equal ["www.google.com"]
|
74
|
+
|
75
|
+
C.urls(:inherit => 0).must_equal []
|
76
|
+
C.urls(:inherit => false).must_equal []
|
77
|
+
C.urls(:inherit => nil).must_equal []
|
78
|
+
C.urls(:inherit => 1).must_equal []
|
79
|
+
C.urls(:inherit => 2).must_equal ["www.google.com"]
|
80
|
+
C.urls(:inherit => -1).must_equal ["www.google.com"]
|
81
|
+
C.urls(:inherit => true).must_equal ["www.google.com"]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "accepts ':inherit, <val>' syntax" do
|
85
|
+
B.name(:inherit, 0).must_equal Hash.new
|
86
|
+
B.name(:inherit, false).must_equal Hash.new
|
87
|
+
B.name(:inherit, nil).must_equal Hash.new
|
88
|
+
B.name(:inherit, 1).must_equal({:first => 'jon', :last => 'smith'})
|
89
|
+
B.name(:inherit, true).must_equal({:first => 'jon', :last => 'smith'})
|
90
|
+
B.name(:inherit, -1).must_equal({:first => 'jon', :last => 'smith'})
|
91
|
+
|
92
|
+
C.name(:inherit, 0).must_equal Hash.new
|
93
|
+
C.name(:inherit, false).must_equal Hash.new
|
94
|
+
C.name(:inherit, nil).must_equal Hash.new
|
95
|
+
C.name(:inherit, 1).must_equal Hash.new
|
96
|
+
C.name(:inherit, 2).must_equal({:first => 'jon', :last => 'smith'})
|
97
|
+
C.name(:inherit, true).must_equal({:first => 'jon', :last => 'smith'})
|
98
|
+
C.name(:inherit, -1).must_equal({:first => 'jon', :last => 'smith'})
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "manual override has no effect on non-blank container properties" do
|
102
|
+
it "works" do
|
103
|
+
C.urls << "www.yahoo.com"
|
104
|
+
|
105
|
+
A.urls_is_blank?.must_equal false
|
106
|
+
B.urls_is_blank?.must_equal true
|
107
|
+
C.urls_is_blank?.must_equal false
|
108
|
+
|
109
|
+
A.urls.must_equal ["www.google.com"]
|
110
|
+
A.urls(:inherit).must_equal ["www.google.com"]
|
111
|
+
A.urls(:inherit => false).must_equal ["www.google.com"]
|
112
|
+
A.urls(:inherit => nil).must_equal ["www.google.com"]
|
113
|
+
A.urls(:inherit => 0).must_equal ["www.google.com"]
|
114
|
+
A.urls(:inherit => 1).must_equal ["www.google.com"]
|
115
|
+
A.urls(:inherit => 2).must_equal ["www.google.com"]
|
116
|
+
A.urls(:inherit => -1).must_equal ["www.google.com"]
|
117
|
+
A.urls(:inherit => true).must_equal ["www.google.com"]
|
118
|
+
|
119
|
+
C.urls.must_equal ["www.yahoo.com"]
|
120
|
+
C.urls(:inherit).must_equal ["www.yahoo.com"]
|
121
|
+
C.urls(:inherit, false).must_equal ["www.yahoo.com"]
|
122
|
+
C.urls(:inherit, nil).must_equal ["www.yahoo.com"]
|
123
|
+
C.urls(:inherit, 0).must_equal ["www.yahoo.com"]
|
124
|
+
C.urls(:inherit, 1).must_equal ["www.yahoo.com"]
|
125
|
+
C.urls(:inherit, 2).must_equal ["www.yahoo.com"]
|
126
|
+
C.urls(:inherit, -1).must_equal ["www.yahoo.com"]
|
127
|
+
C.urls(:inherit, true).must_equal ["www.yahoo.com"]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "container types: property-wide inheritance" do
|
134
|
+
before do
|
135
|
+
A = Class.new{extend CC}
|
136
|
+
B = Class.new(A)
|
137
|
+
C = Class.new(B)
|
138
|
+
|
139
|
+
@props = A.cascade do
|
140
|
+
urls :default => ["www.google.com"], :inherit => true
|
141
|
+
name :default => {:first => 'jon', :last => 'smith'}, :inherit => true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
after do
|
146
|
+
Object.send :remove_const, :A
|
147
|
+
Object.send :remove_const, :B
|
148
|
+
Object.send :remove_const, :C
|
149
|
+
end
|
150
|
+
|
151
|
+
it "inherits by default" do
|
152
|
+
@props[:urls][:inherit].must_equal true
|
153
|
+
@props[:name][:inherit].must_equal true
|
154
|
+
|
155
|
+
B.urls.must_equal ["www.google.com"]
|
156
|
+
C.urls.must_equal ["www.google.com"]
|
157
|
+
|
158
|
+
B.name.must_equal({:first => 'jon', :last => 'smith'})
|
159
|
+
C.name.must_equal({:first => 'jon', :last => 'smith'})
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "a descendent updates a container property" do
|
163
|
+
before do
|
164
|
+
B.urls = "www.bing.com"
|
165
|
+
end
|
166
|
+
|
167
|
+
it "cascades down its descendents" do
|
168
|
+
C.urls.must_equal ["www.bing.com"]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "overriding the inheritance effect" do
|
173
|
+
it "accepts ':inherit => <val>' syntax" do
|
174
|
+
B.urls.must_equal ["www.google.com"]
|
175
|
+
C.urls.must_equal ["www.google.com"]
|
176
|
+
|
177
|
+
B.urls(:inherit => false).must_equal []
|
178
|
+
C.urls(:inherit => false).must_equal []
|
179
|
+
|
180
|
+
B.urls(:inherit => 0).must_equal []
|
181
|
+
C.urls(:inherit => 0).must_equal []
|
182
|
+
|
183
|
+
B.urls(:inherit => 1).must_equal ["www.google.com"]
|
184
|
+
C.urls(:inherit => 1).must_equal []
|
185
|
+
end
|
186
|
+
|
187
|
+
it "accepts ':inherit, <val>' syntax" do
|
188
|
+
B.urls.must_equal ["www.google.com"]
|
189
|
+
C.urls.must_equal ["www.google.com"]
|
190
|
+
|
191
|
+
B.urls(:inherit, false).must_equal []
|
192
|
+
C.urls(:inherit, false).must_equal []
|
193
|
+
|
194
|
+
B.urls(:inherit, 0).must_equal []
|
195
|
+
C.urls(:inherit, 0).must_equal []
|
196
|
+
|
197
|
+
B.urls(:inherit, 1).must_equal ["www.google.com"]
|
198
|
+
C.urls(:inherit, 1).must_equal []
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|