File: constant_order_helper.rb

package info (click to toggle)
ruby-rgen 0.10.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,428 kB
  • sloc: ruby: 11,344; xml: 1,368; yacc: 72; makefile: 10
file content (89 lines) | stat: -rw-r--r-- 3,489 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
module RGen

module MetamodelBuilder

# The purpose of the ConstantOrderHelper is to capture the definition order of RGen metamodel builder 
# classes, modules and enums. The problem is that Ruby doesn't seem to track the order of
# constants being created in a module. However the order is important because it defines the order
# of eClassifiers and eSubpackages in a EPackage.
#
# It would be helpful here if Ruby provided a +const_added+ callback, but this is not the case up to now.
#
# The idea for capturing is that all events of creating a RGen class, module or enum are reported to the
# ConstantOrderHelper singleton.
# For classes and modules it tries to add their names to the parent's +_constantOrder+ array.
# The parent module is derived from the class's or module's name. However, the new name is only added
# if the respective parent module has a new constant (which is not yet in +_constantOrder+) which
# points to the new class or module.
# For enums it is a bit more complicated, because at the time the enum is created, the parent
# module does not yet contain the constant to which the enum is assigned. Therefor, the enum is remembered
# and it is tried to be stored on the next event (class, module or enum) within the module which was
# created last (which was last extended with ModuleExtension). If it can not be found in that module,
# all parent modules of the last module are searched. This way it should also be correctly entered in
# case it was defined outside of the last created module. 
# Note that an enum is not stored to the constant order array unless another event occurs. That's why
# it is possible that one enum is missing at the enum. This needs to be taken care of by the ECore transformer.
#
# This way of capturing should be sufficient for the regular use cases of the RGen metamodel builder language.
# However, it is possible to write code which messes this up, see unit tests for details.
# In the worst case, the new classes, modules or enums will just not be found in a parent module and thus be ignored.
#
ConstantOrderHelper = Class.new do

  def initialize
    @currentModule = nil
    @pendingEnum = nil
  end

  def classCreated(c)
    handlePendingEnum
    cont = containerModule(c)
    name = (c.name || "").split("::").last
    return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name)
    cont._constantOrder << name
  end

  def moduleCreated(m)
    handlePendingEnum
    cont = containerModule(m)
    name = (m.name || "").split("::").last
    return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name)
    cont._constantOrder << name
    @currentModule = m
  end

  def enumCreated(e)
    handlePendingEnum
    @pendingEnum = e
  end

  private

  def containerModule(m)
    containerName = (m.name || "").split("::")[0..-2].join("::")
    containerName.empty? ? nil : eval(containerName, TOPLEVEL_BINDING)
  end 

  def handlePendingEnum
    return unless @pendingEnum
    m = @currentModule
    while m
      if m.respond_to?(:_constantOrder)
        newConstants = m.constants - m._constantOrder
        const = newConstants.find{|c| m.const_get(c).object_id == @pendingEnum.object_id}
        if const
          m._constantOrder << const.to_s
          break
        end
      end
      m = containerModule(m)
    end
    @pendingEnum = nil
  end
      
end.new

end

end