rgen 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/MIT-LICENSE +20 -0
- data/README +73 -0
- data/lib/ea/xmi_class_instantiator.rb +45 -0
- data/lib/ea/xmi_helper.rb +26 -0
- data/lib/ea/xmi_metamodel.rb +19 -0
- data/lib/ea/xmi_object_instantiator.rb +42 -0
- data/lib/ea/xmi_to_classmodel.rb +78 -0
- data/lib/ea/xmi_to_objectmodel.rb +89 -0
- data/lib/mmgen/metamodel_generator.rb +19 -0
- data/lib/mmgen/mm_ext/uml_classmodel_ext.rb +71 -0
- data/lib/mmgen/mmgen.rb +21 -0
- data/lib/mmgen/templates/uml_classmodel.tpl +63 -0
- data/lib/rgen/array_extensions.rb +23 -0
- data/lib/rgen/auto_class_creator.rb +56 -0
- data/lib/rgen/environment.rb +57 -0
- data/lib/rgen/metamodel_builder.rb +102 -0
- data/lib/rgen/metamodel_builder/build_helper.rb +29 -0
- data/lib/rgen/metamodel_builder/builder_extensions.rb +191 -0
- data/lib/rgen/metamodel_builder/builder_runtime.rb +67 -0
- data/lib/rgen/name_helper.rb +18 -0
- data/lib/rgen/template_language.rb +169 -0
- data/lib/rgen/template_language/directory_template_container.rb +51 -0
- data/lib/rgen/template_language/output_handler.rb +84 -0
- data/lib/rgen/template_language/template_container.rb +153 -0
- data/lib/rgen/template_language/template_helper.rb +26 -0
- data/lib/rgen/transformer.rb +316 -0
- data/lib/rgen/xml_instantiator/dependency_resolver.rb +23 -0
- data/lib/rgen/xml_instantiator/xml_instantiator.rb +78 -0
- data/lib/rgen/xml_instantiator/xml_parser.rb +39 -0
- data/lib/uml/objectmodel_instantiator.rb +53 -0
- data/lib/uml/uml_classmodel.rb +92 -0
- data/lib/uml/uml_objectmodel.rb +65 -0
- data/test/array_extensions_test.rb +54 -0
- data/test/environment_test.rb +47 -0
- data/test/metamodel_builder_test.rb +175 -0
- data/test/metamodel_generator_test.rb +45 -0
- data/test/metamodel_generator_test/TestModel.rb +40 -0
- data/test/metamodel_generator_test/expected_result.txt +40 -0
- data/test/output_handler_test.rb +40 -0
- data/test/rgen_test.rb +13 -0
- data/test/template_language_test.rb +46 -0
- data/test/template_language_test/expected_result.txt +10 -0
- data/test/template_language_test/templates/content/chapter.tpl +5 -0
- data/test/template_language_test/templates/index/c/cmod.tpl +1 -0
- data/test/template_language_test/templates/index/chapter.tpl +3 -0
- data/test/template_language_test/templates/root.tpl +22 -0
- data/test/template_language_test/testout.txt +10 -0
- data/test/transformer_test.rb +176 -0
- data/test/xmi_class_instantiator_test.rb +107 -0
- data/test/xmi_instantiator_test/testmodel.eap +0 -0
- data/test/xmi_instantiator_test/testmodel.xml +962 -0
- data/test/xmi_object_instantiator_test.rb +65 -0
- metadata +117 -0
data/lib/mmgen/mmgen.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'ea/xmi_instantiator'
|
2
|
+
require 'mmgen/metamodel_generator'
|
3
|
+
|
4
|
+
include XMIClassInstantiator
|
5
|
+
include MMGen::MetamodelGenerator
|
6
|
+
|
7
|
+
unless ARGV.length == 1
|
8
|
+
puts "Usage: mmgen.rb <xmi_class_model_file>"
|
9
|
+
exit
|
10
|
+
else
|
11
|
+
file_name = ARGV.shift
|
12
|
+
out_file = file_name.gsub(/\.\w+$/,'')
|
13
|
+
puts out_file
|
14
|
+
end
|
15
|
+
|
16
|
+
envUML = RGen::Environment.new
|
17
|
+
File.open(file_name) { |f|
|
18
|
+
instantiateUMLClassModel(envUML, f.read)
|
19
|
+
}
|
20
|
+
|
21
|
+
generateMetamodel(envUML, out_file)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
<% define 'GenerateClassModel', :for => UMLPackage do |filename, modules| %>
|
3
|
+
<% file filename do %>
|
4
|
+
require 'rgen/metamodel_builder'
|
5
|
+
|
6
|
+
<% expand 'GeneratePackage', modules %>
|
7
|
+
<%nl%>
|
8
|
+
<% expand 'GenerateAssocs' %>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% define 'GeneratePackage', :for => UMLPackage do |modules| %>
|
13
|
+
module <%= moduleName %><% iinc %>
|
14
|
+
<% for c in sortedClasses %><%nl%>
|
15
|
+
<% expand 'ModuleHeader', modules, :for => c %><%iinc%>
|
16
|
+
<% (c.superclasses.name & modules).each do |n| %>
|
17
|
+
include <%= n %>
|
18
|
+
<% end %>
|
19
|
+
<% if modules.include?(c.className) %>
|
20
|
+
extend RGen::MetamodelBuilder::BuilderExtensions
|
21
|
+
<% end %>
|
22
|
+
has_one 'name', String
|
23
|
+
<% for a in c.attributes %>
|
24
|
+
has_one '<%= a.name %>', <%= a.RubyType %>
|
25
|
+
<% end %><%idec%>
|
26
|
+
end
|
27
|
+
<% end %><%nl%>
|
28
|
+
<% for p in subpackages %>
|
29
|
+
<%nl%><% expand 'GeneratePackage', modules, :for => p %>
|
30
|
+
<% end %><%idec%>
|
31
|
+
end
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% define 'GenerateAssocs', :for => UMLPackage do %>
|
35
|
+
<% for a in allClasses.assocEnds>>:assoc %>
|
36
|
+
<% if a.endA.one? %>
|
37
|
+
<% if a.endB.one? %>
|
38
|
+
<%= a.endA.clazz.qualifiedName(this) %>.one_to_one '<%= a.endB.MName %>', <%= a.endB.clazz.qualifiedName(this) %>, '<%= a.endA.MName %>'
|
39
|
+
<% elsif a.endB.many? %>
|
40
|
+
<%= a.endA.clazz.qualifiedName(this) %>.one_to_many '<%= a.endB.MName %>', <%= a.endB.clazz.qualifiedName(this) %>, '<%= a.endA.MName %>'
|
41
|
+
<% end %>
|
42
|
+
<% elsif a.endA.many? %>
|
43
|
+
<% if a.endB.one? %>
|
44
|
+
<%= a.endB.clazz.qualifiedName(this) %>.one_to_many '<%= a.endA.MName %>', <%= a.endA.clazz.qualifiedName(this) %>, '<%= a.endB.MName %>'
|
45
|
+
<% elsif a.endB.many? %>
|
46
|
+
<%= a.endB.clazz.qualifiedName(this) %>.many_to_many '<%= a.endA.MName %>', <%= a.endA.clazz.qualifiedName(this) %>, '<%= a.endB.MName %>'
|
47
|
+
<% end %>
|
48
|
+
<% end %>
|
49
|
+
<% end %>
|
50
|
+
<% end %>
|
51
|
+
|
52
|
+
<% define 'ModuleHeader' do |modules| %>
|
53
|
+
<% if modules.include?(name) %>
|
54
|
+
module <%= className %>
|
55
|
+
<% else %>
|
56
|
+
class <%= className %> < <% nows %>
|
57
|
+
<% if rsc=RubySuperclass(modules) %><% nows %>
|
58
|
+
<%= rsc.className %>
|
59
|
+
<% else %><% nows %>
|
60
|
+
RGen::MetamodelBuilder::MMBase
|
61
|
+
<% end %>
|
62
|
+
<% end %>
|
63
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/metamodel_builder'
|
5
|
+
|
6
|
+
class Array
|
7
|
+
|
8
|
+
def >>(method)
|
9
|
+
compact.inject([]) { |r,e| r | ( (o=e.send(method)).is_a?(Array) ? o : [o] ) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(m, *args)
|
13
|
+
super unless size == 0 or compact.any?{|e| e.is_a? RGen::MetamodelBuilder::MMBase}
|
14
|
+
compact.inject([]) { |r,e|
|
15
|
+
if e.is_a? RGen::MetamodelBuilder::MMBase
|
16
|
+
r | ( (o=e.send(m)).is_a?(Array) ? o : [o] )
|
17
|
+
else
|
18
|
+
raise StandardError.new("Trying to call a method on an array element not a RGen MMBase")
|
19
|
+
end
|
20
|
+
}.compact
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/metamodel_builder'
|
5
|
+
|
6
|
+
module RGen
|
7
|
+
|
8
|
+
class Base
|
9
|
+
extend MetamodelBuilder
|
10
|
+
def initialize(env=nil)
|
11
|
+
env << self if env
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class AutoCreatedClass < Base
|
16
|
+
def method_missing(m,*args)
|
17
|
+
return super unless self.class.parent.accEnabled
|
18
|
+
if m.to_s =~ /(.*)=$/
|
19
|
+
self.class.has_one($1)
|
20
|
+
send(m,args[0])
|
21
|
+
elsif args.size == 0
|
22
|
+
self.class.has_many(m)
|
23
|
+
send(m)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# will be "extended" to the auto created class
|
29
|
+
module ParentAccess
|
30
|
+
def parent=(p)
|
31
|
+
@parent = p
|
32
|
+
end
|
33
|
+
def parent
|
34
|
+
@parent
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module AutoClassCreator
|
39
|
+
attr_reader :accEnabled
|
40
|
+
def const_missing(className)
|
41
|
+
return super unless @accEnabled
|
42
|
+
module_eval("class "+className.to_s+" < RGen::AutoCreatedClass; end")
|
43
|
+
c = const_get(className)
|
44
|
+
c.extend(ParentAccess)
|
45
|
+
c.parent = self
|
46
|
+
c
|
47
|
+
end
|
48
|
+
def enableACC
|
49
|
+
@accEnabled = true
|
50
|
+
end
|
51
|
+
def disableACC
|
52
|
+
@accEnabled = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
# An Environment is used to hold model elements.
|
4
|
+
#
|
5
|
+
class Environment
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@elements = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# Add a model element. Returns the environment so <code><<</code> can be chained.
|
12
|
+
#
|
13
|
+
def <<(el)
|
14
|
+
@elements << el
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finds and returns model elements in the environment.
|
19
|
+
#
|
20
|
+
# The search description argument must be a hash specifying attribute/value pairs.
|
21
|
+
# Only model elements are returned which respond to the specified attribute methods
|
22
|
+
# and return the specified values as result of these attribute methods.
|
23
|
+
#
|
24
|
+
# As a special hash key :class can be used to look for model elements of a specific
|
25
|
+
# class. In this case an array of possible classes can optionally be given.
|
26
|
+
#
|
27
|
+
def find(desc)
|
28
|
+
result = []
|
29
|
+
classes = desc[:class] if desc[:class] and desc[:class].is_a?(Array)
|
30
|
+
classes = [ desc[:class] ] if !classes and desc[:class]
|
31
|
+
elements.each {|e|
|
32
|
+
failed = false
|
33
|
+
failed = true if classes and !classes.any?{ |c| e.is_a?(c) }
|
34
|
+
desc.each_pair { |k,v|
|
35
|
+
failed = true if k != :class and ( !e.respond_to?(k) or e.send(k) != v )
|
36
|
+
}
|
37
|
+
result << e unless failed
|
38
|
+
}
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the elements of the environment as an array
|
43
|
+
#
|
44
|
+
def elements
|
45
|
+
@elements.dup
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method can be used to instantiate a class and automatically put it into
|
49
|
+
# the environment. The new instance is returned.
|
50
|
+
#
|
51
|
+
def new(clazz)
|
52
|
+
@elements << clazz.new
|
53
|
+
@elements[-1]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/metamodel_builder/builder_runtime'
|
5
|
+
require 'rgen/metamodel_builder/builder_extensions'
|
6
|
+
|
7
|
+
module RGen
|
8
|
+
|
9
|
+
# MetamodelBuilder can be used to create a metamodel, i.e. Ruby classes which
|
10
|
+
# act as metamodel elements.
|
11
|
+
#
|
12
|
+
# To create a new metamodel element, create a Ruby class which inherits from
|
13
|
+
# MetamodelBuilder::MMBase
|
14
|
+
#
|
15
|
+
# class Person < RGen::MetamodelBuilder::MMBase
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# This way a couple of class methods are made available to the new class.
|
19
|
+
# These methods can be used to:
|
20
|
+
# * add attributes to the class
|
21
|
+
# * add associations with other classes
|
22
|
+
#
|
23
|
+
# Here is an example:
|
24
|
+
#
|
25
|
+
# class Person < RGen::MetamodelBuilder::MMBase
|
26
|
+
# has_one 'name', String
|
27
|
+
# has_one 'age', Integer
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# class House < RGen::MetamodelBuilder::MMBase
|
31
|
+
# has_one 'address'
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Person.many_to_many 'homes', House, 'inhabitants'
|
35
|
+
#
|
36
|
+
# See BuilderExtensions for details about the available class methods.
|
37
|
+
#
|
38
|
+
# =Attributes
|
39
|
+
#
|
40
|
+
# The example above creates two classes 'Person' and 'House'. Person has the attributes
|
41
|
+
# 'name' and 'age', House has the attribute 'address'. The attributes can be
|
42
|
+
# accessed on instances of the classes in the following way:
|
43
|
+
#
|
44
|
+
# p = Person.new
|
45
|
+
# p.name = "MyName"
|
46
|
+
# p.age = 22
|
47
|
+
# p.name # => "MyName"
|
48
|
+
# p.age # => 22
|
49
|
+
#
|
50
|
+
# Note that the class Person takes care of the type of its attributes. As
|
51
|
+
# declared above, a 'name' can only be a String, an 'age' must be an Integer.
|
52
|
+
# So the following would return an exception:
|
53
|
+
#
|
54
|
+
# p.name = :myName # => exception: can not put a Symbol where a String is expected
|
55
|
+
#
|
56
|
+
# If the type of an attribute should be left undefined, just leave away the
|
57
|
+
# second argument of 'has_one' as show at the attribute 'address' for House.
|
58
|
+
#
|
59
|
+
# =Associations
|
60
|
+
#
|
61
|
+
# As well as attributes show up as instance methods, associations bring their own
|
62
|
+
# accessor methods. For the Person-to-House association this would be:
|
63
|
+
#
|
64
|
+
# h1 = House.new
|
65
|
+
# h1.address = "Street1"
|
66
|
+
# h2 = House.new
|
67
|
+
# h2.address = "Street2"
|
68
|
+
# p.addHomes(h1)
|
69
|
+
# p.addHomes(h2)
|
70
|
+
# p.removeHomes(h1)
|
71
|
+
# p.homes # => [ h2 ]
|
72
|
+
#
|
73
|
+
# The Person-to-House association is _bidirectional_. This means that with the
|
74
|
+
# addition of a House to a Person, the Person is also added to the House. Thus:
|
75
|
+
#
|
76
|
+
# h1.inhabitants # => []
|
77
|
+
# h2.inhabitants # => [ p ]
|
78
|
+
#
|
79
|
+
# Note that the association is defined between two specific classes, instances of
|
80
|
+
# different classes can not be added. Thus, the following would result in an
|
81
|
+
# exception:
|
82
|
+
#
|
83
|
+
# p.addHomes(:justASymbol) # => exception: can not put a Symbol where a House is expected
|
84
|
+
#
|
85
|
+
# _Unidirectional_ associations can be thought of as attributes as shown above. This means
|
86
|
+
# that 'has_one' or 'has_many' can be used to define such associations. Again, the
|
87
|
+
# type of the attribute/association can be specified as a second argument.
|
88
|
+
#
|
89
|
+
module MetamodelBuilder
|
90
|
+
|
91
|
+
# Use this class as a start for new metamodel elements (i.e. Ruby classes)
|
92
|
+
# by inheriting for it.
|
93
|
+
#
|
94
|
+
# See MetamodelBuilder for an example.
|
95
|
+
class MMBase
|
96
|
+
include BuilderRuntime
|
97
|
+
extend BuilderExtensions
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/name_helper'
|
5
|
+
|
6
|
+
module RGen
|
7
|
+
|
8
|
+
module MetamodelBuilder
|
9
|
+
|
10
|
+
class BuildHelper
|
11
|
+
include NameHelper
|
12
|
+
|
13
|
+
def self.build(mod,bnd,tpl)
|
14
|
+
mod.module_eval ERB.new(tpl).result(BuildHelper.new(bnd).bnd)
|
15
|
+
end
|
16
|
+
def initialize(bnd)
|
17
|
+
@outer_binding = bnd
|
18
|
+
end
|
19
|
+
def bnd
|
20
|
+
binding
|
21
|
+
end
|
22
|
+
def method_missing(m)
|
23
|
+
eval("#{m}",@outer_binding)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
require 'rgen/metamodel_builder/build_helper'
|
6
|
+
|
7
|
+
module RGen
|
8
|
+
|
9
|
+
module MetamodelBuilder
|
10
|
+
|
11
|
+
# This module provides methods which can be used to setup a metamodel element.
|
12
|
+
# The module is used to +extend+ MetamodelBuilder::MMBase, i.e. add the module's
|
13
|
+
# methods as class methods.
|
14
|
+
#
|
15
|
+
# MetamodelBuilder::MMBase should be used as a start for new metamodel elements.
|
16
|
+
# See MetamodelBuilder for an example.
|
17
|
+
#
|
18
|
+
module BuilderExtensions
|
19
|
+
|
20
|
+
# Add a single attribute or unidirectional association.
|
21
|
+
# 'role' specifies the name which is used to access the attribute.
|
22
|
+
# 'target_class' is optional and can be used to fix the type of objects which
|
23
|
+
# can be held by this attribute.
|
24
|
+
#
|
25
|
+
# This class method adds the following instance methods, where 'role' is to be
|
26
|
+
# replaced by the given role name:
|
27
|
+
# class#role # getter
|
28
|
+
# class#role=(value) # setter
|
29
|
+
#
|
30
|
+
def has_one(role, target_class=nil)
|
31
|
+
has_one_internal(role, target_class)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add an unidirectional _many_ association.
|
35
|
+
# 'role' specifies the name which is used to access the attribute.
|
36
|
+
# 'target_class' is optional and can be used to fix the type of objects which
|
37
|
+
# can be referenced by this association.
|
38
|
+
#
|
39
|
+
# This class method adds the following instance methods, where 'role' is to be
|
40
|
+
# replaced by the given role name:
|
41
|
+
# class#addRole(value)
|
42
|
+
# class#removeRole(value)
|
43
|
+
# class#role # getter, returns an array
|
44
|
+
# Note that the first letter of the role name is turned into an uppercase
|
45
|
+
# for the add and remove methods.
|
46
|
+
#
|
47
|
+
def has_many(role, target_class=nil)
|
48
|
+
has_many_internal(role, target_class)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a bidirectional one-to-many association between two classes.
|
52
|
+
# The class this method is called on is refered to as _own_class_ in
|
53
|
+
# the following.
|
54
|
+
#
|
55
|
+
# Instances of own_class can use 'own_role' to access _many_ associated instances
|
56
|
+
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
|
57
|
+
# access _one_ associated instance of own_class.
|
58
|
+
#
|
59
|
+
# This class method adds the following instance methods where 'ownRole' and
|
60
|
+
# 'targetRole' are to be replaced by the given role names:
|
61
|
+
# own_class#addOwnRole(value)
|
62
|
+
# own_class#removeOwnRole(value)
|
63
|
+
# own_class#ownRole
|
64
|
+
# target_class#targetRole
|
65
|
+
# target_class#targetRole=(value)
|
66
|
+
# Note that the first letter of the role name is turned into an uppercase
|
67
|
+
# for the add and remove methods.
|
68
|
+
#
|
69
|
+
# When an element is added/set on either side, this element also receives the element
|
70
|
+
# is is added to as a new element.
|
71
|
+
#
|
72
|
+
def one_to_many(own_role, target_class, target_role)
|
73
|
+
has_many_internal(own_role,target_class,target_role,:one)
|
74
|
+
target_class.has_one_internal(target_role,self,own_role,:many)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add a bidirectional many-to-many association between two classes.
|
78
|
+
# The class this method is called on is refered to as _own_class_ in
|
79
|
+
# the following.
|
80
|
+
#
|
81
|
+
# Instances of own_class can use 'own_role' to access _many_ associated instances
|
82
|
+
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
|
83
|
+
# access _many_ associated instances of own_class.
|
84
|
+
#
|
85
|
+
# This class method adds the following instance methods where 'ownRole' and
|
86
|
+
# 'targetRole' are to be replaced by the given role names:
|
87
|
+
# own_class#addOwnRole(value)
|
88
|
+
# own_class#removeOwnRole(value)
|
89
|
+
# own_class#ownRole
|
90
|
+
# target_class#addTargetRole
|
91
|
+
# target_class#removeTargetRole=(value)
|
92
|
+
# target_class#targetRole
|
93
|
+
# Note that the first letter of the role name is turned into an uppercase
|
94
|
+
# for the add and remove methods.
|
95
|
+
#
|
96
|
+
# When an element is added on either side, this element also receives the element
|
97
|
+
# is is added to as a new element.
|
98
|
+
#
|
99
|
+
def many_to_many(own_role, target_class, target_role)
|
100
|
+
has_many_internal(own_role,target_class,target_role,:many)
|
101
|
+
target_class.has_many_internal(target_role,self,own_role,:many)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Add a bidirectional one-to-one association between two classes.
|
105
|
+
# The class this method is called on is refered to as _own_class_ in
|
106
|
+
# the following.
|
107
|
+
#
|
108
|
+
# Instances of own_class can use 'own_role' to access _one_ associated instance
|
109
|
+
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
|
110
|
+
# access _one_ associated instance of own_class.
|
111
|
+
#
|
112
|
+
# This class method adds the following instance methods where 'ownRole' and
|
113
|
+
# 'targetRole' are to be replaced by the given role names:
|
114
|
+
# own_class#ownRole
|
115
|
+
# own_class#ownRole=(value)
|
116
|
+
# target_class#targetRole
|
117
|
+
# target_class#targetRole=(value)
|
118
|
+
#
|
119
|
+
# When an element is set on either side, this element also receives the element
|
120
|
+
# is is added to as the new element.
|
121
|
+
#
|
122
|
+
def one_to_one(own_role, target_class, target_role)
|
123
|
+
has_one_internal(own_role,target_class,target_role,:one)
|
124
|
+
target_class.has_one_internal(target_role,self,own_role,:one)
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
def has_one_internal(name, cls=nil, role=nil, kind=nil)
|
130
|
+
BuildHelper.build self, binding, <<-CODE
|
131
|
+
@@one_assocs ||= []
|
132
|
+
@@one_assocs << name
|
133
|
+
def #{name}=(val)
|
134
|
+
return if val == @#{name}
|
135
|
+
<% if cls %>
|
136
|
+
raise _assignmentTypeError(self,val,#{cls}) unless val.nil? or val.is_a? #{cls}
|
137
|
+
<% end %>
|
138
|
+
oldval = @#{name}
|
139
|
+
@#{name} = val
|
140
|
+
_unregister(self,oldval,"#{role}","#{kind}")
|
141
|
+
_register(self,val,"#{role}","#{kind}")
|
142
|
+
end
|
143
|
+
def #{name}
|
144
|
+
@#{name}
|
145
|
+
end
|
146
|
+
alias get<%= firstToUpper(name) %> #{name}
|
147
|
+
alias set<%= firstToUpper(name) %> #{name}=
|
148
|
+
CODE
|
149
|
+
end
|
150
|
+
|
151
|
+
def has_many_internal(name, cls=nil, role=nil, kind = nil)
|
152
|
+
BuildHelper.build self, binding, <<-CODE
|
153
|
+
@@many_assocs ||= []
|
154
|
+
@@many_assocs << name
|
155
|
+
def add<%= firstToUpper(name) %>(val)
|
156
|
+
@#{name} = [] unless @#{name}
|
157
|
+
return if val.nil? or @#{name}.include?(val)
|
158
|
+
<% if cls %>
|
159
|
+
raise _assignmentTypeError(self,val,#{cls}) unless val.nil? or val.is_a? #{cls}
|
160
|
+
<% end %>
|
161
|
+
@#{name}.push val
|
162
|
+
_register(self, val, "#{role}", "#{kind}")
|
163
|
+
end
|
164
|
+
def remove<%= firstToUpper(name) %>(val)
|
165
|
+
@#{name} = [] unless @#{name}
|
166
|
+
return unless @#{name}.include?(val)
|
167
|
+
@#{name}.delete val
|
168
|
+
_unregister(self, val, "#{role}", "#{kind}")
|
169
|
+
end
|
170
|
+
def #{name}
|
171
|
+
( @#{name} ? @#{name}.dup : [] )
|
172
|
+
end
|
173
|
+
def #{name}=(val)
|
174
|
+
return if val.nil?
|
175
|
+
raise _assignmentTypeError(self, val, Array) unless val.is_a? Array
|
176
|
+
#{name}.each {|e|
|
177
|
+
remove<%= firstToUpper(name) %>(e)
|
178
|
+
}
|
179
|
+
val.each {|v|
|
180
|
+
add<%= firstToUpper(name) %>(v)
|
181
|
+
}
|
182
|
+
end
|
183
|
+
alias get<%= firstToUpper(name) %> #{name}
|
184
|
+
alias set<%= firstToUpper(name) %> #{name}=
|
185
|
+
CODE
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|