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