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,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
|