cascading_classes 0.1.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 ADDED
@@ -0,0 +1,190 @@
1
+ # CascadingClasses
2
+
3
+ This library allows you to easily create properties that cascade down a class tree. For example:
4
+
5
+ require 'cascading_classes'
6
+
7
+ class Parent
8
+ extend CC
9
+ end
10
+
11
+ class Child < Parent; end
12
+
13
+ class GrandChild < Child; end
14
+
15
+ now create two properties that will cascade down all descendents
16
+
17
+ Parent.cascade :eye_color, :hair_color
18
+
19
+ set the properties on ```Parent```
20
+
21
+ Parent.eye_color = "brown"
22
+ Parent.hair_color = "black"
23
+
24
+ all child classes are affected
25
+
26
+ p Child.eye_color # => "brown"
27
+ p GrandChild.eye_color # => "brown"
28
+
29
+ p Child.hair_color # => "black"
30
+ p GrandChild.hair_color # => "black"
31
+
32
+ now change the ```eye_color``` and ```hair_color``` properties on ```Child```
33
+
34
+ Child.eye_color = "blue"
35
+ Child.hair_color = "blond"
36
+
37
+ only descendents of ```Child``` are affected
38
+
39
+ p Parent.eye_color # => "brown"
40
+ p Child.eye_color # => "blue"
41
+ p GrandChild.eye_color # => "blue"
42
+
43
+
44
+ Getting Started
45
+ ---------------
46
+
47
+ Assume the class structure from above:
48
+
49
+ class Parent
50
+ extend CC
51
+ end
52
+
53
+ class Child < Parent; end
54
+
55
+ class GrandChild < Child; end
56
+
57
+ ## Setting default values
58
+
59
+ Setting default values for cascaded properties are easy. The first method is to bracket them in Array pairs. For example:
60
+
61
+ Parent.cascade [:eye_color, "hazel"], [:hair_color, "brown"], :has_education
62
+
63
+ sets the ```eye_color``` and ```hair_color``` properties on ```Parent``` to "hazel" and "brown" and initializes, but does not set, the ```has_education``` property.
64
+
65
+
66
+ ### Alternative syntax: all!
67
+
68
+ You also create cascaded properties via ```Parent.all!```
69
+
70
+ For example, the following are equivalent:
71
+
72
+ Parent.cascade :arms, :legs, :head
73
+ Parent.all! :arms, :legs, :head
74
+
75
+ this initiates, but does not set, two cascading properties: ```arms```, ```legs```
76
+
77
+ p Parent.arms # => nil
78
+ p Child.arms # => nil
79
+ p GrandChild.arms # => nil
80
+
81
+ since no default was given, all are nil
82
+
83
+ Again, to set a default use brackets:
84
+
85
+ Parent.all! :arms, [:legs, 2], [:head, :none]
86
+
87
+
88
+ ### Alternative syntax: block arguments
89
+
90
+ You can also create cascading properties with a block. The major difference is that you must not prefix your property with a colon.
91
+
92
+ For example
93
+
94
+ Parent.cascade [:eye_color, "blue"], [:hair_color, "blond"]
95
+
96
+ is equivalent to
97
+
98
+ Parent.cascade do
99
+ eye_color :default => "blue"
100
+ hair_color :default => "blond"
101
+ end
102
+
103
+ or
104
+
105
+ Parent.all! do
106
+ eye_color :initial => "blue"
107
+ hair_color :initial => "blond"
108
+ end
109
+
110
+ #### More block examples
111
+
112
+ The folowing are all equivalent
113
+ ==============================
114
+
115
+ Parent.cascade :address, :phone, [:email, "jon@example.com"]
116
+
117
+ Parent.cascade :address, :phone do
118
+ email :initial => "jon@example.com"
119
+ end
120
+
121
+ Parent.all! do
122
+ address
123
+ phone
124
+ email :start => "jon@example.com"
125
+ end
126
+
127
+ Parent.all!{ address; phone; email :default => "jon@example.com" }
128
+
129
+ You can also set a property in a block using ```set_property```
130
+
131
+ Parent.cascade do
132
+ set_property :address
133
+ set_property :phone
134
+ set_property :email, :default => "jon@example.com"
135
+ end
136
+
137
+
138
+ ## Quick Summary
139
+
140
+ * require the gem: ```require cascading_classes```
141
+ * extend or include the module
142
+ class Foo
143
+ extend CascadingClasses
144
+ end
145
+ * CC is equivalent to CascadingClasses
146
+ * create some cascading properties by listing them
147
+ A.cascade :desk, :chair, :lamp
148
+ * or by using a block
149
+ A.cascade{ desk; chair; lamp }
150
+ * get and set the property on any descendent
151
+ class B < A; end
152
+ B.desk = "a large, wooden structure in need of a fix"
153
+ class C < A; end
154
+ C.lamp = "old and rusty"
155
+
156
+ ### What's the difference between ```extend CC``` and ```include CC```?
157
+
158
+ First, in either case you are dealing with class instance variables and class methods. ```A.cascade :eye_color; A.eye_color``` sets ```@eye_color``` on the singleton class of A.
159
+
160
+ But if you also want the properties to cascade down to instance variables you have three options. The first way is to ```include CC```, which will create cascading instance properties by default.
161
+
162
+ For example:
163
+
164
+ class Parent
165
+ include CC
166
+ end
167
+
168
+ Parent.cascade [:eye_color, "blue"]
169
+ p = Parent.new
170
+ p p.eye_color # => "blue"
171
+
172
+ class Child < Parent; end
173
+
174
+ c = Child.new
175
+ p c.eye_color # => "blue"
176
+
177
+ The other two ways to make properties cascade down to instances is via:
178
+
179
+ Parent.cascade [:hair_color, "blue", true]
180
+
181
+ or
182
+
183
+ Parent.cascade do
184
+ hair_color :default => "blue", :instances => true
185
+ end
186
+
187
+ In either case, even if you ```extend CC```, all instance variables of all descendents will have the ```hair_color``` property.
188
+
189
+
190
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "spec/*_spec.rb"
6
+ end
7
+
@@ -0,0 +1,163 @@
1
+ module CascadingClasses
2
+
3
+ # based off
4
+ # http://blog.oleganza.com/post/115377756/correct-blankslate-in-ruby
5
+ class BlankSlate
6
+ class << self; alias __undef_method undef_method; end
7
+ keep = [:instance_eval, :object_id]
8
+ ancestors.inject([]){|res, a| res + (a.instance_methods - keep)}.uniq.
9
+ each{|m| (__undef_method(m) rescue nil) unless m =~ /^__/ }
10
+ end
11
+
12
+ class Helper < CascadingClasses::BlankSlate
13
+
14
+ def method_missing(meth, *args, &block)
15
+ case meth
16
+ when /inspect/
17
+ super
18
+ when /set_property/
19
+ __property__(args[0], *args[1..-1], &block)
20
+ else
21
+ __property__(meth, *args, &block)
22
+ end
23
+ end
24
+
25
+ def __property__(name, args={}, &block)
26
+ @names << name unless @names.include? name
27
+
28
+ @defaults[name] = if args.include? :default
29
+ args[:default]
30
+ elsif args.include? :initial
31
+ args[:initial]
32
+ elsif args.include? :start
33
+ args[:start]
34
+ elsif args.include? :begin
35
+ args[:begin]
36
+ else
37
+ nil
38
+ end
39
+
40
+ @instances_too[name] = if args.include? :classes_only
41
+ !args[:classes_only]
42
+ elsif args.include? :only_classes
43
+ !args[:only_classes]
44
+ elsif args.include? :classes_only
45
+ !args[:classes_only]
46
+ elsif args.include? :exclude_instances
47
+ !args[:exclude_instances]
48
+ elsif args.include? :skip_instances
49
+ !args[:skip_instances]
50
+ elsif args.include? :not_instances
51
+ !args[:not_instances]
52
+ elsif args.include? :apply_to_instances
53
+ args[:apply_to_instances]
54
+ elsif args.include? :both
55
+ args[:both]
56
+ elsif args.include? :instances_too
57
+ args[:instances_too]
58
+ elsif args.include? :instances
59
+ args[:instances]
60
+ elsif args.include? :instance
61
+ args[:instance]
62
+ else
63
+ @apply_to_instances
64
+ end
65
+ end
66
+
67
+ def initialize(apply_to_instances, &block)
68
+ @apply_to_instances = apply_to_instances
69
+ @names = []
70
+ @defaults = {}
71
+ @instances_too = {} # values are true/false
72
+
73
+ return unless block_given?
74
+ if block.arity == 0
75
+ instance_eval &block
76
+ else
77
+ raise "ArgumentError: wrong number of args for block: #{block.arity} for 1"
78
+ end
79
+ end
80
+
81
+ end # class Helper
82
+
83
+ # returns an array
84
+ # apply_to_instances: whether properties should be set on instances of the classes too
85
+ def self.cascading_props(apply_to_instances, *properties, &block)
86
+ props = {} # values are defaults
87
+ instances_too = {} # values are true/false
88
+ properties.each do |prop|
89
+ if Array === prop and prop.size == 3
90
+ props[prop[0].to_sym] = prop[1]
91
+ instances_too[prop[0].to_sym] = !!prop[2]
92
+ elsif Array === prop and prop.size == 2
93
+ props[prop[0].to_sym] = prop[1]
94
+ else
95
+ props[prop.to_sym] = nil
96
+ end
97
+ end
98
+
99
+ props.each{|prop, default| instances_too[prop] =
100
+ apply_to_instances unless instances_too.include? prop}
101
+
102
+ if block_given?
103
+ a = Helper.new(apply_to_instances, &block)
104
+ names, defaults, inst =
105
+ a.instance_eval{ [@names, @defaults, @instances_too] }
106
+ names.each do |prop|
107
+ props[prop] = defaults[prop]
108
+ instances_too[prop] = inst[prop]
109
+ end
110
+ end
111
+
112
+ props.map do |prop, default|
113
+ mod = Module.new do
114
+
115
+ define_method :extended do |klass|
116
+ klass.class_eval{ instance_variable_set "@#{prop}", default }
117
+ end
118
+ module_function :extended
119
+
120
+ define_method :included do |klass|
121
+ klass.class_eval{ instance_variable_set "@#{prop}", default }
122
+ end
123
+ module_function :included
124
+
125
+ # getter has dual role as setter:
126
+ # m.prop val <=> m.prop = val
127
+ define_method prop do |*args|
128
+ if args.empty? # getter
129
+ val = instance_variable_get "@#{prop}"
130
+ if val.nil?
131
+ # reach up into the class first, then into superclass
132
+ if self.class == Class
133
+ try_super = superclass.instance_eval{ respond_to? "#{prop}" }
134
+ return superclass.send("#{prop}") if try_super
135
+ else
136
+ try_klass_methd = self.class.send :respond_to?, prop
137
+ return self.class.send(prop) if try_klass_methd
138
+ end
139
+ end
140
+ val
141
+ else # setter
142
+ if args.size == 1 and Array === args[0]
143
+ send("#{prop}=", args[0])
144
+ else
145
+ send("#{prop}=", *args)
146
+ end
147
+ end
148
+ end
149
+
150
+ # todo: setter validations ??
151
+ define_method "#{prop}=" do |*vals|
152
+ if vals.size == 1 and Array === vals[0]
153
+ vals = vals[0]
154
+ end
155
+ instance_variable_set "@#{prop}", vals[0]
156
+ end
157
+
158
+ end # new module
159
+ [prop, props[prop], instances_too[prop], mod]
160
+ end
161
+ end
162
+
163
+ end # module CascadingClasses
@@ -0,0 +1,75 @@
1
+ require 'cascading_classes/cascading_classes'
2
+
3
+ CC = CascadingClasses unless defined? CC
4
+
5
+ # trivialities:
6
+ # module not meant to be both included and extended
7
+
8
+
9
+ # class Foo
10
+ # include CC
11
+ # cascade :one, :two, :three, ...
12
+ # end
13
+ # Foo.one = 12
14
+ # Foo.new.one
15
+ # Bar = Class.new(Foo).one
16
+
17
+ module CascadingClasses
18
+
19
+ # VERSION: "1.0.0"
20
+ # note, VERSION will be undetected unless called directly
21
+ def self.const_missing(name)
22
+ case name
23
+ when :VERSION then "1.0.0"
24
+ else raise NameError
25
+ end
26
+ end
27
+
28
+ def respond_to?(const)
29
+ (const.to_s == 'VERSION') ? true : super
30
+ end
31
+ module_function :respond_to?
32
+
33
+ def self.included(klass)
34
+ has_all = klass.instance_methods.include? :all!
35
+ klass.instance_eval do
36
+ def cascade(*props, &block)
37
+ res = CascadingClasses.cascading_props(true, *props, &block)
38
+ res.map! do |prop, default, apply_to_instances, mod|
39
+ send(:extend, mod)
40
+ send(:include, mod) if apply_to_instances
41
+ [prop, [default, apply_to_instances]]
42
+ end
43
+ {self.name => Hash[res]}
44
+ end
45
+
46
+ class << klass
47
+ unless instance_methods.include? :all!
48
+ alias_method :all!, :cascade
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def self.extended(klass)
55
+ has_all = klass.instance_methods.include? :all
56
+ klass.instance_eval do
57
+ def cascade(*props, &block)
58
+ res = CascadingClasses.cascading_props(false, *props, &block)
59
+ res.map! do |prop, default, apply_to_instances, mod|
60
+ send(:extend, mod)
61
+ send(:include, mod) if apply_to_instances
62
+ [prop, [default, apply_to_instances]]
63
+ end
64
+ {self.name => Hash[res]}
65
+ end
66
+
67
+ class << klass
68
+ unless instance_methods.include? :all!
69
+ alias_method :all!, :cascade
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ end # module CascadingClases
@@ -0,0 +1,146 @@
1
+ begin
2
+ require_relative 'helper_spec'
3
+ rescue NameError
4
+ require File.expand_path('helper_spec', __FILE__)
5
+ end
6
+
7
+ describe "when a class extends CascadingClasses and an alternative syntax is used" do
8
+
9
+ # this is the same as:
10
+ # class Parent
11
+ # extend CascadingClasses
12
+ # end
13
+ #
14
+ # class Child < Parent; end
15
+ #
16
+ # class GrandChild < Child; end
17
+ before do
18
+ Parent = Class.new{extend CC}
19
+ Child = Class.new(Parent)
20
+ GrandChild = Class.new(Child)
21
+ end
22
+
23
+ describe "when an alternative syntax is given (0)" do
24
+ before do
25
+ Parent.cascade [:hair_color, "black"], :eye_color, [:piercings, false]
26
+
27
+ Child.hair_color = "blond"
28
+ Child.eye_color = "brown"
29
+ end
30
+
31
+ it "should give the same result" do
32
+ Parent.hair_color.must_equal "black"
33
+ Child.hair_color.must_equal "blond"
34
+ GrandChild.hair_color.must_equal "blond"
35
+
36
+ Parent.eye_color.must_equal nil
37
+ Child.eye_color.must_equal "brown"
38
+ GrandChild.eye_color.must_equal "brown"
39
+
40
+ Parent.piercings.must_equal false
41
+ Child.piercings.must_equal false
42
+ GrandChild.piercings.must_equal false
43
+ end
44
+ end
45
+
46
+ describe "when an alternate syntax is given (1)" do
47
+ before do
48
+ Parent.all! [:hair_color, "black"], :eye_color, [:piercings, false]
49
+
50
+ Child.hair_color = "blond"
51
+ Child.eye_color = "brown"
52
+ end
53
+
54
+ it "should give the same result" do
55
+ Parent.hair_color.must_equal "black"
56
+ Child.hair_color.must_equal "blond"
57
+ GrandChild.hair_color.must_equal "blond"
58
+
59
+ Parent.eye_color.must_equal nil
60
+ Child.eye_color.must_equal "brown"
61
+ GrandChild.eye_color.must_equal "brown"
62
+
63
+ Parent.piercings.must_equal false
64
+ Child.piercings.must_equal false
65
+ GrandChild.piercings.must_equal false
66
+ end
67
+ end
68
+
69
+ describe "when an alternative syntax is given (2)" do
70
+ before do
71
+ Parent.cascade :eye_color do
72
+ hair_color :begin => "black"
73
+ piercings :initial => false
74
+ end
75
+
76
+ Child.hair_color = "blond"
77
+ Child.eye_color = "brown"
78
+ end
79
+
80
+ it "should give the same result" do
81
+ Parent.hair_color.must_equal "black"
82
+ Child.hair_color.must_equal "blond"
83
+ GrandChild.hair_color.must_equal "blond"
84
+
85
+ Parent.eye_color.must_equal nil
86
+ Child.eye_color.must_equal "brown"
87
+ GrandChild.eye_color.must_equal "brown"
88
+
89
+ Parent.piercings.must_equal false
90
+ Child.piercings.must_equal false
91
+ GrandChild.piercings.must_equal false
92
+ end
93
+ end
94
+
95
+ describe "when an alternative syntax is given (3)" do
96
+ before do
97
+ Parent.all! :eye_color do
98
+ hair_color :start => "black"
99
+ piercings :default => false
100
+ end
101
+
102
+ Child.hair_color = "blond"
103
+ Child.eye_color = "brown"
104
+ end
105
+
106
+ it "should give the same result" do
107
+ Parent.hair_color.must_equal "black"
108
+ Child.hair_color.must_equal "blond"
109
+ GrandChild.hair_color.must_equal "blond"
110
+
111
+ Parent.eye_color.must_equal nil
112
+ Child.eye_color.must_equal "brown"
113
+ GrandChild.eye_color.must_equal "brown"
114
+
115
+ Parent.piercings.must_equal false
116
+ Child.piercings.must_equal false
117
+ GrandChild.piercings.must_equal false
118
+ end
119
+ end
120
+
121
+ describe "when an alternative syntax is given (3)" do
122
+ before do
123
+ Parent.all! :eye_color do
124
+ set_property :hair_color, :start => "black"
125
+ set_property :piercings, :initial => false
126
+ end
127
+
128
+ Child.hair_color = "blond"
129
+ Child.eye_color = "brown"
130
+ end
131
+
132
+ it "should give the same result" do
133
+ Parent.hair_color.must_equal "black"
134
+ Child.hair_color.must_equal "blond"
135
+ GrandChild.hair_color.must_equal "blond"
136
+
137
+ Parent.eye_color.must_equal nil
138
+ Child.eye_color.must_equal "brown"
139
+ GrandChild.eye_color.must_equal "brown"
140
+
141
+ Parent.piercings.must_equal false
142
+ Child.piercings.must_equal false
143
+ GrandChild.piercings.must_equal false
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,133 @@
1
+ begin
2
+ require_relative 'helper_spec'
3
+ rescue NameError
4
+ require File.expand_path('helper_spec', __FILE__)
5
+ end
6
+
7
+ # require 'minitest/autorun'
8
+ # require 'cascading_classes'
9
+
10
+ # class << MiniTest::Spec
11
+ # alias_method :all, :it
12
+ # end
13
+
14
+ describe "when a class extends CascadingClasses" do
15
+
16
+ # this is the same as:
17
+ # class Parent
18
+ # extend CascadingClasses
19
+ # end
20
+ #
21
+ # class Child < Parent; end
22
+ #
23
+ # class GrandChild < Child; end
24
+ before do
25
+ Parent = Class.new{extend CC}
26
+ Child = Class.new(Parent)
27
+ GrandChild = Class.new(Child)
28
+ end
29
+
30
+ describe "when a property is set from above" do
31
+ before do
32
+ Parent.cascade(:hair_color)
33
+
34
+ Parent.hair_color = :blond
35
+ end
36
+
37
+ it "should cascade down to its descendents" do
38
+ Parent.hair_color.must_equal :blond
39
+ Child.hair_color.must_equal :blond
40
+ GrandChild.hair_color.must_equal :blond
41
+ end
42
+ end
43
+
44
+ describe "when a property is set from below" do
45
+ before do
46
+ Parent.cascade do
47
+ arms :default => 2
48
+ legs :default => 2
49
+ end
50
+
51
+ Child.arms = 3
52
+ GrandChild.legs = 1
53
+ end
54
+
55
+ all "descendents are influenced and no acestors are affected" do
56
+ Parent.arms.must_equal 2
57
+ Child.arms.must_equal 3
58
+ GrandChild.arms.must_equal 3
59
+
60
+ Parent.legs.must_equal 2
61
+ Child.legs.must_equal 2
62
+ GrandChild.legs.must_equal 1
63
+ end
64
+ end
65
+
66
+ describe "when a property has a default and has not been set by any descendent" do
67
+ before do
68
+ Parent.cascade [:eye_color, "red"]
69
+ end
70
+
71
+ all "descendents should reflect that default" do
72
+ Parent.eye_color.must_equal "red"
73
+ Child.eye_color.must_equal "red"
74
+ GrandChild.eye_color.must_equal "red"
75
+ end
76
+ end
77
+
78
+ describe "when a property has no default and has not been set by any ancestor" do
79
+ before do
80
+ Parent.cascade :has_house
81
+ end
82
+
83
+ all "descendents should reflect the same" do
84
+ Parent.has_house.must_equal nil
85
+ Child.has_house.must_equal nil
86
+ GrandChild.has_house.must_equal nil
87
+ end
88
+ end
89
+
90
+ describe "when a child sets a property when its parent has set it too" do
91
+ before do
92
+ Parent.all! :trigger
93
+
94
+ Parent.trigger = "silent"
95
+ Child.trigger = "loud"
96
+ end
97
+
98
+ all "cascading is scoped off the youngest child with a set property" do
99
+ Parent.trigger.must_equal "silent"
100
+ Child.trigger.must_equal "loud"
101
+ GrandChild.trigger.must_equal "loud"
102
+ end
103
+ end
104
+
105
+ describe "when a parent has a property with a default set" do
106
+ before do
107
+ ## Parent.cascade{ :time, :default => 32}
108
+ Parent.cascade [:time, 32]
109
+ end
110
+
111
+ it "won't matter if the property is first accessed by the child" do
112
+ Child.time
113
+
114
+ Child.time.must_equal 32
115
+ Parent.time.must_equal 32
116
+ end
117
+ end
118
+
119
+ describe "when a parent has a property with a default set" do
120
+ before do
121
+ Parent.cascade [:time, 32]
122
+ end
123
+
124
+ it "won't matter if the property is first accessed by the parent" do
125
+ Parent.time
126
+
127
+ Child.time.must_equal 32
128
+ Parent.time.must_equal 32
129
+ end
130
+ end
131
+
132
+
133
+ end
@@ -0,0 +1,85 @@
1
+ begin
2
+ require_relative 'helper_spec'
3
+ rescue NameError
4
+ require File.expand_path('helper_spec', __FILE__)
5
+ end
6
+
7
+ describe "when CascadingClasses is included by a class" do
8
+
9
+
10
+ # this is the same as:
11
+ # class A
12
+ # include CascadingClasses
13
+ # end
14
+ #
15
+ # class B < A; end
16
+ #
17
+ # class C < B; end
18
+ #
19
+ # a = A.new
20
+ # b = B.new
21
+ # c = C.new
22
+ before do
23
+ A = Class.new{include CC}
24
+ B = Class.new(A)
25
+ C = Class.new(B)
26
+ @a, @b, @c = A.new, B.new, C.new
27
+ end
28
+
29
+ describe "when CascadingClasses is included in a module" do
30
+ before do
31
+ end
32
+
33
+ it "must pass all 'extended' test" do
34
+ # how do you run those tests??
35
+ file = File.join(File.expand_path(File.dirname(__FILE__)), 'extended_spec.rb')
36
+ result = `ruby #{file}`
37
+ result.must_match /(0) failures/
38
+ result.must_match /(0) errors/
39
+ end
40
+ end
41
+
42
+ describe "when a property is set on a class" do
43
+ before do
44
+ A.all! :once, :twice # same as A.cascade :once, :twice
45
+
46
+ A.once = :thinking
47
+ B.twice = :speaking
48
+ end
49
+
50
+ all "cascading flows from that class downwards to all child classes and child objects" do
51
+ # :once
52
+ A.once.must_equal :thinking
53
+ @a.once.must_equal :thinking
54
+ B.once.must_equal :thinking
55
+ @b.once.must_equal :thinking
56
+ C.once.must_equal :thinking
57
+ @c.once.must_equal :thinking
58
+
59
+ # :twice
60
+ A.twice.must_equal nil
61
+ @a.twice.must_equal nil
62
+ B.twice.must_equal :speaking
63
+ @b.twice.must_equal :speaking
64
+ C.twice.must_equal :speaking
65
+ @c.twice.must_equal :speaking
66
+ end
67
+ end
68
+
69
+ describe "when a property is set on an object" do
70
+ before do
71
+ A.all! :table
72
+
73
+ @a.table = "a large, rectangular object with spotty marks"
74
+ end
75
+
76
+ it "only affects that object" do
77
+ A.table.must_equal nil
78
+ @a.table.must_equal "a large, rectangular object with spotty marks"
79
+ B.table.must_equal nil
80
+ @b.table.must_equal nil
81
+ C.table.must_equal nil
82
+ @c.table.must_equal nil
83
+ end
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cascading_classes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Gables
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-03 00:00:00.000000000Z
13
+ dependencies: []
14
+ description:
15
+ email: jeremy.gables@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - Rakefile
22
+ - lib/cascading_classes.rb
23
+ - lib/cascading_classes/cascading_classes.rb
24
+ - spec/extended_spec.rb
25
+ - spec/included_spec.rb
26
+ - spec/alternative_syntax_spec.rb
27
+ homepage: http://github.com/gables/cascading_classes
28
+ licenses: []
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 1.8.5
48
+ signing_key:
49
+ specification_version: 3
50
+ summary: Easily create properties whose values cascade down class trees
51
+ test_files:
52
+ - spec/extended_spec.rb
53
+ - spec/included_spec.rb
54
+ - spec/alternative_syntax_spec.rb