Working on adding checksum support to design documents to handle updates

master
Sam Lown 2011-04-13 15:42:28 +02:00
parent a6becd7305
commit 221e5a5470
8 changed files with 89 additions and 41 deletions

View File

@ -12,21 +12,22 @@ GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionpack (3.0.5)
activemodel (= 3.0.5)
activesupport (= 3.0.5)
actionpack (3.0.6)
activemodel (= 3.0.6)
activesupport (= 3.0.6)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.4)
i18n (~> 0.5.0)
rack (~> 1.2.1)
rack-mount (~> 0.6.13)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.5)
activesupport (= 3.0.5)
activemodel (3.0.6)
activesupport (= 3.0.6)
builder (~> 2.1.2)
i18n (~> 0.4)
activesupport (3.0.5)
i18n (~> 0.5.0)
activesupport (3.0.6)
bouncy-castle-java (1.5.0145.2)
builder (2.1.2)
couchrest (1.1.0.pre2)
json (~> 1.5.1)
@ -36,16 +37,19 @@ GEM
erubis (2.6.6)
abstract (>= 1.0.0)
i18n (0.5.0)
jruby-openssl (0.7.3)
bouncy-castle-java
json (1.5.1)
json (1.5.1-java)
mime-types (1.16)
rack (1.2.1)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
railties (3.0.5)
actionpack (= 3.0.5)
activesupport (= 3.0.5)
railties (3.0.6)
actionpack (= 3.0.6)
activesupport (= 3.0.6)
rake (>= 0.8.7)
thor (~> 0.14.4)
rake (0.8.7)
@ -63,12 +67,14 @@ GEM
tzinfo (0.3.26)
PLATFORMS
java
ruby
DEPENDENCIES
activemodel (~> 3.0.0)
couchrest (= 1.1.0.pre2)
couchrest_model!
jruby-openssl (>= 0.7.3)
mime-types (~> 1.15)
rack-test (>= 0.5.7)
railties (~> 3.0.0)

View File

@ -30,5 +30,6 @@ Gem::Specification.new do |s|
s.add_dependency(%q<railties>, "~> 3.0.0")
s.add_development_dependency(%q<rspec>, ">= 2.0.0")
s.add_development_dependency(%q<rack-test>, ">= 0.5.7")
s.add_development_dependency("jruby-openssl", ">= 0.7.3")
end

View File

@ -7,7 +7,7 @@ module CouchRest
module ClassMethods
def design_doc
@design_doc ||= Design.new(default_design_doc)
@design_doc ||= ::CouchRest::Design.new(default_design_doc)
end
# Use when something has been changed, like a view, so that on the next request
@ -90,24 +90,17 @@ module CouchRest
# Writes out a design_doc to a given database, returning the
# updated design doc
def update_design_doc(design_doc, db, force = false)
design_doc['couchrest-hash'] = design_doc.checksum
saved = stored_design_doc(db)
if saved
changes = force
design_doc['views'].each do |name, view|
if !compare_views(saved['views'][name], view)
changes = true
saved['views'][name] = view
end
end
if changes
if force || saved['couchrest-hash'] != design_doc['couchrest-hash']
saved.merge!(design_doc)
db.save_doc(saved)
end
design_doc
else
design_doc.database = db
design_doc.save
design_doc
db.save_doc(design_doc)
end
design_doc
end
# Return true if the two views match
@ -117,7 +110,7 @@ module CouchRest
end
end # module ClassMethods
end
end
end

View File

@ -90,6 +90,13 @@ module CouchRest
result ? all.last : limit(1).descending.all.last
end
# Return the number of documents in the currently defined result set.
# Use <tt>#count</tt> for the total number of documents regardless
# of the current limit defined.
def length
docs.length
end
# Perform a count operation based on the current view. If the view
# can be reduced, the reduce will be performed and return the first
# value. This is okay for most simple queries, but may provide
@ -383,30 +390,24 @@ module CouchRest
def execute
return self.result if result
raise "Database must be defined in model or view!" if use_database.nil?
retryable = true
# Remove the reduce value if its not needed
query.delete(:reduce) unless can_reduce?
begin
self.result = model.design_doc.view_on(use_database, name, query.reject{|k,v| v.nil?})
rescue RestClient::ResourceNotFound => e
if retryable
model.save_design_doc(use_database)
retryable = false
retry
else
raise e
end
end
# Save the design doc for the current database. This should be efficient
# and check for changes
model.save_design_doc(use_database)
self.result = model.design_doc.view_on(use_database, name, query.reject{|k,v| v.nil?})
end
# Class Methods
class << self
# Simplified view creation. A new view will be added to the
# provided model's design document using the name and options.
#
# If the view name starts with "by_" and +:by+ is not provided in
# the options, the new view's map method will be interpretted and
# the options, the new view's map method will be interpreted and
# generated automatically. For example:
#
# View.create(Meeting, "by_date_and_name")

View File

@ -0,0 +1,15 @@
CouchRest::Database.class_eval do
alias :delete_orig! :delete!
def delete!
clear_model_fresh_cache
delete_orig!
end
# If the database is deleted, ensure that the design docs will be refreshed.
def clear_model_fresh_cache
::CouchRest::Model::Base.subclasses.each{|klass| klass.req_design_doc_refresh if klass.respond_to?(:req_design_doc_refresh)}
end
end

View File

@ -0,0 +1,24 @@
CouchRest::Design.class_eval do
# Calculate a checksum of the Design document. Used for ensuring the latest
# version has been sent to the database.
#
# This will generate an flatterned, ordered array of all the elements of the
# design document, convert to string then generate an MD5 Hash. This should
# result in a consisitent Hash accross all platforms.
#
def checksum
# create a copy of basic elements
base = self.dup
base.delete('_id')
base.delete('_rev')
result = nil
flatten =
lambda{|v|
v.is_a?(Hash) ? v.flatten.map{|v| flatten.call(v)}.flatten : v
}
Digest::MD5.hexdigest(flatten.call(base).sort.join(''))
end
end

View File

@ -45,7 +45,8 @@ require "couchrest/model/designs"
require "couchrest/model/designs/view"
# Monkey patches applied to couchrest
require "couchrest/model/support/couchrest"
require "couchrest/model/support/couchrest_database"
require "couchrest/model/support/couchrest_design"
# Core Extensions
require "couchrest/model/core_extensions/hash"
require "couchrest/model/core_extensions/time_parsing"

View File

@ -163,6 +163,13 @@ describe "Design View" do
end
end
describe "#length" do
it "should provide a length from the docs array" do
@obj.should_receive(:docs).and_return([1, 2, 3])
@obj.length.should eql(3)
end
end
describe "#count" do
it "should raise an error if view prepared for group" do
@obj.should_receive(:query).and_return({:group => true})