Rather than use somebody’s couch gem, (which may or may not return JSON when you expect it to!), what I’m doing here is rolling my own compact Couch library class in Ruby. The class simply uses net/ssh to make properly formed REST calls to the CouchDB. You also need the ‘json’ gem installed. (Also, I’m using Ruby 1.8.7)
The Couch class needs a host, port and db parameter passed into its initialize method. The db is the name of the db, while the host and port are used by the request method to form the URL. Most every call to Couch is either a GET or a PUT (there is also a DELETE), so we only need a couple of methods (get, put) to create the right kind of Net::Http object, make the REST call and convert the Http response into JSON.
Here is couch_lib.rb
# begin code
require ‘net/ssh’
require ‘json’
class Couch
def initialize(host, db, port=5984, options = nil)
@host = host
@port = port
@db = db
@options = options
end
def request(req)
res = Net::HTTP.start(@host, @port) { |http|http.request(req) }
unless res.kind_of?(Net::HTTPSuccess)
# if there was some kind of Http error, show it
puts res
end
res
end
def get(id, parse=true)
res = request(Net::HTTP::Get.new(“/#{@db}/#{id}”))
parse or return res.body
JSON.parse res.body
end
def put(id, doc)
req = Net::HTTP::Put.new(“/#{@db}/#{id}”)
req["content-type"] = “application/json”
begin
req.body = JSON.generate(doc)
res = request(req)
$DEBUG and puts “put response: #{res.code} :: #{res.body}”
rescue
current = get(id)
doc["_rev"] = current["_rev"]
req.body = JSON.generate(doc)
res = request(req)
end
hash = JSON.parse res.body
end
# here we have a method to create the database
def create
req = Net::HTTP::Put.new(“/#{@db}/”)
req["content-type"] = “application/json”
res = request(req)
JSON.parse res.body
end
# use clear method to drop and create a db
def clear
begin
request(Net::HTTP::Delete.new(“/#{@db}”))
rescue
end
req = Net::HTTP::Put.new(“/#{@db}/”)
req["content-type"] = “application/json”
res = request(req)
JSON.parse res.body
end
end
# end code
To test the Couch class, I created a new database, put some data into it, created and stored some views, and make queries. The default parameters assume that CouchDB is running on localhost on default port 5984, but those are easily configurable.
# begin code
require ‘rubygems’
require ‘couch_lib’
$DEBUG = true
class CouchTest
def initialize(p={})
@params = { “host” => “127.0.0.1″,
“port” => “5984″,
“db” => “pwp”
}.merge! p
begin
@db = Couch.new(@params["host"], @params["db"])
rescue => e
puts(‘oops. is couchdb running at http://#{@params["host"]}:#{@params["port"]}?’)
exit(-1)
end
# make pwp database
make_pwp_db
# make pwp-specific view
make_pwp_views
end
def make_pwp_db
puts “delete the existing db and create a new one”
@db.clear
unwritten = {“title” => “unwritten rules”,”date” => “2010″,”category” =>”urban fantasy”}
sidewalk = {“title” => “secret sidewalk”,”date” => “2007″,”category” =>”urban fantasy”}
renegade = {“title” => “renegade robot”,”date” => “2010″,”category” =>”urban fantasy”}
zone = {“title” => “time zone”,”date” => “1999″,”category” =>”science fiction”}
missy = {“title” => “missy tonight”,”date” => “2008″,”category” =>”atheist fiction”}
@db.put(“unwritten”, unwritten)
@db.put(“sidewalk”, sidewalk)
@db.put(“renegade”, renegade)
@db.put(“zone”, zone)
@db.put(“missy”, missy)
end
def make_pwp_views
titles_view = { “_id” => “_design/titles “,”language” => “javascript”,
“views” => {
“result” => {“map” => “function(doc) { if (doc.title) { emit(doc.title, doc)} }” },
“all” => {“map” => “function(doc) { emit(null, doc) }” }
}
}
dates_view = { “_id” => “_design/dates “,”language” => “javascript”,
“views” => {
“result” => {“map” => “function(doc) { if (doc.date) { emit(doc.date, doc)} }” }
}
}
categories_view = { “_id” => “_design/categories “,”language” => “javascript”,
“views” => {
“result” => {“map” => “function(doc) { if (doc.category) { emit(doc.category, doc) } }” }
}
}
@db.put “_design/titles”, titles_view
@db.put “_design/dates”, dates_view
@db.put “_design/categories”, categories_view
end
def delete_doc(doc)
@db.delete(doc)
end
def get_all_items(sorted=false)
# curl -X GET http://127.0.0.1:5984/pwp/_design/titles/_view/results
puts “==> get all items”
dataset = Array.new
items = @db.get(“_design/titles/_view/all”)["rows"]
items.each do |row|
dataset << format_item(row["value"])
end
dataset.sort! if sorted
dataset.each { | s | puts s } if $DEBUG
return dataset
end
def get_all_items_by_date()
puts “==> get all items by date”
dataset = Array.new
items = @db.get(“_design/dates/_view/result”)["rows"]
items.each do |row|
dataset << format_item(row["value"])
end
dataset.each { | s | puts s } if $DEBUG
return dataset
end
def get_by_date(target=”2010″)
puts “==> get by date: #{target}”
dataset = Array.new
items = @db.get(“_design/dates/_view/result”)["rows"]
items.each do |row|
item = row["value"]["date"]
if item.include?(target)
$DEBUG and puts JSON.pretty_generate(row)
dataset << format_item(row["value"])
end
end
dataset.each { | s | puts s } if $DEBUG
return dataset
end
def get_by_title(target=”e”)
puts “==> get by title: #{target}”
dataset = Array.new
items = @db.get(“_design/titles/_view/result”)["rows"]
items.each do |row|
item = row["value"]["title"]
if item.include?(target)
dataset << format_item(row["value"])
end
end
dataset.each { | s | puts s }
return dataset
end
def get_by_category(target=”e”)
puts “==> get by category: #{target}”
dataset = Array.new
items = @db.get(“_design/categories/_view/result”)["rows"]
items.each do |row|
item = row["value"]["category"]
if item.include?(target)
dataset << format_item(row["value"])
end
end
dataset.each { | s | puts s } if $DEBUG
return dataset
end
def format_item(item)
return “title: #{item["title"]}\tdate: #{item["date"]}\tcategory: #{item["category"]}”
end
def update_renegade
puts “==> update renegade, change its category”
renegade = {“title” => “renegade robot”,”date” => “2010″,”category” =>”science fiction”}
@db.update “renegade”, renegade
end
def delete_renegade
puts “==> delete renegade”
r = @db.get(“renegade”)
@db.delete(r)
end
end
# usage example: ruby couch_test.rb host=localhost port=5984
if $0 == __FILE__
params = {}
ARGV.each do|a|
args = a.split(“=”)
params[args[0]] = args[1].chomp
end
db = CouchTest.new(params)
db.get_all_items(true)
db.get_all_items_by_date
db.get_by_date(“2010″)
db.get_by_category(“science fiction”)
db.get_by_title(“secret”)
db.update_renegade
db.get_by_category(“science fiction”)
# db.delete_renegade
# db.get_by_category(“science fiction”)
end
# end code