Hello camping people I've written a Ruby library for working with CouchDB. It's a pretty thin API - it provides a database object, a document object (a glorified hash) and a design object.
In case anyone's not familiar with CouchDB, it's a schema-less JSON document database with a HTTP interface. You store documents in the database, then query the database with MapReduce views. Your map and reduce queries are just JavaScript, and they're stored as fields of design documents. Generally, each design document corresponds to an 'app'. So it's all lovely and simple, and I've been working on writing a module that plugs my API into Camping. Models look like this: > module MyFood::Models > class Recipe < CouchDocument > needs :name > needs :ingredients > needs :instructions > suggests :dates_cooked, :array > suggests :cost > end > end CouchDocument is a hash with some methods for pushing to and pulling from the document database, and some validation functions. "needs" and "suggests" both just refer to keys. I haven't decided exactly how to implement "suggests" yet; "needs" enforces the existence of particular keys before a document can be pushed. It also looks at the name of the class and uses it for a "kind" or "type" key. I've written a small library that uses S Expressions to parse Ruby into JavaScript. It's limited, but for the moment it's adequate to my purposes: writing JavaScript MapReduce functions as Ruby blocks. This means you can do this: > module MyFood::MapReduce > view :untried_recipes do > map do > emit(doc.name, doc) if doc.kind == "Recipe" and doc.dates_cooked.length == 0 > end > end > > > view :cost_of_crazy_huge_indian_meal > map do > emit(doc.name, doc.cost) if doc.kind == "Recipe" and doc.cuisine == "South > Asian" > end > reduce do > return sum(values) > end > end > end And you get a design document with two entries in the "views" field: > "map"=> > "function ( doc ) { > if( doc.kind == "Recipe" && doc.dates_cooked.length == 0 ) { > emit(doc.name, doc) > } > }" And: > "map" => > "function ( doc ) { > if( doc.kind == "Recipe" && doc.cuisine == "South Asian" ) { > emit(doc.name, doc.cost) > } > }", "reduce"=> > "function ( key, values, rereduce ) { > return sum(values) > }" So, yeah, whatever, that's all well and good, you can write your queries and it'll update the design document on the server so you can just do a HTTP GET request to the view's URL and CouchDB will return your rows or whatever. Cool. The thing is, I have no idea how to unify this with Camping. I feel like a "relaxing" schema-less thing like this is the perfect match for really small web projects, and there's the added bonus that since the CouchDB settings are just defined in a global variable at the top of your app, you can have different databases - or even different database servers - for different Camping apps running on the same web server if you like. There's been a bit of discussion on the list lately about how great it is that Ruby's controllers belong to classes - there's an intrinsic logic to it. I want something as intuitive as that for this query language, but I don't know how to do it. The earliest implementation of this I did just had a Design class, and when you wrote a view it'd define a method on the Design class with that view's name - so, above, you'd call Design.untried_recipes, or Design.cost_of_blahblah, and it'd query Couch for the result. That's okay until you have more than a small handful of views - more than that, and there's too much to keep track of. The views could just belong to models, so you could call Recipe.untried, or something, but the thing about MapReduce is that so many of your queries don't naturally belong to a particular "model", and I don't really want to built an architecture that makes people tend to write their models and queries as if it's SQL and you need to normalise and get all heavy with joins and generally ruin your life with boredom. If you want a view with particular data, you write a view that gives you exactly that, you save it, CouchDB automatically caches it in your fancy B-trees, and you're rolling. It's beautiful, but I don't know how to make it user-friendly. So uh does anyone have any advice? -- Cerales (@lodoicea)
_______________________________________________ Camping-list mailing list Camping-list@rubyforge.org http://rubyforge.org/mailman/listinfo/camping-list