On Sun, Aug 28, 2011 at 04:58, Daniel Bryan <danbr...@gmail.com> wrote:
> 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. > > … > > 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) > }" > > > Woah, that sounds pretty cool. Are you using RubyParser or Ripper? 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. > I agree. What's the point of namespaces (modules/classes) if you're going to store everything in the same place? > 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. > I actually have the same problem with ActiveRecord: There's so many things that's working across tables, but it forces you to put it in one model. I don't think that even makes sense in a SQL-world. It doesn't make sense in a relational world, but it seems people like objects so well that they try to put the (quite sensible) relational model in a object-oriented world… > So uh > > does anyone > > have any advice? > > -- Cerales (@lodoicea) > It's a hard problem that you've stumbled upon; trying to integrate two quite different point-of-views (Ruby vs CouchDb) in an elegant way. Okay, let's see… First of all: Does all MapReduce-queries work on different types of documents; i.e. does it make sense to run MapReduce on just one type of documents? I guess so? In that cases, it would be quite useful to say `Receipes.tagged_with("Bacon")`… What if you introduce the concept of *contexts*? So all CouchDocument are automatically one context (that only include one type of document), but you can also introduce your own contexts. So you can have a CookingUser-context, which describes a User and what he can do with Receipes. In your controller you can say `CookingUser.new(current_user).untried_recepies`. Or maybe a UserReceipes which has an "untried"-view. So contexts are just a lightweight class where you can place your views. The developer has to decide what the context should be called and how they should work. The point is that the contexts should mirror the use-cases of the data, and as there might be many ways to look at the same data, you can't have an automatic way of saying where the view belongs. It all depends on the point-of-view. I guess what I'm really trying to say: Don't have one class where you put everything. Don't force the views to belong to one set of classes. Do something in-between: Let the views live where they make sense, sometimes on a CouchDocument-class (when the views only work on that class), sometimes on a custom class (where it's the developer who decides what that class *means*). I'll have to admit that these are mainly my thoughts on the general "It sucks to place all queries in only ActiveRecord-classes"-problem I mentioned above, but I hope this might be applicable in other cases too…
_______________________________________________ Camping-list mailing list Camping-list@rubyforge.org http://rubyforge.org/mailman/listinfo/camping-list