addywaddy

addywaddy

addywaddy  //  

Jan 22 / 12:15pm

Introducing CouchSurfer

I recently switched over from dm-couchdb-adapter to couchrest for a little project I've been working on for far too long now. One of the reasons was to force me to think more about CouchDB views and avoid accidentally using temporary views.

CouchRest comes with CouchRest::Model, which provides a nice ORM. I wrote a mix-in which sat in my lib/folder and implemented basic associations and validations and worked well for my needs. And left it at that. For about 2 days.

I then heard that Chris was planning on extracting CouchRest::Model out of CouchRest and setting it up as a separate project and, fancying a go at it myself while include the above mentioned features, I set about building my very first gem. The first step was to implement the model, which involved copious amounts of copy and paste (although I made the task a bit harder for myself by rewriting it as a mix-in). I then added associations, validations (using the Validatable gem plus writing a validates_uniqueness_of method) also as mix-ins. The fruits of my labour can be found here:
 
 http://github.com/addywaddy/couchsurfer
 
Check out the README for a basic introduction to what it can do. I must say, although I've been programming Rails/Ruby/Merb for a while now, I've never had much of a use-case for meta-programming so it's been great fun getting from how I wanted the DSL to look to implementing it so that it actually worked.

Now, back to that little project … :)

Comments (2)

Nov 14 / 12:21am

CouchDB and Pagination

It's taken me a while to figure out how to implement pagination with CouchDB but after trawling the interweb I finally posted my question on the couch-db user group and got the reply I needed (Thanks Paul Carey!). I have some results which need to be paginated

  First Page Query:
-----------------
/my_db/_view/document/all
?count=25
&startkey_docid=null
&group=true
&endkey=["5455a49f69aeb7e9e6573159d4dfbca9",{}]
&descending=false
&startkey=["5455a49f69aeb7e9e6573159d4dfbca9",null]

Next Pages Query:
-----------------
/my_db/_view/document/all
?count=25
&startkey_docid=e2a9572739696a257cc2fb25a7f6e495
&group=true
&endkey=["5455a49f69aeb7e9e6573159d4dfbca9",{}]
&descending=false
&startkey=["5455a49f69aeb7e9e6573159d4dfbca9",
"e2a9572739696a257cc2fb25a7f6e495"]

Previous Pages Query:
---------------------
/my_db/_view/document/all
?count=25
&startkey_docid=e2a9572739696a257cc2fb25a7f6e495
&group=true
&endkey=["5455a49f69aeb7e9e6573159d4dfbca9",null]
&descending=true
&startkey=["5455a49f69aeb7e9e6573159d4dfbca9",
"e2a9572739696a257cc2fb25a7f6e495"]

Spot the differences between Previous and Next? To list results in reverse order in couch, you set 'descending' to true. I'd seen this in the pagination Futon, but I was still was getting unexpected results. Paul's reply fixed this though: I hadn't changed to endkey to refer to the start of my results (using null instead of {} in the second part of my composite key). The only thing left to do was reverse my results when descending using Array#reverse! Two things I still want to implement. I don't want the show the 'Next' link when I'm on the last page. I'd prefer to use numbered links alá "1 2 3 4 … 25" instead of "Previous/Next".

Comments (3)

Nov 13 / 2:16pm

Counting Elements with XPath

I've been trying to figure out for the last couple of evenings how to check that a table in one of my specs has the correct amount of rows.Finally, I've found out how to do it:

  it "should have 10 rows" do
html = request("/resource/with/table").body
html.should have_xpath("//table/tbody/tr[count(../*)=10]")
end

I've no idea what the "../*" means but it works!

Update:

From the interweb ( http://www.zvon.org/xxl/XPathTutorial/Output/example1.html ):

"The basic XPath syntax is similar to filesystem addressing."

So, we're going up a level (..) and selecting all tr's in tbody (*).

Comments (0)

Sep 26 / 3:04am

Upgrading your Merb app

Just a quick one here. If you want to upgrade an app generated from an older gem version, cd into your app's folder and run:

  $ merb-gen app . 

Merb's Generator will then interactively show you what's new and where conflicts exist in a similar manner to when you run 'rails .' in an existing rails app.

Comments (0)

Sep 17 / 8:34am

CouchDB on Merb

I recently stumbled across this series of blog posts regarding couchdb and rails and, having a couple of small merb projects behind me, thought I'd find out how to integrate couchdb into a merb application.

NOTE: The following example application is built on merb 0.96 and DM 0.96.

So let's generate our new app and call it .. er .. 'cd_collection'.

    merb-gen app cd_collection
cd cd_collection

We're going to use datamapper for our ORM so first things first, in init.rb:

    use_orm :datamapper

Now run 'rake dm:db:database_yaml' on the command line and rename the template created in config/ database.yml. Edit this as follows:

    adapter:  couchdb
database: cd_collection
host:     127.0.0.1
port:     5984

The adapter tells datamapper to use the dm-couchdb-adapter gem which you should have if you installed dm-more.

 

Aimee's blog covers how to install couchdb but if you're on a mac and running leopard, there is a nice installer available here.
Start couchdb, click on the magnifying glass and a tab should open in your browser with the admin interface for couchdb. Create a database 'cd_collection'.

Let's generate a cd resource for our merb app:

    merb-gen resource cd

and edit the cd.rb model:

    property :id, String, :serial => true, :key => true, :field => :_id
property :rev, String, :field => :_rev
property :title, String

The RDoc for dm-couchdb-adapter mentions only 'property :name, String' but I couldn't get it properly working without the :key and :field attributes.

Now lets make our merb app aware of our cd resource in router.rb:

    r.resources :cds

Before we start our app, we need to add our fields to the forms created my our merb-gen resource command. Edit the 'new' and 'edit' views in cds/ to include:

    <%= partial(:form) %>

and then create a '_form.html.erb' containing:

    <%= text_field :title %>

While we're at it, let's change the index and show views to display our CDs' titles (I'll leave that up to you:-)). One more thing before we start our app. The views added by our resource generation contain methods (specifically the link, form and error helpers) that merb won't understand out of the box. Back to init.rb:

    dependencies "merb-assets", "merb_helpers", "dm-validations"

So let's start up our merb app and navigate to /cds. New -> Enter title -> Save ... Striker! Our first CD has been saved in couchdb. Couchdb uses uuids instead of autoincrement by default which may disappoint those of you wanting to see cd/1 after saving in the address bar.

 

So there we go. I've added 10 CDs now, and I'd like to differentiate them by artist. An artist can have many CDs ... Here's something quit cool about Datamapper. Lets say that you want to keep your CDs in couchdb but would rather have your artists in an relational database. Firstly we need to inform Datamapper about our additional 'repository' in database.yml:

    repositories:
sqlite:
adapter:  sqlite3
database: /path/to/cd_artists.sqlite3
host:     localhost

We can now generate an artist resource as we did for our cds previously. Open up artist.rb and add the following:

    property :id, Integer, :serial => true
property :name, String
has 0..n, :cds
def self.default_repository_name
:sqlite
end

We're telling our Artist model to use the repository named :sqlite. Our association however will look in the default repository which in our case is couchdb. Lets edit the views again for our artists and add an extra field to our cds '_form' partial:

    <%= text_field :title %>
<%= select :artist_id, :collection => Artist.all.map{|a| [a.id, a.name]} %>

So we can associate a CD to a particular artist. Likewise, we can edit our 'show' view for artists to list their CDs:

    <ul>
<% @artist.cds.each do |cd| %>
<li>
<%= cd.title %>
</li>
<% end %>
</ul>

We didn't need to migrate our CD model as couchdb doesn't require a set schema. We will need to now though to get our Artist model up and running so on the command line run 'rake dm:db:automigrate'. You should see CREATE TABLE "artists" somewhere there in the output.
OK. Lets start our app again. You should now be able to add artists and associate CDs to a particular artist. Me like!

Download CD Collection here

Comments (1)