couchrest_model/lib/couchrest/model/design_doc.rb

129 lines
4.0 KiB
Ruby

# encoding: utf-8
module CouchRest
module Model
module DesignDoc
extend ActiveSupport::Concern
module ClassMethods
def design_doc
@design_doc ||= if auto_update_design_doc
::CouchRest::Design.new(default_design_doc)
else
stored_design_doc || ::CouchRest::Design.new(default_design_doc)
end
end
def design_doc_id
"_design/#{design_doc_slug}"
end
def design_doc_slug
self.to_s
end
def design_doc_uri(db = database)
"#{db.root}/#{design_doc_id}"
end
# Retreive the latest version of the design document directly
# from the database. This is never cached and will return nil if
# the design is not present.
#
# Use this method if you'd like to compare revisions [_rev] which
# is not stored in the normal design doc.
def stored_design_doc(db = database)
db.get(design_doc_id)
rescue RestClient::ResourceNotFound
nil
end
# Save the design doc onto a target database in a thread-safe way,
# not modifying the model's design_doc
#
# See also save_design_doc! to always save the design doc even if there
# are no changes.
def save_design_doc(db = database, force = false)
update_design_doc(db, force)
end
# Force the update of the model's design_doc even if it hasn't changed.
def save_design_doc!(db = database)
save_design_doc(db, true)
end
private
def design_doc_cache
Thread.current[:couchrest_design_cache] ||= {}
end
def design_doc_cache_checksum(db)
design_doc_cache[design_doc_uri(db)]
end
def set_design_doc_cache_checksum(db, checksum)
design_doc_cache[design_doc_uri(db)] = checksum
end
# Writes out a design_doc to a given database if forced
# or the stored checksum is not the same as the current
# generated checksum.
#
# Returns the original design_doc provided, but does
# not update it with the revision.
def update_design_doc(db, force = false)
return design_doc unless force || auto_update_design_doc
# Grab the design doc's checksum
checksum = design_doc.checksum!
# If auto updates enabled, check checksum cache
return design_doc if auto_update_design_doc && design_doc_cache_checksum(db) == checksum
retries = 1
begin
# Load up the stored doc (if present), update, and save
saved = stored_design_doc(db)
if saved
if force || saved['couchrest-hash'] != checksum
saved.merge!(design_doc)
db.save_doc(saved)
@design_doc = saved # update memo to point to the document we actually saved
end
else
design_doc.delete('_rev') # This is a new document and so doesn't have a revision yet
db.save_doc(design_doc)
end
rescue RestClient::Conflict
# if we get a conflict retry the operation...
raise if retries < 1
retries -= 1
retry
end
# Ensure checksum cached for next attempt if using auto updates
set_design_doc_cache_checksum(db, checksum) if auto_update_design_doc
design_doc
end
def default_design_doc
{
"_id" => design_doc_id,
"language" => "javascript",
"views" => {
'all' => {
'map' => "function(doc) {
if (doc['#{self.model_type_key}'] == '#{self.to_s}') {
emit(doc['_id'],1);
}
}"
}
}
}
end
end # module ClassMethods
end
end
end