mongoid 1.1.3 → 1.1.4

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.
Files changed (60) hide show
  1. data/.watchr +20 -2
  2. data/VERSION +1 -1
  3. data/caliper.yml +4 -0
  4. data/lib/mongoid.rb +0 -1
  5. data/lib/mongoid/associations.rb +25 -13
  6. data/lib/mongoid/associations/belongs_to.rb +15 -17
  7. data/lib/mongoid/associations/belongs_to_related.rb +12 -14
  8. data/lib/mongoid/associations/has_many.rb +53 -35
  9. data/lib/mongoid/associations/has_many_related.rb +10 -15
  10. data/lib/mongoid/associations/has_one.rb +31 -30
  11. data/lib/mongoid/associations/has_one_related.rb +18 -20
  12. data/lib/mongoid/associations/options.rb +10 -0
  13. data/lib/mongoid/associations/proxy.rb +18 -1
  14. data/lib/mongoid/criteria.rb +9 -233
  15. data/lib/mongoid/criterion/complex.rb +21 -0
  16. data/lib/mongoid/criterion/exclusion.rb +63 -0
  17. data/lib/mongoid/criterion/inclusion.rb +91 -0
  18. data/lib/mongoid/criterion/optional.rb +96 -0
  19. data/lib/mongoid/document.rb +2 -2
  20. data/lib/mongoid/extensions/hash/accessors.rb +6 -0
  21. data/lib/mongoid/extensions/hash/criteria_helpers.rb +2 -2
  22. data/lib/mongoid/extensions/symbol/inflections.rb +1 -1
  23. data/mongoid.gemspec +53 -3
  24. data/spec/integration/mongoid/associations_spec.rb +41 -0
  25. data/spec/models/address.rb +39 -0
  26. data/spec/models/animal.rb +6 -0
  27. data/spec/models/comment.rb +8 -0
  28. data/spec/models/country_code.rb +6 -0
  29. data/spec/models/employer.rb +5 -0
  30. data/spec/models/game.rb +6 -0
  31. data/spec/models/inheritance.rb +56 -0
  32. data/spec/models/location.rb +5 -0
  33. data/spec/models/mixed_drink.rb +4 -0
  34. data/spec/models/name.rb +13 -0
  35. data/spec/models/namespacing.rb +11 -0
  36. data/spec/models/patient.rb +4 -0
  37. data/spec/models/person.rb +97 -0
  38. data/spec/models/pet.rb +7 -0
  39. data/spec/models/pet_owner.rb +6 -0
  40. data/spec/models/phone.rb +7 -0
  41. data/spec/models/post.rb +15 -0
  42. data/spec/models/translation.rb +5 -0
  43. data/spec/models/vet_visit.rb +5 -0
  44. data/spec/spec_helper.rb +9 -326
  45. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +26 -5
  46. data/spec/unit/mongoid/associations/belongs_to_spec.rb +96 -30
  47. data/spec/unit/mongoid/associations/has_many_related_spec.rb +32 -12
  48. data/spec/unit/mongoid/associations/has_many_spec.rb +48 -12
  49. data/spec/unit/mongoid/associations/has_one_related_spec.rb +29 -4
  50. data/spec/unit/mongoid/associations/has_one_spec.rb +46 -1
  51. data/spec/unit/mongoid/associations/options_spec.rb +58 -0
  52. data/spec/unit/mongoid/associations_spec.rb +58 -1
  53. data/spec/unit/mongoid/criteria_spec.rb +71 -735
  54. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  55. data/spec/unit/mongoid/criterion/exclusion_spec.rb +75 -0
  56. data/spec/unit/mongoid/criterion/inclusion_spec.rb +213 -0
  57. data/spec/unit/mongoid/criterion/optional_spec.rb +244 -0
  58. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +20 -0
  59. metadata +53 -3
  60. data/lib/mongoid/complex_criterion.rb +0 -10
data/.watchr CHANGED
@@ -1,3 +1,14 @@
1
+ # Watchr is the preferred method to run specs automatically over rspactor for
2
+ # Mongoid. If you are using vim, you can add the file:
3
+ #
4
+ # ~/.vim/ftdetect/watchr.vim
5
+ #
6
+ # This should have only the following line in it:
7
+ #
8
+ # autocmd BufNewFile,BufRead *.watchr setf ruby
9
+ #
10
+ # This will enable vim to recognize this file as ruby code should you wish to
11
+ # edit it.
1
12
  def run(cmd)
2
13
  puts cmd
3
14
  system cmd
@@ -7,5 +18,12 @@ def spec(file)
7
18
  run "spec -O spec/spec.opts #{file}"
8
19
  end
9
20
 
10
- watch("spec/.*/*_spec\.rb") {|md| p md[0]; spec(md[0]) }
11
- watch('lib/(.*/.*)\.rb') {|md| p md[1]; spec("spec/unit/#{md[1]}_spec.rb") }
21
+ watch("spec/.*/*_spec\.rb") do |match|
22
+ p match[0]
23
+ spec(match[0])
24
+ end
25
+
26
+ watch('lib/(.*/.*)\.rb') do |match|
27
+ p match[1]
28
+ spec("spec/unit/#{match[1]}_spec.rb")
29
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.3
1
+ 1.1.4
@@ -0,0 +1,4 @@
1
+ test_directories_to_calculate:
2
+ - spec/unit
3
+ - spec/integration
4
+ - spec/integration/mongoid
@@ -44,7 +44,6 @@ require "mongoid/callbacks"
44
44
  require "mongoid/commands"
45
45
  require "mongoid/config"
46
46
  require "mongoid/contexts"
47
- require "mongoid/complex_criterion"
48
47
  require "mongoid/criteria"
49
48
  require "mongoid/extensions"
50
49
  require "mongoid/errors"
@@ -59,14 +59,16 @@ module Mongoid # :nodoc:
59
59
  # include Mongoid::Document
60
60
  # belongs_to :person, :inverse_of => :addresses
61
61
  # end
62
- def belongs_to(name, options = {})
62
+ def belongs_to(name, options = {}, &block)
63
63
  unless options.has_key?(:inverse_of)
64
64
  raise Errors::InvalidOptions.new("Options for belongs_to association must include :inverse_of")
65
65
  end
66
66
  self.embedded = true
67
67
  add_association(
68
68
  Associations::BelongsTo,
69
- Associations::Options.new(options.merge(:name => name))
69
+ Associations::Options.new(
70
+ options.merge(:name => name, :extend => block)
71
+ )
70
72
  )
71
73
  end
72
74
 
@@ -84,11 +86,13 @@ module Mongoid # :nodoc:
84
86
  # belongs_to_related :person
85
87
  # end
86
88
  #
87
- def belongs_to_related(name, options = {})
89
+ def belongs_to_related(name, options = {}, &block)
88
90
  field "#{name.to_s}_id"
89
91
  add_association(
90
92
  Associations::BelongsToRelated,
91
- Associations::Options.new(options.merge(:name => name))
93
+ Associations::Options.new(
94
+ options.merge(:name => name, :extend => block)
95
+ )
92
96
  )
93
97
  end
94
98
 
@@ -111,10 +115,12 @@ module Mongoid # :nodoc:
111
115
  # include Mongoid::Document
112
116
  # belongs_to :person, :inverse_of => :addresses
113
117
  # end
114
- def has_many(name, options = {})
118
+ def has_many(name, options = {}, &block)
115
119
  add_association(
116
120
  Associations::HasMany,
117
- Associations::Options.new(options.merge(:name => name))
121
+ Associations::Options.new(
122
+ options.merge(:name => name, :extend => block)
123
+ )
118
124
  )
119
125
  end
120
126
 
@@ -132,10 +138,12 @@ module Mongoid # :nodoc:
132
138
  # has_many_related :posts
133
139
  # end
134
140
  #
135
- def has_many_related(name, options = {})
141
+ def has_many_related(name, options = {}, &block)
136
142
  add_association(
137
143
  Associations::HasManyRelated,
138
- Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
144
+ Associations::Options.new(
145
+ options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
146
+ )
139
147
  )
140
148
  before_save do |document|
141
149
  document.update_associations(name)
@@ -161,8 +169,10 @@ module Mongoid # :nodoc:
161
169
  # include Mongoid::Document
162
170
  # belongs_to :person
163
171
  # end
164
- def has_one(name, options = {})
165
- opts = Associations::Options.new(options.merge(:name => name))
172
+ def has_one(name, options = {}, &block)
173
+ opts = Associations::Options.new(
174
+ options.merge(:name => name, :extend => block)
175
+ )
166
176
  type = Associations::HasOne
167
177
  add_association(type, opts)
168
178
  add_builder(type, opts)
@@ -182,10 +192,12 @@ module Mongoid # :nodoc:
182
192
  # include Mongoid::Document
183
193
  # has_one_related :game
184
194
  # end
185
- def has_one_related(name, options = {})
195
+ def has_one_related(name, options = {}, &block)
186
196
  add_association(
187
197
  Associations::HasOneRelated,
188
- Associations::Options.new(options.merge(:name => name, :parent_key => self.name.foreign_key))
198
+ Associations::Options.new(
199
+ options.merge(:name => name, :parent_key => self.name.foreign_key, :extend => block)
200
+ )
189
201
  )
190
202
  before_save do |document|
191
203
  document.update_association(name)
@@ -227,7 +239,7 @@ module Mongoid # :nodoc:
227
239
  def add_builder(type, options)
228
240
  name = options.name.to_s
229
241
  define_method("build_#{name}") do |attrs|
230
- reset(name) { type.new(self, attrs, options) }
242
+ reset(name) { type.new(self, attrs.stringify_keys, options) }
231
243
  end
232
244
  end
233
245
 
@@ -4,10 +4,8 @@ module Mongoid #:nodoc:
4
4
  class BelongsTo #:nodoc:
5
5
  include Proxy
6
6
 
7
- attr_reader :document, :options
8
-
9
7
  # Creates the new association by setting the internal
10
- # document as the passed in Document. This should be the
8
+ # target as the passed in Document. This should be the
11
9
  # parent.
12
10
  #
13
11
  # All method calls on this object will then be delegated
@@ -15,22 +13,18 @@ module Mongoid #:nodoc:
15
13
  #
16
14
  # Options:
17
15
  #
18
- # document: The parent +Document+
16
+ # target: The parent +Document+
19
17
  # options: The association options
20
- def initialize(document, options)
21
- @document, @options = document, options
18
+ def initialize(target, options)
19
+ @target, @options = target, options
20
+ extends(options)
22
21
  end
23
22
 
24
23
  # Returns the parent document. The id param is present for
25
24
  # compatibility with rails, however this could be overwritten
26
25
  # in the future.
27
26
  def find(id)
28
- @document
29
- end
30
-
31
- # Delegate all missing methods over to the parent +Document+.
32
- def method_missing(name, *args, &block)
33
- @document.send(name, *args, &block)
27
+ @target
34
28
  end
35
29
 
36
30
  class << self
@@ -43,8 +37,8 @@ module Mongoid #:nodoc:
43
37
  # document: The parent +Document+
44
38
  # options: The association options
45
39
  def instantiate(document, options)
46
- parent = document._parent
47
- parent.nil? ? nil : new(parent, options)
40
+ target = document._parent
41
+ target.nil? ? nil : new(target, options)
48
42
  end
49
43
 
50
44
  # Returns the macro used to create the association.
@@ -55,10 +49,14 @@ module Mongoid #:nodoc:
55
49
  # Perform an update of the relationship of the parent and child. This
56
50
  # is initialized by setting a parent object as the association on the
57
51
  # +Document+. Will properly set a has_one or a has_many.
58
- def update(parent, child, options)
59
- child.parentize(parent, options.inverse_of)
52
+ #
53
+ # Returns:
54
+ #
55
+ # A new +BelongsTo+ association proxy.
56
+ def update(target, child, options)
57
+ child.parentize(target, options.inverse_of)
60
58
  child.notify
61
- parent
59
+ instantiate(child, options)
62
60
  end
63
61
  end
64
62
  end
@@ -4,8 +4,6 @@ module Mongoid #:nodoc:
4
4
  class BelongsToRelated #:nodoc:
5
5
  include Proxy
6
6
 
7
- attr_reader :document, :options
8
-
9
7
  # Initializing a related association only requires looking up the object
10
8
  # by its id.
11
9
  #
@@ -13,13 +11,10 @@ module Mongoid #:nodoc:
13
11
  #
14
12
  # document: The +Document+ that contains the relationship.
15
13
  # options: The association +Options+.
16
- def initialize(document, foreign_key, options)
17
- @document = options.klass.find(foreign_key)
18
- end
19
-
20
- # Delegate all missing methods over to the +Document+.
21
- def method_missing(name, *args)
22
- @document.send(name, *args)
14
+ def initialize(document, foreign_key, options, target = nil)
15
+ @options = options
16
+ @target = target || options.klass.find(foreign_key)
17
+ extends(options)
23
18
  end
24
19
 
25
20
  class << self
@@ -31,9 +26,9 @@ module Mongoid #:nodoc:
31
26
  #
32
27
  # document: The +Document+ that contains the relationship.
33
28
  # options: The association +Options+.
34
- def instantiate(document, options)
29
+ def instantiate(document, options, target = nil)
35
30
  foreign_key = document.send(options.foreign_key)
36
- foreign_key.blank? ? nil : new(document, foreign_key, options)
31
+ foreign_key.blank? ? nil : new(document, foreign_key, options, target)
37
32
  end
38
33
 
39
34
  # Returns the macro used to create the association.
@@ -53,9 +48,12 @@ module Mongoid #:nodoc:
53
48
  # Example:
54
49
  #
55
50
  # <tt>BelongsToRelated.update(game, person, options)</tt>
56
- def update(related, parent, options)
57
- parent.send("#{options.foreign_key}=", related.id) if related
58
- related
51
+ def update(target, parent, options)
52
+ if target
53
+ parent.send("#{options.foreign_key}=", target.id)
54
+ return instantiate(parent, options, target)
55
+ end
56
+ target
59
57
  end
60
58
  end
61
59
 
@@ -4,44 +4,43 @@ module Mongoid #:nodoc:
4
4
  class HasMany
5
5
  include Proxy
6
6
 
7
- attr_accessor :association_name, :klass, :options
7
+ attr_accessor :association_name, :klass
8
8
 
9
9
  # Appends the object to the +Array+, setting its parent in
10
10
  # the process.
11
11
  def <<(*objects)
12
12
  objects.flatten.each do |object|
13
13
  object.parentize(@parent, @association_name)
14
- @documents << object
14
+ @target << object
15
15
  object.notify
16
16
  end
17
17
  end
18
18
 
19
+ alias :concat :<<
20
+ alias :push :<<
21
+
19
22
  # Clears the association, and notifies the parents of the removal.
20
23
  def clear
21
- unless @documents.empty?
22
- object = @documents.first
24
+ unless @target.empty?
25
+ object = @target.first
23
26
  object.changed(true)
24
27
  object.notify_observers(object, true)
25
- @documents.clear
28
+ @target.clear
26
29
  end
27
30
  end
28
31
 
29
- # Appends the object to the +Array+, setting its parent in
30
- # the process.
31
- def concat(*objects)
32
- self << objects
33
- end
34
-
35
32
  # Builds a new Document and adds it to the association collection. The
36
33
  # document created will be of the same class as the others in the
37
34
  # association, and the attributes will be passed into the constructor.
38
35
  #
39
- # Returns the newly created object.
36
+ # Returns:
37
+ #
38
+ # The newly created Document.
40
39
  def build(attrs = {}, type = nil)
41
40
  object = type ? type.instantiate : @klass.instantiate
42
41
  object.parentize(@parent, @association_name)
43
42
  object.write_attributes(attrs)
44
- @documents << object
43
+ @target << object
45
44
  object
46
45
  end
47
46
 
@@ -50,7 +49,9 @@ module Mongoid #:nodoc:
50
49
  # association, and the attributes will be passed into the constructor and
51
50
  # the new object will then be saved.
52
51
  #
53
- # Returns the newly created object.
52
+ # Returns:
53
+ #
54
+ # Rhe newly created Document.
54
55
  def create(attrs = {}, type = nil)
55
56
  object = build(attrs, type)
56
57
  object.save
@@ -58,10 +59,16 @@ module Mongoid #:nodoc:
58
59
  end
59
60
 
60
61
  # Finds a document in this association.
62
+ #
61
63
  # If :all is passed, returns all the documents
64
+ #
62
65
  # If an id is passed, will return the document for that id.
66
+ #
67
+ # Returns:
68
+ #
69
+ # Array or single Document.
63
70
  def find(param)
64
- return @documents if param == :all
71
+ return @target if param == :all
65
72
  return detect { |document| document.id == param }
66
73
  end
67
74
 
@@ -72,27 +79,29 @@ module Mongoid #:nodoc:
72
79
  #
73
80
  # This then delegated all methods to the array class since this is
74
81
  # essentially a proxy to an array itself.
75
- def initialize(document, options)
76
- @parent, @association_name, @klass, @options = document, options.name, options.klass, options
77
- attributes = document.raw_attributes[@association_name]
78
- @documents = attributes ? attributes.collect do |attrs|
79
- type = attrs["_type"]
80
- child = type ? type.constantize.instantiate(attrs) : @klass.instantiate(attrs)
81
- child.parentize(@parent, @association_name)
82
- child
83
- end : []
82
+ #
83
+ # Options:
84
+ #
85
+ # parent: The parent document to the association.
86
+ # options: The association options.
87
+ def initialize(parent, options)
88
+ @parent, @association_name = parent, options.name
89
+ @klass, @options = options.klass, options
90
+ initialize_each(parent.raw_attributes[@association_name])
91
+ extends(options)
84
92
  end
85
93
 
86
- # Delegate all missing methods over to the documents array unless a
87
- # criteria or named scope exists on the association class. If that is the
88
- # case then call that method.
94
+ # If the target array does not respond to the supplied method then try to
95
+ # find a named scope or criteria on the class and send the call there.
96
+ #
97
+ # If the method exists on the array, use the default proxy behavior.
89
98
  def method_missing(name, *args, &block)
90
- unless @documents.respond_to?(name)
99
+ unless @target.respond_to?(name)
91
100
  criteria = @klass.send(name, *args)
92
- criteria.documents = @documents
101
+ criteria.documents = @target
93
102
  return criteria
94
103
  end
95
- @documents.send(name, *args, &block)
104
+ super
96
105
  end
97
106
 
98
107
  # Used for setting associations via a nested attributes setter from the
@@ -101,16 +110,25 @@ module Mongoid #:nodoc:
101
110
  # Options:
102
111
  #
103
112
  # attributes: A +Hash+ of integer keys and +Hash+ values.
113
+ #
114
+ # Returns:
115
+ #
116
+ # The newly build target Document.
104
117
  def nested_build(attributes)
105
118
  attributes.values.each do |attrs|
106
119
  build(attrs)
107
120
  end
108
121
  end
109
122
 
110
- # Appends the object to the +Array+, setting its parent in
111
- # the process.
112
- def push(*objects)
113
- self << objects
123
+ protected
124
+ # Initializes each of the attributes in the hash.
125
+ def initialize_each(attributes)
126
+ @target = attributes ? attributes.collect do |attrs|
127
+ klass = attrs.klass
128
+ child = klass ? klass.instantiate(attrs) : @klass.instantiate(attrs)
129
+ child.parentize(@parent, @association_name)
130
+ child
131
+ end : []
114
132
  end
115
133
 
116
134
  class << self
@@ -137,7 +155,7 @@ module Mongoid #:nodoc:
137
155
  def update(children, parent, options)
138
156
  parent.remove_attribute(options.name)
139
157
  children.assimilate(parent, options)
140
- new(parent, options)
158
+ instantiate(parent, options)
141
159
  end
142
160
  end
143
161
 
@@ -4,14 +4,12 @@ module Mongoid #:nodoc:
4
4
  class HasManyRelated #:nodoc:
5
5
  include Proxy
6
6
 
7
- attr_reader :klass
8
-
9
7
  # Appends the object to the +Array+, setting its parent in
10
8
  # the process.
11
9
  def <<(*objects)
12
10
  objects.flatten.each do |object|
13
11
  object.send("#{@foreign_key}=", @parent.id)
14
- @documents << object
12
+ @target << object
15
13
  object.save unless @parent.new_record?
16
14
  end
17
15
  end
@@ -24,7 +22,7 @@ module Mongoid #:nodoc:
24
22
  def build(attributes = {})
25
23
  name = @parent.class.to_s.underscore
26
24
  object = @klass.instantiate(attributes.merge(name => @parent))
27
- @documents << object
25
+ @target << object
28
26
  object
29
27
  end
30
28
 
@@ -59,15 +57,11 @@ module Mongoid #:nodoc:
59
57
  #
60
58
  # document: The +Document+ that contains the relationship.
61
59
  # options: The association +Options+.
62
- def initialize(document, options)
60
+ def initialize(document, options, target = nil)
63
61
  @parent, @klass = document, options.klass
64
62
  @foreign_key = document.class.to_s.foreign_key
65
- @documents = @klass.all(:conditions => { @foreign_key => document.id })
66
- end
67
-
68
- # Delegate all missing methods over to the documents array.
69
- def method_missing(name, *args, &block)
70
- @documents.send(name, *args, &block)
63
+ @target = target || @klass.all(:conditions => { @foreign_key => document.id })
64
+ extends(options)
71
65
  end
72
66
 
73
67
  # Delegates to <<
@@ -82,8 +76,8 @@ module Mongoid #:nodoc:
82
76
  #
83
77
  # document: The +Document+ that contains the relationship.
84
78
  # options: The association +Options+.
85
- def instantiate(document, options)
86
- new(document, options)
79
+ def instantiate(document, options, target = nil)
80
+ new(document, options, target)
87
81
  end
88
82
 
89
83
  # Returns the macro used to create the association.
@@ -103,9 +97,10 @@ module Mongoid #:nodoc:
103
97
  # Example:
104
98
  #
105
99
  # <tt>RelatesToOne.update(game, person, options)</tt>
106
- def update(related, document, options)
100
+ def update(target, document, options)
107
101
  name = document.class.to_s.underscore
108
- related.each { |child| child.send("#{name}=", document) }
102
+ target.each { |child| child.send("#{name}=", document) }
103
+ instantiate(document, options, target)
109
104
  end
110
105
  end
111
106