diff --git a/lib/couchrest/core/model.rb b/lib/couchrest/core/model.rb
index c0867d5..1a2b08f 100644
--- a/lib/couchrest/core/model.rb
+++ b/lib/couchrest/core/model.rb
@@ -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 use_database
- attr_accessor :default_database
- attr_accessor :template
+ # this is the CouchRest::Database that model classes will use unless
+ # they override it with use_database
+ 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 updated_at and created_at 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
diff --git a/spec/couchrest/core/model_spec.rb b/spec/couchrest/core/model_spec.rb
index 9781357..b79703b 100644
--- a/spec/couchrest/core/model_spec.rb
+++ b/spec/couchrest/core/model_spec.rb
@@ -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