Archives for November 2009
MongoDB plugin for Griffon
A couple of weeks ago, I showed how you could make MongoDB a bit more Groovy. In this post, I'll be covering a preview release of the Mongo DB plugin. Behind the scenes, this plugin uses Guice for Dependency Injection.
Getting started:
Install the mongodb plugin:
griffon install-plugin mongo-db
In your conf directory create a file named MongoConfig.groovy listing the database connection details, like this:
mongo {
host="localhost"
port=27017
dbName="mine"
collectionName="testcoll"
}
Add the following bit to your Application.groovy file:
guice {
modules = [ "MongoModule"]
}
MongoModule.groovy is provided by the plugin. Getting a collection Collections are the approximate equivalent to a RDBMS table. Collections will be wh2ere you do most of your work. We can get a reference to a collection by adding @Inject MongoDBCollectionFactory factory to our controller and calling injector.injectMembers(this). Guice uses the settings in MongoConfig to properly wire our Mongo and DB instance giving us a CollectionFactory. Let's make a collection called posts and populate a couple of documents:
def coll2 = factory.getCollection("posts")
coll2.insert(['title':'Whatever', 'postcreated':new Date(), 'comments':[
'text':'A sample comment','datecreated':new Date(109,10,13)]] as BasicDBObject)
coll2.insert(['title':'Whatever1', 'postcreated':new Date()] as BasicDBObject)
coll2.insert(['title':'Whatever3', 'postcreated':new Date()] as BasicDBObject)
Dynamic Finders
Just before the factory returns the collection, it decorates it with code to generate GORM-like dynamic finders with operators like LessThan, LessThanEquals, GreaterThan, NotEqual, etc. Let's query our collection for all posts with a specific title:
def cursor3 = coll2.findByTitle('Whatever')
The return value is a DBCursor that we can iterate though. In addition to dynamic finders, the MongoDB plugin allows you to access child documents. This is extra useful because Mongo usually stores related documents as child documents of a parent. An underscore between two property names indicates a property on a sub-document. So if you wanted to find the comments created on a specific date. You'd use a query like this:
coll.findByComments_DateCreated(new Date('2009-10-10'))
This plugin, codenamed MonGorm, is a preview release so everything isn't fully baked. Comments/feedback welcome (emails/tweets better than comments on this post)
BarCampDC3
RuPy 2009
MongoDB made more Groovy
The session after mine here at RuPy was MongoDB presented by Mike Dirlof(@mdirlof). I looked at the Java examples yet they left a bit to be desired as they had the usual Java verbosity problems. One of the cooler lesser known things about Groovy is that Groovy can coerce objects to a specific interface. We use this alot for one-off WindowListerners interfaces and such. That same concept can be applied to the classes as well. We can take code from the Java MongoDB tutorial to make it more Groovy.
import com.mongodb.*
def m = new Mongo()
def db = m.getDB("mydb")
def coll = db.getCollection("testCollection")
coll.drop()
def doc = [name:"MongoDB", type:"database", count:1,
info: [x:203, y:102]
] as BasicDBObject
def doc2 = [name:"MongoDB2", type:"database", count:2,
info: [x:203, y:102] ] as BasicDBObject
coll.insert(doc)
coll.insert(doc2)
println coll.getCount()
def obj = coll.findOne([count:1] as BasicDBObject)
println obj
println "showing a custom query"
def cur = coll.find([count:['$lt':3]] as BasicDBObject)
while(cur.hasNext()) {
println cur.next()
}
Because MongoDB's BasicDBObject is a subclass of HashMap, we can produce concise code that looks closer to the Ruby, Python, and Javascript examples Matt has presented. We can also nest documents within documents, turtles all the way down. Only the othermost document needs to be cast, the rest get cast to documents automatically. It seems that MongoDB's special params begin with a dollar sign ($gt, $lt, etc) need to be passed as a String literal with apostrophes.
Griffon Guice Plugin
Having come back from StrangeLoop last week and talking to some our users, I've been thinking a lot potential blockers that might be preventing people from using Griffon in their work applications. One is those is probably dependency injection. Why I chose Guice - I detest XML. I like Guice's modules as a non-XML tag soup way to specify classes to be bound. - Guice's footprint is several magnitudes smaller than Spring. The smallest Guice application requires only 600KB of jar dependencies whereas a comparable Spring application would require several megabytes. Our users are already taking a hit for having to get the groovy-all.jar over the wire. I don't want to add to that pain.
Getting Started
Given an app with the Guice plugin installed(griffon install-plugin guice), let's start by creating a couple classes in our src/ directory. Below is a Notifier interface and an implementation:
Notifier.groovy
public interface Notifier {
public void sendMessage(String message);
}
Mail.groovy
public class Mail implements Notifier {
public void sendMessage(String message) {
println "Sending ${message} by Mail"
}
}
As mentioned before, Guice uses Modules instead of XML to specify binding. We can bind our Notifier to the implementation Mail very easily:
GuiceAppModule.groovy
import com.google.inject.*
public class GuiceAppModule extends AbstractModule {
@Override
protected void configure() {
bind(Notifier.class).to(Mail.class);
}
}
Wiring in the Griffon bits
Our plugin does most of the heavy lifting resolving the names of our modules and injecting a Guice injector into the required classes. We just need to give it a few configuration details in griffon-app/conf/Application.groovy. We need to tell Guice where to inject and what modules to inject:
guice {
injectInto = ["controller"]
modules = ["GuiceAppModule"]
}
The last piece is injecting and using our member fields from our controller. In the controller or wherever you deem appropriate, the following will inject the members:
GuiceDemoController.groovy
import com.google.inject.*
class GuiceDemoController {
// these will be injected by Griffon
def model
def view
@Inject Notifier notifier
void mvcGroupInit(Map args) {
// this method is called after model and view are injected
injector.injectMembers(this)
notifier.sendMessage("test");
}
}
When run, the app will print "Sending test by Mail" to the console. The maintainers of Guice caution somewhat against member injection as being less testable. A more traditional constructor-based injection would force Guice to reach below the plugin layer. It's still early days, it might find its way down there anyways.