mongoid 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
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