class RGen::ECore::ECoreToRuby

ECoreToRuby can turn ECore models into their Ruby metamodel representations

Attributes

_ecore_to_ruby[RW]

Public Class Methods

new() click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 11
def initialize
  @modules = {}
  @classifiers = {}
  @features_added = {}
  @reserved = Set.new(Object.methods)
end

Public Instance Methods

add_features(eclass) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 240
def add_features(eclass)
  return false if @features_added[eclass]
  c = @classifiers[eclass]
  eclass.eStructuralFeatures.each do |f|
    w1 = FeatureWrapper.new(f, @classifiers) 
    w2 = FeatureWrapper.new(f.eOpposite, @classifiers) if f.is_a?(RGen::ECore::EReference) && f.eOpposite
    c.module_eval do
      if w1.many?
        _build_many_methods(w1, w2)
      else
        _build_one_methods(w1, w2)
      end
    end
  end
  @features_added[eclass] = true
  eclass.eSuperTypes.each do |t|
    add_features(t)
  end
  true
end
create_module(epackage, under=Module.new) click to toggle source

Create a Ruby module representing epackage. This includes all nested modules/packages, classes and enums.

If a parent module is provided with the “under” parameter, the new module will be nested under the parent module.

If the parent module has a non-temporary name, (more precisely: a non-temporary classpath) i.e. if it is reachable via a path of constant names from the root, then the nested modules and classes will also have non-temporary names. In particular, this means that they will keep their names even if they are assigned to new constants.

If no parent module is provided or the parent module has a temporary name by itself, then the nested modules and classes will also have temporary names. This means that their name will stay 'volatile' until they are assigned to constants reachable from the root and the Module#name method is called for the first time.

While the second approach is more flexible, it can come with a major performance impact. The reason is that Ruby searches the space of all known non-temporary classes/modules every time the name of a class/module with a temporary name is queried.

# File lib/rgen/ecore/ecore_to_ruby.rb, line 42
def create_module(epackage, under=Module.new)
  with_empty_constant_order_helper do
    temp = under.to_s.start_with?("#")
    mod = create_module_internal(epackage, under, temp)

    epackage.eAllClassifiers.each do |c| 
      if c.is_a?(RGen::ECore::EClass)
        create_class(c, temp)
      elsif c.is_a?(RGen::ECore::EEnum)
        create_enum(c)
      end
    end

    load_classes_with_reserved_keywords(epackage)
    mod
  end
end

Private Instance Methods

create_class(eclass, temp) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 96
  def create_class(eclass, temp)
    return @classifiers[eclass] if @classifiers[eclass]

    mod = @modules[eclass.ePackage]
    if temp
      cls = Class.new(super_class(eclass, temp)) do
        abstract if eclass.abstract
        class << self
          attr_accessor :_ecore_to_ruby
         end
      end
      mod.const_set(eclass.name, cls)
    else
      mod.module_eval <<-END
        class #{eclass.name} < #{super_class(eclass, temp)}
          #{eclass.abstract ? 'abstract' : ''}
          class << self
            attr_accessor :_ecore_to_ruby
          end
        end
      END
      cls = mod.const_get(eclass.name)
    end

    class << eclass
      attr_accessor :instanceClass
      def instanceClassName
        instanceClass.to_s
      end
    end
    eclass.instanceClass = cls

    cls::ClassModule.module_eval do
      alias _method_missing method_missing
      def method_missing(m, *args)
        if self.class._ecore_to_ruby.add_features(self.class.ecore)
          send(m, *args)
        else
          _method_missing(m, *args)
        end
      end
      alias _respond_to respond_to?
      def respond_to?(m, include_all=false)
        self.class._ecore_to_ruby.add_features(self.class.ecore)
        _respond_to(m)
      end
    end
    @classifiers[eclass] = cls
    cls._set_ecore_internal(eclass)
    cls._ecore_to_ruby = self

    cls
  end
create_enum(eenum) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 150
def create_enum(eenum)
  return @classifiers[eenum] if @classifiers[eenum]

  e = RGen::MetamodelBuilder::DataTypes::Enum.new(eenum.eLiterals.collect{|l| l.name.to_sym})
  @classifiers[eenum] = e

  @modules[eenum.ePackage].const_set(eenum.name, e)
  e
end
create_module_internal(epackage, under, temp) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 72
  def create_module_internal(epackage, under, temp)
    return @modules[epackage] if @modules[epackage]
    
    if temp
      mod = Module.new do
        extend RGen::MetamodelBuilder::ModuleExtension
      end
      under.const_set(epackage.name, mod)
    else
      under.module_eval <<-END
        module #{epackage.name}
          extend RGen::MetamodelBuilder::ModuleExtension
        end
      END
      mod = under.const_get(epackage.name)
    end
    @modules[epackage] = mod

    epackage.eSubpackages.each{|p| create_module_internal(p, mod, temp)}
    mod._set_ecore_internal(epackage)

    mod
  end
load_classes_with_reserved_keywords(epackage) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 62
def load_classes_with_reserved_keywords(epackage)
  epackage.eAllClassifiers.each do |eclass|
    # we early load classes which have ruby reserved keywords
    if eclass.is_a?(RGen::ECore::EClass)
      reserved_used = eclass.eStructuralFeatures.any? { |f| @reserved.include?(f.name.to_sym) }
      add_features(eclass) if reserved_used
    end
  end
end
method_missing(m, *args) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 130
def method_missing(m, *args)
  if self.class._ecore_to_ruby.add_features(self.class.ecore)
    send(m, *args)
  else
    _method_missing(m, *args)
  end
end
respond_to?(m, include_all=false) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 138
def respond_to?(m, include_all=false)
  self.class._ecore_to_ruby.add_features(self.class.ecore)
  _respond_to(m)
end
super_class(eclass, temp) click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 193
def super_class(eclass, temp)
  super_types = eclass.eSuperTypes
  if temp
    case super_types.size
    when 0
      RGen::MetamodelBuilder::MMBase
    when 1
      create_class(super_types.first, temp)
    else
      RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t, temp)})
    end
  else
    case super_types.size
    when 0
      "RGen::MetamodelBuilder::MMBase"
    when 1
      create_class(super_types.first, temp).name
    else
      "RGen::MetamodelBuilder::MMMultiple(" + 
        super_types.collect{|t| create_class(t, temp).name}.join(",") + ")"
    end
  end
end
with_empty_constant_order_helper() { || ... } click to toggle source
# File lib/rgen/ecore/ecore_to_ruby.rb, line 223
def with_empty_constant_order_helper
  orig_coh = RGen::MetamodelBuilder::ConstantOrderHelper
  RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
  RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, EmptyConstantOrderHelper.new)

  begin
    result = yield
  ensure
    RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
    RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, orig_coh)
  end

  result
end