Created
August 4, 2009 19:27
-
-
Save hallettj/161472 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'couch_foo' | |
## Setup code. Put this in an initializer to set up a database connection. | |
CouchFoo::Base.set_database(:host => "http://localhost:5984", | |
:database => "couch_foo_example") | |
# Uncomment this line if you are using Rails | |
#CouchFoo::Base.logger = Rails.logger | |
## Examples of models | |
class User < CouchFoo::Base | |
property :name, String | |
property :handle, String | |
property :email, String | |
property :created_at, Time | |
# Type declarations are encouraged but are now required. Properties can be | |
# given with no declared type. Values for those properties will be | |
# serialized and stored inside a string value. | |
has_many :posts | |
end | |
class Post < CouchFoo::Base | |
property :title, String | |
property :text, String | |
property :user_id, String | |
property :created_at, Time | |
belongs_to :user | |
# Views can be defined explicitly. This map function creates a view that | |
# indexes documents by content length. | |
view :length, <<-EOF | |
function(doc) { | |
if (doc.ruby_class == "#{self.class.name}" && doc.text) { | |
emit(doc.text.length, doc); | |
} | |
} | |
EOF | |
end | |
## couch_foo in action | |
user = User.create(:name => "Jesse", :handle => "hallettj") | |
# Associations are handled transparently. | |
user.posts.create(:title => "First Post", :text => "Hello, world!") | |
# Views are created on demand. This query implicitly creates a 'title' view. | |
post = Post.find(:first, :conditions => { :title => "First Post" }) | |
# Elegant API for retrieving multiple documents. | |
short_posts = Post.find(:all, :conditions => { :length => 0..140 }) | |
long_posts = Post.find(:all, :use_key => :length, :startkey => 141) | |
# All of the views created through a given class are stored in one design | |
# document with a matching name. Thus model classes map to design documents. | |
# Supports transactions via CouchDB's bulk API | |
Post.bulk_save_default = true | |
(2..1000).each do |i| | |
Post.create(:user => user, :title => "Post ##{i}", :text => Array.new(i, "post").join(" ")) | |
end | |
Post.database.commit | |
Post.bulk_save_default = false | |
# Sort based on a column other than the lookup key. Normally query results are | |
# sorted by the lookup key. In this case CouchFoo 'cheats' by creating a view | |
# with keys that are a composite of two fields. | |
Post.find(:all, :use_key => [:title, :user_id], :conditions => { :user_id => user.id }) | |
# Retrieve records that match a set of discrete keys. This requires CouchDB >= 0.9. | |
User.find(:all, :conditions => { :handle => ['hallettj', 'igalko', 'pdxruby'] }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Examples copied from the CouchRest examples directory. | |
## examples/model/example.rb | |
require 'couchrest' | |
def show obj | |
puts obj.inspect | |
puts | |
end | |
## Setup code. Put this in an initializer to set up a database connection. | |
COUCHDB_SERVER = CouchRest.new | |
COUCHDB_SERVER.default_database = 'couchrest-extendeddoc-example' | |
## Examples of CouchRest models. | |
class Author < CouchRest::ExtendedDocument | |
use_database COUCHDB_SERVER.default_database | |
property :name | |
end | |
class Post < CouchRest::ExtendedDocument | |
use_database COUCHDB_SERVER.default_database | |
property :title | |
property :body | |
property :author, :cast_as => 'Author' | |
timestamps! | |
end | |
class Comment < CouchRest::ExtendedDocument | |
use_database COUCHDB_SERVER.default_database | |
property :commenter, :cast_as => 'Author' | |
timestamps! | |
def post= post | |
self["post_id"] = post.id | |
end | |
def post | |
Post.get(self['post_id']) if self['post_id'] | |
end | |
end | |
## CouchRest models in action. | |
puts "Act I: CRUD" | |
puts "Create an author." | |
quentin = Author.new("name" => "Quentin Hazel") | |
puts "Create a new post." | |
post = Post.new(:title => "First Post", :body => "Lorem ipsum dolor sit amet, consectetur adipisicing elit...") | |
puts "Add the author to the post." | |
post.author = quentin | |
puts "Save the post." | |
post.save | |
puts "Load the post." | |
reloaded = Post.get(post.id) | |
puts "\nAdd some comments to the post." | |
comment_one = Comment.new :text => "Blah blah blah", :commenter => {:name => "Joe Sixpack"} | |
comment_two = Comment.new :text => "Yeah yeah yeah", :commenter => {:name => "Jane Doe"} | |
comment_three = Comment.new :text => "Whatever...", :commenter => {:name => "John Stewart"} | |
# TODO - maybe add some magic here? | |
comment_one.post = post | |
comment_two.post = post | |
comment_three.post = post | |
comment_one.save | |
comment_two.save | |
comment_three.save | |
puts "We can load a post through its comment (no magic here)." | |
show post = comment_one.post | |
puts "\nLet's save an author to her own document." | |
jane = comment_two['commenter'] | |
jane.save | |
puts "Oh, that's neat! Because Ruby passes hash valuee by reference, Jane's new id has been added to the comment she left." | |
show comment_two | |
puts "Of course, we'd better remember to save it." | |
comment_two.save | |
puts "Oooh, denormalized... feel the burn!" | |
puts "Act II: Views" | |
puts "Let's find all the comments that go with our post." | |
puts "Our post has id #{post.id}, so lets find all the comments with that post_id." | |
class Comment | |
view_by :post_id | |
end | |
comments = Comment.by_post_id :key => post.id | |
puts "That was too easy." | |
puts "We can even wrap it up in a finder on the Post class." | |
class Post | |
def comments | |
Comment.by_post_id :key => id | |
end | |
end | |
puts "Gimme 5 minutes and I'll roll this into the framework. ;)" | |
puts | |
puts "There is a lot more that can be done with views, but a lot of the interesting stuff is joins, which of course range across types. We'll pick up where we left off, next time." | |
## See more examples at: | |
## http://jchrisa.net/drl/_design/sofa/_show/post/couchrest__model___orm__the_cou |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Overview of Object-Document Mapping libraries for Ruby and CouchDB. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'couch_potato' | |
CouchPotato::Config.database_name = 'odm_overview' | |
class Address | |
attr_accessor :name, :street, :city, :state, :zip | |
def initialize(attrs={}) | |
attrs.each do |k,v| | |
setter = k.to_s + '=' | |
send(k.to_s + '=', v) if respond_to? setter | |
end | |
end | |
end | |
class LineItem | |
attr_accessor :product_code, :qty, :price_per_unit | |
def initialize(attrs={}) | |
attrs.each do |k,v| | |
setter = k.to_s + '=' | |
send(k.to_s + '=', v) if respond_to? setter | |
end | |
end | |
end | |
## The document class | |
class Invoice | |
include CouchPotato::Persistence | |
property :shipping_address #, :class => Address | |
property :billing_address #, :class => Address | |
property :ordered_at, :type => Time | |
property :items | |
property :tax | |
property :shipping | |
property :total | |
validates_presence_of :ordered_at | |
view :all, :key => :ordered_at | |
# view(:total_sales, :type => :aggregate, | |
# :key => :ordered_at, :sum => 'items.qty * items.price_per_unit') | |
# view(:average_sale, :type => :aggregate, | |
# :key => :ordered_at, :average => 'items.qty * items.price_per_unit') | |
view(:total_sales, :type => :raw, :map => %q{ | |
function(doc) { | |
var i; | |
if (doc.items) { | |
for (i = 0; i < doc.items.length; i += 1) { | |
emit(doc.ordered_at, doc.items[i].qty * doc.items[i].price_per_unit); | |
} | |
} | |
} | |
}, | |
:reduce => %q{ | |
function(keys, values, rereduce) { | |
return sum(values); | |
} | |
}, :results_filter => lambda { |results| results['rows'].map { |r| r['value'] } }) | |
view(:average_sale, :type => :raw, :map => %q{ | |
function(doc) { | |
var i, total = 0; | |
if (doc.items) { | |
for (i = 0; i < doc.items.length; i += 1) { | |
total += doc.items[i].qty * doc.items[i].price_per_unit; | |
} | |
emit(doc.ordered_at, total); | |
} | |
} | |
}, | |
:reduce => %q{ | |
function(keys, values, rereduce) { | |
return sum(values) / values.length; | |
} | |
}, :results_filter => lambda { |results| results['rows'].map { |r| r['value'] } }) | |
end | |
# Initialize views | |
CouchPotato.database.view Invoice.all | |
CouchPotato.database.view Invoice.total_sales | |
## Creating a document | |
invoice = Invoice.new( | |
:shipping_address => Address.new(:name => 'Joe Smith', | |
:street => '123 Main St', | |
:city => 'Anytown', | |
:state => 'CA', | |
:zip => '12345'), | |
:billing_address => Address.new(:name => 'Joe Smith', | |
:street => '123 Main St', | |
:city => 'Anytown', | |
:state => 'CA', | |
:zip => '12345'), | |
:ordered_at => Time.now.utc, | |
:items => [ | |
LineItem.new(:product_code => 'AX5718', :qty => 1, :price_per_unit => 5.00), | |
LineItem.new(:product_code => 'BB9388', :qty => 3, :price_per_unit => 2.00), | |
], | |
:tax => 0.00, | |
:shipping => 10.00, | |
:total => 21.00 | |
) | |
CouchPotato.database.save_document invoice | |
## Looking up a single document | |
invoice_copy = CouchPotato.database.load_document invoice._id | |
## Querying up documents | |
invoices = CouchPotato.database.view Invoice.all(:key => (Time.now - 300000)..(Time.now), | |
:descending => true) | |
## Aggregations | |
count = CouchPotato.database.view Invoice.all(:reduce => true) | |
puts "%i invoices" % count | |
sales = CouchPotato.database.view Invoice.total_sales(:reduce => true) | |
puts "$%0.2f total sales" % sales | |
average = CouchPotato.database.view Invoice.average_sale(:reduce => true) | |
puts "$%0.2f average sale" % average |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment