metaruby 1.0.0.rc1

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/History.txt +1 -0
  4. data/Manifest.txt +40 -0
  5. data/README.md +318 -0
  6. data/Rakefile +39 -0
  7. data/lib/metaruby/class.rb +120 -0
  8. data/lib/metaruby/dsls/doc.rb +78 -0
  9. data/lib/metaruby/dsls/find_through_method_missing.rb +76 -0
  10. data/lib/metaruby/dsls.rb +2 -0
  11. data/lib/metaruby/gui/exception_view.rb +124 -0
  12. data/lib/metaruby/gui/html/button.rb +65 -0
  13. data/lib/metaruby/gui/html/collection.rb +103 -0
  14. data/lib/metaruby/gui/html/exception_view.css +8 -0
  15. data/lib/metaruby/gui/html/jquery.min.js +154 -0
  16. data/lib/metaruby/gui/html/jquery.selectfilter.js +65 -0
  17. data/lib/metaruby/gui/html/jquery.tagcloud.min.js +8 -0
  18. data/lib/metaruby/gui/html/jquery.tinysort.min.js +8 -0
  19. data/lib/metaruby/gui/html/list.rhtml +24 -0
  20. data/lib/metaruby/gui/html/page.css +55 -0
  21. data/lib/metaruby/gui/html/page.rb +283 -0
  22. data/lib/metaruby/gui/html/page.rhtml +13 -0
  23. data/lib/metaruby/gui/html/page_body.rhtml +17 -0
  24. data/lib/metaruby/gui/html/rock-website.css +694 -0
  25. data/lib/metaruby/gui/html.rb +16 -0
  26. data/lib/metaruby/gui/model_browser.rb +262 -0
  27. data/lib/metaruby/gui/model_selector.rb +266 -0
  28. data/lib/metaruby/gui/rendering_manager.rb +112 -0
  29. data/lib/metaruby/gui/ruby_constants_item_model.rb +253 -0
  30. data/lib/metaruby/gui.rb +9 -0
  31. data/lib/metaruby/inherited_attribute.rb +482 -0
  32. data/lib/metaruby/module.rb +158 -0
  33. data/lib/metaruby/registration.rb +157 -0
  34. data/lib/metaruby/test.rb +79 -0
  35. data/lib/metaruby.rb +17 -0
  36. data/manifest.xml +12 -0
  37. data/test/suite.rb +15 -0
  38. data/test/test_attributes.rb +323 -0
  39. data/test/test_class.rb +68 -0
  40. data/test/test_dsls.rb +49 -0
  41. data/test/test_module.rb +105 -0
  42. data/test/test_registration.rb +182 -0
  43. metadata +160 -0
@@ -0,0 +1,157 @@
1
+ require 'facets/module/spacename'
2
+ require 'facets/module/basename'
3
+ require 'facets/kernel/call_stack'
4
+ require 'utilrb/object/attribute'
5
+ require 'utilrb/module/attr_predicate'
6
+ module MetaRuby
7
+ # Handling of registration of model hierarchies
8
+ #
9
+ # It depends on the mixed-in object to provide a #supermodel method that
10
+ # returns the model that is parent of +self+
11
+ module Registration
12
+ # The place where this model got defined in the source code
13
+ # The tuple is (file,lineno,method), and can be obtained with
14
+ # facet's #call_stack
15
+ # @return [Array<(String,Integer,Symbol)>]
16
+ attr_accessor :definition_location
17
+
18
+ # Tells {#clear_submodels} whether this model should be removed from
19
+ # the model set or not. The default is false (it should be removed)
20
+ #
21
+ # @return [Boolean]
22
+ attr_predicate :permanent_model?, true
23
+
24
+ # [Set] the set of models that are children of this one
25
+ attribute(:submodels) { Set.new }
26
+
27
+ # Returns the model that is parent of this one
28
+ #
29
+ # The default implementation returns superclass if it is extended by
30
+ # this Registration module, and nil otherwise
31
+ def supermodel
32
+ if superclass.respond_to?(:register_submodel)
33
+ superclass
34
+ end
35
+ end
36
+
37
+ # @return [Boolean] true if the definition context (module, class) in
38
+ # which self is registered is permanent or not w.r.t. the model
39
+ # registration functionality of metaruby
40
+ def permanent_definition_context?
41
+ return false if !name
42
+ definition_context_name = spacename
43
+ if !definition_context_name.empty?
44
+ begin
45
+ enclosing_context = constant("::#{definition_context_name}")
46
+ return !enclosing_context.respond_to?(:permanent_model?) || enclosing_context.permanent_model?
47
+ rescue NameError
48
+ false
49
+ end
50
+ else
51
+ true
52
+ end
53
+ end
54
+
55
+ # @return [Boolean] true if the given object can be accessed by resolving its
56
+ # name as a constant
57
+ def self.accessible_by_name?(object)
58
+ return false if !object.respond_to?(:name) || !object.name
59
+ begin
60
+ constant("::#{object.name}") == object
61
+ rescue NameError
62
+ false
63
+ end
64
+ end
65
+
66
+ # @return [Boolean] true if this object can be accessed by resolving its
67
+ # name as a constant
68
+ def accessible_by_name?
69
+ Registration.accessible_by_name?(self)
70
+ end
71
+
72
+ # Call to register a model that is a submodel of +self+
73
+ def register_submodel(klass)
74
+ if !klass.definition_location
75
+ klass.definition_location = call_stack
76
+ end
77
+
78
+ submodels << klass
79
+ if m = supermodel
80
+ m.register_submodel(klass)
81
+ end
82
+ end
83
+
84
+ # Enumerates all models that are submodels of this class
85
+ def each_submodel
86
+ return enum_for(:each_submodel) if !block_given?
87
+ submodels.each do |obj|
88
+ yield(obj)
89
+ end
90
+ end
91
+
92
+ def clear_model
93
+ if !permanent_model?
94
+ if m = supermodel
95
+ m.deregister_submodels([self])
96
+ end
97
+ if Registration.accessible_by_name?(self)
98
+ Registration.deregister_constant(self)
99
+ end
100
+ end
101
+ clear_submodels
102
+ end
103
+
104
+ # Removes the constant that stores the given object in the Ruby constant
105
+ # hierarchy
106
+ #
107
+ # It assumes that calling #name on the object returns the place in the
108
+ # constant hierarchy where it is stored
109
+ def self.deregister_constant(obj)
110
+ constant("::#{obj.spacename}").send(:remove_const, obj.basename)
111
+ end
112
+
113
+ # Clears all registered submodels
114
+ def clear_submodels
115
+ children = self.submodels.find_all { |m| !m.permanent_model? }
116
+ if !children.empty?
117
+ deregister_submodels(children)
118
+ end
119
+
120
+ children.each do |m|
121
+ # Deregister non-permanent models that are registered in the
122
+ # constant hierarchy
123
+ if Registration.accessible_by_name?(m)
124
+ Registration.deregister_constant(m)
125
+ end
126
+ end
127
+
128
+ # This contains the permanent submodels
129
+ #
130
+ # We can call #clear_submodels while iterating here as it is a
131
+ # constraint that all models in #submodels are permanent (and
132
+ # will therefore not be removed)
133
+ submodels.each { |m| m.clear_submodels }
134
+ # And this the non-permanent ones
135
+ children.each { |m| m.clear_submodels }
136
+ true
137
+ end
138
+
139
+ # Deregisters a set of submodels on this model and all its
140
+ # supermodels
141
+ #
142
+ # This is usually not called directly. Use #clear_submodels instead
143
+ #
144
+ # @param [Set] set the set of submodels to remove
145
+ def deregister_submodels(set)
146
+ current_size = submodels.size
147
+ submodels.subtract(set)
148
+ if m = supermodel
149
+ m.deregister_submodels(set)
150
+ end
151
+ current_size != submodels.size
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+
@@ -0,0 +1,79 @@
1
+ # simplecov must be loaded FIRST. Only the files required after it gets loaded
2
+ # will be profiled !!!
3
+ if ENV['TEST_ENABLE_COVERAGE'] == '1'
4
+ begin
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+ rescue LoadError
8
+ require 'metaruby'
9
+ MetaRuby.warn "coverage is disabled because the 'simplecov' gem cannot be loaded"
10
+ rescue Exception => e
11
+ require 'metaruby'
12
+ MetaRuby.warn "coverage is disabled: #{e.message}"
13
+ end
14
+ end
15
+
16
+ require 'metaruby'
17
+ require 'minitest/autorun'
18
+ require 'minitest/spec'
19
+ require 'flexmock/test_unit'
20
+
21
+ if ENV['TEST_ENABLE_PRY'] != '0'
22
+ begin
23
+ require 'pry'
24
+ if ENV['TEST_DEBUG'] == '1'
25
+ require 'pry-rescue/minitest'
26
+ end
27
+ rescue Exception
28
+ MetaRuby.warn "debugging is disabled because the 'pry' gem cannot be loaded"
29
+ end
30
+ end
31
+
32
+ module MetaRuby
33
+ # This module is the common setup for all tests
34
+ #
35
+ # It should be included in the toplevel describe blocks
36
+ #
37
+ # @example
38
+ # require 'metaruby/test'
39
+ # describe MetaRuby do
40
+ # include MetaRuby::SelfTest
41
+ # end
42
+ #
43
+ module SelfTest
44
+ if defined? FlexMock
45
+ include FlexMock::ArgumentTypes
46
+ include FlexMock::MockContainer
47
+ end
48
+
49
+ def setup
50
+ # Setup code for all the tests
51
+ end
52
+
53
+ def teardown
54
+ if defined? FlexMock
55
+ flexmock_teardown
56
+ end
57
+ # Teardown code for all the tests
58
+ end
59
+ end
60
+ end
61
+
62
+ # Workaround a problem with flexmock and minitest not being compatible with each
63
+ # other (currently). See github.com/jimweirich/flexmock/issues/15.
64
+ if defined?(FlexMock) && !FlexMock::TestUnitFrameworkAdapter.method_defined?(:assertions)
65
+ class FlexMock::TestUnitFrameworkAdapter
66
+ attr_accessor :assertions
67
+ end
68
+ FlexMock.framework_adapter.assertions = 0
69
+ end
70
+
71
+ module Minitest
72
+ class Spec
73
+ include MetaRuby::SelfTest
74
+ end
75
+ class Test
76
+ include MetaRuby::SelfTest
77
+ end
78
+ end
79
+
data/lib/metaruby.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'utilrb/object/attribute'
2
+
3
+ require 'metaruby/inherited_attribute'
4
+ require 'metaruby/registration'
5
+ require 'metaruby/module'
6
+ require 'metaruby/class'
7
+
8
+ # The toplevel namespace for MetaRuby
9
+ #
10
+ # MetaRuby is an implementation of a (very small) modelling toolkit that uses
11
+ # the Ruby type system as its meta-metamodel
12
+ require 'utilrb/logger'
13
+ module MetaRuby
14
+ LIB_DIR = File.expand_path('metaruby', File.dirname(__FILE__))
15
+ extend Logger::Root('MetaRuby', Logger::WARN)
16
+ end
17
+
data/manifest.xml ADDED
@@ -0,0 +1,12 @@
1
+ <package>
2
+ <description brief="Modelling using the Ruby language as a metamodel">
3
+ </description>
4
+ <maintainer>Sylvain Joyeux/sylvain.joyeux@m4x.org</maintainer>
5
+ <license>LGPLv3</license>
6
+
7
+ <depend package="utilrb" />
8
+ <depend package="kramdown" optional="1" />
9
+
10
+ <test_depend package="minitest" />
11
+ <test_depend package="flexmock" />
12
+ </package>
data/test/suite.rb ADDED
@@ -0,0 +1,15 @@
1
+ # Coverage is enabled by default when running the whole suite
2
+ ENV['TEST_ENABLE_COVERAGE'] ||= '1'
3
+
4
+ # Require all your test files here. Always prepend ./ and use the relative path
5
+ # to the Ruby library root
6
+ require 'metaruby/test'
7
+ require './test/test_class'
8
+ require './test/test_module'
9
+ require './test/test_attributes'
10
+ require './test/test_registration'
11
+ require './test/test_dsls'
12
+
13
+ # Put the root logger to DEBUG so that all debug blocks are executed
14
+ MetaRuby.logger = Logger.new(File.open("/dev/null", 'w'))
15
+ MetaRuby.logger.level = Logger::DEBUG
@@ -0,0 +1,323 @@
1
+ require 'metaruby/test'
2
+
3
+ class TC_Models < MiniTest::Test
4
+ def test_inherited_attribute_class
5
+ a = Class.new do
6
+ class << self
7
+ extend MetaRuby::Attributes
8
+ inherited_attribute(:signature, :signatures) { Array.new }
9
+ inherited_attribute(:mapped, :map, :map => true) { Hash.new }
10
+ end
11
+ end
12
+ b = Class.new(a) do
13
+ include Module.new # include an empty module between a and b to check that the module
14
+ # is skipped transparently
15
+ singleton_class.inherited_attribute(:child_attribute) { Array.new }
16
+ end
17
+ check_inherited_attribute(a, b)
18
+
19
+ # Test for singleton class support
20
+ object = b.new
21
+ assert(object.singleton_class.respond_to?(:signatures))
22
+ object.singleton_class.signatures << :in_singleton
23
+ assert_equal([:in_singleton], object.singleton_class.signatures)
24
+ end
25
+
26
+ def check_inherited_attribute(base, derived)
27
+ assert(base.respond_to?(:each_signature))
28
+ assert(base.respond_to?(:signatures))
29
+ assert(!base.respond_to?(:has_signature?))
30
+ assert(!base.respond_to?(:find_signatures))
31
+
32
+ assert(base.respond_to?(:each_mapped))
33
+ assert(base.respond_to?(:map))
34
+ assert(base.respond_to?(:has_mapped?))
35
+
36
+ base.signatures << :in_base
37
+ base.map[:base] = 10
38
+ base.map[:overriden] = 20
39
+ assert_equal([:in_base], base.enum_for(:each_signature).to_a)
40
+ assert_equal([10].to_set, base.enum_for(:each_mapped, :base, false).to_set)
41
+
42
+ assert(!base.respond_to?(:child_attribute))
43
+ assert(!base.respond_to?(:each_child_attribute))
44
+ assert(derived.respond_to?(:child_attribute))
45
+ assert(derived.respond_to?(:each_child_attribute))
46
+
47
+ derived.signatures << :in_derived
48
+
49
+ derived.map[:overriden] = 15
50
+ derived.map[:derived] = 25
51
+
52
+ assert_equal([:in_derived, :in_base], derived.enum_for(:each_signature).to_a)
53
+ assert_equal([20, 15].to_set, derived.enum_for(:each_mapped, :overriden, false).to_set)
54
+ assert_equal([15].to_set, derived.enum_for(:each_mapped, :overriden, true).to_set)
55
+ assert_equal([25].to_set, derived.enum_for(:each_mapped, :derived).to_set)
56
+ assert_equal([[:base, 10], [:overriden, 20], [:overriden, 15], [:derived, 25]].to_set, derived.enum_for(:each_mapped, nil, false).to_set)
57
+ assert_equal([[:base, 10], [:overriden, 15], [:derived, 25]].to_set, derived.enum_for(:each_mapped, nil, true).to_set)
58
+ end
59
+
60
+ def test_inherited_attribute_non_mapping_promote
61
+ a = Class.new do
62
+ class << self
63
+ extend MetaRuby::Attributes
64
+ def promote_value(v)
65
+ v
66
+ end
67
+ inherited_attribute(:value, :values) { Array.new }
68
+ end
69
+ end
70
+ b = flexmock(Class.new(a), 'b')
71
+ c = flexmock(Class.new(b), 'c')
72
+ d = flexmock(Class.new(c), 'd')
73
+
74
+ c.should_receive(:promote_value).with(10).and_return("10_b_c").once.ordered
75
+ d.should_receive(:promote_value).with("10_b_c").and_return(12).once.ordered
76
+ c.should_receive(:promote_value).with(11).and_return("11_b_c").once.ordered
77
+ d.should_receive(:promote_value).with("11_b_c").and_return(13).once.ordered
78
+ b.should_receive(:promote_value).with(0).and_return("0_b_c").once.ordered
79
+ c.should_receive(:promote_value).with("0_b_c").and_return("0_c_d").once.ordered
80
+ d.should_receive(:promote_value).with("0_c_d").and_return(2).once.ordered
81
+ b.should_receive(:promote_value).with(1).and_return("1_b_c").once.ordered
82
+ c.should_receive(:promote_value).with("1_b_c").and_return("1_c_d").once.ordered
83
+ d.should_receive(:promote_value).with("1_c_d").and_return(3).once.ordered
84
+
85
+ a.values << 0 << 1
86
+ b.values << 10 << 11
87
+ # Do NOT add anything at the level of C. Its promote_value method should
88
+ # still be called, though
89
+ d.values << 100 << 110
90
+ assert_equal [0, 1], a.each_value.to_a
91
+ assert_equal [100, 110, 12, 13, 2, 3], d.each_value.to_a
92
+ end
93
+
94
+ def test_inherited_attribute_mapping_promote
95
+ a = Class.new do
96
+ class << self
97
+ extend MetaRuby::Attributes
98
+ def promote_value(key, v)
99
+ end
100
+ def name; 'A' end
101
+ inherited_attribute(:value, :values, :map => true) { Hash.new }
102
+ end
103
+ end
104
+ b = Class.new(a)
105
+ c = Class.new(b)
106
+ d = Class.new(c)
107
+
108
+ flexmock(c).should_receive(:promote_value).with('b', 2).and_return("b2_b_c").once.ordered
109
+ flexmock(d).should_receive(:promote_value).with('b', "b2_b_c").and_return(15).once.ordered
110
+
111
+ flexmock(c).should_receive(:promote_value).with('c', 3).and_return("c3_b_c").once.ordered
112
+ flexmock(d).should_receive(:promote_value).with('c', "c3_b_c").and_return(16).once.ordered
113
+
114
+ flexmock(b).should_receive(:promote_value).with('a', 0).and_return("a0_a_b").once.ordered
115
+ flexmock(c).should_receive(:promote_value).with('a', "a0_a_b").and_return("a0_b_c").once.ordered
116
+ flexmock(d).should_receive(:promote_value).with('a', "a0_b_c").and_return(10).once.ordered
117
+
118
+ a.values.merge!('a' => 0, 'b' => 1)
119
+ b.values.merge!('b' => 2, 'c' => 3, 'd' => 4)
120
+ d.values.merge!('d' => 5, 'e' => 6)
121
+ assert_equal [['d', 5], ['e', 6], ['b', 15], ['c', 16], ['a', 10]], d.each_value.to_a
122
+ end
123
+
124
+ def test_inherited_attribute_mapping_promote_non_uniq
125
+ a = Class.new do
126
+ class << self
127
+ extend MetaRuby::Attributes
128
+ def promote_value(key, v)
129
+ end
130
+ inherited_attribute(:value, :values, :map => true) { Hash.new }
131
+ end
132
+ end
133
+ b = flexmock(Class.new(a), 'b')
134
+ c = flexmock(Class.new(b), 'c')
135
+ d = flexmock(Class.new(c), 'd')
136
+
137
+ c.should_receive(:promote_value).with('b', 2).and_return("b2_b_c").once.ordered
138
+ d.should_receive(:promote_value).with('b', "b2_b_c").and_return(12).once.ordered
139
+
140
+ c.should_receive(:promote_value).with('c', 3).and_return("c3_b_c").once.ordered
141
+ d.should_receive(:promote_value).with('c', "c3_b_c").and_return(13).once.ordered
142
+
143
+ c.should_receive(:promote_value).with('d', 4).and_return("d4_b_c").once.ordered
144
+ d.should_receive(:promote_value).with('d', "d4_b_c").and_return(14).once.ordered
145
+
146
+ b.should_receive(:promote_value).with('a', 0).and_return("a0_a_b").once.ordered
147
+ c.should_receive(:promote_value).with('a', "a0_a_b").and_return("a0_b_c").once.ordered
148
+ d.should_receive(:promote_value).with('a', "a0_b_c").and_return(10).once.ordered
149
+
150
+ b.should_receive(:promote_value).with('b', 1).and_return("b1_a_b").once.ordered
151
+ c.should_receive(:promote_value).with('b', "b1_a_b").and_return("b1_b_c").once.ordered
152
+ d.should_receive(:promote_value).with('b', "b1_b_c").and_return(11).once.ordered
153
+
154
+ a.values.merge!('a' => 0, 'b' => 1)
155
+ b.values.merge!('b' => 2, 'c' => 3, 'd' => 4)
156
+ d.values.merge!('d' => 5, 'e' => 6)
157
+ assert_equal [['d', 5], ['e', 6], ['b', 12], ['c', 13], ['d', 14], ['a', 10], ['b', 11]], d.each_value(nil, false).to_a
158
+ end
159
+
160
+ def test_inherited_attribute_mapping_promote_with_key_uniq
161
+ a = Class.new do
162
+ class << self
163
+ extend MetaRuby::Attributes
164
+ def promote_value(key, v)
165
+ end
166
+ inherited_attribute(:value, :values, :map => true) { Hash.new }
167
+ end
168
+ end
169
+ b = flexmock(Class.new(a), 'b')
170
+ c = flexmock(Class.new(b), 'c')
171
+ d = flexmock(Class.new(c), 'd')
172
+
173
+ c.should_receive(:promote_value).with('b', 2).and_return("b2_b_c").once.ordered
174
+ d.should_receive(:promote_value).with('b', "b2_b_c").and_return(12).once.ordered
175
+
176
+ a.values.merge!('a' => 0, 'b' => 1)
177
+ b.values.merge!('b' => 2, 'c' => 3, 'd' => 4)
178
+ d.values.merge!('d' => 5, 'e' => 6)
179
+ assert_equal [12], d.each_value('b', true).to_a
180
+ end
181
+
182
+ def test_inherited_attribute_mapping_promote_with_key_non_uniq
183
+ a = Class.new do
184
+ class << self
185
+ extend MetaRuby::Attributes
186
+ def promote_value(key, v)
187
+ end
188
+ inherited_attribute(:value, :values, :map => true) { Hash.new }
189
+ end
190
+ end
191
+ b = flexmock(Class.new(a), 'b')
192
+ c = flexmock(Class.new(b), 'c')
193
+ d = flexmock(Class.new(c), 'd')
194
+
195
+ c.should_receive(:promote_value).with('b', 2).and_return("b2_b_c").once.ordered
196
+ d.should_receive(:promote_value).with('b', "b2_b_c").and_return(12).once.ordered
197
+
198
+ b.should_receive(:promote_value).with('b', 1).and_return("b1_a_b").once.ordered
199
+ c.should_receive(:promote_value).with('b', "b1_a_b").and_return("b1_b_c").once.ordered
200
+ d.should_receive(:promote_value).with('b', "b1_b_c").and_return(11).once.ordered
201
+
202
+ a.values.merge!('a' => 0, 'b' => 1)
203
+ b.values.merge!('b' => 2, 'c' => 3, 'd' => 4)
204
+ d.values.merge!('d' => 5, 'e' => 6)
205
+ assert_equal [12, 11], d.each_value('b', false).to_a
206
+ end
207
+ end
208
+
209
+ describe MetaRuby::Attributes do
210
+ describe "#inherited_single_value_attribute" do
211
+ attr_reader :base, :sub, :subsub
212
+ describe "plain" do
213
+ before do
214
+ @base = Class.new do
215
+ class << self
216
+ extend MetaRuby::Attributes
217
+ inherited_single_value_attribute :var
218
+ end
219
+ end
220
+ @sub = Class.new(base)
221
+ @subsub = Class.new(sub)
222
+ end
223
+
224
+ it "should set the value if given an argument" do
225
+ base.var(10)
226
+ assert_equal 10, base.var
227
+ end
228
+ it "should return the value from the parent model if not set" do
229
+ base.var(10)
230
+ assert_equal 10, sub.var
231
+ end
232
+ it "should return nil if the instance variable is explicitly set to nil" do
233
+ base.var 10
234
+ sub.var nil
235
+ assert_equal nil, subsub.var
236
+ end
237
+ end
238
+
239
+ describe "with default" do
240
+ before do
241
+ @base = Class.new do
242
+ class << self
243
+ extend MetaRuby::Attributes
244
+ inherited_single_value_attribute(:var) { 10 }
245
+ end
246
+ end
247
+ @sub = Class.new(base)
248
+ @subsub = Class.new(sub)
249
+ end
250
+
251
+ it "should be accessible at each level" do
252
+ assert_equal 10, base.var
253
+ assert_equal 10, sub.var
254
+ assert_equal 10, subsub.var
255
+ end
256
+
257
+ it "should set only the bottom class" do
258
+ assert_equal 10, base.var
259
+ sub.var(nil)
260
+ assert_equal nil, sub.var
261
+ assert_equal nil, subsub.var
262
+ end
263
+ end
264
+
265
+ describe "with promotion" do
266
+ before do
267
+ @base = Class.new do
268
+ class << self
269
+ extend MetaRuby::Attributes
270
+ def promote_var(value); value * 2 end
271
+ inherited_single_value_attribute(:var)
272
+ end
273
+ end
274
+ @sub = Class.new(base) do
275
+ class << self
276
+ def promote_var(value); value * 4 end
277
+ end
278
+ end
279
+ @subsub = Class.new(sub) do
280
+ class << self
281
+ def promote_var(value); value - 10 end
282
+ end
283
+ end
284
+ end
285
+
286
+ it "should apply the promotion method at each level" do
287
+ base.var(10)
288
+ assert_equal 10, base.var
289
+ assert_equal 40, sub.var
290
+ assert_equal 30, subsub.var
291
+ end
292
+ end
293
+
294
+ describe "with default with promotion" do
295
+ before do
296
+ @base = Class.new do
297
+ class << self
298
+ extend MetaRuby::Attributes
299
+ def promote_var(value); value * 2 end
300
+ inherited_single_value_attribute(:var) { 10 }
301
+ end
302
+ end
303
+ @sub = Class.new(base) do
304
+ class << self
305
+ def promote_var(value); value * 4 end
306
+ end
307
+ end
308
+ @subsub = Class.new(sub) do
309
+ class << self
310
+ def promote_var(value); value - 10 end
311
+ end
312
+ end
313
+ end
314
+
315
+ it "should apply the promotion method at each level" do
316
+ assert_equal 10, base.var
317
+ assert_equal 40, sub.var
318
+ assert_equal 30, subsub.var
319
+ end
320
+ end
321
+ end
322
+ end
323
+
@@ -0,0 +1,68 @@
1
+ require 'metaruby/test'
2
+
3
+ class Base
4
+ extend MetaRuby::ModelAsClass
5
+ end
6
+
7
+ module DefinitionContext
8
+ class Klass < Base; end
9
+ end
10
+
11
+ module PermanentDefinitionContext
12
+ def self.permanent_model?; true end
13
+ class Klass < Base; end
14
+ end
15
+
16
+ module NonPermanentDefinitionContext
17
+ def self.permanent_model?; false end
18
+ class Klass < Base; end
19
+ end
20
+
21
+ describe MetaRuby::ModelAsClass do
22
+ include MetaRuby::SelfTest
23
+
24
+ before do
25
+ # Code that is run before each test
26
+ end
27
+ after do
28
+ # Code that is run after each test
29
+ end
30
+
31
+ describe "Using modules as metamodel" do
32
+ it "should apply the Attribute module on sub-metamodels for classes" do
33
+ mod = Module.new { include MetaRuby::ModelAsClass }
34
+ sub = Module.new { include mod }
35
+ assert mod.respond_to?(:inherited_attribute)
36
+ assert sub.respond_to?(:inherited_attribute)
37
+ end
38
+ end
39
+
40
+ describe "#new_submodel" do
41
+ it "should call setup_submodel only once" do
42
+ mod = Module.new { include MetaRuby::ModelAsClass }
43
+ klass = Class.new { extend mod }
44
+ flexmock(klass).should_receive(:setup_submodel).once
45
+ klass.new_submodel
46
+ end
47
+ it "should set permanent_model to false on the class" do
48
+ end
49
+ end
50
+
51
+ describe "creating subclasses" do
52
+ it "should set permanent_model to false on the class if the enclosing context is not properly registered as a constant" do
53
+ definition_context = Module.new do
54
+ Klass = Class.new(Base)
55
+ end
56
+ assert !definition_context.const_get(:Klass).permanent_model?
57
+ end
58
+ it "should set permanent_model to true on the class if the enclosing context is properly registered as a constant but is not responding to permanent_model?" do
59
+ assert DefinitionContext::Klass.permanent_model?
60
+ end
61
+ it "should set permanent_model to true on the class if the enclosing context is permanent" do
62
+ assert PermanentDefinitionContext::Klass.permanent_model?
63
+ end
64
+ it "should set permanent_model to false on the class if the enclosing context is not permanent" do
65
+ assert !NonPermanentDefinitionContext::Klass.permanent_model?
66
+ end
67
+ end
68
+ end