mongomodel 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +6 -0
- data/bin/console +4 -4
- data/lib/mongomodel.rb +4 -4
- data/lib/mongomodel/concerns/associations/base/definition.rb +4 -0
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +7 -16
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +6 -12
- data/lib/mongomodel/concerns/attributes.rb +1 -7
- data/lib/mongomodel/document.rb +0 -1
- data/lib/mongomodel/document/dynamic_finders.rb +2 -69
- data/lib/mongomodel/document/indexes.rb +0 -6
- data/lib/mongomodel/document/persistence.rb +1 -21
- data/lib/mongomodel/document/scopes.rb +59 -135
- data/lib/mongomodel/document/validations/uniqueness.rb +7 -5
- data/lib/mongomodel/support/dynamic_finder.rb +68 -0
- data/lib/mongomodel/support/mongo_operator.rb +29 -0
- data/lib/mongomodel/support/mongo_options.rb +0 -101
- data/lib/mongomodel/support/mongo_order.rb +78 -0
- data/lib/mongomodel/support/scope.rb +186 -0
- data/lib/mongomodel/support/scope/dynamic_finders.rb +21 -0
- data/lib/mongomodel/support/scope/finder_methods.rb +61 -0
- data/lib/mongomodel/support/scope/query_methods.rb +43 -0
- data/lib/mongomodel/support/scope/spawn_methods.rb +35 -0
- data/lib/mongomodel/version.rb +1 -1
- data/mongomodel.gemspec +20 -3
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +1 -1
- data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +1 -1
- data/spec/mongomodel/document/dynamic_finders_spec.rb +0 -1
- data/spec/mongomodel/document/finders_spec.rb +0 -144
- data/spec/mongomodel/document/indexes_spec.rb +2 -2
- data/spec/mongomodel/document/persistence_spec.rb +1 -15
- data/spec/mongomodel/document/scopes_spec.rb +64 -167
- data/spec/mongomodel/support/mongo_operator_spec.rb +29 -0
- data/spec/mongomodel/support/mongo_options_spec.rb +0 -150
- data/spec/mongomodel/support/mongo_order_spec.rb +127 -0
- data/spec/mongomodel/support/scope_spec.rb +932 -0
- data/spec/support/helpers/document_finder_stubs.rb +40 -0
- data/spec/support/matchers/find_with.rb +36 -0
- metadata +22 -5
- data/lib/mongomodel/document/finders.rb +0 -82
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module MongoModel
         | 
| 4 | 
            +
              describe MongoOrder do
         | 
| 5 | 
            +
                def c(field, order)
         | 
| 6 | 
            +
                  MongoOrder::Clause.new(field, order)
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
                subject { MongoOrder.new(c(:name, :ascending), c(:age, :descending)) }
         | 
| 10 | 
            +
              
         | 
| 11 | 
            +
                it "should convert to string" do
         | 
| 12 | 
            +
                  subject.to_s.should == "name ascending, age descending"
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              
         | 
| 15 | 
            +
                describe "#to_sort" do
         | 
| 16 | 
            +
                  it "should convert to mongo sort array" do
         | 
| 17 | 
            +
                    model = mock('model', :properties => mock('properties', :[] => nil))
         | 
| 18 | 
            +
                    subject.to_sort(model).should == [['name', :ascending], ['age', :descending]]
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              
         | 
| 22 | 
            +
                it "should be reversable" do
         | 
| 23 | 
            +
                  subject.reverse.should == MongoOrder.new(c(:name, :descending), c(:age, :ascending))
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              
         | 
| 26 | 
            +
                it "should equal another order object with identical clauses" do
         | 
| 27 | 
            +
                  subject.should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
                it "should equal another order object with different clauses" do
         | 
| 31 | 
            +
                  subject.should_not == MongoOrder.new(c(:name, :ascending))
         | 
| 32 | 
            +
                  subject.should_not == MongoOrder.new(c(:age, :ascending), c(:name, :ascending))
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              
         | 
| 35 | 
            +
                describe "#parse" do
         | 
| 36 | 
            +
                  it "should not change a MongoOrder" do
         | 
| 37 | 
            +
                    MongoOrder.parse(subject).should == subject
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                  it "should convert individual clause to MongoOrder" do
         | 
| 41 | 
            +
                    MongoOrder.parse(c(:name, :ascending)).should == MongoOrder.new(c(:name, :ascending))
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                
         | 
| 44 | 
            +
                  it "should convert symbol to MongoOrder" do
         | 
| 45 | 
            +
                    MongoOrder.parse(:name).should == MongoOrder.new(c(:name, :ascending))
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                
         | 
| 48 | 
            +
                  it "should convert array of clauses to MongoOrder" do
         | 
| 49 | 
            +
                    MongoOrder.parse([c(:name, :ascending), c(:age, :descending)]).should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                  it "should convert array of symbols to MongoOrder" do
         | 
| 53 | 
            +
                    MongoOrder.parse([:name, :age]).should == MongoOrder.new(c(:name, :ascending), c(:age, :ascending))
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                
         | 
| 56 | 
            +
                  it "should convert array of strings to MongoOrder" do
         | 
| 57 | 
            +
                    MongoOrder.parse(['name ASC', 'age DESC']).should == MongoOrder.new(c(:name, :ascending), c(:age, :descending))
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                
         | 
| 60 | 
            +
                  it "should convert string (no order specified) to MongoOrder" do
         | 
| 61 | 
            +
                    MongoOrder.parse('name').should == MongoOrder.new(c(:name, :ascending))
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                
         | 
| 64 | 
            +
                  it "should convert string (single order) to MongoOrder" do
         | 
| 65 | 
            +
                    MongoOrder.parse('name DESC').should == MongoOrder.new(c(:name, :descending))
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                
         | 
| 68 | 
            +
                  it "should convert string (multiple orders) to MongoOrder" do
         | 
| 69 | 
            +
                    MongoOrder.parse('name DESC, age ASC').should == MongoOrder.new(c(:name, :descending), c(:age, :ascending))
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              describe MongoOrder::Clause do
         | 
| 75 | 
            +
                subject { MongoOrder::Clause.new(:name, :ascending) }
         | 
| 76 | 
            +
              
         | 
| 77 | 
            +
                it "should convert to string" do
         | 
| 78 | 
            +
                  subject.to_s.should == "name ascending"
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              
         | 
| 81 | 
            +
                it "should equal another clause with the same field and order" do
         | 
| 82 | 
            +
                  subject.should == MongoOrder::Clause.new(:name, :ascending)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              
         | 
| 85 | 
            +
                it "should equal another clause with a different field or order" do
         | 
| 86 | 
            +
                  subject.should_not == MongoOrder::Clause.new(:age, :ascending)
         | 
| 87 | 
            +
                  subject.should_not == MongoOrder::Clause.new(:name, :descending)
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              
         | 
| 90 | 
            +
                it "should be reversable" do
         | 
| 91 | 
            +
                  subject.reverse.should == MongoOrder::Clause.new(:name, :descending)
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              
         | 
| 94 | 
            +
                describe "#to_sort" do
         | 
| 95 | 
            +
                  context "given property" do
         | 
| 96 | 
            +
                    it "should use property as value to convert to mongo sort" do
         | 
| 97 | 
            +
                      property = mock('property', :as => '_name')
         | 
| 98 | 
            +
                      subject.to_sort(property).should == ['_name', :ascending]
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                
         | 
| 102 | 
            +
                  context "given nil" do
         | 
| 103 | 
            +
                    it "should convert to mongo sort" do
         | 
| 104 | 
            +
                      subject.to_sort(nil).should == ['name', :ascending]
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
              
         | 
| 109 | 
            +
                describe "#parse" do
         | 
| 110 | 
            +
                  let(:asc) { MongoOrder::Clause.new(:name, :ascending) }
         | 
| 111 | 
            +
                  let(:desc) { MongoOrder::Clause.new(:name, :descending) }
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                  it "should create Clause from string (no order)" do
         | 
| 114 | 
            +
                    MongoOrder::Clause.parse('name').should == asc
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                
         | 
| 117 | 
            +
                  it "should create Clause from string (with order)" do
         | 
| 118 | 
            +
                    MongoOrder::Clause.parse('name ASC').should == asc
         | 
| 119 | 
            +
                    MongoOrder::Clause.parse('name asc').should == asc
         | 
| 120 | 
            +
                    MongoOrder::Clause.parse('name ascending').should == asc
         | 
| 121 | 
            +
                    MongoOrder::Clause.parse('name DESC').should == desc
         | 
| 122 | 
            +
                    MongoOrder::Clause.parse('name desc').should == desc
         | 
| 123 | 
            +
                    MongoOrder::Clause.parse('name descending').should == desc
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
            end
         | 
| @@ -0,0 +1,932 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module MongoModel
         | 
| 4 | 
            +
              describe Scope do
         | 
| 5 | 
            +
                define_class(:Post, Document) do
         | 
| 6 | 
            +
                  property :published, Boolean, :default => false
         | 
| 7 | 
            +
                  property :author, String
         | 
| 8 | 
            +
                  property :date, Time
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
                
         | 
| 11 | 
            +
                let(:basic_scope) { Scope.new(Post) }
         | 
| 12 | 
            +
                let(:posts) { (1..5).map { Post.new } }
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                subject { Scope.new(Post) }
         | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                MongoModel::Document.extend(DocumentFinderStubs)
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                def self.subject_loaded(&block)
         | 
| 20 | 
            +
                  context "when loaded" do
         | 
| 21 | 
            +
                    before(:each) { subject.to_a }
         | 
| 22 | 
            +
                    class_eval(&block)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
                
         | 
| 26 | 
            +
                def self.subject_not_loaded(&block)
         | 
| 27 | 
            +
                  context "when not loaded", &block
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                
         | 
| 30 | 
            +
                def self.always(&block)
         | 
| 31 | 
            +
                  subject_loaded(&block)
         | 
| 32 | 
            +
                  subject_not_loaded(&block)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                
         | 
| 35 | 
            +
                
         | 
| 36 | 
            +
                shared_examples_for "all scopes" do
         | 
| 37 | 
            +
                  def finder_conditions
         | 
| 38 | 
            +
                    finder_options[:conditions] || {}
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                  
         | 
| 41 | 
            +
                  describe "#to_a" do
         | 
| 42 | 
            +
                    it "should find and return documents matching conditions" do
         | 
| 43 | 
            +
                      model.should_find(finder_options, posts) do
         | 
| 44 | 
            +
                        subject.to_a.should == posts
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                    
         | 
| 48 | 
            +
                    it "should load the scope" do
         | 
| 49 | 
            +
                      subject.to_a
         | 
| 50 | 
            +
                      subject.should be_loaded
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    
         | 
| 53 | 
            +
                    it "should cache the documents" do
         | 
| 54 | 
            +
                      subject.to_a
         | 
| 55 | 
            +
                      model.should_not_find { subject.to_a }
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                  
         | 
| 59 | 
            +
                  describe "#count" do
         | 
| 60 | 
            +
                    it "should count documents matching conditions and return the result" do
         | 
| 61 | 
            +
                      model.should_count(finder_options, 4) do
         | 
| 62 | 
            +
                        subject.count
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                    
         | 
| 66 | 
            +
                    it "should not cache the result" do
         | 
| 67 | 
            +
                      subject.count
         | 
| 68 | 
            +
                      model.should_count(finder_options, 4) do
         | 
| 69 | 
            +
                        subject.count.should == 4
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                  
         | 
| 74 | 
            +
                  describe "#size" do
         | 
| 75 | 
            +
                    before(:each) { model.stub_find(posts) }
         | 
| 76 | 
            +
                    
         | 
| 77 | 
            +
                    subject_loaded do
         | 
| 78 | 
            +
                      it "should return the number of matching documents" do
         | 
| 79 | 
            +
                        subject.size.should == 5
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                      
         | 
| 82 | 
            +
                      it "should not perform a count on the collection" do
         | 
| 83 | 
            +
                        model.should_not_count { subject.size }
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                    
         | 
| 87 | 
            +
                    subject_not_loaded do
         | 
| 88 | 
            +
                      it "should return the number of matching documents" do
         | 
| 89 | 
            +
                        subject.size.should == 5
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                      
         | 
| 92 | 
            +
                      it "should perform a count on the collection" do
         | 
| 93 | 
            +
                        model.should_count(finder_options, 9) do
         | 
| 94 | 
            +
                          subject.size.should == 9
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                  
         | 
| 100 | 
            +
                  describe "#empty?" do
         | 
| 101 | 
            +
                    context "when no matching documents exist" do
         | 
| 102 | 
            +
                      before(:each) { model.stub_find([]) }
         | 
| 103 | 
            +
                      
         | 
| 104 | 
            +
                      always do
         | 
| 105 | 
            +
                        it { should be_empty }
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                    
         | 
| 109 | 
            +
                    context "when matching documents exist" do
         | 
| 110 | 
            +
                      before(:each) { model.stub_find(posts) }
         | 
| 111 | 
            +
                      
         | 
| 112 | 
            +
                      always do
         | 
| 113 | 
            +
                        it { should_not be_empty }
         | 
| 114 | 
            +
                      end
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                    
         | 
| 117 | 
            +
                    subject_loaded do
         | 
| 118 | 
            +
                      it "should not perform a count on the collection" do
         | 
| 119 | 
            +
                        model.should_not_count { subject.empty? }
         | 
| 120 | 
            +
                      end
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
                  
         | 
| 124 | 
            +
                  describe "#any?" do
         | 
| 125 | 
            +
                    context "when no block given" do
         | 
| 126 | 
            +
                      it "should perform a count on the collection" do
         | 
| 127 | 
            +
                        model.should_count(finder_options, 1) { subject.any? }
         | 
| 128 | 
            +
                      end
         | 
| 129 | 
            +
                      
         | 
| 130 | 
            +
                      context "when no matching documents exist" do
         | 
| 131 | 
            +
                        before(:each) { model.stub_find([]) }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                        always do
         | 
| 134 | 
            +
                          specify { subject.any?.should be_false }
         | 
| 135 | 
            +
                        end
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                      context "when matching documents exist" do
         | 
| 139 | 
            +
                        before(:each) { model.stub_find(posts) }
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                        always do
         | 
| 142 | 
            +
                          specify { subject.any?.should be_true }
         | 
| 143 | 
            +
                        end
         | 
| 144 | 
            +
                      end
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                    
         | 
| 147 | 
            +
                    context "when block given" do
         | 
| 148 | 
            +
                      it "should delegate block to to_a" do
         | 
| 149 | 
            +
                        blk = lambda { |*args| true }
         | 
| 150 | 
            +
                        subject.to_a.should_receive(:any?).with(&blk)
         | 
| 151 | 
            +
                        subject.any?(&blk)
         | 
| 152 | 
            +
                      end
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
                  end
         | 
| 155 | 
            +
                  
         | 
| 156 | 
            +
                  describe "#reset" do
         | 
| 157 | 
            +
                    always do
         | 
| 158 | 
            +
                      it "should return itself" do
         | 
| 159 | 
            +
                        subject.reset.should equal(subject)
         | 
| 160 | 
            +
                      end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                      it "should not be loaded" do
         | 
| 163 | 
            +
                        subject.reset
         | 
| 164 | 
            +
                        subject.should_not be_loaded
         | 
| 165 | 
            +
                      end
         | 
| 166 | 
            +
                    end
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                  
         | 
| 169 | 
            +
                  describe "#reload" do
         | 
| 170 | 
            +
                    always do
         | 
| 171 | 
            +
                      it "should return itself" do
         | 
| 172 | 
            +
                        subject.reload.should equal(subject)
         | 
| 173 | 
            +
                      end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                      it "should be loaded after calling" do
         | 
| 176 | 
            +
                        subject.reload
         | 
| 177 | 
            +
                        subject.should be_loaded
         | 
| 178 | 
            +
                      end
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
                  
         | 
| 182 | 
            +
                  describe "#==" do
         | 
| 183 | 
            +
                    before(:each) { model.stub_find(posts) }
         | 
| 184 | 
            +
                    
         | 
| 185 | 
            +
                    it "should be equal to an array with matching results" do
         | 
| 186 | 
            +
                      subject.should == posts
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                    it "should not be equal to an array with different results" do
         | 
| 190 | 
            +
                      subject.should_not == []
         | 
| 191 | 
            +
                    end
         | 
| 192 | 
            +
                  end
         | 
| 193 | 
            +
                  
         | 
| 194 | 
            +
                  describe "array methods" do
         | 
| 195 | 
            +
                    it "should forward [] to to_a" do
         | 
| 196 | 
            +
                      subject.to_a.should_receive(:[]).with(0)
         | 
| 197 | 
            +
                      subject[0]
         | 
| 198 | 
            +
                    end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                    it "should forward each to to_a" do
         | 
| 201 | 
            +
                      blk = lambda { |*args| true }
         | 
| 202 | 
            +
                      subject.to_a.should_receive(:each).with(&blk)
         | 
| 203 | 
            +
                      subject.each(&blk)
         | 
| 204 | 
            +
                    end
         | 
| 205 | 
            +
                  end
         | 
| 206 | 
            +
                  
         | 
| 207 | 
            +
                  describe "#where" do
         | 
| 208 | 
            +
                    it "should return a new scope" do
         | 
| 209 | 
            +
                      subject.where(:author => "Sam").should be_an_instance_of(Scope)
         | 
| 210 | 
            +
                    end
         | 
| 211 | 
            +
                    
         | 
| 212 | 
            +
                    it "should not be loaded" do
         | 
| 213 | 
            +
                      subject.to_a
         | 
| 214 | 
            +
                      subject.where(:author => "Sam").should_not be_loaded
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                    
         | 
| 217 | 
            +
                    it "should add individual where values" do
         | 
| 218 | 
            +
                      where_scope = subject.where(:author => "Sam")
         | 
| 219 | 
            +
                      where_scope.where_values.should == subject.where_values + [{ :author => "Sam" }]
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                    
         | 
| 222 | 
            +
                    it "should add multiple where values" do
         | 
| 223 | 
            +
                      where_scope = subject.where({ :author => "Sam" }, { :published => false })
         | 
| 224 | 
            +
                      where_scope.where_values.should == subject.where_values + [{ :author => "Sam" }, { :published => false }]
         | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
                  end
         | 
| 227 | 
            +
                  
         | 
| 228 | 
            +
                  describe "#order" do
         | 
| 229 | 
            +
                    it "should return a new scope" do
         | 
| 230 | 
            +
                      subject.order(:author.asc).should be_an_instance_of(Scope)
         | 
| 231 | 
            +
                    end
         | 
| 232 | 
            +
                    
         | 
| 233 | 
            +
                    it "should not be loaded" do
         | 
| 234 | 
            +
                      subject.to_a
         | 
| 235 | 
            +
                      subject.order(:author.asc).should_not be_loaded
         | 
| 236 | 
            +
                    end
         | 
| 237 | 
            +
                    
         | 
| 238 | 
            +
                    it "should add individual order values" do
         | 
| 239 | 
            +
                      order_scope = subject.order(:author.asc)
         | 
| 240 | 
            +
                      order_scope.order_values.should == subject.order_values + [:author.asc]
         | 
| 241 | 
            +
                    end
         | 
| 242 | 
            +
                    
         | 
| 243 | 
            +
                    it "should add multiple order values" do
         | 
| 244 | 
            +
                      order_scope = subject.order(:author.asc, :published.desc)
         | 
| 245 | 
            +
                      order_scope.order_values.should == subject.order_values + [:author.asc, :published.desc]
         | 
| 246 | 
            +
                    end
         | 
| 247 | 
            +
                  end
         | 
| 248 | 
            +
                  
         | 
| 249 | 
            +
                  describe "#select" do
         | 
| 250 | 
            +
                    it "should return a new scope" do
         | 
| 251 | 
            +
                      subject.select(:author).should be_an_instance_of(Scope)
         | 
| 252 | 
            +
                    end
         | 
| 253 | 
            +
                    
         | 
| 254 | 
            +
                    it "should not be loaded" do
         | 
| 255 | 
            +
                      subject.to_a
         | 
| 256 | 
            +
                      subject.select(:author).should_not be_loaded
         | 
| 257 | 
            +
                    end
         | 
| 258 | 
            +
                    
         | 
| 259 | 
            +
                    it "should add individual select values" do
         | 
| 260 | 
            +
                      select_scope = subject.select(:author)
         | 
| 261 | 
            +
                      select_scope.select_values.should == subject.select_values + [:author]
         | 
| 262 | 
            +
                    end
         | 
| 263 | 
            +
                    
         | 
| 264 | 
            +
                    it "should add multiple select values" do
         | 
| 265 | 
            +
                      select_scope = subject.select(:author, :published)
         | 
| 266 | 
            +
                      select_scope.select_values.should == subject.select_values + [:author, :published]
         | 
| 267 | 
            +
                    end
         | 
| 268 | 
            +
                  end
         | 
| 269 | 
            +
                  
         | 
| 270 | 
            +
                  describe "#limit" do
         | 
| 271 | 
            +
                    it "should return a new scope" do
         | 
| 272 | 
            +
                      subject.limit(10).should be_an_instance_of(Scope)
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
                    
         | 
| 275 | 
            +
                    it "should not be loaded" do
         | 
| 276 | 
            +
                      subject.limit(10).should_not be_loaded
         | 
| 277 | 
            +
                    end
         | 
| 278 | 
            +
                    
         | 
| 279 | 
            +
                    it "should override previous limit value" do
         | 
| 280 | 
            +
                      subject.limit(10).limit_value.should == 10
         | 
| 281 | 
            +
                    end
         | 
| 282 | 
            +
                  end
         | 
| 283 | 
            +
                  
         | 
| 284 | 
            +
                  describe "#offset" do
         | 
| 285 | 
            +
                    it "should return a new scope" do
         | 
| 286 | 
            +
                      subject.offset(10).should be_an_instance_of(Scope)
         | 
| 287 | 
            +
                    end
         | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    it "should not be loaded" do
         | 
| 290 | 
            +
                      subject.offset(10).should_not be_loaded
         | 
| 291 | 
            +
                    end
         | 
| 292 | 
            +
                    
         | 
| 293 | 
            +
                    it "should override previous offset value" do
         | 
| 294 | 
            +
                      subject.offset(10).offset_value.should == 10
         | 
| 295 | 
            +
                    end
         | 
| 296 | 
            +
                  end
         | 
| 297 | 
            +
                  
         | 
| 298 | 
            +
                  describe "#from" do
         | 
| 299 | 
            +
                    define_class(:NotAPost, Document)
         | 
| 300 | 
            +
                    
         | 
| 301 | 
            +
                    it "should return a new scope" do
         | 
| 302 | 
            +
                      subject.from(NotAPost.collection).should be_an_instance_of(Scope)
         | 
| 303 | 
            +
                    end
         | 
| 304 | 
            +
                    
         | 
| 305 | 
            +
                    it "should not be loaded" do
         | 
| 306 | 
            +
                      subject.from(NotAPost.collection).should_not be_loaded
         | 
| 307 | 
            +
                    end
         | 
| 308 | 
            +
                    
         | 
| 309 | 
            +
                    it "should override collection" do
         | 
| 310 | 
            +
                      subject.from(NotAPost.collection).collection.should == NotAPost.collection
         | 
| 311 | 
            +
                    end
         | 
| 312 | 
            +
                  end
         | 
| 313 | 
            +
                  
         | 
| 314 | 
            +
                  describe "#first" do
         | 
| 315 | 
            +
                    context "when no matching documents exist" do
         | 
| 316 | 
            +
                      before(:each) { model.stub_find([]) }
         | 
| 317 | 
            +
                      
         | 
| 318 | 
            +
                      always do
         | 
| 319 | 
            +
                        it "should return nil" do
         | 
| 320 | 
            +
                          subject.first.should be_nil
         | 
| 321 | 
            +
                        end
         | 
| 322 | 
            +
                      end
         | 
| 323 | 
            +
                      
         | 
| 324 | 
            +
                      subject_loaded do
         | 
| 325 | 
            +
                        it "should not perform a find" do
         | 
| 326 | 
            +
                          model.should_not_find do
         | 
| 327 | 
            +
                            subject.first
         | 
| 328 | 
            +
                          end
         | 
| 329 | 
            +
                        end
         | 
| 330 | 
            +
                      end
         | 
| 331 | 
            +
                      
         | 
| 332 | 
            +
                      subject_not_loaded do
         | 
| 333 | 
            +
                        it "should find with a limit of 1" do
         | 
| 334 | 
            +
                          model.should_find(finder_options.merge(:limit => 1), []) do
         | 
| 335 | 
            +
                            subject.first
         | 
| 336 | 
            +
                          end
         | 
| 337 | 
            +
                        end
         | 
| 338 | 
            +
                      end
         | 
| 339 | 
            +
                    end
         | 
| 340 | 
            +
                    
         | 
| 341 | 
            +
                    context "when matching documents exist" do
         | 
| 342 | 
            +
                      let(:post) { posts.first }
         | 
| 343 | 
            +
                      before(:each) { model.stub_find([post]) }
         | 
| 344 | 
            +
                      
         | 
| 345 | 
            +
                      always do
         | 
| 346 | 
            +
                        it "should return the first document" do
         | 
| 347 | 
            +
                          subject.first.should == post
         | 
| 348 | 
            +
                        end
         | 
| 349 | 
            +
                      end
         | 
| 350 | 
            +
             | 
| 351 | 
            +
                      subject_not_loaded do
         | 
| 352 | 
            +
                        it "should cache find result" do
         | 
| 353 | 
            +
                          model.should_find(finder_options.merge(:limit => 1), [post]) do
         | 
| 354 | 
            +
                            subject.first
         | 
| 355 | 
            +
                            subject.first
         | 
| 356 | 
            +
                          end
         | 
| 357 | 
            +
                        end
         | 
| 358 | 
            +
                      end
         | 
| 359 | 
            +
                    end
         | 
| 360 | 
            +
                  end
         | 
| 361 | 
            +
                  
         | 
| 362 | 
            +
                  describe "#last" do
         | 
| 363 | 
            +
                    def reversed_finder_options
         | 
| 364 | 
            +
                      order = MongoModel::MongoOrder.parse(finder_options[:order] || [:id.asc])
         | 
| 365 | 
            +
                      finder_options.merge(:order => order.reverse.to_a)
         | 
| 366 | 
            +
                    end
         | 
| 367 | 
            +
                    
         | 
| 368 | 
            +
                    context "when no matching documents exist" do
         | 
| 369 | 
            +
                      before(:each) { model.stub_find([]) }
         | 
| 370 | 
            +
                      
         | 
| 371 | 
            +
                      always do
         | 
| 372 | 
            +
                        it "should return nil" do
         | 
| 373 | 
            +
                          subject.last.should be_nil
         | 
| 374 | 
            +
                        end
         | 
| 375 | 
            +
                      end
         | 
| 376 | 
            +
                      
         | 
| 377 | 
            +
                      subject_loaded do
         | 
| 378 | 
            +
                        it "should not perform a find" do
         | 
| 379 | 
            +
                          model.should_not_find do
         | 
| 380 | 
            +
                            subject.last
         | 
| 381 | 
            +
                          end
         | 
| 382 | 
            +
                        end
         | 
| 383 | 
            +
                      end
         | 
| 384 | 
            +
                      
         | 
| 385 | 
            +
                      subject_not_loaded do
         | 
| 386 | 
            +
                        it "should find with a limit of 1" do
         | 
| 387 | 
            +
                          model.should_find(reversed_finder_options.merge(:limit => 1), []) do
         | 
| 388 | 
            +
                            subject.last
         | 
| 389 | 
            +
                          end
         | 
| 390 | 
            +
                        end
         | 
| 391 | 
            +
                      end
         | 
| 392 | 
            +
                    end
         | 
| 393 | 
            +
                    
         | 
| 394 | 
            +
                    context "when matching documents exist" do
         | 
| 395 | 
            +
                      let(:post) { posts.last }
         | 
| 396 | 
            +
                      before(:each) { model.stub_find([post]) }
         | 
| 397 | 
            +
                      
         | 
| 398 | 
            +
                      always do
         | 
| 399 | 
            +
                        it "should return the last document" do
         | 
| 400 | 
            +
                          subject.last.should == post
         | 
| 401 | 
            +
                        end
         | 
| 402 | 
            +
                      end
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                      subject_not_loaded do
         | 
| 405 | 
            +
                        it "should cache find result" do
         | 
| 406 | 
            +
                          model.should_find(reversed_finder_options.merge(:limit => 1), [post]) do
         | 
| 407 | 
            +
                            subject.last
         | 
| 408 | 
            +
                            subject.last
         | 
| 409 | 
            +
                          end
         | 
| 410 | 
            +
                        end
         | 
| 411 | 
            +
                      end
         | 
| 412 | 
            +
                    end
         | 
| 413 | 
            +
                  end
         | 
| 414 | 
            +
                  
         | 
| 415 | 
            +
                  describe "#all" do
         | 
| 416 | 
            +
                    it "should return all documents" do
         | 
| 417 | 
            +
                      model.should_find(finder_options, posts) do
         | 
| 418 | 
            +
                        subject.all.should == posts
         | 
| 419 | 
            +
                      end
         | 
| 420 | 
            +
                    end
         | 
| 421 | 
            +
                  end
         | 
| 422 | 
            +
                  
         | 
| 423 | 
            +
                  describe "#find" do
         | 
| 424 | 
            +
                    context "with single id" do
         | 
| 425 | 
            +
                      let(:post) { posts.first }
         | 
| 426 | 
            +
                      
         | 
| 427 | 
            +
                      it "should perform find on collection" do
         | 
| 428 | 
            +
                        model.should_find(finder_options.deep_merge(:conditions => { :id => post.id }, :limit => 1), [post]) do
         | 
| 429 | 
            +
                          subject.find(post.id)
         | 
| 430 | 
            +
                        end
         | 
| 431 | 
            +
                      end
         | 
| 432 | 
            +
                      
         | 
| 433 | 
            +
                      context "when document exists" do
         | 
| 434 | 
            +
                        before(:each) { model.stub_find([post]) }
         | 
| 435 | 
            +
                        
         | 
| 436 | 
            +
                        it "should find and return document" do
         | 
| 437 | 
            +
                          subject.find(post.id).should == post
         | 
| 438 | 
            +
                        end
         | 
| 439 | 
            +
                      end
         | 
| 440 | 
            +
             | 
| 441 | 
            +
                      context "when document does not exist" do
         | 
| 442 | 
            +
                        before(:each) { model.stub_find([]) }
         | 
| 443 | 
            +
                        
         | 
| 444 | 
            +
                        it "should raise a DocumentNotFound exception" do
         | 
| 445 | 
            +
                          lambda {
         | 
| 446 | 
            +
                            subject.find('missing')
         | 
| 447 | 
            +
                          }.should raise_error(MongoModel::DocumentNotFound)
         | 
| 448 | 
            +
                        end
         | 
| 449 | 
            +
                      end
         | 
| 450 | 
            +
                    end
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                    context "by multiple ids" do
         | 
| 453 | 
            +
                      let(:post1) { posts.first }
         | 
| 454 | 
            +
                      let(:post2) { posts.last }
         | 
| 455 | 
            +
                      
         | 
| 456 | 
            +
                      it "should perform find on collection" do
         | 
| 457 | 
            +
                        model.should_find(finder_options.deep_merge(:conditions => { :id.in => [post2.id, post1.id] }), [post1, post2]) do
         | 
| 458 | 
            +
                          subject.find(post2.id, post1.id)
         | 
| 459 | 
            +
                        end
         | 
| 460 | 
            +
                      end
         | 
| 461 | 
            +
                      
         | 
| 462 | 
            +
                      context "when all documents exist" do
         | 
| 463 | 
            +
                        before(:each) { model.stub_find([post2, post1]) }
         | 
| 464 | 
            +
                        
         | 
| 465 | 
            +
                        it "should return documents in order given" do
         | 
| 466 | 
            +
                          subject.find(post2.id, post1.id).should == [post2, post1]
         | 
| 467 | 
            +
                        end
         | 
| 468 | 
            +
                      end
         | 
| 469 | 
            +
                    
         | 
| 470 | 
            +
                      context "when some documents do not exist" do
         | 
| 471 | 
            +
                        before(:each) { model.stub_find([post1]) }
         | 
| 472 | 
            +
                        
         | 
| 473 | 
            +
                        it "should raise a DocumentNotFound exception" do
         | 
| 474 | 
            +
                          lambda {
         | 
| 475 | 
            +
                            subject.find(post1.id, 'missing')
         | 
| 476 | 
            +
                          }.should raise_error(MongoModel::DocumentNotFound)
         | 
| 477 | 
            +
                        end
         | 
| 478 | 
            +
                      end
         | 
| 479 | 
            +
                    end
         | 
| 480 | 
            +
                  end
         | 
| 481 | 
            +
                  
         | 
| 482 | 
            +
                  describe "#exists?" do
         | 
| 483 | 
            +
                    let(:post) { posts.first }
         | 
| 484 | 
            +
             | 
| 485 | 
            +
                    it "should perform a count on the collection" do
         | 
| 486 | 
            +
                      model.should_count(finder_options.deep_merge(:conditions => { :id => post.id }), 1) do
         | 
| 487 | 
            +
                        subject.exists?(post.id)
         | 
| 488 | 
            +
                      end
         | 
| 489 | 
            +
                    end
         | 
| 490 | 
            +
             | 
| 491 | 
            +
                    context "when the document exists" do
         | 
| 492 | 
            +
                      before(:each) { model.stub_find([post])}
         | 
| 493 | 
            +
                      
         | 
| 494 | 
            +
                      it "should return true" do
         | 
| 495 | 
            +
                        subject.exists?(post.id).should be_true
         | 
| 496 | 
            +
                      end
         | 
| 497 | 
            +
                    end
         | 
| 498 | 
            +
             | 
| 499 | 
            +
                    context "when the document does not exist" do
         | 
| 500 | 
            +
                      before(:each) { model.stub_find([])}
         | 
| 501 | 
            +
                      
         | 
| 502 | 
            +
                      it "should return false" do
         | 
| 503 | 
            +
                        subject.exists?('missing').should be_false
         | 
| 504 | 
            +
                      end
         | 
| 505 | 
            +
                    end
         | 
| 506 | 
            +
                  end
         | 
| 507 | 
            +
                  
         | 
| 508 | 
            +
                  describe "#delete_all" do
         | 
| 509 | 
            +
                    it "should remove all matching documents from collection" do
         | 
| 510 | 
            +
                      model.should_delete(finder_conditions) do
         | 
| 511 | 
            +
                        subject.delete_all
         | 
| 512 | 
            +
                      end
         | 
| 513 | 
            +
                    end
         | 
| 514 | 
            +
                    
         | 
| 515 | 
            +
                    subject_loaded do
         | 
| 516 | 
            +
                      it "should reset the scope" do
         | 
| 517 | 
            +
                        subject.delete_all
         | 
| 518 | 
            +
                        subject.should_not be_loaded
         | 
| 519 | 
            +
                      end
         | 
| 520 | 
            +
                    end
         | 
| 521 | 
            +
                  end
         | 
| 522 | 
            +
                  
         | 
| 523 | 
            +
                  describe "#delete" do
         | 
| 524 | 
            +
                    context "by single id" do
         | 
| 525 | 
            +
                      it "should remove the document from the collection" do
         | 
| 526 | 
            +
                        model.should_delete(finder_conditions.merge(:id => "the-id")) do
         | 
| 527 | 
            +
                          subject.delete("the-id")
         | 
| 528 | 
            +
                        end
         | 
| 529 | 
            +
                      end
         | 
| 530 | 
            +
                      
         | 
| 531 | 
            +
                      subject_loaded do
         | 
| 532 | 
            +
                        it "should reset the scope" do
         | 
| 533 | 
            +
                          subject.delete("the-id")
         | 
| 534 | 
            +
                          subject.should_not be_loaded
         | 
| 535 | 
            +
                        end
         | 
| 536 | 
            +
                      end
         | 
| 537 | 
            +
                    end
         | 
| 538 | 
            +
                    
         | 
| 539 | 
            +
                    context "by multiple ids" do
         | 
| 540 | 
            +
                      it "should remove the document from the collection" do
         | 
| 541 | 
            +
                        model.should_delete(finder_conditions.merge(:id.in => ["first-id", "second-id"])) do
         | 
| 542 | 
            +
                          subject.delete("first-id", "second-id")
         | 
| 543 | 
            +
                        end
         | 
| 544 | 
            +
                      end
         | 
| 545 | 
            +
                      
         | 
| 546 | 
            +
                      subject_loaded do
         | 
| 547 | 
            +
                        it "should reset the scope" do
         | 
| 548 | 
            +
                          subject.delete("first-id", "second-id")
         | 
| 549 | 
            +
                          subject.should_not be_loaded
         | 
| 550 | 
            +
                        end
         | 
| 551 | 
            +
                      end
         | 
| 552 | 
            +
                    end
         | 
| 553 | 
            +
                  end
         | 
| 554 | 
            +
                  
         | 
| 555 | 
            +
                  describe "#destroy_all" do
         | 
| 556 | 
            +
                    let(:post1) { posts.first }
         | 
| 557 | 
            +
                    let(:post2) { posts.last }
         | 
| 558 | 
            +
                    
         | 
| 559 | 
            +
                    before(:each) { model.stub_find([post1, post2]) }
         | 
| 560 | 
            +
                    
         | 
| 561 | 
            +
                    it "should destroy all matching documents individually" do
         | 
| 562 | 
            +
                      Post.should_delete(:id => post1.id)
         | 
| 563 | 
            +
                      Post.should_delete(:id => post2.id)
         | 
| 564 | 
            +
                      subject.destroy_all
         | 
| 565 | 
            +
                    end
         | 
| 566 | 
            +
                    
         | 
| 567 | 
            +
                    subject_loaded do
         | 
| 568 | 
            +
                      it "should reset the scope" do
         | 
| 569 | 
            +
                        subject.destroy_all
         | 
| 570 | 
            +
                        subject.should_not be_loaded
         | 
| 571 | 
            +
                      end
         | 
| 572 | 
            +
                    end
         | 
| 573 | 
            +
                  end
         | 
| 574 | 
            +
                  
         | 
| 575 | 
            +
                  describe "#destroy" do
         | 
| 576 | 
            +
                    context "by single id" do
         | 
| 577 | 
            +
                      let(:post) { posts.first }
         | 
| 578 | 
            +
                      
         | 
| 579 | 
            +
                      before(:each) { model.stub_find([post]) }
         | 
| 580 | 
            +
                      
         | 
| 581 | 
            +
                      it "should destroy the retrieved document" do
         | 
| 582 | 
            +
                        Post.should_delete(:id => post.id)
         | 
| 583 | 
            +
                        subject.destroy(post.id)
         | 
| 584 | 
            +
                      end
         | 
| 585 | 
            +
                      
         | 
| 586 | 
            +
                      subject_loaded do
         | 
| 587 | 
            +
                        it "should reset the scope" do
         | 
| 588 | 
            +
                          subject.destroy(post.id)
         | 
| 589 | 
            +
                          subject.should_not be_loaded
         | 
| 590 | 
            +
                        end
         | 
| 591 | 
            +
                      end
         | 
| 592 | 
            +
                    end
         | 
| 593 | 
            +
                    
         | 
| 594 | 
            +
                    context "by multiple ids" do
         | 
| 595 | 
            +
                      let(:post1) { posts.first }
         | 
| 596 | 
            +
                      let(:post2) { posts.last }
         | 
| 597 | 
            +
                      
         | 
| 598 | 
            +
                      before(:each) { model.stub_find([post1, post2]) }
         | 
| 599 | 
            +
                      
         | 
| 600 | 
            +
                      it "should destroy the documents individually" do
         | 
| 601 | 
            +
                        Post.should_delete(:id => post1.id)
         | 
| 602 | 
            +
                        Post.should_delete(:id => post2.id)
         | 
| 603 | 
            +
                        subject.destroy(post1.id, post2.id)
         | 
| 604 | 
            +
                      end
         | 
| 605 | 
            +
                      
         | 
| 606 | 
            +
                      subject_loaded do
         | 
| 607 | 
            +
                        it "should reset the scope" do
         | 
| 608 | 
            +
                          subject.destroy(post1.id, post2.id)
         | 
| 609 | 
            +
                          subject.should_not be_loaded
         | 
| 610 | 
            +
                        end
         | 
| 611 | 
            +
                      end
         | 
| 612 | 
            +
                    end
         | 
| 613 | 
            +
                  end
         | 
| 614 | 
            +
                end
         | 
| 615 | 
            +
                
         | 
| 616 | 
            +
                
         | 
| 617 | 
            +
                context "without criteria" do
         | 
| 618 | 
            +
                  subject { basic_scope }
         | 
| 619 | 
            +
                  
         | 
| 620 | 
            +
                  context "when initialized" do
         | 
| 621 | 
            +
                    it { should_not be_loaded }
         | 
| 622 | 
            +
                  end
         | 
| 623 | 
            +
                  
         | 
| 624 | 
            +
                  context "when loaded" do
         | 
| 625 | 
            +
                    before(:each) { subject.to_a }
         | 
| 626 | 
            +
                    its(:clone) { should_not be_loaded }
         | 
| 627 | 
            +
                  end
         | 
| 628 | 
            +
                  
         | 
| 629 | 
            +
                  def model
         | 
| 630 | 
            +
                    Post
         | 
| 631 | 
            +
                  end
         | 
| 632 | 
            +
                  
         | 
| 633 | 
            +
                  def finder_options
         | 
| 634 | 
            +
                    {}
         | 
| 635 | 
            +
                  end
         | 
| 636 | 
            +
                  
         | 
| 637 | 
            +
                  it_should_behave_like "all scopes"
         | 
| 638 | 
            +
                  
         | 
| 639 | 
            +
                  it "should use collection from class" do
         | 
| 640 | 
            +
                    subject.collection.should == Post.collection
         | 
| 641 | 
            +
                  end
         | 
| 642 | 
            +
                  
         | 
| 643 | 
            +
                  describe "#inspect" do
         | 
| 644 | 
            +
                    before(:each) { Post.stub_find(posts) }
         | 
| 645 | 
            +
             | 
| 646 | 
            +
                    it "should delegate to to_a" do
         | 
| 647 | 
            +
                      subject.inspect.should == posts.inspect
         | 
| 648 | 
            +
                    end
         | 
| 649 | 
            +
                  end
         | 
| 650 | 
            +
                  
         | 
| 651 | 
            +
                  describe "#==" do
         | 
| 652 | 
            +
                    define_class(:NotAPost, Document)
         | 
| 653 | 
            +
                    
         | 
| 654 | 
            +
                    it "should be equal to a new scope for the same class" do
         | 
| 655 | 
            +
                      subject.should == Scope.new(Post)
         | 
| 656 | 
            +
                    end
         | 
| 657 | 
            +
             | 
| 658 | 
            +
                    it "should not be equal to a scope for a different class" do
         | 
| 659 | 
            +
                      subject.should_not == Scope.new(NotAPost)
         | 
| 660 | 
            +
                    end
         | 
| 661 | 
            +
                  end
         | 
| 662 | 
            +
                  
         | 
| 663 | 
            +
                  describe "#reverse_order" do
         | 
| 664 | 
            +
                    subject { basic_scope.reverse_order }
         | 
| 665 | 
            +
                    
         | 
| 666 | 
            +
                    it "should return a new scope" do
         | 
| 667 | 
            +
                      subject.should be_an_instance_of(Scope)
         | 
| 668 | 
            +
                    end
         | 
| 669 | 
            +
                    
         | 
| 670 | 
            +
                    it "should not be loaded" do
         | 
| 671 | 
            +
                      basic_scope.to_a # Load parent scope
         | 
| 672 | 
            +
                      subject.should_not be_loaded
         | 
| 673 | 
            +
                    end
         | 
| 674 | 
            +
                    
         | 
| 675 | 
            +
                    it "should set the order value to descending by id" do
         | 
| 676 | 
            +
                      subject.order_values.should == [:id.desc]
         | 
| 677 | 
            +
                    end
         | 
| 678 | 
            +
                  end
         | 
| 679 | 
            +
                  
         | 
| 680 | 
            +
                  describe "#build" do
         | 
| 681 | 
            +
                    it "should return a new document" do
         | 
| 682 | 
            +
                      subject.build.should be_an_instance_of(Post)
         | 
| 683 | 
            +
                    end
         | 
| 684 | 
            +
                    
         | 
| 685 | 
            +
                    it "should be aliased as #new" do
         | 
| 686 | 
            +
                      subject.new(:id => '123').should == subject.build(:id => '123')
         | 
| 687 | 
            +
                    end
         | 
| 688 | 
            +
                  end
         | 
| 689 | 
            +
                  
         | 
| 690 | 
            +
                  describe "#create" do
         | 
| 691 | 
            +
                    it "should return a new document" do
         | 
| 692 | 
            +
                      subject.create.should be_an_instance_of(Post)
         | 
| 693 | 
            +
                    end
         | 
| 694 | 
            +
                    
         | 
| 695 | 
            +
                    it "should save the document" do
         | 
| 696 | 
            +
                      subject.create.should_not be_a_new_record
         | 
| 697 | 
            +
                    end
         | 
| 698 | 
            +
                  end
         | 
| 699 | 
            +
                  
         | 
| 700 | 
            +
                  describe "#apply_finder_options" do
         | 
| 701 | 
            +
                    it "should return a new scope" do
         | 
| 702 | 
            +
                      subject.apply_finder_options({}).should be_an_instance_of(Scope)
         | 
| 703 | 
            +
                    end
         | 
| 704 | 
            +
                    
         | 
| 705 | 
            +
                    it "should set where values from options" do
         | 
| 706 | 
            +
                      scope = subject.apply_finder_options({ :conditions => { :author => "John" } })
         | 
| 707 | 
            +
                      scope.where_values.should == [{ :author => "John" }]
         | 
| 708 | 
            +
                    end
         | 
| 709 | 
            +
                    
         | 
| 710 | 
            +
                    it "should set order values from options" do
         | 
| 711 | 
            +
                      scope = subject.apply_finder_options({ :order => :author.desc })
         | 
| 712 | 
            +
                      scope.order_values.should == [:author.desc]
         | 
| 713 | 
            +
                    end
         | 
| 714 | 
            +
                    
         | 
| 715 | 
            +
                    it "should set select values from options" do
         | 
| 716 | 
            +
                      scope = subject.apply_finder_options({ :select => [:id, :author] })
         | 
| 717 | 
            +
                      scope.select_values.should == [:id, :author]
         | 
| 718 | 
            +
                    end
         | 
| 719 | 
            +
                    
         | 
| 720 | 
            +
                    it "should set offset value from options" do
         | 
| 721 | 
            +
                      scope = subject.apply_finder_options({ :offset => 40 })
         | 
| 722 | 
            +
                      scope.offset_value.should == 40
         | 
| 723 | 
            +
                    end
         | 
| 724 | 
            +
                    
         | 
| 725 | 
            +
                    it "should set limit value from options" do
         | 
| 726 | 
            +
                      scope = subject.apply_finder_options({ :limit => 50 })
         | 
| 727 | 
            +
                      scope.limit_value.should == 50
         | 
| 728 | 
            +
                    end
         | 
| 729 | 
            +
                  end
         | 
| 730 | 
            +
                end
         | 
| 731 | 
            +
             | 
| 732 | 
            +
                
         | 
| 733 | 
            +
                context "with criteria" do
         | 
| 734 | 
            +
                  define_class(:OtherPost, Document)
         | 
| 735 | 
            +
                  
         | 
| 736 | 
            +
                  let(:timestamp) { Time.now }
         | 
| 737 | 
            +
                  let(:scoped) do
         | 
| 738 | 
            +
                    basic_scope.where(:author => "Sam").
         | 
| 739 | 
            +
                                where(:published => true).
         | 
| 740 | 
            +
                                where(:date.lt => timestamp).
         | 
| 741 | 
            +
                                order(:author.asc).
         | 
| 742 | 
            +
                                order(:published.desc).
         | 
| 743 | 
            +
                                select(:author).
         | 
| 744 | 
            +
                                select(:published).
         | 
| 745 | 
            +
                                offset(15).
         | 
| 746 | 
            +
                                limit(7).
         | 
| 747 | 
            +
                                from(OtherPost.collection)
         | 
| 748 | 
            +
                  end
         | 
| 749 | 
            +
                  
         | 
| 750 | 
            +
                  subject { scoped }
         | 
| 751 | 
            +
                  
         | 
| 752 | 
            +
                  def model
         | 
| 753 | 
            +
                    OtherPost
         | 
| 754 | 
            +
                  end
         | 
| 755 | 
            +
                  
         | 
| 756 | 
            +
                  def finder_options
         | 
| 757 | 
            +
                    {
         | 
| 758 | 
            +
                      :conditions => { "author" => "Sam", "published" => true, "date" => { "$lt" => timestamp } },
         | 
| 759 | 
            +
                      :order => [:author.asc, :published.desc],
         | 
| 760 | 
            +
                      :select => [:author, :published],
         | 
| 761 | 
            +
                      :offset => 15,
         | 
| 762 | 
            +
                      :limit => 7
         | 
| 763 | 
            +
                    }
         | 
| 764 | 
            +
                  end
         | 
| 765 | 
            +
                  
         | 
| 766 | 
            +
                  it_should_behave_like "all scopes"
         | 
| 767 | 
            +
                  
         | 
| 768 | 
            +
                  describe "#build" do
         | 
| 769 | 
            +
                    it "should use equality where conditions as attributes" do
         | 
| 770 | 
            +
                      doc = subject.build
         | 
| 771 | 
            +
                      doc.author.should == "Sam"
         | 
| 772 | 
            +
                      doc.published.should be_true
         | 
| 773 | 
            +
                      doc.date.should be_nil
         | 
| 774 | 
            +
                    end
         | 
| 775 | 
            +
                  end
         | 
| 776 | 
            +
                  
         | 
| 777 | 
            +
                  describe "#create" do
         | 
| 778 | 
            +
                    it "should use equality where conditions as attributes" do
         | 
| 779 | 
            +
                      doc = subject.create
         | 
| 780 | 
            +
                      doc.author.should == "Sam"
         | 
| 781 | 
            +
                      doc.published.should be_true
         | 
| 782 | 
            +
                      doc.date.should be_nil
         | 
| 783 | 
            +
                    end
         | 
| 784 | 
            +
                  end
         | 
| 785 | 
            +
                  
         | 
| 786 | 
            +
                  describe "#reverse_order" do
         | 
| 787 | 
            +
                    subject { scoped.reverse_order }
         | 
| 788 | 
            +
                    
         | 
| 789 | 
            +
                    it "should set the order values to the reverse order" do
         | 
| 790 | 
            +
                      subject.order_values.should == [:author.desc, :published.asc]
         | 
| 791 | 
            +
                    end
         | 
| 792 | 
            +
                  end
         | 
| 793 | 
            +
                  
         | 
| 794 | 
            +
                  describe "#except" do
         | 
| 795 | 
            +
                    context "given :where" do
         | 
| 796 | 
            +
                      it "should return a new scope without where values" do
         | 
| 797 | 
            +
                        s = subject.except(:where)
         | 
| 798 | 
            +
                        s.where_values.should be_empty
         | 
| 799 | 
            +
                        s.order_values.should == [:author.asc, :published.desc]
         | 
| 800 | 
            +
                        s.select_values.should == [:author, :published]
         | 
| 801 | 
            +
                        s.offset_value.should == 15
         | 
| 802 | 
            +
                        s.limit_value.should == 7
         | 
| 803 | 
            +
                        s.collection.should == OtherPost.collection
         | 
| 804 | 
            +
                      end
         | 
| 805 | 
            +
                    end
         | 
| 806 | 
            +
             | 
| 807 | 
            +
                    context "given :order" do
         | 
| 808 | 
            +
                      it "should return a new scope without order values" do
         | 
| 809 | 
            +
                        s = subject.except(:order)
         | 
| 810 | 
            +
                        s.where_values.should == [{ :author => "Sam" }, { :published => true }, { :date.lt => timestamp }]
         | 
| 811 | 
            +
                        s.order_values.should be_empty
         | 
| 812 | 
            +
                        s.select_values.should == [:author, :published]
         | 
| 813 | 
            +
                        s.offset_value.should == 15
         | 
| 814 | 
            +
                        s.limit_value.should == 7
         | 
| 815 | 
            +
                        s.collection.should == OtherPost.collection
         | 
| 816 | 
            +
                      end
         | 
| 817 | 
            +
                    end
         | 
| 818 | 
            +
             | 
| 819 | 
            +
                    context "given :select" do
         | 
| 820 | 
            +
                      it "should return a new scope without select values" do
         | 
| 821 | 
            +
                        s = subject.except(:select)
         | 
| 822 | 
            +
                        s.where_values.should == [{ :author => "Sam" }, { :published => true }, { :date.lt => timestamp }]
         | 
| 823 | 
            +
                        s.order_values.should == [:author.asc, :published.desc]
         | 
| 824 | 
            +
                        s.select_values.should be_empty
         | 
| 825 | 
            +
                        s.offset_value.should == 15
         | 
| 826 | 
            +
                        s.limit_value.should == 7
         | 
| 827 | 
            +
                        s.collection.should == OtherPost.collection
         | 
| 828 | 
            +
                      end
         | 
| 829 | 
            +
                    end
         | 
| 830 | 
            +
             | 
| 831 | 
            +
                    context "given :offset" do
         | 
| 832 | 
            +
                      it "should return a new scope without offset value" do
         | 
| 833 | 
            +
                        s = subject.except(:offset)
         | 
| 834 | 
            +
                        s.where_values.should == [{ :author => "Sam" }, { :published => true }, { :date.lt => timestamp }]
         | 
| 835 | 
            +
                        s.order_values.should == [:author.asc, :published.desc]
         | 
| 836 | 
            +
                        s.select_values.should == [:author, :published]
         | 
| 837 | 
            +
                        s.offset_value.should be_nil
         | 
| 838 | 
            +
                        s.limit_value.should == 7
         | 
| 839 | 
            +
                        s.collection.should == OtherPost.collection
         | 
| 840 | 
            +
                      end
         | 
| 841 | 
            +
                    end
         | 
| 842 | 
            +
             | 
| 843 | 
            +
                    context "given :limit" do
         | 
| 844 | 
            +
                      it "should return a new scope without limit value" do
         | 
| 845 | 
            +
                        s = subject.except(:limit)
         | 
| 846 | 
            +
                        s.where_values.should == [{ :author => "Sam" }, { :published => true }, { :date.lt => timestamp }]
         | 
| 847 | 
            +
                        s.order_values.should == [:author.asc, :published.desc]
         | 
| 848 | 
            +
                        s.select_values.should == [:author, :published]
         | 
| 849 | 
            +
                        s.offset_value.should == 15
         | 
| 850 | 
            +
                        s.limit_value.should be_nil
         | 
| 851 | 
            +
                        s.collection.should == OtherPost.collection
         | 
| 852 | 
            +
                      end
         | 
| 853 | 
            +
                    end
         | 
| 854 | 
            +
             | 
| 855 | 
            +
                    context "given :from" do
         | 
| 856 | 
            +
                      it "should return a new scope with default collection" do
         | 
| 857 | 
            +
                        s = subject.except(:from)
         | 
| 858 | 
            +
                        s.where_values.should == [{ :author => "Sam" }, { :published => true }, { :date.lt => timestamp }]
         | 
| 859 | 
            +
                        s.order_values.should == [:author.asc, :published.desc]
         | 
| 860 | 
            +
                        s.select_values.should == [:author, :published]
         | 
| 861 | 
            +
                        s.offset_value.should == 15
         | 
| 862 | 
            +
                        s.limit_value.should == 7
         | 
| 863 | 
            +
                        s.collection.should == Post.collection
         | 
| 864 | 
            +
                      end
         | 
| 865 | 
            +
                    end
         | 
| 866 | 
            +
                  end
         | 
| 867 | 
            +
                  
         | 
| 868 | 
            +
                  describe "#merge" do
         | 
| 869 | 
            +
                    let(:merged) do
         | 
| 870 | 
            +
                      basic_scope.where(:date.gt => timestamp-1.year).
         | 
| 871 | 
            +
                                  order(:date.desc).
         | 
| 872 | 
            +
                                  select(:date)
         | 
| 873 | 
            +
                    end
         | 
| 874 | 
            +
                    let(:result) { subject.merge(merged) }
         | 
| 875 | 
            +
                    
         | 
| 876 | 
            +
                    it "should combine where values from scopes" do
         | 
| 877 | 
            +
                      result.where_values.should == [
         | 
| 878 | 
            +
                        { :author => "Sam" },
         | 
| 879 | 
            +
                        { :published => true },
         | 
| 880 | 
            +
                        { :date.lt => timestamp },
         | 
| 881 | 
            +
                        { :date.gt => timestamp-1.year }
         | 
| 882 | 
            +
                      ]
         | 
| 883 | 
            +
                    end
         | 
| 884 | 
            +
                    
         | 
| 885 | 
            +
                    it "should combine order values from scopes" do
         | 
| 886 | 
            +
                      result.order_values.should == [:author.asc, :published.desc, :date.desc]
         | 
| 887 | 
            +
                    end
         | 
| 888 | 
            +
                    
         | 
| 889 | 
            +
                    it "should combine select values from scopes" do
         | 
| 890 | 
            +
                      result.select_values.should == [:author, :published, :date]
         | 
| 891 | 
            +
                    end
         | 
| 892 | 
            +
                    
         | 
| 893 | 
            +
                    context "merged scope has offset value" do
         | 
| 894 | 
            +
                      let(:merged) { basic_scope.offset(10) }
         | 
| 895 | 
            +
                      
         | 
| 896 | 
            +
                      it "should use offset value from merged scope" do
         | 
| 897 | 
            +
                        result.offset_value.should == 10
         | 
| 898 | 
            +
                      end
         | 
| 899 | 
            +
                    end
         | 
| 900 | 
            +
                    
         | 
| 901 | 
            +
                    context "merged scope has no offset value set" do
         | 
| 902 | 
            +
                      let(:merged) { basic_scope }
         | 
| 903 | 
            +
                      
         | 
| 904 | 
            +
                      it "should use offset value from original scope" do
         | 
| 905 | 
            +
                        result.offset_value.should == 15
         | 
| 906 | 
            +
                      end
         | 
| 907 | 
            +
                    end
         | 
| 908 | 
            +
                    
         | 
| 909 | 
            +
                    context "merged scope has limit value" do
         | 
| 910 | 
            +
                      let(:merged) { basic_scope.limit(50) }
         | 
| 911 | 
            +
                      
         | 
| 912 | 
            +
                      it "should use limit value from merged scope" do
         | 
| 913 | 
            +
                        result.limit_value.should == 50
         | 
| 914 | 
            +
                      end
         | 
| 915 | 
            +
                    end
         | 
| 916 | 
            +
                    
         | 
| 917 | 
            +
                    context "merged scope has no limit value set" do
         | 
| 918 | 
            +
                      let(:merged) { basic_scope }
         | 
| 919 | 
            +
                      
         | 
| 920 | 
            +
                      it "should use limit value from original scope" do
         | 
| 921 | 
            +
                        result.limit_value.should == 7
         | 
| 922 | 
            +
                      end
         | 
| 923 | 
            +
                    end
         | 
| 924 | 
            +
                    
         | 
| 925 | 
            +
                    it "should use from value (collection) from merged scope" do
         | 
| 926 | 
            +
                      merged = basic_scope.from(Post.collection)
         | 
| 927 | 
            +
                      subject.merge(merged).collection.should == Post.collection
         | 
| 928 | 
            +
                    end
         | 
| 929 | 
            +
                  end
         | 
| 930 | 
            +
                end
         | 
| 931 | 
            +
              end
         | 
| 932 | 
            +
            end
         |