move to class_inheritable_accessor
This commit is contained in:
parent
c170008deb
commit
355d408730
2 changed files with 96 additions and 39 deletions
|
@ -1,3 +1,5 @@
|
|||
require 'rubygems'
|
||||
require 'extlib'
|
||||
require 'digest/md5'
|
||||
|
||||
# = CouchRest::Model - ORM, the CouchDB way
|
||||
|
@ -62,20 +64,26 @@ module CouchRest
|
|||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
# this is the CouchRest::Database that model classes will use unless
|
||||
# they override it with <tt>use_database</tt>
|
||||
attr_accessor :default_database
|
||||
attr_accessor :template
|
||||
# this is the CouchRest::Database that model classes will use unless
|
||||
# they override it with <tt>use_database</tt>
|
||||
cattr_accessor :default_database
|
||||
|
||||
class_inheritable_accessor :casts
|
||||
class_inheritable_accessor :default_obj
|
||||
class_inheritable_accessor :class_database
|
||||
class_inheritable_accessor :generated_design_doc
|
||||
class_inheritable_accessor :design_doc_slug_cache
|
||||
class_inheritable_accessor :design_doc_fresh
|
||||
|
||||
class << self
|
||||
# override the CouchRest::Model-wide default_database
|
||||
def use_database db
|
||||
@database = db
|
||||
self.class_database = db
|
||||
end
|
||||
|
||||
# returns the CouchRest::Database instance that this class uses
|
||||
def database
|
||||
@database || CouchRest::Model.default_database
|
||||
self.class_database || CouchRest::Model.default_database
|
||||
end
|
||||
|
||||
# load a document from the database
|
||||
|
@ -84,18 +92,21 @@ module CouchRest
|
|||
new(doc)
|
||||
end
|
||||
|
||||
def all opts = {}
|
||||
view_name = "#{design_doc_slug}/all"
|
||||
raw = opts.delete(:raw)
|
||||
view = fetch_view(view_name, opts)
|
||||
process_view_results view, raw
|
||||
end
|
||||
|
||||
# Cast a field as another class. The class must be happy to have the
|
||||
# field's primitive type as the argument to it's constucture. Classes
|
||||
# which inherit from CouchRest::Model are happy to act as sub-objects
|
||||
# for any fields that are stored in JSON as object (and therefore are
|
||||
# parsed from the JSON as Ruby Hashes).
|
||||
def cast field, opts = {}
|
||||
@casts ||= {}
|
||||
@casts[field.to_s] = opts
|
||||
end
|
||||
|
||||
def casts
|
||||
@casts
|
||||
self.casts ||= {}
|
||||
self.casts[field.to_s] = opts
|
||||
end
|
||||
|
||||
# Defines methods for reading and writing from fields in the document.
|
||||
|
@ -128,11 +139,11 @@ module CouchRest
|
|||
end
|
||||
|
||||
def default
|
||||
@default
|
||||
self.default_obj
|
||||
end
|
||||
|
||||
|
||||
def set_default hash
|
||||
@default = hash
|
||||
self.default_obj = hash
|
||||
end
|
||||
|
||||
# Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
|
||||
|
@ -236,7 +247,7 @@ module CouchRest
|
|||
type = self.to_s
|
||||
|
||||
method_name = "by_#{keys.join('_and_')}"
|
||||
@@design_doc ||= default_design_doc
|
||||
self.generated_design_doc ||= default_design_doc
|
||||
|
||||
if opts[:map]
|
||||
view = {}
|
||||
|
@ -245,7 +256,7 @@ module CouchRest
|
|||
view['reduce'] = opts.delete(:reduce)
|
||||
opts[:reduce] = false
|
||||
end
|
||||
@@design_doc['views'][method_name] = view
|
||||
generated_design_doc['views'][method_name] = view
|
||||
else
|
||||
doc_keys = keys.collect{|k|"doc['#{k}']"}
|
||||
key_protection = doc_keys.join(' && ')
|
||||
|
@ -257,30 +268,24 @@ module CouchRest
|
|||
}
|
||||
}
|
||||
JAVASCRIPT
|
||||
@@design_doc['views'][method_name] = {
|
||||
generated_design_doc['views'][method_name] = {
|
||||
'map' => map_function
|
||||
}
|
||||
end
|
||||
|
||||
@@design_doc_fresh = false
|
||||
self.design_doc_fresh = false
|
||||
|
||||
self.meta_class.instance_eval do
|
||||
define_method method_name do |*args|
|
||||
query = opts.merge(args[0] || {})
|
||||
query[:raw] = true if query[:reduce]
|
||||
unless @@design_doc_fresh
|
||||
unless design_doc_fresh
|
||||
refresh_design_doc
|
||||
end
|
||||
raw = query.delete(:raw)
|
||||
view_name = "#{design_doc_slug}/#{method_name}"
|
||||
|
||||
view = fetch_view(view_name, query)
|
||||
if raw
|
||||
view
|
||||
else
|
||||
# TODO this can be optimized once the include-docs patch is applied
|
||||
view['rows'].collect{|r|new(database.get(r['id']))}
|
||||
end
|
||||
process_view_results view, raw
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -292,6 +297,15 @@ module CouchRest
|
|||
|
||||
private
|
||||
|
||||
def process_view_results view, raw=false
|
||||
if raw
|
||||
view
|
||||
else
|
||||
# TODO this can be optimized once the include-docs patch is applied
|
||||
view['rows'].collect{|r|new(database.get(r['id']))}
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_view view_name, opts
|
||||
retryable = true
|
||||
begin
|
||||
|
@ -309,19 +323,27 @@ module CouchRest
|
|||
end
|
||||
|
||||
def design_doc_slug
|
||||
return @design_doc_slug if @design_doc_slug && @@design_doc_fresh
|
||||
return design_doc_slug_cache if design_doc_slug_cache && design_doc_fresh
|
||||
funcs = []
|
||||
@@design_doc['views'].each do |name, view|
|
||||
funcs << "#{name}/#{view}"
|
||||
generated_design_doc['views'].each do |name, view|
|
||||
funcs << "#{name}/#{view['map']}#{view['reduce']}"
|
||||
end
|
||||
md5 = Digest::MD5.hexdigest(funcs.sort.join(''))
|
||||
@design_doc_slug = "#{self.to_s}-#{md5}"
|
||||
self.design_doc_slug_cache = "#{self.to_s}-#{md5}"
|
||||
end
|
||||
|
||||
def default_design_doc
|
||||
{
|
||||
"language" => "javascript",
|
||||
"views" => {}
|
||||
"views" => {
|
||||
'all' => {
|
||||
'map' => "function(doc) {
|
||||
if (doc['couchrest-type'] == '#{self.to_s}') {
|
||||
emit(null,null);
|
||||
}
|
||||
}"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -329,21 +351,19 @@ module CouchRest
|
|||
did = "_design/#{design_doc_slug}"
|
||||
saved = database.get(did) rescue nil
|
||||
if saved
|
||||
@@design_doc['views'].each do |name, view|
|
||||
generated_design_doc['views'].each do |name, view|
|
||||
saved['views'][name] = view
|
||||
end
|
||||
database.save(saved)
|
||||
else
|
||||
@@design_doc['_id'] = did
|
||||
database.save(@@design_doc)
|
||||
generated_design_doc['_id'] = did
|
||||
database.save(generated_design_doc)
|
||||
end
|
||||
@@design_doc_fresh = true
|
||||
self.design_doc_fresh = true
|
||||
end
|
||||
|
||||
end # class << self
|
||||
|
||||
|
||||
|
||||
# returns the database used by this model's class
|
||||
def database
|
||||
self.class.database
|
||||
|
|
|
@ -29,6 +29,7 @@ class Course < CouchRest::Model
|
|||
key_accessor :title
|
||||
cast :questions, :as => [Question]
|
||||
cast :professor, :as => Person
|
||||
view_by :title
|
||||
end
|
||||
|
||||
class Article < CouchRest::Model
|
||||
|
@ -177,6 +178,25 @@ describe CouchRest::Model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "finding all instances of a model" do
|
||||
before(:all) do
|
||||
WithTemplate.new('important-field' => '1').save
|
||||
WithTemplate.new('important-field' => '2').save
|
||||
WithTemplate.new('important-field' => '3').save
|
||||
WithTemplate.new('important-field' => '4').save
|
||||
end
|
||||
it "should make the design doc" do
|
||||
WithTemplate.all
|
||||
puts d = WithTemplate.design_doc.to_json
|
||||
d.should == 'xs'
|
||||
end
|
||||
it "should find all" do
|
||||
rs = WithTemplate.all :raw => true
|
||||
rs.should == 'x'
|
||||
rs.length.should == 4
|
||||
end
|
||||
end
|
||||
|
||||
describe "getting a model with a subobject field" do
|
||||
before(:all) do
|
||||
course_doc = {
|
||||
|
@ -356,6 +376,23 @@ describe CouchRest::Model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "another model with a simple view" do
|
||||
before(:all) do
|
||||
Course.database.delete! rescue nil
|
||||
@db = @cr.create_db(TESTDB) rescue nil
|
||||
Course.new(:title => 'aaa').save
|
||||
Course.new(:title => 'bbb').save
|
||||
end
|
||||
it "should make the design doc" do
|
||||
doc = Course.design_doc
|
||||
doc['views']['all']['map'].should include('Course')
|
||||
end
|
||||
it "should get them" do
|
||||
rs = Course.by_title
|
||||
rs.length.should == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "a model with a compound key view" do
|
||||
before(:all) do
|
||||
written_at = Time.now - 24 * 3600 * 7
|
||||
|
|
Loading…
Reference in a new issue