Mod-it library provides an API to ease creations of EMF Models in XTend.

Using for tests

EMod-it instance frees developers of tedious EMF factory syntax making model description close to JSon file.

It also suppresses the constraint of sequencing code by providing a registry and resolving references during an assembling phase.

It is provided with reverse engine EReversIt which generates Xtend classes from existing models.

Using such syntax helps to:

  • Speed up the creation of tests

  • Improve the readability of the tests

  • Improve maintainability when the tested metamodel changes

  • Ease the creation of inter-connected models

It has been created and used in industrial project to ease the creation input for JUnit tests on the metamodel and business rules.

Using as integrated DSL.

Going further is it possible to reference dynamic model from EMF api.

For example, Eclipse Sirius or E4 descriptors can be generated and loaded at runtime avoiding static XML.

Moreover, Sirius expressions can be replaced by Xtend lambda avoiding AQL syntax or declaration of Java static classes.

Installation

Install the feature Mod-it Feature (…​) from the following update site:

Origin of the project

Usage

How to start

Create a Mod-it factory

extension factory = EModIt.using(YourPackage.eINSTANCE)

Keyword extension provides new fonctions to Class of EObject

Examples

All the examples use the following metamodel: ![Dummy Metamodel for tests](docs/img/BoemTestMM.jpg)

How to create an element

// Single element creation
val pool = A.create [
  name = "AName"
].assemble

How to add children

val model = A.create [
  name = "AName";

  // Single valued containment feature
  childNodeA = Node.create [
    name = "SingleChildName"
  ]

  // Adds one child
  childrenNodeA += Node.create [
    name = "ANodeName0"
  ]

  // Adds several children at once
  childrenNodeA += #[
    Node.create [
      name = "ANodeName1"
    ],
    Node.create [
      name = "ANodeName2"
    ]
  ]
].assemble

How to reference between elements

val model = B.create [
  name = "AName"

  // Adds one child with reference
  childrenNodeA += Node.createAs("id0") [
      name = "ANodeName0"
  ]
  // Adds several children at once with references
  childrenNodeA += #[
    Node.createAs("id1") [
      name = "ANodeName1"
    ],
    Node.createAs("id2") [
      name = "ANodeName2"
    ]
  ]

  // Adds several children at once with references
  childrenNodeB += #[
    Node.createAs("id3") [
      name = "ANodeName3"
    ],
    Node.createAs("id4") [
      name = "ANodeName4"
    ]
  ]
].assemble

// Accessing node
assertEquals("ANodeName0", model.access(Node, "id0").name)
assertEquals("ANodeName1", model.access(Node, "id1").name)
assertEquals("ANodeName2", model.access(Node, "id2").name)

assertEquals(2, model.root.childrenNodeB.size)
assertEquals("ANodeName3", model.access(Node, "id3").name)
assertEquals("ANodeName4", model.access(Node, "id4").name)

You can even register an id provider against the Factory description.

It will compute an id for each element (at create time) using your rules.

For example:

extension EModit factory = EModit.using(BoemTestPackage.eINSTANCE)[
  idProvider = [
    if (it instanceof NamedElement) name
    null
  ]
]
//...

val model = B.create [
  name = "AName"
  // Adds one child with reference
  childrenNodeA += Node.create [
    name = "ANodeName0"
  ]
  // Adds several children at once with references
  childrenNodeA += #[
    Node.create [
      name = "ANodeName1"
    ],
    Node.create [
      name = "ANodeName2"
    ]
  ]

  // Adds several children at once with references
  childrenNodeB += #[
    Node.create [
      name = "ANodeName3"
    ],
    Node.create [
      name = "ANodeName4"
    ]
  ]
].assemble

// Accessing node
assertEquals("ANodeName0", model.access(Node, "ANodeName0").name)
assertEquals("ANodeName1", model.access(Node, "ANodeName1").name)
assertEquals("ANodeName2", model.access(Node, "ANodeName2").name)

assertEquals(2, model.root.childrenNodeB.size)
assertEquals("ANodeName3", model.access(Node, "ANodeName3").name)
assertEquals("ANodeName4", model.access(Node, "ANodeName4").name)

You can also use a shorter syntax to access your elements

val model = A.create [
  name = "AName"
  childrenNodeA += Node.createAs("id1") [
    name = "ANode"
  ]
].assemble

val target = model.access(Node, "id1");
val target2 = ("id1" => model) as Node
assertTrue(target2 == target)
val target3 = model.access("id1")
assertTrue(target3 == target)
assertTrue(target == "id1" => model)

How to create references

val it = A.create [
  name = "AName"
  autoContainementA += B.createAs("id0") [
    referenceNodeA = Node.createRef("id1")
  ]
  childrenNodeA += Node.createAs("id1") [
    name = "ANode"
  ]
].assemble

or with a shorter syntax

val it = A.create [
  name = "AName"
  autoContainementA += B.createAs("id0") [
    referenceNodeA = Node.ref("id1")
  ]
  childrenNodeA += Node.createAs("id1") [
    name = "ANode"
  ]
].assemble

How to update an element

// In real code, declare pool as extension
val pool = A.create [
  autoContainementA += B.createAs("B") [
    autoContainementA += C.createAs("D")
  ]
].assemble

assertEquals(null, pool.root.name)
assertEquals(null, pool.access(NamedElement, "B").name)
assertEquals(null, pool.access(A, "C").name)

pool.update [
  name = "NameA"
  autoContainementA.get(0).with [
    name = "NameB"
    autoContainementA.get(0).with [
      name = "NameD"
    ]
  ]
]

assertEquals("NameA", pool.root.name)
assertEquals("NameB", pool.access(B, "B").name)
assertEquals("NameC", pool.access(C, "C").name)

pool.access(B, "B").name = "NameB2"
pool.access(C, "C").name = "NameB2"

assertEquals("NameB2", pool.access(B, "B").name)
assertEquals("NameC2", pool.access(C, "C").name)

License