rgen 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/lib/ea/xmi_class_instantiator.rb +17 -16
- data/lib/ea/xmi_helper.rb +1 -1
- data/lib/ea/xmi_metamodel.rb +25 -10
- data/lib/ea/xmi_object_instantiator.rb +16 -12
- data/lib/ea/xmi_to_classmodel.rb +19 -19
- data/lib/ea/xmi_to_objectmodel.rb +17 -14
- data/lib/mmgen/mmgen.rb +12 -7
- data/lib/rgen/instantiator.rb +62 -0
- data/lib/rgen/metamodel_builder/builder_extensions.rb +30 -4
- data/lib/rgen/model_dumper.rb +24 -0
- data/lib/rgen/name_helper.rb +4 -1
- data/lib/rgen/transformer.rb +20 -2
- data/lib/rgen/xml_instantiator.rb +132 -0
- data/test/metamodel_builder_test.rb +86 -0
- data/test/metamodel_generator_test.rb +1 -2
- data/test/rgen_test.rb +1 -0
- data/test/transformer_test.rb +27 -0
- data/test/xmi_class_instantiator_test.rb +8 -91
- data/test/xmi_instantiator_test/class_model_checker.rb +97 -0
- data/test/xmi_object_instantiator_test.rb +3 -3
- data/test/xml_instantiator_test/testmodel.xml +7 -0
- data/test/xml_instantiator_test.rb +83 -0
- metadata +9 -6
- data/lib/rgen/xml_instantiator/dependency_resolver.rb +0 -23
- data/lib/rgen/xml_instantiator/xml_instantiator.rb +0 -78
- data/lib/rgen/xml_instantiator/xml_parser.rb +0 -39
data/CHANGELOG
CHANGED
@@ -7,3 +7,12 @@
|
|
7
7
|
* Added model transformation language (Transformer)
|
8
8
|
* Now RGen is distributed as a gem
|
9
9
|
* More complete documentation
|
10
|
+
|
11
|
+
=0.3.0 (October 9th, 2006)
|
12
|
+
|
13
|
+
* Improved XML Instantiator (Namespaces, Resolver, Customization)
|
14
|
+
* Added many_to_one builder method
|
15
|
+
* Added attribute reflection to MMBase (one_attributes, many_attributes)
|
16
|
+
* Added +copy+ method to Transformer
|
17
|
+
* Added simple model dumper module
|
18
|
+
* Fixed mmgen/mmgen.rb
|
@@ -1,18 +1,15 @@
|
|
1
|
-
require 'rgen/xml_instantiator
|
1
|
+
require 'rgen/xml_instantiator'
|
2
2
|
require 'rgen/environment'
|
3
3
|
require 'uml/uml_classmodel'
|
4
4
|
require 'ea/xmi_metamodel'
|
5
5
|
|
6
6
|
# This module can be used to instantiate an UMLClassModel from an XMI description.
|
7
|
-
# It should be mixed into the using class.
|
8
7
|
#
|
9
8
|
# Here is an example:
|
10
9
|
#
|
11
|
-
# include XMIClassInstantiator
|
12
|
-
#
|
13
10
|
# envUML = RGen::Environment.new
|
14
11
|
# File.open(MODEL_DIR+"/testmodel.xml") { |f|
|
15
|
-
# instantiateUMLClassModel(envUML, f.read)
|
12
|
+
# XMIClassInstantiator.new.instantiateUMLClassModel(envUML, f.read)
|
16
13
|
# }
|
17
14
|
#
|
18
15
|
# # now use the newly created UML model
|
@@ -22,24 +19,28 @@ require 'ea/xmi_metamodel'
|
|
22
19
|
#
|
23
20
|
# This module relies on XmiToClassmodel to do the actual transformation.
|
24
21
|
#
|
25
|
-
|
22
|
+
class XMIClassInstantiator < RGen::XMLInstantiator
|
26
23
|
|
27
24
|
include UMLClassModel
|
25
|
+
|
26
|
+
map_tag_ns "omg.org/UML1.3", XMIMetaModel::UML
|
27
|
+
|
28
|
+
resolve_by_id :typeClass, :src => :type, :id => :xmi_id
|
29
|
+
resolve_by_id :subtypeClass, :src => :subtype, :id => :xmi_id
|
30
|
+
resolve_by_id :supertypeClass, :src => :supertype, :id => :xmi_id
|
28
31
|
|
32
|
+
def initialize
|
33
|
+
@envXMI = RGen::Environment.new
|
34
|
+
super(@envXMI, XMIMetaModel, true)
|
35
|
+
end
|
36
|
+
|
29
37
|
# This method does the actual work.
|
30
38
|
def instantiateUMLClassModel(envOut, str)
|
31
|
-
|
32
|
-
|
33
|
-
i.resolveById("xmi_id","subtype")
|
34
|
-
i.resolveById("xmi_id","supertype")
|
35
|
-
end
|
36
|
-
|
37
|
-
envXMI = RGen::Environment.new
|
38
|
-
inst.instantiate(envXMI, str)
|
39
|
-
|
39
|
+
instantiate(str)
|
40
|
+
|
40
41
|
require 'ea/xmi_to_classmodel'
|
41
42
|
|
42
|
-
XmiToClassmodel.new(envXMI,envOut).transform
|
43
|
+
XmiToClassmodel.new(@envXMI,envOut).transform
|
43
44
|
end
|
44
45
|
|
45
46
|
end
|
data/lib/ea/xmi_helper.rb
CHANGED
data/lib/ea/xmi_metamodel.rb
CHANGED
@@ -1,19 +1,34 @@
|
|
1
|
+
module XMIMetaModel
|
1
2
|
|
2
|
-
module
|
3
|
+
module UML
|
4
|
+
|
3
5
|
include RGen::MetamodelBuilder
|
4
|
-
class
|
6
|
+
class Classifier_feature < MMBase
|
7
|
+
has_many 'operation'
|
8
|
+
end
|
9
|
+
class ClassifierRole < MMBase
|
10
|
+
end
|
11
|
+
class Clazz < ClassifierRole
|
12
|
+
has_many 'modelElement_stereotype'
|
5
13
|
end
|
6
|
-
class
|
14
|
+
class Operation < MMBase
|
15
|
+
has_one 'parent'
|
7
16
|
end
|
8
|
-
class
|
17
|
+
class Generalization < MMBase
|
9
18
|
end
|
10
|
-
class
|
19
|
+
class ModelElement_stereotype < MMBase
|
20
|
+
has_one 'parent'
|
11
21
|
end
|
12
|
-
class
|
22
|
+
class AssociationEnd < MMBase
|
13
23
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
24
|
+
class AssociationEndRole < MMBase
|
25
|
+
end
|
26
|
+
ClassifierRole.one_to_many 'associationEnds', AssociationEnd, 'typeClass'
|
27
|
+
ClassifierRole.one_to_many 'associationEndRoles', AssociationEndRole, 'typeClass'
|
28
|
+
Clazz.one_to_many 'generalizationsAsSubtype', Generalization, 'subtypeClass'
|
29
|
+
Clazz.one_to_many 'generalizationsAsSupertype', Generalization, 'supertypeClass'
|
30
|
+
|
18
31
|
end
|
32
|
+
|
33
|
+
end
|
19
34
|
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require 'rgen/xml_instantiator
|
1
|
+
require 'rgen/xml_instantiator'
|
2
2
|
require 'rgen/environment'
|
3
3
|
require 'uml/uml_objectmodel'
|
4
4
|
require 'ea/xmi_metamodel'
|
5
5
|
|
6
6
|
# This module can be used to instantiate an UMLObjectModel from an XMI description.
|
7
|
-
# It should be mixed into the using class.
|
8
7
|
#
|
9
8
|
# Here is an example:
|
10
9
|
#
|
@@ -12,7 +11,7 @@ require 'ea/xmi_metamodel'
|
|
12
11
|
#
|
13
12
|
# envUML = RGen::Environment.new
|
14
13
|
# File.open(MODEL_DIR+"/testmodel.xml") { |f|
|
15
|
-
# instantiateUMLObjectModel(envUML, f.read)
|
14
|
+
# XMIClassInstantiator.new.instantiateUMLObjectModel(envUML, f.read)
|
16
15
|
# }
|
17
16
|
#
|
18
17
|
# # now use the newly created UML model
|
@@ -22,21 +21,26 @@ require 'ea/xmi_metamodel'
|
|
22
21
|
#
|
23
22
|
# This module relies on XmiToObjectmodel to do the actual transformation.
|
24
23
|
#
|
25
|
-
|
24
|
+
class XMIObjectInstantiator < RGen::XMLInstantiator
|
26
25
|
|
27
26
|
include UMLObjectModel
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
envXMI = RGen::Environment.new
|
35
|
-
inst.instantiate(envXMI, str)
|
28
|
+
map_tag_ns "omg.org/UML1.3", XMIMetaModel::UML
|
29
|
+
|
30
|
+
resolve_by_id :typeClass, :src => :type, :id => :xmi_id
|
36
31
|
|
32
|
+
def initialize
|
33
|
+
@envXMI = RGen::Environment.new
|
34
|
+
super(@envXMI, XMIMetaModel, true)
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method does the actual work.
|
38
|
+
def instantiateUMLObjectModel(envOut, str)
|
39
|
+
instantiate(str)
|
40
|
+
|
37
41
|
require 'ea/xmi_to_objectmodel'
|
38
42
|
|
39
|
-
XmiToObjectmodel.new(envXMI,envOut).transform
|
43
|
+
XmiToObjectmodel.new(@envXMI,envOut).transform
|
40
44
|
end
|
41
45
|
|
42
46
|
end
|
data/lib/ea/xmi_to_classmodel.rb
CHANGED
@@ -17,45 +17,45 @@ class XmiToClassmodel < RGen::Transformer
|
|
17
17
|
# Do the actual transformation.
|
18
18
|
# Input and output environment have to be provided to the transformer constructor.
|
19
19
|
def transform
|
20
|
-
trans(:class => XMIMetaModel::
|
20
|
+
trans(:class => XMIMetaModel::UML::Clazz)
|
21
21
|
end
|
22
22
|
|
23
|
-
transform XMIMetaModel::
|
23
|
+
transform XMIMetaModel::UML::Package, :to => UMLPackage do
|
24
24
|
{ :name => name,
|
25
|
-
:superpackage => trans(
|
25
|
+
:superpackage => trans(parent.parent.is_a?(XMIMetaModel::UML::Package) ? parent.parent : nil) }
|
26
26
|
end
|
27
27
|
|
28
|
-
transform XMIMetaModel::
|
28
|
+
transform XMIMetaModel::UML::Attribute, :to => UMLAttribute do
|
29
29
|
tv = TaggedValueHelper.new(@current_object)
|
30
30
|
{ :name => name, :type => tv['type'] }
|
31
31
|
end
|
32
32
|
|
33
|
-
transform XMIMetaModel::
|
33
|
+
transform XMIMetaModel::UML::Operation, :to => UMLOperation do
|
34
34
|
{ :name => name }
|
35
35
|
end
|
36
36
|
|
37
|
-
transform XMIMetaModel::
|
37
|
+
transform XMIMetaModel::UML::TaggedValue, :to => UMLTaggedValue do
|
38
38
|
{ :tag => tag, :value => value }
|
39
39
|
end
|
40
40
|
|
41
|
-
transform XMIMetaModel::
|
41
|
+
transform XMIMetaModel::UML::Clazz, :to => UMLClass do
|
42
42
|
{ :name => name,
|
43
|
-
:package => trans(
|
44
|
-
:attributes => trans(
|
45
|
-
:operations => trans(
|
46
|
-
:taggedvalues => trans(
|
47
|
-
:stereotypes =>
|
48
|
-
:subclasses => trans(
|
49
|
-
:assocEnds => trans(
|
43
|
+
:package => trans(parent.parent.is_a?(XMIMetaModel::UML::Package) ? parent.parent : nil),
|
44
|
+
:attributes => trans(classifier_feature.attribute),
|
45
|
+
:operations => trans(classifier_feature.operation),
|
46
|
+
:taggedvalues => trans(modelElement_taggedValue.taggedValue),
|
47
|
+
:stereotypes => modelElement_stereotype.stereotype.name,
|
48
|
+
:subclasses => trans(generalizationsAsSupertype.subtypeClass),
|
49
|
+
:assocEnds => trans(associationEnds)}
|
50
50
|
end
|
51
51
|
|
52
|
-
transform XMIMetaModel::
|
52
|
+
transform XMIMetaModel::UML::Association, :to => :scAssociationClass do
|
53
53
|
{ :endA => trans(scAssocEnds[0]),
|
54
54
|
:endB => trans(scAssocEnds[1]) }
|
55
55
|
end
|
56
56
|
|
57
57
|
method :scAssocEnds do
|
58
|
-
|
58
|
+
association_connection.associationEnd
|
59
59
|
end
|
60
60
|
|
61
61
|
method :scAssociationClass do
|
@@ -64,11 +64,11 @@ class XmiToClassmodel < RGen::Transformer
|
|
64
64
|
UMLAggregation : UMLAssociation )
|
65
65
|
end
|
66
66
|
|
67
|
-
transform XMIMetaModel::
|
67
|
+
transform XMIMetaModel::UML::AssociationEnd, :to => UMLAssociationEnd do
|
68
68
|
# since we don't want to figure out if we are end A or end B,
|
69
69
|
# we let the association transformer do the work
|
70
|
-
trans(
|
71
|
-
{ :clazz => trans(
|
70
|
+
trans(parent.parent)
|
71
|
+
{ :clazz => trans(typeClass),
|
72
72
|
:role => name,
|
73
73
|
:multiplicity => multiplicity,
|
74
74
|
:composite => (aggregation == 'composite'),
|
@@ -17,22 +17,25 @@ class XmiToObjectmodel < RGen::Transformer
|
|
17
17
|
# Do the actual transformation.
|
18
18
|
# Input and output environment have to be provided to the transformer constructor.
|
19
19
|
def transform
|
20
|
-
trans(
|
20
|
+
trans(
|
21
|
+
@env_in.find(:class => XMIMetaModel::UML::ClassifierRole)-
|
22
|
+
@env_in.find(:class => XMIMetaModel::UML::Clazz)
|
23
|
+
)
|
21
24
|
end
|
22
25
|
|
23
|
-
transform XMIMetaModel::
|
26
|
+
transform XMIMetaModel::UML::Package, :to => UMLPackage do
|
24
27
|
{ :name => name,
|
25
|
-
:superpackage => trans(
|
28
|
+
:superpackage => trans(parent.parent.is_a?(XMIMetaModel::UML::Package) ? parent.parent : nil) }
|
26
29
|
end
|
27
30
|
|
28
|
-
transform XMIMetaModel::
|
29
|
-
trans(
|
30
|
-
trans(
|
31
|
+
transform XMIMetaModel::UML::ClassifierRole, :to => UMLObject, :if => :isEASTypeObject do
|
32
|
+
trans(associationEndRoles.parent.parent)
|
33
|
+
trans(associationEnds.parent.parent)
|
31
34
|
taggedValues = TaggedValueHelper.new(@current_object)
|
32
35
|
{ :name => name,
|
33
36
|
:classname => taggedValues['classname'],
|
34
37
|
:attributeSettings => createAttributeSettings(taggedValues['runstate']),
|
35
|
-
:package => trans(
|
38
|
+
:package => trans(parent.parent.parent.parent) }
|
36
39
|
end
|
37
40
|
|
38
41
|
method :isEASTypeObject do
|
@@ -56,8 +59,8 @@ class XmiToObjectmodel < RGen::Transformer
|
|
56
59
|
result
|
57
60
|
end
|
58
61
|
|
59
|
-
transform XMIMetaModel::
|
60
|
-
ends =
|
62
|
+
transform XMIMetaModel::UML::AssociationRole, :to => UMLAssociation, :if => :isEATypeAssociation do
|
63
|
+
ends = association_connection.associationEndRole
|
61
64
|
{ :endA => trans(ends[0]), :endB => trans(ends[1])}
|
62
65
|
end
|
63
66
|
|
@@ -66,21 +69,21 @@ class XmiToObjectmodel < RGen::Transformer
|
|
66
69
|
taggedValues['ea_type'] == 'Association'
|
67
70
|
end
|
68
71
|
|
69
|
-
transform XMIMetaModel::
|
70
|
-
ends =
|
72
|
+
transform XMIMetaModel::UML::Association, :to => UMLAggregation do
|
73
|
+
ends = association_connection.associationEnd
|
71
74
|
{ :endA => trans(ends[0]), :endB => trans(ends[1])}
|
72
75
|
end
|
73
76
|
|
74
|
-
transform XMIMetaModel::
|
77
|
+
transform XMIMetaModel::UML::AssociationEndRole, :to => UMLAssociationEnd do
|
75
78
|
buildAssociationEnd
|
76
79
|
end
|
77
80
|
|
78
|
-
transform XMIMetaModel::
|
81
|
+
transform XMIMetaModel::UML::AssociationEnd, :to => UMLAssociationEnd do
|
79
82
|
buildAssociationEnd
|
80
83
|
end
|
81
84
|
|
82
85
|
method :buildAssociationEnd do
|
83
|
-
{ :object => trans(
|
86
|
+
{ :object => trans(typeClass),
|
84
87
|
:role => name,
|
85
88
|
:composite => (aggregation == 'composite'),
|
86
89
|
:navigable => (isNavigable == 'true') }
|
data/lib/mmgen/mmgen.rb
CHANGED
@@ -1,21 +1,26 @@
|
|
1
|
-
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),"..")
|
2
|
+
|
3
|
+
require 'ea/xmi_class_instantiator'
|
2
4
|
require 'mmgen/metamodel_generator'
|
3
5
|
|
4
|
-
include XMIClassInstantiator
|
5
6
|
include MMGen::MetamodelGenerator
|
6
7
|
|
7
|
-
unless ARGV.length
|
8
|
-
puts "Usage: mmgen.rb <xmi_class_model_file>"
|
8
|
+
unless ARGV.length >= 2
|
9
|
+
puts "Usage: mmgen.rb <xmi_class_model_file> <root package> (<module>)*"
|
9
10
|
exit
|
10
11
|
else
|
11
12
|
file_name = ARGV.shift
|
12
|
-
|
13
|
+
root_package_name = ARGV.shift
|
14
|
+
modules = ARGV
|
15
|
+
out_file = file_name.gsub(/\.\w+$/,'.rb')
|
13
16
|
puts out_file
|
14
17
|
end
|
15
18
|
|
16
19
|
envUML = RGen::Environment.new
|
17
20
|
File.open(file_name) { |f|
|
18
|
-
instantiateUMLClassModel(envUML, f.read)
|
21
|
+
XMIClassInstantiator.new.instantiateUMLClassModel(envUML, f.read)
|
19
22
|
}
|
20
23
|
|
21
|
-
|
24
|
+
rootPackage = envUML.find(:class => UMLClassModel::UMLPackage).select{|p| p.name == root_package_name}.first
|
25
|
+
|
26
|
+
generateMetamodel(rootPackage, out_file, modules)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
class Instantiator
|
4
|
+
|
5
|
+
ResolverDescription = Struct.new(:from, :attribute, :block) # :nodoc:
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :resolver_descs
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(env, mod)
|
12
|
+
@env = env
|
13
|
+
end
|
14
|
+
|
15
|
+
# Specifies that +attribute+ should be resolved. If +:class+ is specified,
|
16
|
+
# resolve +attribute+ only for objects of type class.
|
17
|
+
# The block must return the value to which the attribute should be assigned.
|
18
|
+
# The object for which the attribute is to be resolved will be accessible
|
19
|
+
# in the current context within the block.
|
20
|
+
#
|
21
|
+
def self.resolve(attribute, desc=nil, &block)
|
22
|
+
from = (desc.is_a?(Hash) && desc[:class])
|
23
|
+
self.resolver_descs ||= []
|
24
|
+
self.resolver_descs << ResolverDescription.new(from, attribute, block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Resolves +attribute+ to a model element which has attribute +:id+ set to the
|
28
|
+
# value currently in attribute +:src+
|
29
|
+
#
|
30
|
+
def self.resolve_by_id(attribute, desc)
|
31
|
+
id_attr = (desc.is_a?(Hash) && desc[:id])
|
32
|
+
src_attr = (desc.is_a?(Hash) && desc[:src])
|
33
|
+
raise StandardError.new("No id attribute given.") unless id_attr
|
34
|
+
resolve(attribute) do
|
35
|
+
@env.find(id_attr => @current_object.send(src_attr)).first
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def method_missing(m, *args) #:nodoc:
|
42
|
+
if @current_object
|
43
|
+
@current_object.send(m)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resolve
|
50
|
+
self.class.resolver_descs ||= []
|
51
|
+
self.class.resolver_descs.each { |desc|
|
52
|
+
@env.find(:class => desc.from).each { |e|
|
53
|
+
old_object, @current_object = @current_object, e
|
54
|
+
e.send("#{desc.attribute}=", instance_eval(&desc.block)) if e.respond_to?("#{desc.attribute}=")
|
55
|
+
@current_object = old_object
|
56
|
+
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -74,6 +74,12 @@ module BuilderExtensions
|
|
74
74
|
target_class.has_one_internal(target_role,self,own_role,:many)
|
75
75
|
end
|
76
76
|
|
77
|
+
# This is the inverse of one_to_many provided for convenience.
|
78
|
+
def many_to_one(own_role, target_class, target_role)
|
79
|
+
has_one_internal(own_role,target_class,target_role,:many)
|
80
|
+
target_class.has_many_internal(target_role,self,own_role,:one)
|
81
|
+
end
|
82
|
+
|
77
83
|
# Add a bidirectional many-to-many association between two classes.
|
78
84
|
# The class this method is called on is refered to as _own_class_ in
|
79
85
|
# the following.
|
@@ -124,12 +130,31 @@ module BuilderExtensions
|
|
124
130
|
target_class.has_one_internal(target_role,self,own_role,:one)
|
125
131
|
end
|
126
132
|
|
133
|
+
# Returns the names of the classes attributes pointing to a single object.
|
134
|
+
# The result includes this kind of attributes of the superclass.
|
135
|
+
def one_attributes
|
136
|
+
@one_attributes ||= []
|
137
|
+
result = @one_attributes.dup
|
138
|
+
result += superclass.one_attributes if superclass.respond_to?(:one_attributes)
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns the names of the classes attributes pointing to many other objects
|
143
|
+
# The result includes this kind of attributes of the superclass.
|
144
|
+
def many_attributes
|
145
|
+
@many_attributes ||= []
|
146
|
+
result = @many_attributes.dup
|
147
|
+
result += superclass.many_attributes if superclass.respond_to?(:many_attributes)
|
148
|
+
result
|
149
|
+
end
|
150
|
+
|
127
151
|
protected
|
128
152
|
|
129
153
|
def has_one_internal(name, cls=nil, role=nil, kind=nil)
|
154
|
+
@one_attributes ||= []
|
155
|
+
return if @one_attributes.include?(name)
|
156
|
+
@one_attributes << name
|
130
157
|
BuildHelper.build self, binding, <<-CODE
|
131
|
-
@@one_assocs ||= []
|
132
|
-
@@one_assocs << name
|
133
158
|
def #{name}=(val)
|
134
159
|
return if val == @#{name}
|
135
160
|
<% if cls %>
|
@@ -149,9 +174,10 @@ module BuilderExtensions
|
|
149
174
|
end
|
150
175
|
|
151
176
|
def has_many_internal(name, cls=nil, role=nil, kind = nil)
|
177
|
+
@many_attributes ||= []
|
178
|
+
return if @many_attributes.include?(name)
|
179
|
+
@many_attributes << name
|
152
180
|
BuildHelper.build self, binding, <<-CODE
|
153
|
-
@@many_assocs ||= []
|
154
|
-
@@many_assocs << name
|
155
181
|
def add<%= firstToUpper(name) %>(val)
|
156
182
|
@#{name} = [] unless @#{name}
|
157
183
|
return if val.nil? or @#{name}.include?(val)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
module ModelDumper
|
4
|
+
|
5
|
+
def dump(obj=nil)
|
6
|
+
obj ||= self
|
7
|
+
if obj.is_a?(Array)
|
8
|
+
obj.collect {|o| dump(o)}.join("\n\n")
|
9
|
+
elsif obj.class.respond_to?(:one_attributes) && obj.class.respond_to?(:many_attributes)
|
10
|
+
([obj.to_s] +
|
11
|
+
obj.class.one_attributes.collect { |a|
|
12
|
+
" #{a} => #{obj.getGeneric(a)}"
|
13
|
+
} +
|
14
|
+
obj.class.many_attributes.collect { |a|
|
15
|
+
" #{a} => [ #{obj.getGeneric(a).join(', ')} ]"
|
16
|
+
}).join("\n")
|
17
|
+
else
|
18
|
+
obj.to_s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/rgen/name_helper.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
module RGen
|
5
5
|
|
6
6
|
module NameHelper
|
7
|
-
def
|
7
|
+
def normalize(name)
|
8
8
|
name.gsub(/[\.:]/,'_')
|
9
9
|
end
|
10
10
|
def className(object)
|
@@ -13,6 +13,9 @@ module NameHelper
|
|
13
13
|
def firstToUpper(str)
|
14
14
|
str[0..0].upcase + ( str[1..-1] || "" )
|
15
15
|
end
|
16
|
+
def firstToLower(str)
|
17
|
+
str[0..0].downcase + ( str[1..-1] || "" )
|
18
|
+
end
|
16
19
|
end
|
17
20
|
|
18
21
|
end
|
data/lib/rgen/transformer.rb
CHANGED
@@ -196,6 +196,22 @@ class Transformer
|
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
199
|
+
# This class method specifies that all objects of class +from+ are to be copied
|
200
|
+
# into an object of class +to+. If +to+ is omitted, +from+ is used as target class.
|
201
|
+
# During copy, all attributes according to
|
202
|
+
# MetamodelBuilder::BuilderExtensions.one_attributes and
|
203
|
+
# MetamodelBuilder::BuilderExtensions.many_attributes of the target object
|
204
|
+
# are set to their transformed counterparts of the source object.
|
205
|
+
#
|
206
|
+
def self.copy(from, to=nil)
|
207
|
+
transform(from, :to => to || from) do
|
208
|
+
Hash[*(@current_object.class.one_attributes +
|
209
|
+
@current_object.class.many_attributes).inject([]) {|l,a|
|
210
|
+
l + [a.to_sym, trans(@current_object.send(a))]
|
211
|
+
}]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
199
215
|
# Define a transformer method for the current transformer class.
|
200
216
|
# In contrast to regular Ruby methods, a method defined this way executes in the
|
201
217
|
# context of the object currently being transformed.
|
@@ -228,10 +244,12 @@ class Transformer
|
|
228
244
|
# * a hash used as input to Environment#find with the result being transformed
|
229
245
|
#
|
230
246
|
def trans(obj)
|
247
|
+
obj = @env_in.find(obj) if obj.is_a?(Hash)
|
231
248
|
return nil if obj.nil?
|
249
|
+
return obj if obj.is_a?(TrueClass) or obj.is_a?(FalseClass) or obj.is_a?(Numeric) or obj.is_a?(Symbol)
|
232
250
|
return @transformer_results[obj] if @transformer_results[obj]
|
233
|
-
obj =
|
234
|
-
return obj.collect{|o| trans(o)}.compact if obj.is_a?
|
251
|
+
return @transformer_results[obj] = obj.dup if obj.is_a?(String)
|
252
|
+
return obj.collect{|o| trans(o)}.compact if obj.is_a? Array
|
235
253
|
raise StandardError.new("No transformer for class #{obj.class.name}") unless self.class._transformer_blocks[obj.class]
|
236
254
|
block_desc = _evaluateCondition(obj)
|
237
255
|
return nil unless block_desc
|