CreateNodeEvent
instance for each constructed node as follows:
public class CreateNodeEvent {
def name
def node
def parent
def isRoot // set to true, if the new node is a root node
}
If not a root node, the closure should return the node, or possibly a different node, when it is done.
Using a Build Visitor to Build a List of Root Nodes
An example closure that builds a list of all root nodes might be:
{ if(isRoot) { myList << it.node }; it.node }
In fact, I thought that usage might be so common that I included another set of
buildList
methods that returns a list of root nodes for you.I'd also thought about exposing the
ObjectGraphBuilder.postNodeCompletion()
method, but it doesn't give you the node name, which is sometimes useful to have. It also doesn't tell you whether you're dealing with a root node (though you can probably derive that from the fact that parent == null
for a root node.)Using a Build Visitor to Build a Map of Root Nodes
Here's another build visitor example: say I have classes
Order
and OrderLine
as follows and I want to use MetaBuilder to read Orders
from a file into a map called orders
, keyed on id
:A Hibernate Example
class OrderLine {
def upc
def qty
def price
}
class Order {
def id
def lines = []
}
MetaBuilder mb = new MetaBuilder(getClass().getClassLoader())
mb.define {
order(factory: Order) {
properties {
id(req: true)
}
collections {
lines {
line (factory: OrderLine) {
properties {
upc(req: true)
qty(req: true)
price(req: true)
}
}
}
}
}
}
def orders = [:]
mb.build( { if(it.isRoot) { orders[it.node.id] = it.node }; it.node }, new File("orders.mb").toURL() )
Andres Almiray's recent blog post on using
ObjectGraphBuilder
to create Grails fixtures inspired this next example, which extends the previousexample to insert each object into a database using Hibernate:
Final Thoughts
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction()
mb.build( { if(it.isRoot) { session.save(it.node) }; it.node }, new File("orders.mb").toURL() )
session.getTransaction().commit()
One of the benefits of this feature is that it enables you to process large numbers of build objects without having to incur the overhead of building a list to contain them all.
2 comments:
Basically you are injecting a ResultCollector strategy which may be implemented with a Closure, clever. I guess the same can be done with OGB thanks to FBS.addPostNodeCompletionDelegate(Closure) (I believe you changed MB to be a child of FBS too, right?)
Just a matter of taste but I would prefer setting the strategy as the second parameter of build() that way it can be marked as optional
MB.build( Object src, Closure resultCollector = null )
Great work!
@andres - Thanks! MetaBuilder itself is not a subclass of OGB. However, MB internally delegates to instances of MetaObjectGraphBuilder (MOGB), which is a subclass of OGB.
MOGBs are created every time the user starts a build, which is necessary if the user creates builds within builds, otherwise MB would get confused regarding which schema is being built. E.g.:
mb.build {
foo {
bar = mb.build {
something { ...}
}
}
I'll think about the optional parameter for version 1.2.
Post a Comment