rgen 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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/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
- module XMIClassInstantiator
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
- inst = RGen::XMLInstantiator.new(XMIMetaModel, true) do |i|
32
- i.resolveById("xmi_id","type")
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
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  class TaggedValueHelper < MapHelper
18
18
  def initialize(element)
19
- super('tag','value',element.UML_ModelElement_taggedValue.UML_TaggedValue)
19
+ super('tag','value',element.modelElement_taggedValue.taggedValue)
20
20
  end
21
21
  end
22
22
 
@@ -1,19 +1,34 @@
1
+ module XMIMetaModel
1
2
 
2
- module XMIMetaModel
3
+ module UML
4
+
3
5
  include RGen::MetamodelBuilder
4
- class UML_Classifier_feature < MMBase
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 UML_Class < MMBase
14
+ class Operation < MMBase
15
+ has_one 'parent'
7
16
  end
8
- class UML_Operation < MMBase
17
+ class Generalization < MMBase
9
18
  end
10
- class UML_Generalization < MMBase
19
+ class ModelElement_stereotype < MMBase
20
+ has_one 'parent'
11
21
  end
12
- class UML_ModelElement_stereotype < MMBase
22
+ class AssociationEnd < MMBase
13
23
  end
14
- UML_Classifier_feature.one_to_many 'UML_Operation', UML_Operation, '_p_UML_Classifier_feature'
15
- UML_Class.one_to_many 'UML_ModelElement_stereotype', UML_ModelElement_stereotype, '_p_UML_Class'
16
- # UML_Class.one_to_many 'UML_Generalization_supertype', UML_Generalization, 'supertype_UML_Class'
17
- # UML_Class.one_to_many 'UML_Generalization_subtype', UML_Generalization, 'subtype_UML_Class'
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/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
- module XMIObjectInstantiator
24
+ class XMIObjectInstantiator < RGen::XMLInstantiator
26
25
 
27
26
  include UMLObjectModel
28
27
 
29
- def instantiateUMLObjectModel(envOut, str)
30
- inst = RGen::XMLInstantiator.new(XMIMetaModel, true) do |i|
31
- i.resolveById("xmi_id","type")
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
@@ -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::UML_Class)
20
+ trans(:class => XMIMetaModel::UML::Clazz)
21
21
  end
22
22
 
23
- transform XMIMetaModel::UML_Package, :to => UMLPackage do
23
+ transform XMIMetaModel::UML::Package, :to => UMLPackage do
24
24
  { :name => name,
25
- :superpackage => trans(_p_UML_Namespace_ownedElement._p_UML_Package) }
25
+ :superpackage => trans(parent.parent.is_a?(XMIMetaModel::UML::Package) ? parent.parent : nil) }
26
26
  end
27
27
 
28
- transform XMIMetaModel::UML_Attribute, :to => UMLAttribute do
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::UML_Operation, :to => UMLOperation do
33
+ transform XMIMetaModel::UML::Operation, :to => UMLOperation do
34
34
  { :name => name }
35
35
  end
36
36
 
37
- transform XMIMetaModel::UML_TaggedValue, :to => UMLTaggedValue do
37
+ transform XMIMetaModel::UML::TaggedValue, :to => UMLTaggedValue do
38
38
  { :tag => tag, :value => value }
39
39
  end
40
40
 
41
- transform XMIMetaModel::UML_Class, :to => UMLClass do
41
+ transform XMIMetaModel::UML::Clazz, :to => UMLClass do
42
42
  { :name => name,
43
- :package => trans(_p_UML_Namespace_ownedElement._p_UML_Package),
44
- :attributes => trans(getUML_Classifier_feature.UML_Attribute),
45
- :operations => trans(getUML_Classifier_feature.UML_Operation),
46
- :taggedvalues => trans(getUML_ModelElement_taggedValue.UML_TaggedValue),
47
- :stereotypes => getUML_ModelElement_stereotype.UML_Stereotype.name,
48
- :subclasses => trans(getUML_Generalization_supertype.subtype_UML_Class),
49
- :assocEnds => trans(getUML_AssociationEnd_type)}
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::UML_Association, :to => :scAssociationClass do
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
- getUML_Association_connection.UML_AssociationEnd
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::UML_AssociationEnd, :to => UMLAssociationEnd do
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(_p_UML_Association_connection._p_UML_Association)
71
- { :clazz => trans(type_UML_Class),
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(:class => XMIMetaModel::UML_ClassifierRole)
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::UML_Package, :to => UMLPackage do
26
+ transform XMIMetaModel::UML::Package, :to => UMLPackage do
24
27
  { :name => name,
25
- :superpackage => trans(_p_UML_Namespace_ownedElement._p_UML_Package) }
28
+ :superpackage => trans(parent.parent.is_a?(XMIMetaModel::UML::Package) ? parent.parent : nil) }
26
29
  end
27
30
 
28
- transform XMIMetaModel::UML_ClassifierRole, :to => UMLObject, :if => :isEASTypeObject do
29
- trans(getUML_AssociationEndRole_type._p_UML_Association_connection._p_UML_AssociationRole)
30
- trans(getUML_AssociationEnd_type._p_UML_Association_connection._p_UML_Association)
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(_p_UML_Namespace_ownedElement._p_UML_Collaboration._p_UML_Namespace_ownedElement._p_UML_Package) }
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::UML_AssociationRole, :to => UMLAssociation, :if => :isEATypeAssociation do
60
- ends = getUML_Association_connection.UML_AssociationEndRole
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::UML_Association, :to => UMLAggregation do
70
- ends = getUML_Association_connection.UML_AssociationEnd
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::UML_AssociationEndRole, :to => UMLAssociationEnd do
77
+ transform XMIMetaModel::UML::AssociationEndRole, :to => UMLAssociationEnd do
75
78
  buildAssociationEnd
76
79
  end
77
80
 
78
- transform XMIMetaModel::UML_AssociationEnd, :to => UMLAssociationEnd do
81
+ transform XMIMetaModel::UML::AssociationEnd, :to => UMLAssociationEnd do
79
82
  buildAssociationEnd
80
83
  end
81
84
 
82
85
  method :buildAssociationEnd do
83
- { :object => trans(type_UML_ClassifierRole),
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
- require 'ea/xmi_instantiator'
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 == 1
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
- out_file = file_name.gsub(/\.\w+$/,'')
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
- generateMetamodel(envUML, out_file)
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
@@ -4,7 +4,7 @@
4
4
  module RGen
5
5
 
6
6
  module NameHelper
7
- def normalizeName(name)
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
@@ -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 = @env_in.find(obj) if obj.is_a?(Hash)
234
- return obj.collect{|o| trans(o)}.compact if obj.is_a? Enumerable
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