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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/History.txt +1 -0
- data/Manifest.txt +40 -0
- data/README.md +318 -0
- data/Rakefile +39 -0
- data/lib/metaruby/class.rb +120 -0
- data/lib/metaruby/dsls/doc.rb +78 -0
- data/lib/metaruby/dsls/find_through_method_missing.rb +76 -0
- data/lib/metaruby/dsls.rb +2 -0
- data/lib/metaruby/gui/exception_view.rb +124 -0
- data/lib/metaruby/gui/html/button.rb +65 -0
- data/lib/metaruby/gui/html/collection.rb +103 -0
- data/lib/metaruby/gui/html/exception_view.css +8 -0
- data/lib/metaruby/gui/html/jquery.min.js +154 -0
- data/lib/metaruby/gui/html/jquery.selectfilter.js +65 -0
- data/lib/metaruby/gui/html/jquery.tagcloud.min.js +8 -0
- data/lib/metaruby/gui/html/jquery.tinysort.min.js +8 -0
- data/lib/metaruby/gui/html/list.rhtml +24 -0
- data/lib/metaruby/gui/html/page.css +55 -0
- data/lib/metaruby/gui/html/page.rb +283 -0
- data/lib/metaruby/gui/html/page.rhtml +13 -0
- data/lib/metaruby/gui/html/page_body.rhtml +17 -0
- data/lib/metaruby/gui/html/rock-website.css +694 -0
- data/lib/metaruby/gui/html.rb +16 -0
- data/lib/metaruby/gui/model_browser.rb +262 -0
- data/lib/metaruby/gui/model_selector.rb +266 -0
- data/lib/metaruby/gui/rendering_manager.rb +112 -0
- data/lib/metaruby/gui/ruby_constants_item_model.rb +253 -0
- data/lib/metaruby/gui.rb +9 -0
- data/lib/metaruby/inherited_attribute.rb +482 -0
- data/lib/metaruby/module.rb +158 -0
- data/lib/metaruby/registration.rb +157 -0
- data/lib/metaruby/test.rb +79 -0
- data/lib/metaruby.rb +17 -0
- data/manifest.xml +12 -0
- data/test/suite.rb +15 -0
- data/test/test_attributes.rb +323 -0
- data/test/test_class.rb +68 -0
- data/test/test_dsls.rb +49 -0
- data/test/test_module.rb +105 -0
- data/test/test_registration.rb +182 -0
- 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
|
+
|
data/test/test_class.rb
ADDED
@@ -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
|