Merge branch 'master' of github.com:couchrest/couchrest_model into improve_associations
This commit is contained in:
commit
ee31946e07
2
Rakefile
2
Rakefile
|
@ -24,7 +24,7 @@ begin
|
|||
gemspec.homepage = "http://github.com/couchrest/couchrest_model"
|
||||
gemspec.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos", "Will Leinweber", "Sam Lown"]
|
||||
gemspec.extra_rdoc_files = %w( README.md LICENSE THANKS.md )
|
||||
gemspec.files = %w( LICENSE README.md Rakefile THANKS.md history.txt couchrest.gemspec) + Dir["{examples,lib,spec,utils}/**/*"] - Dir["spec/tmp"]
|
||||
gemspec.files = %w( LICENSE README.md Rakefile THANKS.md history.txt couchrest.gemspec) + Dir["{examples,lib,spec}/**/*"] - Dir["spec/tmp"]
|
||||
gemspec.has_rdoc = true
|
||||
gemspec.add_dependency("couchrest", ">= 1.0.0")
|
||||
gemspec.add_dependency("mime-types", ">= 1.15")
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'couchrest')
|
||||
|
||||
def show obj
|
||||
puts obj.inspect
|
||||
puts
|
||||
end
|
||||
|
||||
SERVER = CouchRest.new
|
||||
SERVER.default_database = 'couchrest-extendeddoc-example'
|
||||
|
||||
class Author < CouchRest::ExtendedDocument
|
||||
use_database SERVER.default_database
|
||||
property :name
|
||||
|
||||
def drink_scotch
|
||||
puts "... glug type glug ... I'm #{name} ... type glug glug ..."
|
||||
end
|
||||
end
|
||||
|
||||
class Post < CouchRest::ExtendedDocument
|
||||
use_database SERVER.default_database
|
||||
|
||||
property :title
|
||||
property :body
|
||||
property :author, :cast_as => 'Author'
|
||||
|
||||
timestamps!
|
||||
end
|
||||
|
||||
class Comment < CouchRest::ExtendedDocument
|
||||
use_database 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
|
||||
|
||||
puts "Act I: CRUD"
|
||||
puts
|
||||
puts "(pause for dramatic effect)"
|
||||
puts
|
||||
sleep 2
|
||||
|
||||
puts "Create an author."
|
||||
quentin = Author.new("name" => "Quentin Hazel")
|
||||
show quentin
|
||||
|
||||
puts "Create a new post."
|
||||
post = Post.new(:title => "First Post", :body => "Lorem ipsum dolor sit amet, consectetur adipisicing elit...")
|
||||
show post
|
||||
|
||||
puts "Add the author to the post."
|
||||
post.author = quentin
|
||||
show post
|
||||
|
||||
puts "Save the post."
|
||||
post.save
|
||||
show post
|
||||
|
||||
puts "Load the post."
|
||||
reloaded = Post.get(post.id)
|
||||
show reloaded
|
||||
|
||||
puts "The author of the post is an instance of Author."
|
||||
reloaded.author.drink_scotch
|
||||
|
||||
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
|
||||
|
||||
show comment_one
|
||||
show comment_two
|
||||
show comment_three
|
||||
|
||||
puts "We can load a post through its comment (no magic here)."
|
||||
show post = comment_one.post
|
||||
|
||||
puts "Commenters are also authors."
|
||||
comment_two['commenter'].drink_scotch
|
||||
comment_one['commenter'].drink_scotch
|
||||
comment_three['commenter'].drink_scotch
|
||||
|
||||
puts "\nLet's save an author to her own document."
|
||||
jane = comment_two['commenter']
|
||||
jane.save
|
||||
show jane
|
||||
|
||||
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
|
||||
show comment_two
|
||||
|
||||
puts "Oooh, denormalized... feel the burn!"
|
||||
puts
|
||||
puts
|
||||
puts
|
||||
puts "Act II: Views"
|
||||
puts
|
||||
puts
|
||||
sleep 2
|
||||
|
||||
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."
|
||||
puts
|
||||
|
||||
class Comment
|
||||
view_by :post_id
|
||||
end
|
||||
|
||||
comments = Comment.by_post_id :key => post.id
|
||||
show comments
|
||||
|
||||
puts "That was too easy."
|
||||
puts "We can even wrap it up in a finder on the Post class."
|
||||
puts
|
||||
|
||||
class Post
|
||||
def comments
|
||||
Comment.by_post_id :key => id
|
||||
end
|
||||
end
|
||||
|
||||
show post.comments
|
||||
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."
|
|
@ -39,7 +39,7 @@ module CouchRest
|
|||
attr_accessor :casted_by
|
||||
|
||||
|
||||
# Instantiate a new ExtendedDocument by preparing all properties
|
||||
# Instantiate a new CouchRest::Model::Base by preparing all properties
|
||||
# using the provided document hash.
|
||||
#
|
||||
# Options supported:
|
||||
|
|
|
@ -7,11 +7,15 @@ module CouchRest
|
|||
def create_attachment(args={})
|
||||
raise ArgumentError unless args[:file] && args[:name]
|
||||
return if has_attachment?(args[:name])
|
||||
self['_attachments'] ||= {}
|
||||
set_attachment_attr(args)
|
||||
rescue ArgumentError => e
|
||||
raise ArgumentError, 'You must specify :file and :name'
|
||||
end
|
||||
|
||||
# return all attachments
|
||||
def attachments
|
||||
self['_attachments'] ||= {}
|
||||
end
|
||||
|
||||
# reads the data from an attachment
|
||||
def read_attachment(attachment_name)
|
||||
|
@ -30,13 +34,13 @@ module CouchRest
|
|||
|
||||
# deletes a file attachment from the current doc
|
||||
def delete_attachment(attachment_name)
|
||||
return unless self['_attachments']
|
||||
self['_attachments'].delete attachment_name
|
||||
return unless attachments
|
||||
attachments.delete attachment_name
|
||||
end
|
||||
|
||||
# returns true if attachment_name exists
|
||||
def has_attachment?(attachment_name)
|
||||
!!(self['_attachments'] && self['_attachments'][attachment_name] && !self['_attachments'][attachment_name].empty?)
|
||||
!!(attachments && attachments[attachment_name] && !attachments[attachment_name].empty?)
|
||||
end
|
||||
|
||||
# returns URL to fetch the attachment from
|
||||
|
@ -62,7 +66,7 @@ module CouchRest
|
|||
def set_attachment_attr(args)
|
||||
content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file].path)
|
||||
content_type ||= (get_mime_type(args[:name]) || 'text/plain')
|
||||
self['_attachments'][args[:name]] = {
|
||||
attachments[args[:name]] = {
|
||||
'content_type' => content_type,
|
||||
'data' => args[:file].read
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@ describe "Model attachments" do
|
|||
@obj.delete_attachment(@attachment_name)
|
||||
@obj.has_attachment?(@attachment_name).should be_false
|
||||
end
|
||||
|
||||
it 'should return false if an attachment has been removed and reloaded' do
|
||||
@obj.delete_attachment(@attachment_name)
|
||||
reloaded_obj = Basic.get(@obj.id)
|
||||
reloaded_obj.has_attachment?(@attachment_name).should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "creating an attachment" do
|
||||
|
@ -46,14 +53,14 @@ describe "Model attachments" do
|
|||
@obj.create_attachment(:file => @file_ext, :name => @attachment_name)
|
||||
@obj.save.should be_true
|
||||
reloaded_obj = Basic.get(@obj.id)
|
||||
reloaded_obj['_attachments'][@attachment_name].should_not be_nil
|
||||
reloaded_obj.attachments[@attachment_name].should_not be_nil
|
||||
end
|
||||
|
||||
it "should create an attachment from file without an extension" do
|
||||
@obj.create_attachment(:file => @file_no_ext, :name => @attachment_name)
|
||||
@obj.save.should be_true
|
||||
reloaded_obj = Basic.get(@obj.id)
|
||||
reloaded_obj['_attachments'][@attachment_name].should_not be_nil
|
||||
reloaded_obj.attachments[@attachment_name].should_not be_nil
|
||||
end
|
||||
|
||||
it 'should raise ArgumentError if :file is missing' do
|
||||
|
@ -66,19 +73,19 @@ describe "Model attachments" do
|
|||
|
||||
it 'should set the content-type if passed' do
|
||||
@obj.create_attachment(:file => @file_ext, :name => @attachment_name, :content_type => @content_type)
|
||||
@obj['_attachments'][@attachment_name]['content_type'].should == @content_type
|
||||
@obj.attachments[@attachment_name]['content_type'].should == @content_type
|
||||
end
|
||||
|
||||
it "should detect the content-type automatically" do
|
||||
@obj.create_attachment(:file => File.open(FIXTURE_PATH + '/attachments/couchdb.png'), :name => "couchdb.png")
|
||||
@obj['_attachments']['couchdb.png']['content_type'].should == "image/png"
|
||||
@obj.attachments['couchdb.png']['content_type'].should == "image/png"
|
||||
end
|
||||
|
||||
it "should use name to detect the content-type automatically if no file" do
|
||||
file = File.open(FIXTURE_PATH + '/attachments/couchdb.png')
|
||||
file.stub!(:path).and_return("badfilname")
|
||||
@obj.create_attachment(:file => File.open(FIXTURE_PATH + '/attachments/couchdb.png'), :name => "couchdb.png")
|
||||
@obj['_attachments']['couchdb.png']['content_type'].should == "image/png"
|
||||
@obj.attachments['couchdb.png']['content_type'].should == "image/png"
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -113,7 +120,7 @@ describe "Model attachments" do
|
|||
file = File.open(FIXTURE_PATH + '/attachments/README')
|
||||
@file.should_not == file
|
||||
@obj.update_attachment(:file => file, :name => @attachment_name, :content_type => @content_type)
|
||||
@obj['_attachments'][@attachment_name]['content_type'].should == @content_type
|
||||
@obj.attachments[@attachment_name]['content_type'].should == @content_type
|
||||
end
|
||||
|
||||
it 'should delete an attachment that exists' do
|
||||
|
@ -143,6 +150,27 @@ describe "Model attachments" do
|
|||
it 'should return the attachment URI' do
|
||||
@obj.attachment_uri(@attachment_name).should == "#{Basic.database.uri}/#{@obj.id}/#{@attachment_name}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#attachments" do
|
||||
before(:each) do
|
||||
@obj = Basic.new
|
||||
@file = File.open(FIXTURE_PATH + '/attachments/test.html')
|
||||
@attachment_name = 'my_attachment'
|
||||
@obj.create_attachment(:file => @file, :name => @attachment_name)
|
||||
@obj.save.should be_true
|
||||
end
|
||||
|
||||
it 'should return an empty Hash when document does not have any attachment' do
|
||||
new_obj = Basic.new
|
||||
new_obj.save.should be_true
|
||||
new_obj.attachments.should == {}
|
||||
end
|
||||
|
||||
it 'should return a Hash with all attachments' do
|
||||
@file.rewind
|
||||
@obj.attachments.should == { @attachment_name =>{ "data" => "PCFET0NUWVBFIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPHRpdGxlPlRlc3Q8L3RpdGxlPgogIDwvaGVhZD4KICA8Ym9keT4KICAgIDxwPgogICAgICBUZXN0CiAgICA8L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==", "content_type" => "text/html"}}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,14 +21,14 @@ begin
|
|||
class ExtendedChild < ExtendedParent
|
||||
end
|
||||
|
||||
describe "Using chained inheritance without CouchRest::ExtendedDocument" do
|
||||
describe "Using chained inheritance without CouchRest::Model::Base" do
|
||||
it "should preserve inheritable attributes" do
|
||||
PlainParent.foo.should == :bar
|
||||
PlainChild.foo.should == :bar
|
||||
end
|
||||
end
|
||||
|
||||
describe "Using chained inheritance with CouchRest::ExtendedDocument" do
|
||||
describe "Using chained inheritance with CouchRest::Model::Base" do
|
||||
it "should preserve inheritable attributes" do
|
||||
ExtendedParent.foo.should == :bar
|
||||
ExtendedChild.foo.should == :bar
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
require 'rubygems'
|
||||
require 'couchrest'
|
||||
|
||||
# set the source db and map view
|
||||
source = CouchRest.new("http://127.0.0.1:5984").database('source-db')
|
||||
source_view = 'mydesign/view-map'
|
||||
|
||||
# set the target db
|
||||
target = CouchRest.new("http://127.0.0.1:5984").database('target-db')
|
||||
|
||||
|
||||
pager = CouchRest::Pager.new(source)
|
||||
|
||||
# pager will yield once per uniq key in the source view
|
||||
|
||||
pager.key_reduce(source_view, 10000) do |key, values|
|
||||
# create a doc from the key and the values
|
||||
example_doc = {
|
||||
:key => key,
|
||||
:values => values.uniq
|
||||
}
|
||||
|
||||
target.save(example_doc)
|
||||
|
||||
# keep us up to date with progress
|
||||
puts k if (rand > 0.9)
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
require 'rubygems'
|
||||
require 'couchrest'
|
||||
|
||||
# subset.rb replicates a percentage of a database to a fresh database.
|
||||
# use it to create a smaller dataset on which to prototype views.
|
||||
|
||||
# specify the source database
|
||||
source = CouchRest.new("http://127.0.0.1:5984").database('source-db')
|
||||
|
||||
# specify the target database
|
||||
target = CouchRest.new("http://127.0.0.1:5984").database('target-db')
|
||||
|
||||
# pager efficiently yields all view rows
|
||||
pager = CouchRest::Pager.new(source)
|
||||
|
||||
pager.all_docs(1000) do |rows|
|
||||
docs = rows.collect do |r|
|
||||
# the percentage of docs to clone
|
||||
next if rand > 0.1
|
||||
doc = source.get(r['id'])
|
||||
doc.delete('_rev')
|
||||
doc
|
||||
end.compact
|
||||
puts docs.length
|
||||
next if docs.empty?
|
||||
|
||||
puts docs.first['_id']
|
||||
target.bulk_save(docs)
|
||||
end
|
||||
|
Loading…
Reference in a new issue