<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -35,11 +35,17 @@ JAVASCRIPT
     end
     
     # Dispatches to any named view.
+    # (using the database where this design doc was saved)
     def view view_name, query={}, &amp;block
+      view_on database, view_name, query, &amp;block
+    end
+
+    # Dispatches to any named view in a specific database
+    def view_on db, view_name, query={}, &amp;block
       view_name = view_name.to_s
       view_slug = &quot;#{name}/#{view_name}&quot;
       defaults = (self['views'][view_name] &amp;&amp; self['views'][view_name][&quot;couchrest-defaults&quot;]) || {}
-      fetch_view(view_slug, defaults.merge(query), &amp;block)
+      db.view(view_slug, defaults.merge(query), &amp;block)
     end
 
     def name
@@ -64,22 +70,6 @@ JAVASCRIPT
         (self['views'][view][&quot;couchrest-defaults&quot;]||{})
     end
 
-    # def fetch_view_with_docs name, opts, raw=false, &amp;block
-    #   if raw
-    #     fetch_view name, opts, &amp;block
-    #   else
-    #     begin
-    #       view = fetch_view name, opts.merge({:include_docs =&gt; true}), &amp;block
-    #       view['rows'].collect{|r|new(r['doc'])} if view['rows']
-    #     rescue
-    #       # fallback for old versions of couchdb that don't 
-    #       # have include_docs support
-    #       view = fetch_view name, opts, &amp;block
-    #       view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
-    #     end
-    #   end
-    # end
-
     def fetch_view view_name, opts, &amp;block
       database.view(view_name, opts, &amp;block)
     end</diff>
      <filename>lib/couchrest/core/design.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,7 +19,7 @@ module CouchRest
         def design_doc_slug
           return design_doc_slug_cache if (design_doc_slug_cache &amp;&amp; design_doc_fresh)
           funcs = []
-          design_doc ||= Design.new(default_design_doc)
+          self.design_doc ||= Design.new(default_design_doc)
           design_doc['views'].each do |name, view|
             funcs &lt;&lt; &quot;#{name}/#{view['map']}#{view['reduce']}&quot;
           end
@@ -43,21 +43,42 @@ module CouchRest
         end
 
         def refresh_design_doc
-          did = design_doc_id
-          saved = database.get(did) rescue nil
+          design_doc['_id'] = design_doc_id
+          design_doc.delete('_rev')
+          #design_doc.database = nil
+          self.design_doc_fresh = true
+        end
+
+        # Save the design doc onto the default database, and update the
+        # design_doc attribute
+        def save_design_doc
+          refresh_design_doc unless design_doc_fresh
+          self.design_doc = update_design_doc(design_doc)
+        end
+
+        # Save the design doc onto a target database in a thread-safe way,
+        # not modifying the model's design_doc
+        def save_design_doc_on(db)
+          update_design_doc(Design.new(design_doc), db)
+        end
+
+        private
+
+        # Writes out a design_doc to a given database, returning the
+        # updated design doc
+        def update_design_doc(design_doc, db = database)
+          saved = db.get(design_doc['_id']) rescue nil
           if saved
             design_doc['views'].each do |name, view|
               saved['views'][name] = view
             end
-            database.save_doc(saved)
-            self.design_doc = saved
+            db.save_doc(saved)
+            saved
           else
-            design_doc['_id'] = did
-            design_doc.delete('_rev')
-            design_doc.database = database
+            design_doc.database = db
             design_doc.save
+            design_doc
           end
-          self.design_doc_fresh = true
         end
         
       end # module ClassMethods</diff>
      <filename>lib/couchrest/mixins/design_doc.rb</filename>
    </modified>
    <modified>
      <diff>@@ -36,8 +36,8 @@ module CouchRest
         end
         
         # Load a document from the database by id
-        def get(id)
-          doc = database.get id
+        def get(id, db = database)
+          doc = db.get id
           new(doc)
         end
         </diff>
      <filename>lib/couchrest/mixins/document_queries.rb</filename>
    </modified>
    <modified>
      <diff>@@ -54,6 +54,10 @@ module CouchRest
         # themselves. By default &lt;tt&gt;Post.by_date&lt;/tt&gt; will return the
         # documents included in the generated view.
         #  
+        # Calling with :database =&gt; [instance of CouchRest::Database] will
+        # send the query to a specific database, otherwise it will go to
+        # the model's default database (use_database)
+        #  
         # CouchRest::Database#view options can be applied at view definition
         # time as defaults, and they will be curried and used at view query
         # time. Or they can be overridden at query time.
@@ -67,7 +71,7 @@ module CouchRest
         # that model won't be available until generation is complete. This can
         # take some time with large databases. Strategies are in the works.
         #  
-        # To understand the capabilities of this view system more compeletly,
+        # To understand the capabilities of this view system more completely,
         # it is recommended that you read the RSpec file at
         # &lt;tt&gt;spec/core/model_spec.rb&lt;/tt&gt;.
 
@@ -97,12 +101,13 @@ module CouchRest
             refresh_design_doc
           end
           query[:raw] = true if query[:reduce]        
+          db = query.delete(:database) || database
           raw = query.delete(:raw)
-          fetch_view_with_docs(name, query, raw, &amp;block)
+          fetch_view_with_docs(db, name, query, raw, &amp;block)
         end
 
-        def all_design_doc_versions
-          database.documents :startkey =&gt; &quot;_design/#{self.to_s}-&quot;, 
+        def all_design_doc_versions(db = database)
+          db.documents :startkey =&gt; &quot;_design/#{self.to_s}-&quot;, 
             :endkey =&gt; &quot;_design/#{self.to_s}-\u9999&quot;
         end
 
@@ -111,11 +116,11 @@ module CouchRest
         # and consistently using the latest code, is the way to clear out old design 
         # docs. Running it to early could mean that live code has to regenerate
         # potentially large indexes.
-        def cleanup_design_docs!
-          ddocs = all_design_doc_versions
+        def cleanup_design_docs!(db = database)
+          ddocs = all_design_doc_versions(db)
           ddocs[&quot;rows&quot;].each do |row|
             if (row['id'] != design_doc_id)
-              database.delete_doc({
+              db.delete_doc({
                 &quot;_id&quot; =&gt; row['id'],
                 &quot;_rev&quot; =&gt; row['value']['rev']
               })
@@ -125,30 +130,31 @@ module CouchRest
 
         private
 
-        def fetch_view_with_docs(name, opts, raw=false, &amp;block)
+        def fetch_view_with_docs(db, name, opts, raw=false, &amp;block)
           if raw || (opts.has_key?(:include_docs) &amp;&amp; opts[:include_docs] == false)
-            fetch_view(name, opts, &amp;block)
+            fetch_view(db, name, opts, &amp;block)
           else
             begin
-              view = fetch_view name, opts.merge({:include_docs =&gt; true}), &amp;block
+              view = fetch_view db, name, opts.merge({:include_docs =&gt; true}), &amp;block
               view['rows'].collect{|r|new(r['doc'])} if view['rows']
             rescue
               # fallback for old versions of couchdb that don't 
               # have include_docs support
-              view = fetch_view(name, opts, &amp;block)
+              view = fetch_view(db, name, opts, &amp;block)
               view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
             end
           end
         end
 
-        def fetch_view view_name, opts, &amp;block
+        def fetch_view(db, view_name, opts, &amp;block)
+          raise &quot;A view needs a database to operate on (specify :database option, or use_database in the #{self.class} class)&quot; unless db
           retryable = true
           begin
-            design_doc.view(view_name, opts, &amp;block)
-            # the design doc could have been deleted by a rogue process
+            design_doc.view_on(db, view_name, opts, &amp;block)
+            # the design doc may not have been saved yet on this database
           rescue RestClient::ResourceNotFound =&gt; e
             if retryable
-              refresh_design_doc
+              save_design_doc_on(db)
               retryable = false
               retry
             else</diff>
      <filename>lib/couchrest/mixins/views.rb</filename>
    </modified>
    <modified>
      <diff>@@ -77,10 +77,10 @@ module CouchRest
     end
     
     # Temp solution to make the view_by methods available
-    def self.method_missing(m, *args)
+    def self.method_missing(m, *args, &amp;block)
       if has_view?(m)
         query = args.shift || {}
-        view(m, query, *args)
+        view(m, query, *args, &amp;block)
       else
         super
       end</diff>
      <filename>lib/couchrest/more/extended_document.rb</filename>
    </modified>
    <modified>
      <diff>@@ -57,6 +57,13 @@ describe CouchRest::Design do
       res = @des.view :by_name
       res[&quot;rows&quot;][0][&quot;key&quot;].should == &quot;x&quot;
     end
+    it &quot;should be queryable on specified database&quot; do
+      @des.name = &quot;mydesign&quot;
+      @des.save
+      @des.database = nil
+      res = @des.view_on @db, :by_name
+      res[&quot;rows&quot;][0][&quot;key&quot;].should == &quot;x&quot;
+    end
   end
   
   describe &quot;from a saved document&quot; do</diff>
      <filename>spec/couchrest/core/design_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,13 @@ require File.join(FIXTURE_PATH, 'more', 'article')
 require File.join(FIXTURE_PATH, 'more', 'course')
 
 describe &quot;ExtendedDocument views&quot; do
+
+  class Unattached &lt; CouchRest::ExtendedDocument
+    # Note: no use_database here
+    property :title
+    property :questions
+    view_by :title
+  end
   
   describe &quot;a model with simple views and a default param&quot; do
     before(:all) do
@@ -75,12 +82,18 @@ describe &quot;ExtendedDocument views&quot; do
     end
     it &quot;should yield&quot; do
       courses = []
-      rs = Course.by_title # remove me
       Course.view(:by_title) do |course|
         courses &lt;&lt; course
       end
       courses[0][&quot;doc&quot;][&quot;title&quot;].should =='aaa'
     end
+    it &quot;should yield with by_key method&quot; do
+      courses = []
+      Course.by_title do |course|
+        courses &lt;&lt; course
+      end
+      courses[0][&quot;doc&quot;][&quot;title&quot;].should =='aaa'
+    end
   end
 
 
@@ -103,6 +116,76 @@ describe &quot;ExtendedDocument views&quot; do
     end
   end
 
+  describe &quot;a model class not tied to a database&quot; do
+    before(:all) do
+      reset_test_db!
+      @db = TEST_SERVER.default_database
+      %w{aaa bbb ddd eee}.each do |title|
+        u = Unattached.new(:title =&gt; title)
+        u.database = @db
+        u.save
+        @first_id ||= u.id
+      end
+    end
+    it &quot;should barf on all if no database given&quot; do
+      lambda{Unattached.all}.should raise_error
+    end
+    it &quot;should query all&quot; do
+      rs = Unattached.all :database=&gt;@db
+      rs.length.should == 4
+    end
+    it &quot;should barf on query if no database given&quot; do
+      lambda{Unattached.view :by_title}.should raise_error
+    end
+    it &quot;should make the design doc upon first query&quot; do
+      Unattached.by_title :database=&gt;@db
+      doc = Unattached.design_doc
+      doc['views']['all']['map'].should include('Unattached')
+    end
+    it &quot;should merge query params&quot; do
+      rs = Unattached.by_title :database=&gt;@db, :startkey=&gt;&quot;bbb&quot;, :endkey=&gt;&quot;eee&quot;
+      rs.length.should == 3
+    end
+    it &quot;should query via view&quot; do
+      view = Unattached.view :by_title, :database=&gt;@db
+      designed = Unattached.by_title :database=&gt;@db
+      view.should == designed
+    end
+    it &quot;should yield&quot; do
+      things = []
+      Unattached.view(:by_title, :database=&gt;@db) do |thing|
+        things &lt;&lt; thing
+      end
+      things[0][&quot;doc&quot;][&quot;title&quot;].should =='aaa'
+    end
+    it &quot;should barf on get if no database given&quot; do
+      lambda{Unattached.get(&quot;aaa&quot;)}.should raise_error
+    end
+    it &quot;should get from specific database&quot; do
+      u = Unattached.get(@first_id, @db)
+      u.title.should == &quot;aaa&quot;
+    end
+    it &quot;should barf on first if no database given&quot; do
+      lambda{Unattached.first}.should raise_error
+    end
+    it &quot;should get first&quot; do
+      u = Unattached.first :database=&gt;@db
+      u.title.should =~ /\A...\z/
+    end
+    it &quot;should barf on all_design_doc_versions if no database given&quot; do
+      lambda{Unattached.all_design_doc_versions}.should raise_error
+    end
+    it &quot;should clean up design docs left around on specific database&quot; do
+      Unattached.by_title :database=&gt;@db
+      Unattached.all_design_doc_versions(@db)[&quot;rows&quot;].length.should == 1
+      Unattached.view_by :questions
+      Unattached.by_questions :database=&gt;@db
+      Unattached.all_design_doc_versions(@db)[&quot;rows&quot;].length.should == 2
+      Unattached.cleanup_design_docs!(@db)
+      Unattached.all_design_doc_versions(@db)[&quot;rows&quot;].length.should == 1
+    end
+  end
+
   describe &quot;a model with a compound key view&quot; do
     before(:all) do
       Article.design_doc_fresh = false
@@ -177,14 +260,11 @@ describe &quot;ExtendedDocument views&quot; do
       newdocs[&quot;rows&quot;].length.should == @design_docs[&quot;rows&quot;].length
     end
     it &quot;should create a new version of the design document on view access&quot; do
-      old_design_doc = Article.database.documents(:key =&gt; @design_docs[&quot;rows&quot;].first[&quot;key&quot;], :include_docs =&gt; true)[&quot;rows&quot;][0][&quot;doc&quot;]
+      ddocs = Article.all_design_doc_versions[&quot;rows&quot;].length
       Article.view_by :updated_at
       Article.by_updated_at
-      newdocs = Article.database.documents({:startkey =&gt; &quot;_design/&quot;, :endkey =&gt; &quot;_design/\u9999&quot;})
-
-      doc = Article.database.documents(:key =&gt; @design_docs[&quot;rows&quot;].first[&quot;key&quot;], :include_docs =&gt; true)[&quot;rows&quot;][0][&quot;doc&quot;]
-      doc[&quot;_rev&quot;].should_not        == old_design_doc[&quot;_rev&quot;]
-      doc[&quot;views&quot;].keys.should include(&quot;by_updated_at&quot;)
+      Article.all_design_doc_versions[&quot;rows&quot;].length.should == ddocs + 1
+      Article.design_doc[&quot;views&quot;].keys.should include(&quot;by_updated_at&quot;)
     end
   end
 
@@ -196,12 +276,11 @@ describe &quot;ExtendedDocument views&quot; do
       Article.by_field
     end
     it &quot;should clean them up&quot; do
-      ddocs = Article.all_design_doc_versions
       Article.view_by :stream
       Article.by_stream
+      Article.all_design_doc_versions[&quot;rows&quot;].length.should &gt; 1
       Article.cleanup_design_docs!
-      ddocs = Article.all_design_doc_versions
-      ddocs[&quot;rows&quot;].length.should == 1
+      Article.all_design_doc_versions[&quot;rows&quot;].length.should == 1
     end
   end
 end</diff>
      <filename>spec/couchrest/more/extended_doc_view_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f9278a4ca6d4ea5e29de850f05cca041f0a72979</id>
    </parent>
  </parents>
  <author>
    <name>Brian Candler</name>
    <email>b.candler@pobox.com</email>
  </author>
  <url>http://github.com/jchris/couchrest/commit/ec7848b783637cb44bb4b684e8e104f64bb1d55b</url>
  <id>ec7848b783637cb44bb4b684e8e104f64bb1d55b</id>
  <committed-date>2009-03-27T04:27:37-07:00</committed-date>
  <authored-date>2009-03-27T04:27:37-07:00</authored-date>
  <message>Multiple database support for ExtendedDocument.

New optional parameters are available to select the database:

Mixins::DocumentQueries
  * get &lt;id&gt;, &lt;db&gt;
  * all :database =&gt; &lt;db&gt;
  * first :database =&gt; &lt;db&gt;

Mixins::Views
  * view &lt;name&gt;, :database =&gt; &lt;db&gt;
  * all_design_doc_versions &lt;db&gt;
  * cleanup_design_docs! &lt;db&gt;

Mixins::DesignDoc
  * refresh_design_doc now only updates the design_doc _id and removes _rev
  * call save_design_doc to save and update the design_doc
  * call save_design_doc_on &lt;db&gt; to save the design doc on a given
    database without modifying the model's design_doc object

Design (core/design.rb)
  * new method view_on &lt;db&gt;, ...

Bug fixes:
  * design_doc_slug in mixins/design_doc.rb was using an empty document
    to calculate the slug each time
  * method_missing in core/extended_document.rb now passes a block through</message>
  <tree>9c422c2745553370ffed75ad978cb425f15d4b98</tree>
  <committer>
    <name>Brian Candler</name>
    <email>b.candler@pobox.com</email>
  </committer>
</commit>
