Monday, June 28, 2010

Integrate Rails with CouchDB using ActiveCouch

This document assumes you have CouchDB 0.8.1 + as well as Rails 2.1.1 + installed.
To install CouchDB please refer Installing CouchDb on windows
.
So a lot of the evolution of ActiveCouch correlates with the level of frustration with the library and making it better implies making our lives better. There are maybe better/more-lightweight wrappers for CouchDB out there.

Install ActiveCouch as a Rails Plugin

The biggest advantage of ActiveCouch v0.2.x is that it can be used as a Rails plugin as well as, as a standalone gem. You can install activecouch gem by the command
$ gem install activecouch

ActiveCouch uses the JSON gem for fast JSON serialization/deserialization, so install that first:

$ gem install json
Then, create your Rails project

$ rails couchify
Then, install ActiveCouch as a plugin:


$ cd couchify
$ ./script/plugin install git://github.com/arunthampi/activecouch.git
This installs ActiveCouch in RAILS_ROOT/vendor/plugins

Follow up – Get it started

You need to create a file called activecouch.yml in RAILS_ROOT/config which looks something like this (Of course, depending on your deployment strategy, your test and production sites will be something else)

development:
  site: http://localhost:5984/
test:
  site: http://localhost:5984/
production:
  site: http://localhost:5984/ 

Create your ActiveCouch model

This is now very simple in ActiveCouch. At the root of your Rails project, simply enter
$ ./script/generate activecouch_model cat
This generates a cat.rb file in RAILS_ROOT/app/models which looks something like this:

class Cat < ActiveCouch::Base
    site YAML::load(File.open(File.join(Rails.root,
                  'config', 'activecouch.yml')))[Rails.env]['site']
end
All set to use CouchDB database depending on your Rails environment. Now ActiveCouch assumes that you already have a database called ‘cats’ created in CouchDB, so that you can start creating all your CouchDB documents. No matter, Rake tasks to the rescue:

Creating your database

If you goto RAILS_ROOT of your app, and enter the following:

$ rake --tasks
You will see some of the following:

rake activecouch:create_db           # Creates a database in CouchDB
rake activecouch:delete_db           # Deletes a database from CouchDB
rake activecouch:delete_view         # Deletes a view in CouchDB
rake activecouch:save_view           # Saves a view in CouchDB
So to create your database, just enter

$ rake activecouch:create_db db=cats
And boom, you have your database called cats created in ActiveCouch.
If for some reason, you need to delete your database, the answer is simple:

$ rake activecouch:delete_db db=cats
Awesome, now you can go ahead and edit your model to have various fields, for example a cat should probably have a name, so your model will look something like this:

class Cat < ActiveCouch::Base
    site YAML::load(File.open(File.join(Rails.root,
                  'config', 'activecouch.yml')))[Rails.env]['site']

    has :first_name
    has :last_name
end

Creating your first ActiveCouch Objects

To see if this works, open up ./script/console and type in the following:

> cat = Cat.new(:first_name => "Mr.", :last_name => "Jinks")
> cat.save
Or

> cat = Cat.create(:first_name => "Mrs.", :last_name => "Jinks")

Extract db data

CouchDB lets you query your database using something called Views. The CouchDB wiki has a lot of information on how views work. You should also probably consider watching Jan Lenhardt’s awesome talk on CouchDB , which explains a lot of the differences between Relational Databases and Document-Oriented Databases.
ActiveCouch tries to abstract a lot of the view behaviour from you (some people have said it is inelegant , so I’m hoping for some better ideas from people reading this). For example, for the Cat model above, suppose you want to query for all cats by their last name, you can create a view at /cats/design/by_last_name/by_lastname and ActiveCouch lets you query your CouchDB database like so:

cats = Cat.find(:all, :params => {:last_name => "Jinks"})
The price you pay for the somewhat unwieldy view names, is that you get very convenient find methods. Of course if you have created your custom views, you can always use the find_from_url method which works something like this:

cats = Cat.find_from_url("/cats/_view/by_last_name/by_last_name?key=%22Jinks%22")
Ok, back to views. ActiveCouch lets you create your views using a very simple generator. So from your RAILS_ROOT again,

$ ./script/generate activecouch_view ByLastName
This creates an ActiveCouch::View class in RAILS_ROOT/app/models. You can edit the view as given below, so that you can start querying by last name:


class ByLastName < ActiveCouch::View
  define :for_db => 'cats' do 
    with_key 'last_name'
  end
end
Done. But this view is not yet in the CouchDB database, so you need to save it into the database.

Saving your view

To save your view, you can use the built-in Rake task to save your views:

$ rake activecouch:save_view db=cats view=ByLastName
And boom, your view is saved.
Now suppose you want your view to be saved in many databases inside CouchDB. i.e. you have databases such as dogs, people, etc. and all of them need to be queried by their last name. No matter, you can use db=_all_dbs as part of your Rake task and this will save the view into all databases.

$ rake activecouch:save_view db=_all_dbs view=ByLastName
Now you are ready to start querying your database and finding objects.

Installing CouchDB

CouchDB for windows is available at
http://shockdragon.com/couchdb/couchdb-0.9-windows.zip
Simply download it and then extract the zip and execute 'couch_start.bat' from the bin directory, then Futon will be available at
http://127.0.0.1:5984/_utils
 
For more information refer the links:
http://wiki.apache.org/couchdb/Installing_on_Windows
http://wiki.apache.org/couchdb/Windows_binary_installer
http://people.apache.org/~mhammond/dist/snapshots/
http://people.apache.org/~mhammond/dist/0.11.0/

Thursday, June 24, 2010

Prawnto to generate pdf documents.

You need to install the following
gem install prawn
ruby script/plugin install git://github.com/thorny-sun/prawnto.git

Now add a link in your view

<%= link_to "Printable Invoice (PDF)", order_path(@order, :format => 'pdf') %>

Your controller

prawnto :prawn => { :top_margin => 75 }

def show
  @order = Order.find(params[:id])
end
 
create show.pdf.prawn in your views and add
 
pdf.text "Order ##{@order.id}", :size => 30, :style => :bold

pdf.move_down(30)

items = @order.cart.line_items.map do |item|
  [
    item.product.name,
    item.quantity,
    number_to_currency(item.unit_price),
    number_to_currency(item.full_price)
  ]
end

pdf.table items, :border_style => :grid,
  :row_colors => ["FFFFFF","DDDDDD"],
  :headers => ["Product", "Qty", "Unit Price", "Full Price"],
  :align => { 0 => :left, 1 => :right, 2 => :right, 3 => :right }

pdf.move_down(10)

pdf.text "Total Price: #{number_to_currency(@order.cart.total_price)}", :size => 16, :style => :bold
 
For more information refer: http://railscasts.com/episodes/153-pdfs-with-prawn
For more information on prawnto refer: http://www.cracklabs.com/prawnto 

Wednesday, June 16, 2010

Using Faster CSV to parse txt files

You can parse txt files using the following code
count = 1
    ActiveRecord::Base.connection.execute("TRUNCATE your_table")
    @parsed_file = FasterCSV.parse(params[:import][:file].read).each do |row|
      if count == 0              
        unless row[0].nil?
          data = row[0].split "\t"
          self.create(:attribute1 => data[0], :attribute2 => data[1], :attribute3 => data[2])
          table_data << data
        end
      end
      count = 0
    end

Tuesday, June 1, 2010

Paperclip plugin Errno::EACCES Permission denied on windows

If you get the following error on windows while using paperclip plugin
[paperclip] Saving attachments.
[paperclip] deleting C:/Documents and Settings/Sandeep/My Documents/NetBeansProjects/logicare/trunk/public/system/datas/208/original/WLHBSITEAM.csv
  [4;36;1mSQL (0.0ms) [0m   [0;1mROLLBACK [0m

Errno::EACCES (Permission denied - C:/Documents and Settings/Sandeep/My Documents/NetBeansProjects/logicare/trunk/public/system/datas/208/original/WLHBSITEAM.csv):
  c:/ruby/lib/ruby/1.8/fileutils.rb:1297:in `unlink'
  c:/ruby/lib/ruby/1.8/fileutils.rb:1297:in `remove_file'
  c:/ruby/lib/ruby/1.8/fileutils.rb:1305:in `platform_support'
  c:/ruby/lib/ruby/1.8/fileutils.rb:1296:in `remove_file'
  c:/ruby/lib/ruby/1.8/fileutils.rb:771:in `remove_file'
  c:/ruby/lib/ruby/1.8/fileutils.rb:549:in `rm'
  c:/ruby/lib/ruby/1.8/fileutils.rb:548:in `each'
  c:/ruby/lib/ruby/1.8/fileutils.rb:548:in `rm'
  app/controllers/nhs_datas_controller.rb:54:in `update'
  app/controllers/nhs_datas_controller.rb:53:in `update'

Steps to fix the problem
1) create patches.rb in lib folder and add these lines
require 'tempfile'
class Tempfile
  def size
    if @tmpfile
      @tmpfile.fsync
      @tmpfile.flush
      @tmpfile.stat.size
    else
      0
    end
  end
end
2) Add require "patches.rb" in environment.rb inside Rails::Initializer.run
3)Make sure you have installed http://www.imagemagick.org/script/binary-releases.php#windows
4) Restart the server and you should be able to add, delete and update files.

 There is another possibility why you will get permission denied error. This is when u have the file related data saved in associated tables. Say users table has data_id and there is files table has the related data i.e data_file_name, data_content_type etc.
In this scenario when you try you update the file then you will get permission denied errors. One possible way to fix this issue is.
1) first upload the new file
2) Then update the related table i.e users table with data_id . Now the link to old data is lost.
3) You have the old file id as params while submit. so using the get the file and the delete it.
Sample code
@data = Data.new
    @data.data = params[:data][:data]
    @data.save
    @user = User.find_by_data_id(params[:id])
    @user.data_id = @data.id
    @user.save
    @old_data = Data.find(params[:id])
    @old_data.destroy
Let me know if there is any better way to fix this issue.