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 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