mongoid 1.0.1 → 1.0.2

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/HISTORY CHANGED
@@ -1,3 +1,17 @@
1
+ === 1.0.2
2
+ - Named scopes get added functionality:
3
+
4
+ - named scopes can now be criteria objects.
5
+
6
+ - named scoped can now be procs with criteria.
7
+
8
+ - named scopes and class methods that return
9
+ criteria can be chained with each other.
10
+
11
+ - When calling save on an embedded document whose
12
+ validation passes but the parent's validation
13
+ fails, it will properly return false.
14
+
1
15
  === 1.0.1
2
16
  - Documents now have named_scopes similar to
3
17
  ActiveRecord named scopes. Please see rdoc or
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
@@ -45,14 +45,15 @@ require "mongoid/commands"
45
45
  require "mongoid/config"
46
46
  require "mongoid/complex_criterion"
47
47
  require "mongoid/criteria"
48
- require "mongoid/named_scopes"
49
48
  require "mongoid/extensions"
50
49
  require "mongoid/errors"
51
- require "mongoid/fields/field"
50
+ require "mongoid/field"
52
51
  require "mongoid/fields"
53
52
  require "mongoid/finders"
54
53
  require "mongoid/indexes"
55
54
  require "mongoid/memoization"
55
+ require "mongoid/named_scope"
56
+ require "mongoid/scope"
56
57
  require "mongoid/timestamps"
57
58
  require "mongoid/versioning"
58
59
  require "mongoid/components"
@@ -15,9 +15,12 @@ module Mongoid #:nodoc:
15
15
  doc.run_callbacks :before_save
16
16
  parent = doc._parent
17
17
  doc.new_record = false
18
- parent ? Save.execute(parent) : doc.collection.save(doc.attributes)
19
- doc.run_callbacks :after_save
20
- return true
18
+ if parent ? Save.execute(parent, validate) : doc.collection.save(doc.attributes)
19
+ doc.run_callbacks :after_save
20
+ return true
21
+ else
22
+ return false
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -15,7 +15,7 @@ module Mongoid #:nodoc
15
15
  include Observable
16
16
  include Validatable
17
17
  extend Finders
18
- extend NamedScopes
18
+ extend NamedScope
19
19
  end
20
20
  end
21
21
  end
@@ -103,7 +103,7 @@ module Mongoid #:nodoc:
103
103
  @count ||= @klass.collection.find(@selector, process_options).count
104
104
  end
105
105
 
106
- # Translate the supplied argument hash
106
+ # Merges the supplied argument hash into a single criteria
107
107
  #
108
108
  # Options:
109
109
  #
@@ -111,10 +111,10 @@ module Mongoid #:nodoc:
111
111
  #
112
112
  # Example:
113
113
  #
114
- # <tt>criteria.translate(:where => { :field => "value"}, :limit => 20)</tt>
114
+ # <tt>criteria.fuse(:where => { :field => "value"}, :limit => 20)</tt>
115
115
  #
116
116
  # Returns <tt>self</tt>
117
- def criteria(criteria_conditions = {})
117
+ def fuse(criteria_conditions = {})
118
118
  criteria_conditions.inject(self) do |criteria, (key, value)|
119
119
  criteria.send(key, value)
120
120
  end
@@ -453,6 +453,12 @@ module Mongoid #:nodoc:
453
453
  (@options[:limit] || 20).to_i
454
454
  end
455
455
 
456
+ # Returns the selector and options as a +Hash+ that would be passed to a
457
+ # scope for use with named scopes.
458
+ def scoped
459
+ { :where => @selector }.merge(@options)
460
+ end
461
+
456
462
  # Adds a criterion to the +Criteria+ that specifies how many results to skip
457
463
  # when returning Documents. This is mostly used in conjunction with
458
464
  # <tt>limit()</tt> to handle paginated results, and is similar to the
@@ -11,9 +11,11 @@ require "mongoid/extensions/hash/accessors"
11
11
  require "mongoid/extensions/hash/assimilation"
12
12
  require "mongoid/extensions/hash/conversions"
13
13
  require "mongoid/extensions/hash/criteria_helpers"
14
+ require "mongoid/extensions/hash/scoping"
14
15
  require "mongoid/extensions/integer/conversions"
15
16
  require "mongoid/extensions/nil/assimilation"
16
17
  require "mongoid/extensions/object/conversions"
18
+ require "mongoid/extensions/proc/scoping"
17
19
  require "mongoid/extensions/string/conversions"
18
20
  require "mongoid/extensions/string/inflections"
19
21
  require "mongoid/extensions/symbol/inflections"
@@ -46,6 +48,7 @@ class Hash #:nodoc
46
48
  include Mongoid::Extensions::Hash::Accessors
47
49
  include Mongoid::Extensions::Hash::Assimilation
48
50
  include Mongoid::Extensions::Hash::CriteriaHelpers
51
+ include Mongoid::Extensions::Hash::Scoping
49
52
  extend Mongoid::Extensions::Hash::Conversions
50
53
  end
51
54
 
@@ -61,6 +64,10 @@ class Object #:nodoc:
61
64
  include Mongoid::Extensions::Object::Conversions
62
65
  end
63
66
 
67
+ class Proc #:nodoc:
68
+ include Mongoid::Extensions::Proc::Scoping
69
+ end
70
+
64
71
  class String #:nodoc
65
72
  include Mongoid::Extensions::String::Inflections
66
73
  extend Mongoid::Extensions::String::Conversions
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Hash #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ self
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Proc #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ call(*args).scoped
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module NamedScope
4
+ # Creates a named_scope for the +Document+, similar to ActiveRecord's
5
+ # named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
6
+ # chained.
7
+ #
8
+ # Example:
9
+ #
10
+ # class Person
11
+ # include Mongoid::Document
12
+ # field :active, :type => Boolean
13
+ # field :count, :type => Integer
14
+ #
15
+ # named_scope :active, :where => { :active => true }
16
+ # named_scope :count_gt_one, :where => { :count.gt => 1 }
17
+ # named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
18
+ # end
19
+ def named_scope(name, options = {}, &block)
20
+ name = name.to_sym
21
+ scopes[name] = lambda do |parent, *args|
22
+ Scope.new(parent, options.scoped(*args), &block)
23
+ end
24
+ (class << self; self; end).class_eval <<-EOT
25
+ def #{name}(*args)
26
+ scopes[:#{name}].call(self, *args)
27
+ end
28
+ EOT
29
+ end
30
+
31
+ # Return the scopes or default to an empty +Hash+.
32
+ def scopes
33
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
34
+ end
35
+
36
+ end
37
+ end
38
+
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Scope #:nodoc:
4
+
5
+ delegate :scopes, :to => "@parent"
6
+
7
+ # Create the new +Scope+. If a block is passed in, this Scope will extend
8
+ # the block.
9
+ #
10
+ # Options:
11
+ #
12
+ # parent: The class the scope belongs to, or a parent +Scope+.
13
+ # conditions: A +Hash+ of conditions.
14
+ #
15
+ # Example:
16
+ #
17
+ # Mongoid::Scope.new(Person, { :title => "Sir" }) do
18
+ # def knighted?
19
+ # title == "Sir"
20
+ # end
21
+ # end
22
+ def initialize(parent, conditions, &block)
23
+ @parent, @conditions = parent, conditions
24
+ extend Module.new(&block) if block_given?
25
+ end
26
+
27
+ # Return the class for the +Scope+. This will be the parent if the parent
28
+ # is a class, otherwise will be nil.
29
+ def klass
30
+ @klass ||= @parent unless @parent.is_a?(Scope)
31
+ end
32
+
33
+ # Chaining is supported through method_missing. If a scope is already
34
+ # defined with the method name the call will be passed there, otherwise it
35
+ # will be passed to the target or parent.
36
+ def method_missing(name, *args, &block)
37
+ if scopes.include?(name)
38
+ scopes[name].call(self, *args)
39
+ elsif klass
40
+ target.send(name, *args, &block)
41
+ else
42
+ @parent.fuse(@conditions); @parent.send(name, *args, &block)
43
+ end
44
+ end
45
+
46
+ # The +Scope+ must respond like a +Criteria+ object. If this is a parent
47
+ # criteria delegate to the target, otherwise bubble up to the parent.
48
+ def respond_to?(name)
49
+ super || (klass ? target.respond_to?(name) : @parent.respond_to?(name))
50
+ end
51
+
52
+ # Returns the target criteria if it has already been set or creates a new
53
+ # criteria from the parent class.
54
+ def target
55
+ @target ||= klass.criteria.fuse(@conditions)
56
+ end
57
+ end
58
+ end
59
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongoid}
8
- s.version = "1.0.1"
8
+ s.version = "1.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Durran Jordan"]
12
- s.date = %q{2010-01-10}
12
+ s.date = %q{2010-01-11}
13
13
  s.email = %q{durran@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.rdoc"
@@ -61,19 +61,22 @@ Gem::Specification.new do |s|
61
61
  "lib/mongoid/extensions/hash/assimilation.rb",
62
62
  "lib/mongoid/extensions/hash/conversions.rb",
63
63
  "lib/mongoid/extensions/hash/criteria_helpers.rb",
64
+ "lib/mongoid/extensions/hash/scoping.rb",
64
65
  "lib/mongoid/extensions/integer/conversions.rb",
65
66
  "lib/mongoid/extensions/nil/assimilation.rb",
66
67
  "lib/mongoid/extensions/object/conversions.rb",
68
+ "lib/mongoid/extensions/proc/scoping.rb",
67
69
  "lib/mongoid/extensions/string/conversions.rb",
68
70
  "lib/mongoid/extensions/string/inflections.rb",
69
71
  "lib/mongoid/extensions/symbol/inflections.rb",
70
72
  "lib/mongoid/extensions/time/conversions.rb",
73
+ "lib/mongoid/field.rb",
71
74
  "lib/mongoid/fields.rb",
72
- "lib/mongoid/fields/field.rb",
73
75
  "lib/mongoid/finders.rb",
74
76
  "lib/mongoid/indexes.rb",
75
77
  "lib/mongoid/memoization.rb",
76
- "lib/mongoid/named_scopes.rb",
78
+ "lib/mongoid/named_scope.rb",
79
+ "lib/mongoid/scope.rb",
77
80
  "lib/mongoid/timestamps.rb",
78
81
  "lib/mongoid/versioning.rb",
79
82
  "mongoid.gemspec",
@@ -84,7 +87,7 @@ Gem::Specification.new do |s|
84
87
  "spec/integration/mongoid/document_spec.rb",
85
88
  "spec/integration/mongoid/finders_spec.rb",
86
89
  "spec/integration/mongoid/inheritance_spec.rb",
87
- "spec/integration/mongoid/named_scopes_spec.rb",
90
+ "spec/integration/mongoid/named_scope_spec.rb",
88
91
  "spec/spec.opts",
89
92
  "spec/spec_helper.rb",
90
93
  "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
@@ -120,9 +123,11 @@ Gem::Specification.new do |s|
120
123
  "spec/unit/mongoid/extensions/hash/assimilation_spec.rb",
121
124
  "spec/unit/mongoid/extensions/hash/conversions_spec.rb",
122
125
  "spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb",
126
+ "spec/unit/mongoid/extensions/hash/scoping_spec.rb",
123
127
  "spec/unit/mongoid/extensions/integer/conversions_spec.rb",
124
128
  "spec/unit/mongoid/extensions/nil/assimilation_spec.rb",
125
129
  "spec/unit/mongoid/extensions/object/conversions_spec.rb",
130
+ "spec/unit/mongoid/extensions/proc/scoping_spec.rb",
126
131
  "spec/unit/mongoid/extensions/string/conversions_spec.rb",
127
132
  "spec/unit/mongoid/extensions/string/inflections_spec.rb",
128
133
  "spec/unit/mongoid/extensions/symbol/inflections_spec.rb",
@@ -132,7 +137,8 @@ Gem::Specification.new do |s|
132
137
  "spec/unit/mongoid/finders_spec.rb",
133
138
  "spec/unit/mongoid/indexes_spec.rb",
134
139
  "spec/unit/mongoid/memoization_spec.rb",
135
- "spec/unit/mongoid/named_scopes_spec.rb",
140
+ "spec/unit/mongoid/named_scope_spec.rb",
141
+ "spec/unit/mongoid/scope_spec.rb",
136
142
  "spec/unit/mongoid/timestamps_spec.rb",
137
143
  "spec/unit/mongoid/versioning_spec.rb",
138
144
  "spec/unit/mongoid_spec.rb"
@@ -149,7 +155,7 @@ Gem::Specification.new do |s|
149
155
  "spec/integration/mongoid/document_spec.rb",
150
156
  "spec/integration/mongoid/finders_spec.rb",
151
157
  "spec/integration/mongoid/inheritance_spec.rb",
152
- "spec/integration/mongoid/named_scopes_spec.rb",
158
+ "spec/integration/mongoid/named_scope_spec.rb",
153
159
  "spec/spec_helper.rb",
154
160
  "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
155
161
  "spec/unit/mongoid/associations/belongs_to_spec.rb",
@@ -184,9 +190,11 @@ Gem::Specification.new do |s|
184
190
  "spec/unit/mongoid/extensions/hash/assimilation_spec.rb",
185
191
  "spec/unit/mongoid/extensions/hash/conversions_spec.rb",
186
192
  "spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb",
193
+ "spec/unit/mongoid/extensions/hash/scoping_spec.rb",
187
194
  "spec/unit/mongoid/extensions/integer/conversions_spec.rb",
188
195
  "spec/unit/mongoid/extensions/nil/assimilation_spec.rb",
189
196
  "spec/unit/mongoid/extensions/object/conversions_spec.rb",
197
+ "spec/unit/mongoid/extensions/proc/scoping_spec.rb",
190
198
  "spec/unit/mongoid/extensions/string/conversions_spec.rb",
191
199
  "spec/unit/mongoid/extensions/string/inflections_spec.rb",
192
200
  "spec/unit/mongoid/extensions/symbol/inflections_spec.rb",
@@ -196,7 +204,8 @@ Gem::Specification.new do |s|
196
204
  "spec/unit/mongoid/finders_spec.rb",
197
205
  "spec/unit/mongoid/indexes_spec.rb",
198
206
  "spec/unit/mongoid/memoization_spec.rb",
199
- "spec/unit/mongoid/named_scopes_spec.rb",
207
+ "spec/unit/mongoid/named_scope_spec.rb",
208
+ "spec/unit/mongoid/scope_spec.rb",
200
209
  "spec/unit/mongoid/timestamps_spec.rb",
201
210
  "spec/unit/mongoid/versioning_spec.rb",
202
211
  "spec/unit/mongoid_spec.rb"
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::NamedScope do
4
+
5
+ describe ".named_scope" do
6
+
7
+ class Person
8
+ named_scope :doctors, {:where => {:title => 'Dr.'} }
9
+ named_scope :old, criteria.where(:age.gt => 50)
10
+ end
11
+
12
+ before do
13
+ @document = Person.create(:title => "Dr.", :age => 65, :terms => true)
14
+ end
15
+
16
+ after do
17
+ Person.delete_all
18
+ end
19
+
20
+ context "accessing a single named scope" do
21
+
22
+ it "returns the document" do
23
+ Person.doctors.first.should == @document
24
+ end
25
+
26
+ end
27
+
28
+ context "chaining named scopes" do
29
+
30
+ it "returns the document" do
31
+ Person.old.doctors.first.should == @document
32
+ end
33
+
34
+ end
35
+
36
+ context "mixing named scopes and class methods" do
37
+
38
+ it "returns the document" do
39
+ Person.accepted.old.doctors.first.should == @document
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -5,8 +5,8 @@ describe Mongoid::Commands::Save do
5
5
  describe "#execute" do
6
6
 
7
7
  before do
8
- @parent_collection = stub(:save)
9
- @doc_collection = stub(:save)
8
+ @parent_collection = stub(:save => true)
9
+ @doc_collection = stub(:save => true)
10
10
  @parent = stub(:collection => @parent_collection,
11
11
  :valid? => true,
12
12
  :run_callbacks => true,
@@ -860,6 +860,19 @@ describe Mongoid::Criteria do
860
860
 
861
861
  end
862
862
 
863
+ describe "#scoped" do
864
+
865
+ before do
866
+ @criteria = Person.where(:title => "Sir").skip(20)
867
+ end
868
+
869
+ it "returns the selector plus the options" do
870
+ @criteria.scoped.should ==
871
+ { :where => { :title => "Sir", :_type=>{ "$in" => [ "Doctor", "Person" ] } }, :skip => 20 }
872
+ end
873
+
874
+ end
875
+
863
876
  describe "#skip" do
864
877
 
865
878
  context "when value provided" do
@@ -1129,15 +1142,15 @@ describe Mongoid::Criteria do
1129
1142
 
1130
1143
  end
1131
1144
 
1132
- context "#criteria" do
1145
+ context "#fuse" do
1133
1146
 
1134
1147
  it ":where => {:title => 'Test'} returns a criteria with the correct selector" do
1135
- @result = @criteria.criteria(:where => { :title => 'Test' })
1148
+ @result = @criteria.fuse(:where => { :title => 'Test' })
1136
1149
  @result.selector[:title].should == 'Test'
1137
1150
  end
1138
1151
 
1139
1152
  it ":where => {:title => 'Test'}, :skip => 10 returns a criteria with the correct selector and options" do
1140
- @result = @criteria.criteria(:where => { :title => 'Test' }, :skip => 10)
1153
+ @result = @criteria.fuse(:where => { :title => 'Test' }, :skip => 10)
1141
1154
  @result.selector[:title].should == 'Test'
1142
1155
  @result.options.should == { :skip => 10 }
1143
1156
  end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Extensions::Hash::Scoping do
4
+
5
+ describe "#scoped" do
6
+
7
+ it "returns self" do
8
+ { :where => { :active => true } }.scoped.should ==
9
+ { :where => { :active => true } }
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Extensions::Proc::Scoping do
4
+
5
+ describe "#scoped" do
6
+
7
+ context "when the proc accessed a hash" do
8
+
9
+ before do
10
+ @proc = lambda { |number| { :where => { :count => number } } }
11
+ end
12
+
13
+ it "calls the hash with the args" do
14
+ @proc.scoped(10).should == { :where => { :count => 10 } }
15
+ end
16
+
17
+ end
18
+
19
+ context "when the proc calls a criteria" do
20
+
21
+ before do
22
+ @proc = lambda { |title| Person.where(:title => title) }
23
+ end
24
+
25
+ it "returns the criteria scoped" do
26
+ @proc.scoped("Sir").should ==
27
+ { :where => { :_type => { "$in" => [ "Doctor", "Person" ] }, :title => "Sir" } }
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::NamedScope do
4
+
5
+ class Player
6
+ include Mongoid::Document
7
+ field :active, :type => Boolean
8
+ field :frags, :type => Integer
9
+ field :deaths, :type => Integer
10
+ field :status
11
+
12
+ named_scope :active, criteria.where(:active => true) do
13
+ def extension
14
+ "extension"
15
+ end
16
+ end
17
+ named_scope :inactive, :where => { :active => false }
18
+ named_scope :frags_over, lambda { |count| { :where => { :frags.gt => count } } }
19
+ named_scope :deaths_under, lambda { |count| criteria.where(:deaths.lt => count) }
20
+
21
+ class << self
22
+ def alive
23
+ criteria.where(:status => "Alive")
24
+ end
25
+ end
26
+ end
27
+
28
+ describe ".named_scope" do
29
+
30
+ it "adds a class method for the scope" do
31
+ Player.should respond_to(:active)
32
+ end
33
+
34
+ it "adds the scope to the scopes" do
35
+ Player.scopes.should include(:active)
36
+ end
37
+
38
+ context "when options are a hash" do
39
+
40
+ it "adds the selector to the scope" do
41
+ Player.inactive.selector[:active].should be_false
42
+ end
43
+
44
+ end
45
+
46
+ context "when options are a criteria" do
47
+
48
+ it "adds the selector to the scope" do
49
+ Player.active.selector[:active].should be_true
50
+ end
51
+
52
+ end
53
+
54
+ context "when options are a proc" do
55
+
56
+ context "when the proc delegates to a hash" do
57
+
58
+ it "adds the selector to the scope" do
59
+ Player.frags_over(50).selector[:frags].should == { "$gt" => 50 }
60
+ end
61
+
62
+ end
63
+
64
+ context "when the proc delegates to a criteria" do
65
+
66
+ it "adds the selector to the scope" do
67
+ Player.deaths_under(40).selector[:deaths].should == { "$lt" => 40 }
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ context "when a block is supplied" do
75
+
76
+ it "adds the block as an extension" do
77
+ Player.active.extension.should == "extension"
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ context "chained scopes" do
85
+
86
+ context "when chaining two named scopes" do
87
+
88
+ it "merges the criteria" do
89
+ selector = Player.active.frags_over(10).selector
90
+ selector[:active].should be_true
91
+ selector[:frags].should == { "$gt" => 10 }
92
+ end
93
+
94
+ end
95
+
96
+ context "when chaining named scoped with criteria class methods" do
97
+
98
+ it "merges the criteria" do
99
+ selector = Player.active.frags_over(10).alive.selector
100
+ selector[:active].should be_true
101
+ selector[:frags].should == { "$gt" => 10 }
102
+ selector[:status].should == "Alive"
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
@@ -0,0 +1,166 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Scope do
4
+
5
+ describe ".initialize" do
6
+
7
+ before do
8
+ @parent = Mongoid::Scope.new(Person, {})
9
+ end
10
+
11
+ context "when parent is another scope" do
12
+
13
+ before do
14
+ @scope = Mongoid::Scope.new(@parent, {})
15
+ end
16
+
17
+ it "does not set the class" do
18
+ @scope.klass.should be_nil
19
+ end
20
+
21
+ end
22
+
23
+ context "when parent is a class" do
24
+
25
+ before do
26
+ @scope = Mongoid::Scope.new(Person, {})
27
+ end
28
+
29
+ it "returns the parent class" do
30
+ @scope.klass.should == Person
31
+ end
32
+
33
+ end
34
+
35
+ context "when a block is passed in" do
36
+
37
+ before do
38
+ @scope = Mongoid::Scope.new(Person, {}) do
39
+ def extended
40
+ "extended"
41
+ end
42
+ end
43
+ end
44
+
45
+ it "extends the block" do
46
+ @scope.extended.should == "extended"
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ describe "#method_missing" do
54
+
55
+ context "when a scope has been defined for the name" do
56
+
57
+ before do
58
+ @defined = mock
59
+ @class = mock
60
+ @class.expects(:scopes).twice.returns({ :testing => @defined })
61
+ @scope = Mongoid::Scope.new(@class, {})
62
+ end
63
+
64
+ it "calls the matching scope" do
65
+ @defined.expects(:call).with(@scope, "Testing").returns(true)
66
+ @scope.testing("Testing").should be_true
67
+ end
68
+
69
+ end
70
+
71
+ context "when a scope is not defined for the name" do
72
+
73
+ context "when the scope is the parent" do
74
+
75
+ before do
76
+ @target = mock
77
+ @scope = Mongoid::Scope.new(Person, {})
78
+ @scope.instance_variable_set("@target", @target)
79
+ end
80
+
81
+ it "sends the call to the target" do
82
+ @target.expects(:testing).with("Testing").returns(true)
83
+ @scope.testing("Testing").should be_true
84
+ end
85
+
86
+ end
87
+
88
+ context "when the scope is not the parent" do
89
+
90
+ before do
91
+ @parent = mock
92
+ @criteria = mock
93
+ @parent.expects(:scopes).returns({})
94
+ @parent.expects(:is_a?).with(Mongoid::Scope).returns(true)
95
+ @parent.expects(:fuse)
96
+ @scope = Mongoid::Scope.new(@parent, {})
97
+ end
98
+
99
+ it "creates a criteria from the parent scope" do
100
+ @parent.expects(:testing).returns(true)
101
+ @scope.testing.should be_true
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ describe "#respond_to?" do
111
+
112
+ context "when parent is a class" do
113
+
114
+ before do
115
+ @scope = Mongoid::Scope.new(Person, {})
116
+ end
117
+
118
+ it "delegates to the target" do
119
+ @scope.respond_to?(:only).should be_true
120
+ end
121
+
122
+ end
123
+
124
+ context "when parent is a scope" do
125
+
126
+ before do
127
+ @parent = Mongoid::Scope.new(Person, {})
128
+ @scope = Mongoid::Scope.new(@parent, {})
129
+ end
130
+
131
+ it "delegates to the parent" do
132
+ @scope.respond_to?(:only).should be_true
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ describe "#scopes" do
140
+
141
+ before do
142
+ @parent = mock
143
+ @scope = Mongoid::Scope.new(@parent, {})
144
+ end
145
+
146
+ it "delegates to the parent" do
147
+ @parent.expects(:scopes).returns({})
148
+ @scope.scopes.should == {}
149
+ end
150
+
151
+ end
152
+
153
+ describe "#target" do
154
+
155
+ before do
156
+ @scope = Mongoid::Scope.new(Person, { :where => { :title => "Sir" } })
157
+ end
158
+
159
+ it "returns the conditions criteria" do
160
+ @scope.target.selector.should ==
161
+ { :title => "Sir", :_type => { "$in" => [ "Doctor", "Person" ] } }
162
+ end
163
+
164
+ end
165
+
166
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Durran Jordan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-10 00:00:00 -05:00
12
+ date: 2010-01-11 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -127,19 +127,22 @@ files:
127
127
  - lib/mongoid/extensions/hash/assimilation.rb
128
128
  - lib/mongoid/extensions/hash/conversions.rb
129
129
  - lib/mongoid/extensions/hash/criteria_helpers.rb
130
+ - lib/mongoid/extensions/hash/scoping.rb
130
131
  - lib/mongoid/extensions/integer/conversions.rb
131
132
  - lib/mongoid/extensions/nil/assimilation.rb
132
133
  - lib/mongoid/extensions/object/conversions.rb
134
+ - lib/mongoid/extensions/proc/scoping.rb
133
135
  - lib/mongoid/extensions/string/conversions.rb
134
136
  - lib/mongoid/extensions/string/inflections.rb
135
137
  - lib/mongoid/extensions/symbol/inflections.rb
136
138
  - lib/mongoid/extensions/time/conversions.rb
139
+ - lib/mongoid/field.rb
137
140
  - lib/mongoid/fields.rb
138
- - lib/mongoid/fields/field.rb
139
141
  - lib/mongoid/finders.rb
140
142
  - lib/mongoid/indexes.rb
141
143
  - lib/mongoid/memoization.rb
142
- - lib/mongoid/named_scopes.rb
144
+ - lib/mongoid/named_scope.rb
145
+ - lib/mongoid/scope.rb
143
146
  - lib/mongoid/timestamps.rb
144
147
  - lib/mongoid/versioning.rb
145
148
  - mongoid.gemspec
@@ -150,7 +153,7 @@ files:
150
153
  - spec/integration/mongoid/document_spec.rb
151
154
  - spec/integration/mongoid/finders_spec.rb
152
155
  - spec/integration/mongoid/inheritance_spec.rb
153
- - spec/integration/mongoid/named_scopes_spec.rb
156
+ - spec/integration/mongoid/named_scope_spec.rb
154
157
  - spec/spec.opts
155
158
  - spec/spec_helper.rb
156
159
  - spec/unit/mongoid/associations/belongs_to_related_spec.rb
@@ -186,9 +189,11 @@ files:
186
189
  - spec/unit/mongoid/extensions/hash/assimilation_spec.rb
187
190
  - spec/unit/mongoid/extensions/hash/conversions_spec.rb
188
191
  - spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb
192
+ - spec/unit/mongoid/extensions/hash/scoping_spec.rb
189
193
  - spec/unit/mongoid/extensions/integer/conversions_spec.rb
190
194
  - spec/unit/mongoid/extensions/nil/assimilation_spec.rb
191
195
  - spec/unit/mongoid/extensions/object/conversions_spec.rb
196
+ - spec/unit/mongoid/extensions/proc/scoping_spec.rb
192
197
  - spec/unit/mongoid/extensions/string/conversions_spec.rb
193
198
  - spec/unit/mongoid/extensions/string/inflections_spec.rb
194
199
  - spec/unit/mongoid/extensions/symbol/inflections_spec.rb
@@ -198,7 +203,8 @@ files:
198
203
  - spec/unit/mongoid/finders_spec.rb
199
204
  - spec/unit/mongoid/indexes_spec.rb
200
205
  - spec/unit/mongoid/memoization_spec.rb
201
- - spec/unit/mongoid/named_scopes_spec.rb
206
+ - spec/unit/mongoid/named_scope_spec.rb
207
+ - spec/unit/mongoid/scope_spec.rb
202
208
  - spec/unit/mongoid/timestamps_spec.rb
203
209
  - spec/unit/mongoid/versioning_spec.rb
204
210
  - spec/unit/mongoid_spec.rb
@@ -237,7 +243,7 @@ test_files:
237
243
  - spec/integration/mongoid/document_spec.rb
238
244
  - spec/integration/mongoid/finders_spec.rb
239
245
  - spec/integration/mongoid/inheritance_spec.rb
240
- - spec/integration/mongoid/named_scopes_spec.rb
246
+ - spec/integration/mongoid/named_scope_spec.rb
241
247
  - spec/spec_helper.rb
242
248
  - spec/unit/mongoid/associations/belongs_to_related_spec.rb
243
249
  - spec/unit/mongoid/associations/belongs_to_spec.rb
@@ -272,9 +278,11 @@ test_files:
272
278
  - spec/unit/mongoid/extensions/hash/assimilation_spec.rb
273
279
  - spec/unit/mongoid/extensions/hash/conversions_spec.rb
274
280
  - spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb
281
+ - spec/unit/mongoid/extensions/hash/scoping_spec.rb
275
282
  - spec/unit/mongoid/extensions/integer/conversions_spec.rb
276
283
  - spec/unit/mongoid/extensions/nil/assimilation_spec.rb
277
284
  - spec/unit/mongoid/extensions/object/conversions_spec.rb
285
+ - spec/unit/mongoid/extensions/proc/scoping_spec.rb
278
286
  - spec/unit/mongoid/extensions/string/conversions_spec.rb
279
287
  - spec/unit/mongoid/extensions/string/inflections_spec.rb
280
288
  - spec/unit/mongoid/extensions/symbol/inflections_spec.rb
@@ -284,7 +292,8 @@ test_files:
284
292
  - spec/unit/mongoid/finders_spec.rb
285
293
  - spec/unit/mongoid/indexes_spec.rb
286
294
  - spec/unit/mongoid/memoization_spec.rb
287
- - spec/unit/mongoid/named_scopes_spec.rb
295
+ - spec/unit/mongoid/named_scope_spec.rb
296
+ - spec/unit/mongoid/scope_spec.rb
288
297
  - spec/unit/mongoid/timestamps_spec.rb
289
298
  - spec/unit/mongoid/versioning_spec.rb
290
299
  - spec/unit/mongoid_spec.rb
@@ -1,94 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module NamedScopes
4
- # Return the scopes or default to an empty +Hash+.
5
- def scopes
6
- read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
7
- end
8
-
9
- # Creates a named_scope for the +Document+, similar to ActiveRecord's
10
- # named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
11
- # chained.
12
- #
13
- # Example:
14
- #
15
- # class Person
16
- # include Mongoid::Document
17
- # field :active, :type => Boolean
18
- # field :count, :type => Integer
19
- #
20
- # named_scope :active, :where => { :active => true }
21
- # named_scope :count_gt_one, :where => { :count.gt => 1 }
22
- # named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
23
- # end
24
- def named_scope(name, options = {}, &block)
25
- name = name.to_sym
26
- scopes[name] = lambda do |parent_scope, *args|
27
- CriteriaProxy.new(parent_scope, Hash === options ? options : options.call(*args), &block)
28
- end
29
- (class << self; self; end).class_eval <<-EOT
30
- def #{name}(*args)
31
- scopes[:#{name}].call(self, *args)
32
- end
33
- EOT
34
- end
35
-
36
- class CriteriaProxy #:nodoc
37
- attr_accessor :conditions, :klass, :parent_scope
38
-
39
- delegate :scopes, :to => :parent_scope
40
-
41
- # Instantiate the new +CriteriaProxy+. If the conditions contains an
42
- # extension, the proxy will extend from that module. If a block is given
43
- # it will be extended as well.
44
- #
45
- # Example:
46
- #
47
- # <tt>CriteriaProxy.new(parent, :where => { :active => true })</tt>
48
- def initialize(parent_scope, conditions, &block)
49
- conditions ||= {}
50
- [ conditions.delete(:extend) ].flatten.each do |extension|
51
- extend extension
52
- end if conditions.include?(:extend)
53
- extend Module.new(&block) if block_given?
54
- self.klass = parent_scope unless CriteriaProxy === parent_scope
55
- self.parent_scope, self.conditions = parent_scope, conditions
56
- end
57
-
58
- # First check if the proxy has the scope defined, otherwise look to the
59
- # parent scope.
60
- def respond_to?(method, include_private = false)
61
- super || if klass
62
- proxy_found.respond_to?(method, include_private)
63
- else
64
- parent_scope.respond_to?(method, include_private)
65
- end
66
- end
67
-
68
- protected
69
-
70
- def proxy_found
71
- @found || load_found
72
- end
73
-
74
- private
75
-
76
- def method_missing(method, *args, &block)
77
- if scopes.include?(method)
78
- scopes[method].call(self, *args)
79
- elsif klass
80
- proxy_found.send(method, *args, &block)
81
- else
82
- parent_scope.criteria(conditions)
83
- parent_scope.send(method, *args, &block)
84
- end
85
- end
86
-
87
- def load_found
88
- @found = Criteria.new(klass)
89
- @found.criteria(conditions); @found
90
- end
91
- end
92
- end
93
- end
94
-
@@ -1,25 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Mongoid::NamedScopes do
4
-
5
- describe ".named_scope" do
6
-
7
- class Person
8
- named_scope :doctors, {:where => {:title => 'Dr.'}}
9
- end
10
-
11
- before do
12
- @document = Person.create(:title => "Dr.")
13
- end
14
-
15
- after do
16
- Person.delete_all
17
- end
18
-
19
- it "returns the document" do
20
- Person.doctors.first.should == @document
21
- end
22
-
23
- end
24
-
25
- end
@@ -1,86 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Mongoid::NamedScopes do
4
-
5
- context "AR2 scopes" do
6
- module Extension
7
- def extension_module_method
8
- "extension module method"
9
- end
10
- end
11
-
12
- class NamedScopeTest
13
- include Mongoid::Document
14
-
15
- field :active, :type => Boolean
16
- field :count, :type => Integer
17
-
18
- named_scope :active, :where => {:active => true} do
19
- def extension_method
20
- "extension method"
21
- end
22
- end
23
- named_scope :count_gt_one, :where => { :count.gt => 1 }, :extend => Extension
24
- named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
25
- end
26
-
27
- context ".named_scope" do
28
- it "adds the named scope to the hash of scopes" do
29
- NamedScopeTest.scopes.should have_key(:active)
30
- end
31
-
32
- it "creates a class method for the named scope" do
33
- NamedScopeTest.should respond_to(:active)
34
- end
35
- end
36
-
37
- context "accessing a named scope" do
38
- it "is a criteria proxy" do
39
- Mongoid::NamedScopes::CriteriaProxy.should === NamedScopeTest.active
40
- end
41
-
42
- it "responds like a criteria" do
43
- NamedScopeTest.active.should respond_to(:selector)
44
- end
45
-
46
- it "instantiates the criteria" do
47
- criteria = Mongoid::Criteria.new(NamedScopeTest)
48
- Mongoid::Criteria.expects(:new).returns(criteria)
49
- NamedScopeTest.active.selector
50
- end
51
-
52
- it "has set the conditions on the criteria" do
53
- NamedScopeTest.active.selector[:active].should be_true
54
- end
55
-
56
- it "sets the association extension by block" do
57
- NamedScopeTest.active.extension_method.should == "extension method"
58
- end
59
-
60
- it "sets the association extension by :extend" do
61
- NamedScopeTest.count_gt_one.extension_module_method.should == "extension module method"
62
- end
63
-
64
- context "when using a lambda" do
65
- it "accepts parameters to the criteria" do
66
- NamedScopeTest.at_least_count(3).selector[:count].should == {'$gt' => 3}
67
- end
68
- end
69
- end
70
-
71
- context "chained scopes" do
72
- it "instantiates the criteria" do
73
- criteria = Mongoid::Criteria.new(NamedScopeTest)
74
- Mongoid::Criteria.expects(:new).returns(criteria)
75
- NamedScopeTest.active.count_gt_one.selector
76
- end
77
-
78
- it "merges the criteria" do
79
- selector = NamedScopeTest.active.count_gt_one.selector
80
- selector[:count].should == {'$gt' => 1}
81
- selector[:active].should be_true
82
- end
83
- end
84
- end
85
- end
86
-