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,36 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "parents_for()" do
|
8
|
+
before do
|
9
|
+
A = Class.new{extend CC}
|
10
|
+
B = Class.new(A)
|
11
|
+
C = Class.new(B)
|
12
|
+
|
13
|
+
A.cascade do
|
14
|
+
color :default => "red"
|
15
|
+
end
|
16
|
+
|
17
|
+
B.cascade do
|
18
|
+
score :default => 45.32
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
after do
|
23
|
+
Object.send :remove_const, :A
|
24
|
+
Object.send :remove_const, :B
|
25
|
+
Object.send :remove_const, :C
|
26
|
+
end
|
27
|
+
|
28
|
+
it "lists ancestors for given property" do
|
29
|
+
A.parents_for(:color).must_equal []
|
30
|
+
B.parents_for(:color).must_equal [A]
|
31
|
+
C.parents_for(:color).must_equal [B, A]
|
32
|
+
|
33
|
+
B.parents_for(:score).must_equal []
|
34
|
+
C.parents_for(:score).must_equal [B]
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
begin
|
2
|
+
require_relative '../helper_spec'
|
3
|
+
rescue NameError
|
4
|
+
require File.expand_path('../helper_spec', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "custom hash-like class" do
|
8
|
+
before do
|
9
|
+
class MyCustom < Hash
|
10
|
+
def initialize(*arr)
|
11
|
+
return super unless arr.size > 0
|
12
|
+
[*arr].flatten.each_slice(2){|k,v| self[k] = v}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Parent = Class.new{include CC}
|
17
|
+
class Child < Parent; end
|
18
|
+
class GrandChild < Child; end
|
19
|
+
|
20
|
+
@props = Parent.cascade do
|
21
|
+
beta :default => MyCustom.new([:rank, 4], [:color, "red"]),
|
22
|
+
:new => proc{ MyCustom.new },
|
23
|
+
:blank => proc{|v| v.empty?}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
Object.send :remove_const, :MyCustom
|
29
|
+
Object.send :remove_const, :Parent
|
30
|
+
Object.send :remove_const, :Child
|
31
|
+
Object.send :remove_const, :GrandChild
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "by default" do
|
35
|
+
it "is set to not inherit by default" do
|
36
|
+
@props[:beta][:inherit].must_equal false
|
37
|
+
end
|
38
|
+
|
39
|
+
it "descendents (including instances) do not inherit" do
|
40
|
+
Parent.new.beta.must_equal MyCustom.new
|
41
|
+
Child.beta.must_equal MyCustom.new
|
42
|
+
Child.new.beta.must_equal MyCustom.new
|
43
|
+
GrandChild.beta.must_equal MyCustom.new
|
44
|
+
GrandChild.new.beta.must_equal MyCustom.new
|
45
|
+
end
|
46
|
+
|
47
|
+
it "'Parent' has 'default' value" do
|
48
|
+
Parent.beta.must_equal MyCustom.new(:rank, 4, :color, "red")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has type ':MyCustom'" do
|
52
|
+
@props[:beta][:type].must_equal :MyCustom
|
53
|
+
end
|
54
|
+
|
55
|
+
it "has blank value of empty 'MyCustom' object" do
|
56
|
+
mc = MyCustom.new
|
57
|
+
mc.empty?.must_equal true
|
58
|
+
|
59
|
+
@props[:beta][:blank].call(mc).must_equal true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "has new value of empty 'MyCustom' object" do
|
63
|
+
@props[:beta][:new].call.must_equal MyCustom.new
|
64
|
+
end
|
65
|
+
|
66
|
+
it "nonblank values never inherit" do
|
67
|
+
# ???
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "property in instance of 'Parent' is blank" do
|
72
|
+
before do
|
73
|
+
@parent = Parent.new
|
74
|
+
end
|
75
|
+
|
76
|
+
it "can inherit one level up to 'Parent'" do
|
77
|
+
mc = MyCustom.new(:rank, 4); mc[:color] = "red"
|
78
|
+
blank_inherit_helper(@parent, :beta, 1, mc, MyCustom.new)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "'Child'" do
|
83
|
+
describe "property on 'Child' is blank" do
|
84
|
+
it "can inherit one level up to 'Parent'" do
|
85
|
+
mc = MyCustom.new([[:rank, 4], [:color, "red"]])
|
86
|
+
blank_inherit_helper(Child, :beta, 1, mc, MyCustom.new)
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "property on instance of 'Child' is blank" do
|
90
|
+
before do
|
91
|
+
@child = Child.new
|
92
|
+
end
|
93
|
+
|
94
|
+
it "can inherit two levels up to 'Parent'" do
|
95
|
+
mc = MyCustom.new(:rank, 4, :color, "red")
|
96
|
+
blank_inherit_helper(@child, :beta, 2, mc, MyCustom.new)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "property on 'GrandChild' is blank" do
|
101
|
+
it "can inherit two levels up to 'Parent'" do
|
102
|
+
blank_inherit_helper(GrandChild, :beta, 2,
|
103
|
+
MyCustom.new(:rank, 4, :color, "red"),
|
104
|
+
MyCustom.new)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "property on 'Child' is nonblank" do
|
110
|
+
before do
|
111
|
+
Child.beta[:colors] = [:red, :yellow, :blue]
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "property on instance of 'Child' is blank" do
|
115
|
+
before do
|
116
|
+
@child = Child.new
|
117
|
+
end
|
118
|
+
|
119
|
+
it "can inherit one level up to 'Child'" do
|
120
|
+
mc = MyCustom.new; mc[:colors] = [:red, :yellow, :blue]
|
121
|
+
blank_inherit_helper(@child, :beta, 1, mc, MyCustom.new)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "property on 'GrandChild' is blank" do
|
126
|
+
it "can inherit one level up to 'Child'" do
|
127
|
+
mc = MyCustom.new; mc[:colors] = [:red, :yellow, :blue]
|
128
|
+
blank_inherit_helper(GrandChild, :beta, 1, mc, MyCustom.new)
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "property on instance of 'GrandChild' is blank" do
|
132
|
+
before do
|
133
|
+
@grandchild = GrandChild.new
|
134
|
+
end
|
135
|
+
|
136
|
+
it "can inherit two levels up to 'Child'" do
|
137
|
+
mc = MyCustom.new; mc[:colors] = [:red, :yellow, :blue]
|
138
|
+
blank_inherit_helper(@grandchild, :beta, 2, mc, MyCustom.new)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/spec/helper_spec.rb
CHANGED
@@ -2,6 +2,38 @@ $:.unshift File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'cascading_classes'
|
3
3
|
require 'minitest/autorun'
|
4
4
|
|
5
|
-
class << MiniTest::Spec
|
6
|
-
alias_method :all, :it
|
5
|
+
## class << MiniTest::Spec
|
6
|
+
## alias_method :all, :it
|
7
|
+
## end
|
8
|
+
|
9
|
+
def blank_inherit_helper(obj, property, expected_depth, expected_val, empty)
|
10
|
+
obj.send("#{property}_is_blank?").must_equal true
|
11
|
+
|
12
|
+
dont_inherit = (expected_depth < 0) ? [0] : (0...expected_depth).to_a
|
13
|
+
dont_inherit << false << nil
|
14
|
+
dont_inherit.each do |i|
|
15
|
+
obj.send(property, i).must_equal empty
|
16
|
+
obj.send(property, :inherit => i).must_equal empty
|
17
|
+
obj.send(property, :inherit, i).must_equal empty
|
18
|
+
end
|
19
|
+
|
20
|
+
do_inherit = (expected_depth < 0) ? [] : (expected_depth..(expected_depth+1)).to_a
|
21
|
+
do_inherit << true << -1
|
22
|
+
do_inherit.each do |i|
|
23
|
+
obj.send(property, i).must_equal(expected_val)
|
24
|
+
obj.send(property, :inherit => i).must_equal(expected_val)
|
25
|
+
obj.send(property, :inherit, i).must_equal(expected_val)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def nonblank_helper(obj, property, expected)
|
30
|
+
obj.send("#{property}_is_blank?").must_equal false
|
31
|
+
|
32
|
+
vals = [0, 1, 2, 3, -1, false, true, nil]
|
33
|
+
vals.each do |i|
|
34
|
+
obj.send(property, i).must_equal expected
|
35
|
+
obj.send(property, :inherit => i).must_equal expected
|
36
|
+
obj.send(property, :inherit, i).must_equal expected
|
37
|
+
end
|
7
38
|
end
|
39
|
+
|
@@ -0,0 +1,239 @@
|
|
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
|
+
# defaults: hash of defaults
|
10
|
+
def getters(obj, defaults)
|
11
|
+
defaults.each do |key, val|
|
12
|
+
obj.send(key).must_equal val
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def container_getters(obj, depth)
|
17
|
+
obj.urls(false).must_equal Array.new
|
18
|
+
obj.urls(nil).must_equal Array.new
|
19
|
+
0.upto(depth-1).each do |i|
|
20
|
+
obj.urls(i).must_equal Array.new
|
21
|
+
end
|
22
|
+
depth.upto(depth+1).each do |i|
|
23
|
+
obj.urls(i).must_equal ["www.google.com"]
|
24
|
+
end
|
25
|
+
obj.urls(-1).must_equal ["www.google.com"]
|
26
|
+
obj.urls(true).must_equal ["www.google.com"]
|
27
|
+
|
28
|
+
obj.name(false).must_equal Hash.new
|
29
|
+
obj.name(nil).must_equal Hash.new
|
30
|
+
0.upto(depth-1).each do |i|
|
31
|
+
obj.name(i).must_equal Hash.new
|
32
|
+
end
|
33
|
+
depth.upto(depth+1).each do |i|
|
34
|
+
obj.name(i).must_equal({:first => 'jon', :last => 'smith'})
|
35
|
+
end
|
36
|
+
obj.name(true).must_equal({:first => 'jon', :last => 'smith'})
|
37
|
+
obj.name(-1).must_equal({:first => 'jon', :last => 'smith'})
|
38
|
+
end
|
39
|
+
|
40
|
+
def setters(obj)
|
41
|
+
obj.color = "blue"
|
42
|
+
obj.color.must_equal "blue"
|
43
|
+
|
44
|
+
obj.setting = :rural
|
45
|
+
obj.setting.must_equal :rural
|
46
|
+
|
47
|
+
obj.score = 28.1
|
48
|
+
obj.score.must_equal 28.1
|
49
|
+
|
50
|
+
obj.num = 15
|
51
|
+
obj.num.must_equal 15
|
52
|
+
|
53
|
+
obj.available = false
|
54
|
+
obj.available.must_equal false
|
55
|
+
|
56
|
+
obj.urls << "www.yahoo.com" << "www.bing.com"
|
57
|
+
obj.urls.must_equal ["www.yahoo.com", "www.bing.com"]
|
58
|
+
|
59
|
+
obj.urls = ["www.nytimes.com"]
|
60
|
+
obj.urls.must_equal ["www.nytimes.com"]
|
61
|
+
|
62
|
+
obj.verify.must_equal false # doesn't inherit name
|
63
|
+
obj.verify2.must_equal true # inherits name
|
64
|
+
|
65
|
+
obj.name[:first] = 'thomas'
|
66
|
+
obj.name[:last] = 'smith'
|
67
|
+
obj.name.must_equal({:first => 'thomas', :last => 'smith'})
|
68
|
+
|
69
|
+
obj.verify.must_equal true
|
70
|
+
obj.verify2.must_equal true
|
71
|
+
|
72
|
+
obj.verify = Proc.new{|me| me.name[:first] !~ /mas/}
|
73
|
+
obj.verify2 = Proc.new{|me| me.name(true)[:first] !~ /mas/}
|
74
|
+
|
75
|
+
obj.verify.must_equal false
|
76
|
+
obj.verify2.must_equal false
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "instances: via 'extend'" do
|
80
|
+
before do
|
81
|
+
A = Class.new{extend CC}
|
82
|
+
B = Class.new(A)
|
83
|
+
C = Class.new(B)
|
84
|
+
end
|
85
|
+
|
86
|
+
after do
|
87
|
+
Object.send :remove_const, :A
|
88
|
+
Object.send :remove_const, :B
|
89
|
+
Object.send :remove_const, :C
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "by default" do
|
93
|
+
before do
|
94
|
+
@props = A.cascade do
|
95
|
+
color :default => "red"
|
96
|
+
setting :default => :urban
|
97
|
+
score :default => 34.1
|
98
|
+
num :default => 3
|
99
|
+
available :default => true
|
100
|
+
urls :default => ["www.google.com"]
|
101
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
102
|
+
verify :default => Proc.new{|me| me.name[:last] == 'smith'}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "does not create properties for instances" do
|
107
|
+
a = A.new
|
108
|
+
[:color, :setting, :score, :num, :available,
|
109
|
+
:urls, :name, :verify].each do |t|
|
110
|
+
@props[t][:instances_too].must_equal false
|
111
|
+
a.respond_to?(:t).must_equal false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "extend: override default" do
|
117
|
+
before do
|
118
|
+
@props = A.cascade do
|
119
|
+
color :default => "red", :instances => true
|
120
|
+
setting :default => :urban, :instances => true
|
121
|
+
score :default => 34.1, :instances => true
|
122
|
+
num :default => 3, :instances => true
|
123
|
+
available :default => true, :instance => true
|
124
|
+
urls :default => ["www.google.com"], :instance => true
|
125
|
+
name :default => {:first => 'jon', :last => 'smith'}, :instance => true
|
126
|
+
verify :default => Proc.new{|me| me.name[:last] == 'smith'}, :instance => true
|
127
|
+
verify2 :default => Proc.new{|me| me.name(true)[:last] == 'smith'}, :instance => true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "adds properties to instances" do
|
132
|
+
a = A.new
|
133
|
+
[:color, :setting, :score, :num, :available,
|
134
|
+
:urls, :name, :verify, :verify2].each do |t|
|
135
|
+
@props[t][:instances_too].must_equal true
|
136
|
+
a.respond_to?(t).must_equal true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "inherits all non-container properties" do
|
141
|
+
[:color, :setting, :score, :num, :available, :verify, :verify2].each do |t|
|
142
|
+
@props[t][:inherit].must_equal true
|
143
|
+
end
|
144
|
+
@props[:urls][:inherit].must_equal false
|
145
|
+
@props[:name][:inherit].must_equal false
|
146
|
+
|
147
|
+
defaults = A.to_hash # assumes 'to_hash' works; ie: likely testing it too
|
148
|
+
[:urls, :name, :verify, :verify2].each{|j| defaults.delete j}
|
149
|
+
|
150
|
+
getters(A.new, defaults)
|
151
|
+
getters(B.new, defaults)
|
152
|
+
getters(C.new, defaults)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "inherits container properties only when explicitly told to do so" do
|
156
|
+
container_getters(A.new, 1)
|
157
|
+
container_getters(B.new, 2)
|
158
|
+
container_getters(C.new, 3)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "has instance setters" do
|
162
|
+
setters(A.new)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "has subclass instance setters" do
|
166
|
+
setters(B.new)
|
167
|
+
setters(C.new)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "instances: via 'include'" do
|
173
|
+
before do
|
174
|
+
A = Class.new{include CC}
|
175
|
+
B = Class.new(A)
|
176
|
+
C = Class.new(B)
|
177
|
+
end
|
178
|
+
|
179
|
+
after do
|
180
|
+
Object.send :remove_const, :A
|
181
|
+
Object.send :remove_const, :B
|
182
|
+
Object.send :remove_const, :C
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "defaults" do
|
186
|
+
before do
|
187
|
+
@props = A.cascade do
|
188
|
+
color :default => "red"
|
189
|
+
setting :default => :urban
|
190
|
+
score :default => 34.1
|
191
|
+
num :default => 3
|
192
|
+
available :default => true
|
193
|
+
urls :default => ["www.google.com"]
|
194
|
+
name :default => {:first => 'jon', :last => 'smith'}
|
195
|
+
verify :default => Proc.new{|me| me.name[:last] == 'smith'}
|
196
|
+
verify2 :default => Proc.new{|me| me.name(true)[:last] == 'smith'}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "adds properties to instances" do
|
201
|
+
a = A.new
|
202
|
+
[:color, :setting, :score, :num, :available,
|
203
|
+
:urls, :name, :verify, :verify2].each do |t|
|
204
|
+
@props[t][:instances_too].must_equal true
|
205
|
+
a.respond_to?(t).must_equal true
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "inherits all non-container properties" do
|
210
|
+
[:color, :setting, :score, :num, :available, :verify, :verify2].each do |t|
|
211
|
+
@props[t][:inherit].must_equal true
|
212
|
+
end
|
213
|
+
@props[:urls][:inherit].must_equal false
|
214
|
+
@props[:name][:inherit].must_equal false
|
215
|
+
|
216
|
+
defaults = A.to_hash # assumes 'to_hash' works; ie: likely testing it too
|
217
|
+
[:urls, :name, :verify, :verify2].each{|j| defaults.delete j}
|
218
|
+
|
219
|
+
getters(A.new, defaults)
|
220
|
+
getters(B.new, defaults)
|
221
|
+
getters(C.new, defaults)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "inherits container properties only when explicitly told to do so" do
|
225
|
+
container_getters(A.new, 1)
|
226
|
+
container_getters(B.new, 2)
|
227
|
+
container_getters(C.new, 3)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "has instance setters" do
|
231
|
+
setters(A.new)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "has subclass instance setters" do
|
235
|
+
setters(B.new)
|
236
|
+
setters(C.new)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|