=Paper=
{{Paper
|id=Vol-2019/multi_7
|storemode=property
|title=DeepRuby: Extending Ruby with Dual Deep Instantiation
|pdfUrl=https://ceur-ws.org/Vol-2019/multi_7.pdf
|volume=Vol-2019
|authors=Bernd Neumayr,Christoph G. Schuetz,Christian Horner,Michael Schrefl
|dblpUrl=https://dblp.org/rec/conf/models/NeumayrSHS17
}}
==DeepRuby: Extending Ruby with Dual Deep Instantiation==
DeepRuby: Extending Ruby with
Dual Deep Instantiation
Bernd Neumayr Christoph G. Schuetz
Department of Business Informatics – Data & Department of Business Informatics – Data &
Knowledge Engineering Knowledge Engineering
Johannes Kepler University Linz Johannes Kepler University Linz
Linz, Austria Linz, Austria
bernd.neumayr@jku.at christoph.schuetz@jku.at
Christian Horner Michael Schrefl
Department of Business Informatics – Data & Department of Business Informatics – Data &
Knowledge Engineering Knowledge Engineering
Johannes Kepler University Linz Johannes Kepler University Linz
Linz, Austria Linz, Austria
michael.schrefl@jku.at
Abstract—Clabjects, the central construct of multi-level mod- ultimate instance of the clabject or property. For example,
eling, overcome the strict separation of class and object in con- clabject CarModel with potency 2 is instantiated by BMW Z4
ceptual modeling. Ruby, a dynamic object-oriented programming with potency 1 which is in turn instantiated by Peter’s Car
language, similarly treats classes as objects and thus appears as
a natural candidate for implementing clabject-based modeling with potency 0. Clabject CarModel defines a property engine
constructs. In this paper we introduce DeepRuby, a Ruby with potency 2 and target EngineModel which is instantiated
implementation of the core constructs of Dual Deep Instantiation: by BMW Z4 has engine EngineK5 and in turn by Peter’s Car
clabject hierarchies and attributes with separate source potency has engine Engine123; Engine123 is an instance of EngineK5
and target potency. DeepRuby represents clabjects at two layers: which is an instance of EngineModel. Clabject CarModel
the clabject layer and the clabject facet layer. At the clabject
facet layer, a clabject with maximum source potency i-1 and further defines a property listPrice with target currency value
maximum target potency j-1 is represented by a matrix of i × j and potency 1 which is instantiated by BMW Z4 has list price
clabject facets organized using Ruby’s superclass and eigenclass e 42,232.
constructs. Clabject facets can easily be extended with behavior Dual Deep Instantiation [9] (DDI) allows to specify the
implemented in custom methods. number of instantiation steps separately for the source and for
Index Terms—Object oriented programming, Metamodeling
the target of a property. For example, clabject CarModel, as
source, introduces property owner with source potency 2 and
I. I NTRODUCTION
target Person with target potency 1. This property is ultimately
Object-orientation is arguably the most important paradigm instantiated between instances of instances of CarModel as
in programming and conceptual modeling. Statically-typed source and instances of Person as target, for example by Peters
object-oriented programming languages, like Java, and tradi- Car has owner Peter. Clabject CarModel further has a self-
tional conceptual modeling approaches, like E/R and UML, describing property creator with source potency 0 and target
come with a strict separation between class and object. The Person and target potency 1. This property is instantiated
clabject as central construct of multi-level modeling [10] over- between CarModel as source and an instance of Person as
comes this separation and not only plays the roles of class and target, for example by CarModel has creator Peter.
object but also of metaclass, potentially at many classification In previous work, we formalized different variants of DDI
levels. Extending traditional modeling/programming languages in deductive database languages, namely F-Logic [9] and Con-
to supporting clabjects is difficult, due to this inherent mis- ceptBase [11], but without support for implementing behavior.
match. Dynamically typed languages like Ruby overcome the In this paper we introduce DeepRuby, an implementation of
strict separation between object and class: classes are also DDI in Ruby that supports the implementation of custom
treated as objects and may be extended at runtime. Based on methods. DeepRuby makes heavy use of Ruby’s dynamic
this kinship, Ruby suggests itself as a suitable language for programming and metaprogramming facilities [12]. The Deep-
implementing multilevel modeling constructs. Ruby version presented in this paper only implements a subset
Deep Instantiation [2] is one of the most prominent ap- of DDI: it does neither support clabject generalization nor
proaches to multi-level modeling. A potency assigned to a multi-valued attributes. These simplifications allow to set the
clabject or property indicates the number of instantiation focus on the following core idea of DeepRuby.
levels, i.e., the number of instantiation steps to reach the DeepRuby implements DDI at two layers, the clabject
1 module Social
Module 2 class Person
3 attr_accessor(:age)
eigenclass
attr_accessor() 4 def initialize(a)
superclass 5 self.class.incCounter
6 @age = a; end
class Class 7 end
8 class << Person
9 def counter; @counter; end
10 def incCounter
BasicObject #BasicObject 11 if @counter.nil?; @counter = 0; end
12 @counter = @counter + 1; end
13 end
14 class Woman < Person; end
Object 15 Mary = Woman.new(31)
#Object 16 Mary.age = 27
instance_
variables()
17 puts Mary.age # => 27
inspect2() 18 puts Person.counter # => nil
19 puts Woman.counter # => 1
20 class << Mary
21 attr_accessor(:name)
Person 22 end
#Person
23 Mary.name = "Maria"
age()
age=() counter() 24 end
initialize() incCounter() 25 class Object
26 def inspect2
27 str = "(#{self}"
Woman
28 instance_variables.each {|x| str = str +
#Woman 29 " #{x}=#{instance_variable_get(x)}"}
@counter = 1
30 str += ")"; end
31 end
32 puts Social::Person.inspect2
#Mary 33 # => (Social::Person)
Mary:Woman
34 puts Social::Woman.inspect2
@age = 27
name() 35 # => (Social::Woman @counter=1)
@name =„Maria“
name=()
36 puts Social::Mary.inspect2
37 # => (# @age=27 @name="Maria")
Fig. 1. Introductory example to Ruby’s object model: Ruby code and custom graphical representation of Ruby objects. Predefined objects depicted in grey
layer and the clabject facet layer. DeepRuby’s clabject facet a small but intricate example (see Fig. 1).
layer makes explicit each of the otherwise implicit facets Ruby’s modules provide a namespacing mechanism for
of a DDI clabject by a flat Ruby object. For example, constants, such as class names. Class Person (see line 1:2,
clabject facet CarModelˆ(2,1) with property owner=Person that is line 2 in the listing in Fig. 1) is created within
represents the instance-instance-type of CarModel. Clabject module Social and can be accessed outside the module by
facet CarModelˆ(0,1) with property creator=Person represents qualified name Social::Person (line 1:32). Note: method
the self-type of CarModel. CarModelˆ(0,0) with property puts writes a string representation of the given object to an
creator=Peter represents the self-value of CarModel. Clabject IO stream – for illustration, the actual output of the Ruby
facet CarModelˆ(2,2) with property engine=EngineModel rep- program is given in the program as a comment (e.g., # =>
resents the instance-instance-metatype of CarModel. Clabject (Social::Person)).
facet CarModelˆ(1,1) with property listPrice=CurrencyValue Member attributes of a Ruby class are defined as getter
represents the instance-type of CarModel. and setter methods that access instance variables. Instance
In the remainder of the paper, we give, in Sect. II, an variables are created when set by a method. To avoid the need
introduction to Ruby’s object model. In Sect. III, we introduce to write getters and setters by hand, class Module provides
a more intricate example DDI model and its representation a method attr_accessor that creates getter and setter
in DeepRuby. We also explain the clabject naming scheme methods for an attribute of a given name. For example, in line
typically used with DDI for clabjects with more than two 1:3, class Person (an instance of Class which inherits from
instantiation levels. Sect. IV explains the clabject facet layer. class Module) calls attr_accessor for symbol :age to
Sect. V exemplifies the extension of clabject facets with create setter method age= and getter method age in class
custom methods. Sect. VI gives an overview of related work. Person to write and read instance variables @age (names
Sect. VII concludes the paper with ongoing and future work re- of instance variables are marked by prefix ‘@’) of instances of
garding the implementation of advanced constructs of DDI [9] class Person, such as Mary (see line 1:16 and line 1:17).
and Dual Deep Modeling [11]. In Ruby, classes are treated as objects and can have instance
variables themselves, called class instance variables. Classes
II. BACKGROUND : RUBY ’ S O BJECT M ODEL
are instances of class Class and also may have an eigenclass
As a background for forthcoming sections this section (also referred to as singleton class). Methods defined with
explains some relevant aspects of Ruby’s object model along a class’s eigenclass (also referred to as singleton methods
1 module SalesMgmt
2 p = DDI::Model.new(SalesMgmt,3) Product3
3 DDI::Clabject.new(p,1,:Person) categoryMgr1-1 = Person
4 Person.new(:MsBlack) owner3-1 = Person
5 Person.new(:Peter)
6 Peter.define(:spouse,0,1,Person)
Category
7 Peter.ˆ(0,0).spouse = MsBlack Car2
Bike2
8 DDI::Clabject.new(p,2,:CarEngine) categoryMgr0-0
9 CarEngine.new(:EngineK5) = MsBlack categoryMgr0-0
engine2-2 = MsBlack
10 EngineK5.new(:Engine123)
= CarEngine
11 DDI::Clabject.new(p,3,:Product)
12 Product.define(:categoryMgr, 1, 1, Person)
13 .define(:owner, 3, 1, Person) Model BMW Z41 BromptonM6L1
14 Product.new(:Bike) engine1-1 = EngineK5
15 Bike.new(:BromptonM6L)
16 Bike.ˆ(0,0).categoryMgr = MsBlack
17 BromptonM6L.new(:PetersBike) Individual PetersCar0
PetersBike0
18 PetersBike.ˆ(0,0).owner = Peter owner0-0 = Peter
engine0-0=Engine123 owner0-0 = Peter
19 Product.new(:Car)
20 Car.define(:engine, 2, 2, CarEngine)
21 Car.categoryMgr = MsBlack
22 Car.new(:BMWZ4)
23 BMWZ4.ˆ(1,1).engine = EngineK5 Person1
24 BMWZ4.new(:PetersCar) favouriteItem1-3 = Product
25 PetersCar.ˆ(0,0).owner = Peter
26 PetersCar.ˆ(0,0).engine = Engine123
27 Person.define(:favouriteItem, 1, 3, Product) Individual
28 Peter.ˆ(0,0).favouriteItem = PetersCar MsBlack0
29 MsBlack.ˆ(0,1).favouriteItem = BromptonM6L favouriteItem0-1 =
30 puts Peter.favouriteItem.name BromptonM6L
31 # => PetersCar
Peter0
32 puts PetersCar.ˆ(0,1).engine.name
favouriteItem0-0 =
33 # => EngineK5 PetersCar
34 Product.getMembersN(2).each{|c| puts c.name } spouse0-1 = Person
35 # => BromptonM6L \n BMWZ4 spouse0-0 = MsBlack
36 end
Fig. 2. Running example: A DeepRuby program (left) realizing a DDI model (right)
or class methods) can be used to access a class’s class the direct or indirect superclass of all custom classes created
instance variables. The eigenclass of a class has as super- in Ruby programs and also the superclass of class Module
class the eigenclass of the class’s superclass. For example, and Class. A method added to class Object can thus be
Person’s eigenclass (labelled #Person in the graphical called from any Ruby object (with Ruby classes being also
representation) is opened by ‘class << Person’ at line Ruby objects). Method inspect2 (line 1:26) is defined with
1:8. Person’s eigenclass defines a getter method counter class Object; when invoked on an object, it creates a string
(line 1:9) together with a method incCounter (line 1:10) consisting of the object’s name and its instance variables (see
which is called (line 1:5) to increment the counter every lines 1:32–1:36).
time a new object is created. Class Woman has superclass
III. D UAL D EEP I NSTANTIATION IN RUBY – AN E XAMPLE
Person (defined by ‘class Woman < Person’ at line
1:14) and, thus, #Woman (the eigenclass of Woman) has In this section DeepRuby is explained along the example
superclass #Person (the eigenclass of Person). depicted and implemented in Fig. 2. Ruby’s modules are used
Class instance variables really belong to the class (as an as namespacing mechanism. The clabjects of a DDI model
object) and class methods are called in the context of a class are created within such a module/namespace. For example,
object. For example, when calling Woman.new to create a module SalesMgmt (line 2:1) serves as namespace for a DDI
new instance of class Woman the initializer defined with class model with depth 3 (line 2:2), i.e., a model with maximum
Person (line 1:4) is called, incCounter is called in the source and target potencies of 3.
context of class Woman setting instance variable @counter Creating clabject hierarchies. A DDI model consists of one
of Woman to 1 (see comment in line 1:19) and not of Person or more clabject hierarchies. Every clabject hierarchy has one
which remains undefined (see comment in line 1:18). root clabject. A root clabject has a fixed clabject potency
Single objects may also have singleton classes with single- (specifying the number of instantiation levels beneath the root)
ton methods. For example, Mary’s singleton class (opened at and typically has a name. For example, clabject Person (line
line 1:20 by class << Mary and depicted as #Mary) de- 2:3) and clabject Product (line 2:11) are the root clabjects
fines getter and setter methods for accessing instance variable in the SalesMgmt model and have a potency of 1 and 3,
@name of Mary. respectively.
Ruby allows to open existing classes to add additional Clabjects are instantiated by sending message new. The
methods which then affect all direct and indirect instances of new clabject is in the same module as its class and has a
the class. For example, class Object (opened at line 1:25) is potency 1 lower than its class. For example, clabject Person
with potency 1 is instantiated by MsBlack (line 2:4) and by target potency 3, meaning that the range of favouriteItem
Peter (line 2:5), which get potency 0. Clabject Product is given by the members of the members of the members of
with potency 3 is instantiated by Bike (line 2:14) and by clabject Product.
Car (line 2:19) which get potency 2. Querying clabject hierarchies and attributes. The values
Naming clabjects. The names of clabjects in DDI models and (meta) types of a clabject’s attributes are queried by
(such as in Fig. 2) may seem counter-intuitive. For example, sending the attribute name to the clabject facet which is
one would typically consider a class named Car to be a identified by the clabject together with source potency and
specialization (and not an instantiation) of class Product. target potency. For example, sending attribute name engine
In the following we explain how to read such models and to PetersCar’s clabject facet with source potency 0 and tar-
sketch the rationale behind this naming scheme. get potency 1 (line 2:32) returns the type of engine of
It is sometimes argued that deep instantiation’s support for PetersCar, which is EngineK5, which is inherited from
concise modeling comes with the price of lack of concep- BMWZ4.
tual clarity [3]: one clabject may represent multiple domain For getting or setting attributes with source potency 0 and
concepts which makes it more difficult to differentiate these target potency 0 it is not necessary to specify the clabject facet.
different domain concepts. In order to make these different If a message is sent to a clabject it dispatches it to its 0-0 facet.
domain concepts explicit, we proposed [9]–[11] to give mean- For example, when sending attribute name favouriteItem
ingful names to instantiation levels of a clabject and to produce to Peter (line 2:30) it is dispatched to Peterˆ(0,0) and
the name of an implicitly represented domain concept by retrieves Peter’s favourite item, which is his car.
combining a clabject name with a level name. DeepRuby provides methods to navigate clabject hierarchies
For example (see Fig. 2), clabject Product has instantia- to facilitate flexible querying of DDI models. For example,
tion levels Category, Model, and Individual, representing do- line 2:34 retrieves the members of the members of Product,
main concepts Product Category, Product Model, and Product these are BMWZ4 and Brompton.
Individual. Clabject Car is an instance of Product Category DeepRuby provides (1) generic query mechanisms (1a) to
and further represents domain concepts Car Model and Car retrieve attribute values and (meta) types including inherited
Individual (which are specializations of Product Model and values and types (1b) to navigate clabject hierarchies and
Product Individual). Clabject BMWZ4 is an instance of Car retrieve a clabject’s members at a specific level and (2) takes
Model and further represents domain concept BMWZ4 Individ- care of keeping DDI models consistent when defining and
ual (a specialization of Car Individual). Finally, Peters Car setting attributes, with regard to: (2a) correct number of
is an instance of BMWZ4 Individual. instantiation steps at the source and the target, (2b) target
clabjects are compatible with targets at higher potencies, (2c)
Defining and instantiating attributes. Attributes are defined
a newly introduced target does not produce type conflicts at
with a source clabject, a name, a source potency, a target
lower potencies and at descending clabjects.
potency, and a target clabject. For example, clabject Product
defines an attribute with name owner, source potency 3, target IV. D EEP RUBY UNDER THE H OOD
potency 1, and target clabject Person (line 2:13). By freely combining source and target potencies, a clabject
A clabject has many clabject facets, one for each com- c with maximum source potency m (given by the clabject’s
bination of source potency and target potency. In order to potency) and maximum target potency n (given by the DDI
set attribute engine at source potency 1 and target po- model’s depth) has (m + 1) × (n + 1) clabject facets. Every
tency 1 at clabject BMWZ4 to EngineK5, one first selects such facet corresponds to a combination of source potency and
the clabject facet (BMWZ4.ˆ(1,1)) to which one sends target potency. The basic idea of DeepRuby is to represent
engine=EngineK5 (line 2:23). every such clabject facet as a ’flat’ Ruby object (which in
Clabjects with potency 0 have no members, yet they may the current approach is always a class) with instance variables
define attributes with a target potency higher than 0, similar to and methods. For example, clabject Car with potency 2 in a
what can be accomplished in Ruby with singleton classes of model with depth 3 has 12 (3 × 4) clabject facets. The object
an object (e.g., attribute name defined with Mary’s singleton Carˆ(0-0) holds @catMgr=MsBlack and Carˆ(2-2)
class at line 21 in Fig. 1). For example, clabject Peter defines holds @engine=CarEngine as instance variable. The rela-
an attribute spouse with source potency 0, target potency 1, tionships between clabject facets are represented using Ruby
and target Person (line 2:6) and instantiates it with target constructs:
potency 0 and target MsBlack (line 2:7). i,j
• The eigenclass of clabject facet c is clabject
i,(j+1)
Root clabjects with clabject potency 1 are akin to ‘normal’ facet c . For example, the eigenclass of class
classes in that they have individuals as members. They are Carˆ(0,0) is clabject facet Carˆ(0,1).
different from normal classes in that their attributes may have • If clabject c is an instantiation of clabject d, then
a range defined at a higher classification level. For example, every clabject facet ci,j has clabject facet d(i+1),j
Person (line 2:3) has individuals MsBlack (line 2:4) and as superclass. For example, Carˆ(0,0) has super-
Peter (line 2:5) as members, yet it defines an attribute class Productˆ(1,0) and Carˆ(0,1) has superclass
favouriteItem (line 2:27) with target Product and Productˆ(1,1).
eigenclass superclass target potency
0 1 2 3
level(0)
Product3 Product Product^(0,0) Product^(0,1) Product^(0,2) Product^(0,3) 0
categoryMgr1-1 = Person
owner3-1 = Person level(1)
Product^(1,0) Product^(1,1) Product^(1,2) Product^(1,3)
1
@catMgr = Person
catMgr()
source potency
catMgr();catMgr=() catMgr=()
level(2)
Product^(2,0) Product^(2,1) Product^(2,2) Product^(2,3)
2
level(3)
Product^(3,0) Product^(3,1) Product^(3,2) Product^(3,3) 3
@owner = Person
owner() owner()
owner=() owner=()
instantiation_of superclass superclass superclass superclass
level(0)
Car2 Car Car^(0,0) Car^(0,1) Car^(0,2) Car^(0,3)
categoryMgr0-0 = MsBlack @catMgr = MsBlack
engine2-2 = CarEngine
level(1)
Car^(1,0) Car^(1,1) Car^(1,2) Car^(1,3)
level(2)
Car^(2,0) Car^(2,1) Car^(2,2) Car^(2,3)
@engine = CarEngine
engine() engine()
engine=() engine() engine=()
engine=()
instantiation_of superclass superclass
superclass superclass
1
level(0)
BMW Z4 BMWZ4 BMWZ4^(0,0) BMWZ4^(0,1) BMWZ4^(0,2) BMWZ4^(0,3)
engine1-1 = EngineK5
level(1)
BMWZ4^(1,0) BMWZ4^(1,1) BMWZ4^(1,2) BMWZ4^(1,3)
@engine = EngineK5
instantiation_of superclass superclass superclass superclass
level(0)
PetersCar0 PetersCar PetersCar^(0,0) PetersCar^(0,1) PetersCar^(0,2) PetersCar^(0,3)
0-0
owner = Peter @owner = Peter
engine0-0 =Engine123 @engine = Engine123
DeepRuby DeepRuby
DDI Model
Clabject Layer Clabject Facet Layer
Fig. 3. Realizing clabject facet matrices in Ruby using superclass and eigenclass
A clabject is first represented as an instance of class by BMWZ4ˆ(1,1) and in turn by PetersCarˆ(0,1).
Clabject (line A:25 in the Appendix) with an array Target clabjects of attributes (represented as instance
•
levels which holds for each source potency a reference variables) are inherited from superclass ci+1,j to sublass
to the respective instance of class ClabjectFacet (see ci,j (this is implemented generically as part of Deep-
line A:210) with target potency 0, from there one can navigate Ruby). For example, when sending message engine
to other clabject facets along eigenclass relationships. Sending to PetersCarˆ(0,1) one gets EngineK5, which is
message ˆ(i,j) to a clabject c returns clabject facet ci,j . inherited from BMWZ4ˆ(1,1).
A clabject facet’s attribute clabject (line A:213) allows What is the role of eigenclass relationships in Deep-
to navigate back from clabject facet to clabject; for example, Ruby?:
from clabject facet Carˆ(2,1) to clabject Car. • The eigenclass c
i,j+1
of a clabject facet ci,j pro-
What is the role of superclass relationships in Deep- vides methods for accessing instance variables of
Ruby?: ci,j (this comes for free with the eigenclass con-
i+1,j
• Methods are inherited from superclass d to subclass struct). For example, setter method catMgr= de-
i,j
c (this comes for free, since this is what class hier- fined in Productˆ(1,2) is called for setting
archies are traditionally used for). For example, setter @catMgr=Person in Productˆ(1,1)
method engine= defined at Carˆ(2,1) is inherited • Target clabjects (represented as instance variables) at
ci,j+1 act as constraint for target clabjects at ci,j Currency.ˆ(1,1)) (see line 4:4). Second-level mem-
(this is implemented generically as part of DeepRuby). bers of Currency get getter and setter for attribute
For example, target clabject engine=EngineK5 of value by invoking attr_accessor on the eigenclass of
PetersCarˆ(0,1) (inherited from BMWZ4ˆ(1,1)) Currency.ˆ(2,0) (which is Currency.ˆ(2,1)) (line
acts as constraint when invoking setter method engine= 4:7). Currencies Pound, Euro, and Yen are created with
on PetersCarˆ(0,0). their isocode and exchange rate (lines 4:20–4:25). GBP38200
We have further been experimenting with two alternative is an instantiation of Pound (and a second-level member of
representation of the clabject facet matrix: In the first alter- Currency) with value 38200 (line 4:26).
native represenation, clabject facets with target potency 0 are Second-level members of Currency have a method
represented as simple objects and not as classes. This would for pretty printing (defined with the eigenclass of
also be a reasonable design choice since these facets do not Currency.ˆ(2,0) which is Currency.ˆ(2,1)), mak-
act as classes, yet it makes the implementation a bit more ing use of value and isocode. In order to get the
complex. isocode the method needs to first navigate from the clabject
In the second alternative representation, we introduce an facet (instance of ClabjectFacet) to the corresponding
additional layer between DDI-clabject layer and clabject facet clabject (instance of Clabject) along attribute clabject
layer in order to align DeepRuby with DeepTelos [6]. At this and from there along attribute cclass to the corresponding
intermediate layer, a DDI clabject is represented by multiple first-level member of Currency (line 4:9). Sending pretty
simple clabjects. A simple clabject resembles an object, class, to GBP38200 results in ‘GBP 38200’ (line 4:27).
metaclass, or metaˆn class in DeepTelos. Clabject facets with Second-level members of Currency further have a
the same difference between source potency and target potency method toCurrency which takes a first-level member of
are collected into such a simple clabject. For example, clabject Currency as parameter (line 4:11). Sending toCurrency
facets Carˆ(0,0), Carˆ(1,1), Carˆ(2,2) are collected with parameter Euro to GBP38200 produces a new instanti-
into a simple clabject Car 0 and clabject facets Carˆ(1,0), ation of Euro which is pretty printed as ‘EUR 42784.0’ (line
Carˆ(2,1) are collected into a simple clabject Car 1, with 4:28).
Car 0 having class Car 1 as its most-general instance. A The eigenclass of Yen.ˆ(1,0) (which is
simple clabject combining facets where the target potency Yen.ˆ(1,1)) overwrites method pretty inherited
is higher than the source potency (e.g., Productˆ(0,1), from Currency.ˆ(2,1)) to use unicode symbol U instead
Productˆ(1,2), Productˆ(2,3)) cannot be directly of isocode JPY . The new instantiation of Yen created by
represented in DeepTelos. sending toCurrency with parameter Yen to GBP38200 is
In this section we have explained the basic principles of pretty printed as ‘U 5556363.64’ (line 4:33).
DeepRuby’s implementation and use of Ruby’s eigenclass Clabjects UK and Japan instantiate Country and have
construct to implement Dual Deep Instantiation and have local currencies Pound and Yen, respectively. Asking for
sketched two alternative representations. The evaluation and the exchange rate of Japan’s local currency returns 0.0077
fine-tuning of these alternatives is subject to ongoing work. (line 4:39).
Method priceInCountry (see line 4:42) of second-
V. S IMPLE ATTRIBUTES AND C USTOM M ETHODS IN level members of Product takes a country as parameter
D EEP RUBY and converts the listPrice of second-level instantiations
of Product to the country’s local currency, returning a new
Using classes/eigenclasses arranged in superclass hierar- instantiation of the given currency with the value being the
chies for realizing the clabject facet matrix allows to use stan- result of the conversion. Sending priceInCountry with
dard Ruby constructs to implement simple attributes (attributes parameter Japan to BMWZ4 (see line 4:48) returns a new
with non-clabjects as range) and behavior (custom methods) clabject pretty-printed as ‘U 5556363.64’ (line 4:48).
on top of the clabject facet matrix, and to specialize behavior
(i.e., overwrite methods, add additional methods) along the VI. R ELATED W ORK
clabject hierarchy. With the advent of multi-level modeling, the question of
To demonstrate these features, the running example from multi-level model execution emerges. Melanee [1], DeepTe-
Fig. 2 is extended in Fig. 4 with clabject hierarchies los [6], MetaDepth [8], DeepJava [7], and XModeler [4] are
Currency with simple attributes for exchange rate (with modeling tools and frameworks that support model execution,
Euro as reference currency), isocode and value, and Country each pursuing a different strategy with respect to supporting
with a local currency. Clabject Product is extended with a model execution. Multilevel programming may also be real-
listPrice and a method priceInCountry to convert ized in a type-safe manner by metaprogramming and reflective
the list price to the local currency of the given country. constraints [5].
First-level members of Currency receive getters and The Melanee multi-level modeling tool [1] supports model
setters for simple attributes exchRate and isocode execution through a service API and a plug-in mechanism. The
by invoking standard Ruby method attr_accessor communication between modeling and execution environment
on the eigenclass of Currency.ˆ(1,0) (which is can be realized using socket-based communication. Changes
1 module SalesMgmt
2 DDI::Clabject.new(Product.model,2,:Currency)
3 class << Currency.ˆ(1,0)
4 attr_accessor(:isocode, :exchRate)
5 end Currency2
6 class << Currency.ˆ(2,0)
value2
7 attr_accessor(:value) exchRate1
8 def pretty isoCode1
9 "#{clabject.cclass.isocode} #{value}" pretty2()
10 end toCurrency2(Currency1) : Currency2
11 def toCurrency(c)
12 raise "#{c} is not a currency" \
13 unless (c.isMemberN(1,Currency))
14 obj = c.new Euro1 Pound1 Yen1
15 obj.value = (value * clabject.cclass exchRate = 1 exchRate = 1.12 exchRate = 0.0077
16 .exchRate / c.exchRate).round(2) isocode = „EUR“ isocode = „GBP“ isocode = „JPY“
17 return obj pretty1-0()
18 end
19 end
20 Currency.new(:Pound); GBP382000
21 Pound.isocode = "GBP"; Pound.exchRate = 1.12; value= 42784.0 value= 5556363.64
value = 38200
22 Currency.new(:Euro)
23 Euro.isocode = "EUR"; Euro.exchRate = 1
24 Currency.new(:Yen)
25 Yen.isocode = "JPY"; Yen.exchRate = 0.0077 Country1
26 Pound.new(:GBP38200); GBP38200.value = 38200
localCurrency1-1 = Currency
27 puts GBP38200.pretty # => GBP 38200
28 puts GBP38200.toCurrency(Euro).pretty
29 # => EUR 42784.0
30 class << Yen.ˆ(1,0) UK0 Japan0
31 def pretty; "\u00A5 #{value}"; end localCurrency0-0 = Pound localCurrency0-0 = Yen
32 end
33 puts GBP38200.toCurrency(Yen).pretty
34 # => U 5556363.64
35 DDI::Clabject.new(Product.model,1,:Country) Product3
36 Country.define(:localCurrency,1,1,Currency) listPrice2-2 = Currency
37 Country.new(:UK).localCurrency = Pound
38 Country.new(:Japan).localCurrency = Yen priceInCountry2(Country1) : Currency2
39 puts Japan.localCurrency.exchRate #=> 0.0077
40 Product.define(:listPrice, 2, 2, Currency)
41 class << Product.ˆ(2,0) Car2
42 def priceInCountry(country)
43 listPrice.toCurrency(country.localCurrency)
BMW Z41
44 end
45 end listPrice0-0 = GBP38200
46 BMWZ4.ˆ(0,0).listPrice = GBP38200
47 puts BMWZ4.priceInCountry(Japan).pretty
48 # => U 5556363.64
49 end
Fig. 4. Custom methods: DeepRuby program (left) realizing a DDI model with methods (right)
in the modeling environment then automatically reflect in the but comes with powerful metamodeling features unmatched
execution environment, and vice versa. The execution envi- by DeepRuby.
ronment can be implemented as a Java program. Concerning MetaDepth [8] is a text-based multi-level modeling frame-
the definition of execution semantics, different approaches work with potency-based deep instantiation. MetaDepth is a
exist. A “pragmatic” approach, for example, employs a Java Java-based implementation using a custom syntax. Among the
representation of the multi-level model where each clabject in primary features of MetaDepth are multi-level constraints and
the multi-level model corresponds to a single Java class, with derived attributes at different meta-levels. Execution semantics
execution semantics defined using plain Java code. is defined using an OCL extension. MetaDepth provides an
DeepTelos [6] extends the Telos metamodeling language interpreter for the thus defined multi-level models. MetaDepth
and its implementation with “most general instances” to add also supports code generation complying to the Java Metadata
support for deep instantiation. Since DeepTelos defines the Interface.
extensions as a set of Datalog axioms, DeepTelos models are DeepJava [7] is an extension of the Java programming
compatible with ConceptBase, an implementation of a Telos language with a mechanism for potency-based deep instan-
variant. ConceptBase also allows for the definition of exe- tiation. Internally, a compiler transforms DeepJava code into
cutable models using event-condition-action rules. In Sect. IV plain Java. Hence, each DeepJava class translates into a set
we sketched how to group clabject facets into simple clabjects of Java classes, one for each clabject facet. The compiler also
that resemble DeepTelos classes. DeepTelos does not directly generates code for clabject instantiation at runtime, which is
support self-describing clabjects (i.e., clabject with attributes realized using Java’s reflective functions. Clabject instantiation
where the target potency is higher than the source potency) results in the dynamic generation of a number of interfaces.
As a limitation, direct access without getters and setters is [8] de Lara, J., Guerra, E.: Deep meta-modelling with MetaDepth. In: Vitek,
restricted to attributes with potency values smaller than two. J. (ed.) TOOLS 2010. LNCS, vol. 6141, pp. 1–20. Springer (2010)
[9] Neumayr, B., Jeusfeld, M.A., Schrefl, M., Schütz, C.: Dual deep instan-
With respect to Java, Ruby’s eigenclass concept much better tiation and its conceptbase implementation. In: Jarke, M., Mylopoulos,
suits the clabject philosophy of multi-level modeling. As J., Quix, C., Rolland, C., Manolopoulos, Y., Mouratidis, H., Horkoff,
opposed to DeepJava, DeepRuby supports deep instantiation J. (eds.) CAiSE. Lecture Notes in Computer Science, vol. 8484, pp.
503–517. Springer (2014)
with both a source and a target potency, resulting in the [10] Neumayr, B., Schuetz, C.G.: Multilevel modeling. In: Liu, L., Özsu,
generation of a matrix of Ruby classes for each clabject. M.T. (eds.) Encyclopedia of Database Systems. pp. 1–8. Springer New
York, New York, NY (2017)
VII. C ONCLUSION [11] Neumayr, B., Schuetz, C.G., Jeusfeld, M.A., Schrefl, M.: Dual deep
modeling: multi-level modeling with dual potencies and its formalization
In this paper we introduced DeepRuby, a Ruby implemen- in F-Logic. Software & Systems Modeling pp. 1–36 (2016)
tation of the core language constructs of Dual Deep Instantia- [12] Perrotta, P.: Metaprogramming Ruby 2. The Pragmatic Programmers
(2014)
tion [9]: clabject hierarchies and attributes with dual potencies.
The system takes care of consistent instantiation of clabjects A PPENDIX
and attributes and provides methods for querying multi-level D EEP RUBY I MPLEMENTATION
models. Our experiences with implementing DeepRuby have
1 module DDI
confirmed our initial conjecture that a dynamic programming 2
3 class Model
language like Ruby that does not strictly separate classes and 4 attr_reader(
5 :depth, # maximum potency of user clabjects
objects is a good platform for implementing clabject-based 6 :modul, # module/namespace for clabjects
7 )
modeling constructs. 8 def initialize(mod, n)
9 raise "initial maximum-depth too low (n < 1)" if n < 1
In an internal prototype we have also implemented DDI’s 10 @modul = mod
11 @depth = n
advanced modeling constructs (which are missing from the 12 # create module-specific ClabjectLevel-class
13 # because of different eigenclass depth
DeepRuby version presented in this paper): multi-valued prop- 14 cls = Class.new
15 modul.const_set(:ClabjectLevel, cls)
erties, bi-directional properties, and clabject generalization. 16 # include module ClabjectFacet in eigenclasses
17 for n in (0..depth)
The fine-tuning of the advanced prototype and experimentation 18 cls = cls.singleton_class
19 cls.send(:include, ClabjectFacet)
with alternative representations of the clabject facet matrix is 20 end
21 end
subject to ongoing work. 22 end # end Model
23
Dual deep modeling (DDM) [11], an extended version 24
25 class Clabject
of DDI, additionally comes with multi-level cardinality con- 26 attr_reader(
27 :model, # reference to DDI model
straints, property specialization hierarchies, and distinguishes 28 :levels, # array of clabject levels
29 :name,
between property value and property range. Implementing 30 :instantiations, # first-level members
31 :instantiation_of, # class clabject
these constructs in DeepRuby is subject to future work. 32 :potency
33 )
Moving beyond previous implementations of DDI/DDM in 34
35 def cclass
ConceptBase [9] and F-Logic [11], DeepRuby allows to extend 36 instantiation_of
37 end
clabject facets with custom methods. We have exemplified 38
39 def members
the implementation of such methods and their inheritance and 40 instantiations
41 end
specialization along the clabject facet hierarchy. 42
43 def ˆ(srcPtcy,tgtPtcy)
44 facet(srcPtcy,tgtPtcy)
R EFERENCES 45 end
46
47 def facet(srcPtcy,tgtPtcy)
[1] Atkinson, C., Gerbig, R., Metzger, N.: On the execution of deep models. 48 raise "Target potency #{tgtPtcy} above max potency #{model.depth}+1."
In: Mayerhofer, T., Langer, P., Seidewitz, E., Gray, J. (eds.) Proceedings if tgtPtcy > (model.depth+1)
49 return levels[srcPtcy].eigenclassN(tgtPtcy)
of the 1st International Workshop on Executable Modeling. CEUR 50 end
Workshop Proceedings, vol. 1560, pp. 28–33. CEUR-WS.org (2015) 51
52 def initialize(model, potency, name=nil, parent=nil)
[2] Atkinson, C., Kühne, T.: The Essence of Multilevel Metamodeling. In: 53 @name = name
Gogolla, M., Kobryn, C. (eds.) Proceedings of the 4th International 54 @instantiation_of = parent
55 @potency = potency
Conference on the UML 2001, Toronto, Canada. LNCS, vol. 2185, pp. 56 @model = model
19–33. Springer Verlag (Oct 2001) 57 @levels = Array.new(potency+1)
58 for m in (0..potency)
[3] Carvalho, V.A., Almeida, J.P.A.: Toward a well-founded theory for 59 if parent.nil?
multi-level conceptual modeling. Software & Systems Modeling (2016) 60 @levels[m] = createClabjectLevel(model
61 .modul.const_get(:ClabjectLevel), m)
[4] Clark, T., Gonzalez-Perez, C., Henderson-Sellers, B.: A foundation for 62 else
multi-level modelling. In: MULTI 2014. CEUR Workshop Proceedings, 63 @levels[m] = createClabjectLevel(parent
64 .levels[m+1], m)
vol. 1286, pp. 43–52. CEUR-WS.org (2014) 65 end
[5] Draheim, D.: Reflective constraint writing - A symbolic viewpoint of 66 end
67 @instantiations = Array.new
modeling languages. Trans. Large-Scale Data- and Knowledge-Centered 68 model.modul.const_set(name, self) if name
Systems 24, 1–60 (2016) 69 return self
70 end
[6] Jeusfeld, M.A., Neumayr, B.: DeepTelos: Multi-level modeling with 71
most general instances. In: Comyn-Wattiau, I., Tanaka, K., Song, I., 72 def new(name=nil)
73 obj = Clabject.new(
Yamamoto, S., Saeki, M. (eds.) ER 2016. LNCS, vol. 9974, pp. 198– 74 self.model, self.potency-1, name, self)
211. Springer (2016) 75 instantiations << obj
76 return obj
[7] Kuehne, T., Schreiber, D.: Can programming be liberated from the two- 77 end
level style: Multi-level programming with DeepJava. In: Proceedings 78
79
of the 22nd Annual ACM SIGPLAN Conference on Object-oriented 80 def createClabjectLevel(supercls,levelNr)
Programming Systems and Applications. pp. 229–244 (2007) 81 cls = Class.new(supercls)
82 facet = cls 189 facet(srcPtcy,tgtPtcy).send("#{attribute}=", value)
83 for n in (0..model.depth) 190 return self
84 facet.clabject = self 191 end
85 facet.tgtPtcy = n 192
86 facet.srcPtcy = levelNr 193 def get(attribute, srcPtcy, tgtPtcy)
87 if(n < model.depth) 194 facet(srcPtcy,tgtPtcy).send(attribute)
88 facet = facet.singleton_class 195 end
89 end 196
90 end 197 def getValueSettingObject(attribute, srcPtcy, tgtPtcy)
91 return cls 198 facet(srcPtcy,tgtPtcy)
92 end 199 .getValueSettingObject(attribute.to_s.to_sym)
93 200 end
94 def to_s 201
95 name 202 def getMethodDefiningClass(attribute, srcPtcy, tgtPtcy)
96 end 203 facet(srcPtcy,tgtPtcy)
97 204 .method("#{attribute.to_sym}").owner
98 def getMembersN(n) 205 end
99 if n == 0 206
100 return [self] 207 end # end Clabject
101 elsif n == 1 208
102 return instantiations 209
103 elsif n > 1 210 module ClabjectFacet
104 tempAry = [] 211
105 ary = [self] 212 attr_accessor(
106 for m in (1..n) 213 :clabject,
107 ary.each do |cbj| 214 :srcPtcy,
108 tempAry = tempAry + cbj.instantiations 215 :tgtPtcy
109 end 216 )
110 ary = tempAry 217
111 tempAry = [] 218 def to_s
112 end 219 clabjectname = (clabject.respond_to?(:name))? clabject.name : clabject
113 return ary 220 "#{clabjectname}ˆ(#{srcPtcy},#{tgtPtcy})"
114 end 221 end
115 end 222
116 223 def parent
117 def isMember(cbj) 224 superclass
118 if self == cbj 225 end
119 true 226
120 elsif self.respond_to?(:instantiation_of) && 227 def eigenclass
!self.instantiation_of.nil? 228 singleton_class
121 self.instantiation_of.isMember(cbj) 229 end
122 else 230
123 false 231 def inherited(attribute)
124 end 232 if parent.respond_to?(attribute.to_s.to_sym)
125 end 233 parent.send(attribute.to_s.to_sym)
126 234 else
127 def isCompatibleWith(cbj) 235 false
128 return self.isMember(cbj) || cbj.isMember(self) 236 end
129 end 237 end
130 238
131 def isMemberN(n, cbj) 239 def getValueSettingObject(attribute)
132 if n == 0 and self == cbj 240 if instance_variable_get("@#{attribute.to_sym}")
133 true 241 self
134 elsif self.respond_to?(:instantiation_of) && 242 else
!self.instantiation_of.nil? 243 getInheritedValueSettingObject(attribute)
135 self.instantiation_of.isMemberN(n-1, cbj) 244 end
136 else 245 end
137 false 246
138 end 247 def getInheritedValueSettingObject(attribute)
139 end 248 if parent.respond_to?(attribute.to_s.to_sym)
140 249 parent.getValueSettingObject(attribute.to_s.to_sym)
141 def method_missing(method, *args) 250 else
142 if levels[0].respond_to?("#{method}", *args) 251 false
143 levels[0].send("#{method}", *args) 252 end
144 else 253 end
145 raise NoMethodError.new("There is no method called #{method} here") 254
146 end 255 def getMostSpecific(attribute)
147 end 256 getMostSpecificN(attribute)[:val]
148 257 end
149 def define(attribute, srcPtcy, tgtPtcy, value) 258
150 for n in (1..(tgtPtcy+1)) 259 def getMostSpecificN(attribute)
151 obj = facet(srcPtcy,n) 260 if respond_to?(attribute)
152 #crete getter 261 val = self.send(attribute)
153 obj.class_eval(" 262 if val
154 def #{attribute} 263 return {:ptcy => 0, :val => val}
155 if @#{attribute} 264 elsif eigenclass.respond_to?(attribute)
156 @#{attribute} 265 x = eigenclass.getMostSpecificN(attribute) #recursion
157 else 266 if x[:val]
158 inherited(’#{attribute}’) 267 return {:ptcy => x[:ptcy]+1, :val => x[:val]}
159 end 268 end
160 end" 269 end
161 ) 270 end
162 #create setter 271 return {:val => false}
163 obj.class_eval(" 272 end
164 def #{attribute}=(val) 273
165 if valueSettingAllowed(:#{attribute}, val) 274 def valueSettingAllowed(attribute, value)
166 @#{attribute} = val 275 if value.kind_of?(Clabject)
167 end 276 ms = getMostSpecificN(attribute)
168 end" 277 if ms[:val] && !value.isMemberN( ms[:ptcy], ms[:val] )
169 ) 278 raise "#{value.name} is not memberN(#{ms[:ptcy]}) of
170 end #{ms[:val].name}"
171 set(attribute, srcPtcy, tgtPtcy, value) 279 end
172 return self 280 return true
173 end 281 else
174 282 raise "#{value} is no Clabject"
175 def checkDownwardCompatibility(attribute, srcPtcy, tgtPtcy, value) 283 end
176 return true unless levels[srcPtcy].getMostSpecific(attribute) 284 end
177 return true if value.nil? 285
178 for potency in (0..srcPtcy) 286 def eigenclassN(n)
179 getMembersN(potency).each do |cbj| 287 obj = self
180 actMsVal = cbj.levels[srcPtcy-potency] 288 for m in (1..n) # returns self if n=0
181 .getMostSpecific(attribute) 289 obj = obj.singleton_class
182 raise "#{value.name} is not compatible with #{actMsVal.name}" if 290 end
!value.isCompatibleWith( actMsVal ) 291 return obj
183 end 292 end
184 end 293
185 end 294 end # end ClabjectFacet
186 295
187 def set(attribute, srcPtcy, tgtPtcy, value, doDownwardCheck = true) 296 end # end module DDI
188 checkDownwardCompatibility(attribute, srcPtcy, tgtPtcy, value) if
doDownwardCheck