mongomodel 0.2.10 → 0.2.11
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/Gemfile +1 -1
- data/lib/mongomodel.rb +1 -0
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +18 -0
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +20 -0
- data/lib/mongomodel/concerns/attribute_methods/multi_parameter_assignment.rb +42 -0
- data/lib/mongomodel/concerns/attribute_methods/protected.rb +17 -120
- data/lib/mongomodel/document/indexes.rb +17 -4
- data/lib/mongomodel/document/persistence.rb +8 -0
- data/lib/mongomodel/embedded_document.rb +1 -0
- data/lib/mongomodel/support/core_extensions.rb +1 -1
- data/lib/mongomodel/support/mongo_options.rb +2 -2
- data/lib/mongomodel/support/types/date.rb +8 -1
- data/lib/mongomodel/support/types/time.rb +8 -2
- data/lib/mongomodel/version.rb +1 -1
- data/mongomodel.gemspec +4 -4
- data/spec/mongomodel/concerns/activemodel_spec.rb +7 -20
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +52 -0
- data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +68 -0
- data/spec/mongomodel/concerns/attribute_methods/multi_parameter_assignment_spec.rb +40 -0
- data/spec/mongomodel/document/indexes_spec.rb +11 -0
- data/spec/mongomodel/document_spec.rb +8 -3
- data/spec/mongomodel/support/mongo_operator_spec.rb +1 -0
- metadata +12 -18
data/Gemfile
CHANGED
data/lib/mongomodel.rb
CHANGED
@@ -49,6 +49,7 @@ module MongoModel
|
|
49
49
|
autoload :BeforeTypeCast, 'mongomodel/concerns/attribute_methods/before_type_cast'
|
50
50
|
autoload :Protected, 'mongomodel/concerns/attribute_methods/protected'
|
51
51
|
autoload :Dirty, 'mongomodel/concerns/attribute_methods/dirty'
|
52
|
+
autoload :MultiParameterAssignment, 'mongomodel/concerns/attribute_methods/multi_parameter_assignment'
|
52
53
|
end
|
53
54
|
|
54
55
|
module Attributes
|
@@ -11,7 +11,25 @@ module MongoModel
|
|
11
11
|
|
12
12
|
def define!
|
13
13
|
raise "has_many :by => :foreign_key is only valid on Document" unless owner.ancestors.include?(Document)
|
14
|
+
|
14
15
|
super
|
16
|
+
|
17
|
+
define_dependency_callbacks!
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def define_dependency_callbacks!
|
22
|
+
association = self
|
23
|
+
|
24
|
+
if options[:dependent] == :destroy
|
25
|
+
owner.before_destroy do
|
26
|
+
send(association.name).each { |child| child.destroy }
|
27
|
+
end
|
28
|
+
elsif options[:dependent] == :delete
|
29
|
+
owner.before_destroy do
|
30
|
+
send(association.name).delete_all
|
31
|
+
end
|
32
|
+
end
|
15
33
|
end
|
16
34
|
|
17
35
|
methods do |association|
|
@@ -5,6 +5,26 @@ module MongoModel
|
|
5
5
|
:"#{singular_name}_ids"
|
6
6
|
end
|
7
7
|
|
8
|
+
def define!
|
9
|
+
super
|
10
|
+
define_dependency_callbacks!
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_dependency_callbacks!
|
15
|
+
association = self
|
16
|
+
|
17
|
+
if options[:dependent] == :destroy
|
18
|
+
owner.before_destroy do
|
19
|
+
send(association.name).each { |child| child.destroy }
|
20
|
+
end
|
21
|
+
elsif options[:dependent] == :delete
|
22
|
+
owner.before_destroy do
|
23
|
+
send(association.name).delete_all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
8
28
|
properties do |association|
|
9
29
|
property association.property_name, Collection[MongoModel::Reference], :internal => true, :default => []
|
10
30
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MongoModel
|
2
|
+
module AttributeMethods
|
3
|
+
module MultiParameterAssignment
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def attributes=(attrs)#:nodoc:
|
7
|
+
super(transform_multiparameter_attributes(attrs))
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
# Converts multiparameter attributes into array format. For example, the parameters
|
12
|
+
# { "start_date(1i)" => "2010", "start_date(2i)" => "9", "start_date(3i)" => "4" }
|
13
|
+
# will be converted to:
|
14
|
+
# { "start_date" => [2010, 9, 4] }
|
15
|
+
def transform_multiparameter_attributes(attrs)
|
16
|
+
attrs.merge(extract_multiparameter_attributes(attrs))
|
17
|
+
end
|
18
|
+
|
19
|
+
def extract_multiparameter_attributes(attrs)
|
20
|
+
multiparameter_attributes = Hash.new { |h, k| h[k] = [] }
|
21
|
+
|
22
|
+
attrs.each do |k, v|
|
23
|
+
if k.to_s =~ /(.*)\((\d+)([if])?\)/
|
24
|
+
multiparameter_attributes[$1][$2.to_i - 1] = type_cast_attribute_value($3, v)
|
25
|
+
attrs.delete(k)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
multiparameter_attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def type_cast_attribute_value(type, value)
|
33
|
+
case type
|
34
|
+
when 'i', 'f'
|
35
|
+
value.send("to_#{type}")
|
36
|
+
else
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,127 +1,24 @@
|
|
1
1
|
module MongoModel
|
2
2
|
module AttributeMethods
|
3
3
|
module Protected
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# Attributes named in this macro are protected from mass-assignment,
|
17
|
-
# such as <tt>new(attributes)</tt>,
|
18
|
-
# <tt>update_attributes(attributes)</tt>, or
|
19
|
-
# <tt>attributes=(attributes)</tt>.
|
20
|
-
#
|
21
|
-
# Mass-assignment to these attributes will simply be ignored, to assign
|
22
|
-
# to them you can use direct writer methods. This is meant to protect
|
23
|
-
# sensitive attributes from being overwritten by malicious users
|
24
|
-
# tampering with URLs or forms.
|
25
|
-
#
|
26
|
-
# class Customer < Recliner::Document
|
27
|
-
# attr_protected :credit_rating
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# customer = Customer.new("name" => David, "credit_rating" => "Excellent")
|
31
|
-
# customer.credit_rating # => nil
|
32
|
-
# customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
|
33
|
-
# customer.credit_rating # => nil
|
34
|
-
#
|
35
|
-
# customer.credit_rating = "Average"
|
36
|
-
# customer.credit_rating # => "Average"
|
37
|
-
#
|
38
|
-
# To start from an all-closed default and enable attributes as needed,
|
39
|
-
# have a look at +attr_accessible+.
|
40
|
-
#
|
41
|
-
# If the access logic of your application is richer you can use <tt>Hash#except</tt>
|
42
|
-
# or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are
|
43
|
-
# passed to Recliner.
|
44
|
-
#
|
45
|
-
# For example, it could be the case that the list of protected attributes
|
46
|
-
# for a given model depends on the role of the user:
|
47
|
-
#
|
48
|
-
# # Assumes plan_id is not protected because it depends on the role.
|
49
|
-
# params[:account] = params[:account].except(:plan_id) unless admin?
|
50
|
-
# @account.update_attributes(params[:account])
|
51
|
-
#
|
52
|
-
# Note that +attr_protected+ is still applied to the received hash. Thus,
|
53
|
-
# with this technique you can at most _extend_ the list of protected
|
54
|
-
# attributes for a particular mass-assignment call.
|
55
|
-
def attr_protected(*attrs)
|
56
|
-
write_inheritable_attribute(:attr_protected, attrs.map { |a| a.to_s } + protected_attributes)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Specifies a white list of model attributes that can be set via
|
60
|
-
# mass-assignment, such as <tt>new(attributes)</tt>,
|
61
|
-
# <tt>update_attributes(attributes)</tt>, or
|
62
|
-
# <tt>attributes=(attributes)</tt>
|
63
|
-
#
|
64
|
-
# This is the opposite of the +attr_protected+ macro: Mass-assignment
|
65
|
-
# will only set attributes in this list, to assign to the rest of
|
66
|
-
# attributes you can use direct writer methods. This is meant to protect
|
67
|
-
# sensitive attributes from being overwritten by malicious users
|
68
|
-
# tampering with URLs or forms. If you'd rather start from an all-open
|
69
|
-
# default and restrict attributes as needed, have a look at
|
70
|
-
# +attr_protected+.
|
71
|
-
#
|
72
|
-
# class Customer < Recliner::Document
|
73
|
-
# attr_accessible :name, :nickname
|
74
|
-
# end
|
75
|
-
#
|
76
|
-
# customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
|
77
|
-
# customer.credit_rating # => nil
|
78
|
-
# customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
|
79
|
-
# customer.credit_rating # => nil
|
80
|
-
#
|
81
|
-
# customer.credit_rating = "Average"
|
82
|
-
# customer.credit_rating # => "Average"
|
83
|
-
#
|
84
|
-
# If the access logic of your application is richer you can use <tt>Hash#except</tt>
|
85
|
-
# or <tt>Hash#slice</tt> to sanitize the hash of parameters before they are
|
86
|
-
# passed to Recliner.
|
87
|
-
#
|
88
|
-
# For example, it could be the case that the list of accessible attributes
|
89
|
-
# for a given model depends on the role of the user:
|
90
|
-
#
|
91
|
-
# # Assumes plan_id is accessible because it depends on the role.
|
92
|
-
# params[:account] = params[:account].except(:plan_id) unless admin?
|
93
|
-
# @account.update_attributes(params[:account])
|
94
|
-
#
|
95
|
-
# Note that +attr_accessible+ is still applied to the received hash. Thus,
|
96
|
-
# with this technique you can at most _narrow_ the list of accessible
|
97
|
-
# attributes for a particular mass-assignment call.
|
98
|
-
def attr_accessible(*attrs)
|
99
|
-
write_inheritable_attribute(:attr_accessible, attrs.map { |a| a.to_s } + accessible_attributes)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Returns an array of all the attributes that have been protected from mass-assignment.
|
103
|
-
def protected_attributes
|
104
|
-
read_inheritable_attribute(:attr_protected) || []
|
105
|
-
end
|
106
|
-
|
107
|
-
# Returns an array of all the attributes that have been made accessible to mass-assignment.
|
108
|
-
def accessible_attributes
|
109
|
-
read_inheritable_attribute(:attr_accessible) || []
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def attributes=(attrs)#:nodoc:
|
114
|
-
super(remove_protected_attributes(attrs))
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
def remove_protected_attributes(attrs)
|
119
|
-
if self.class.accessible_attributes.empty?
|
120
|
-
attrs.reject { |k, v| self.class.protected_attributes.include?(k.to_s) }
|
121
|
-
else
|
122
|
-
attrs.reject { |k, v| !self.class.accessible_attributes.include?(k.to_s) }
|
123
|
-
end
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
include ActiveModel::MassAssignmentSecurity
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def property(name, *args, &block)#:nodoc:
|
10
|
+
property = super(name, *args, &block)
|
11
|
+
|
12
|
+
attr_protected(name) if property.options[:protected]
|
13
|
+
attr_accessible(name) if property.options[:accessible]
|
14
|
+
|
15
|
+
property
|
124
16
|
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def attributes=(attrs)#:nodoc:
|
20
|
+
super(sanitize_for_mass_assignment(attrs))
|
21
|
+
end
|
125
22
|
end
|
126
23
|
end
|
127
24
|
end
|
@@ -45,7 +45,10 @@ module MongoModel
|
|
45
45
|
class Index
|
46
46
|
def initialize(*keys)
|
47
47
|
options = keys.extract_options!
|
48
|
+
|
48
49
|
@unique = options.delete(:unique)
|
50
|
+
@min = options.delete(:min)
|
51
|
+
@max = options.delete(:max)
|
49
52
|
|
50
53
|
keys.each do |key|
|
51
54
|
self.keys[key.to_sym] = :ascending
|
@@ -64,16 +67,26 @@ module MongoModel
|
|
64
67
|
@unique
|
65
68
|
end
|
66
69
|
|
70
|
+
def geo2d?
|
71
|
+
@geo2d ||= keys.size == 1 && keys.values.first == :geo2d
|
72
|
+
end
|
73
|
+
|
67
74
|
def to_args
|
68
75
|
args = []
|
69
76
|
|
70
|
-
if
|
77
|
+
if geo2d?
|
78
|
+
args << [[keys.keys.first, Mongo::GEO2D]]
|
79
|
+
elsif keys.size == 1 && keys.values.first == :ascending
|
71
80
|
args << keys.keys.first
|
72
81
|
else
|
73
|
-
args << keys.map { |k, o| [k, o == :ascending ?
|
82
|
+
args << keys.map { |k, o| [k, o == :ascending ? Mongo::ASCENDING : Mongo::DESCENDING] }.sort_by { |k| k.first.to_s }
|
83
|
+
end
|
84
|
+
|
85
|
+
if geo2d? && @min && @max
|
86
|
+
args << { :min => @min, :max => @max }
|
87
|
+
elsif unique?
|
88
|
+
args << { :unique => true }
|
74
89
|
end
|
75
|
-
|
76
|
-
args << { :unique => true } if unique?
|
77
90
|
|
78
91
|
args
|
79
92
|
end
|
@@ -98,6 +98,14 @@ module MongoModel
|
|
98
98
|
superclass.collection_name
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
def use_type_selector?
|
103
|
+
!superclass.abstract_class?
|
104
|
+
end
|
105
|
+
|
106
|
+
def type_selector
|
107
|
+
[self.to_s] + descendants.map { |m| m.to_s }
|
108
|
+
end
|
101
109
|
|
102
110
|
def collection
|
103
111
|
@_collection ||= database.collection(collection_name)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Boolean < TrueClass; end
|
2
2
|
|
3
3
|
class Symbol
|
4
|
-
[:lt, :lte, :gt, :gte, :ne, :in, :nin, :mod, :all, :size, :exists].each do |operator|
|
4
|
+
[:lt, :lte, :gt, :gte, :ne, :in, :nin, :mod, :all, :size, :exists, :near].each do |operator|
|
5
5
|
define_method(operator) { MongoModel::MongoOperator.new(self, operator) }
|
6
6
|
end
|
7
7
|
|
@@ -70,8 +70,8 @@ module MongoModel
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def add_type_to_selector
|
73
|
-
|
74
|
-
selector['_type'] = { '$in' =>
|
73
|
+
if @model.use_type_selector? && selector['_type'].nil?
|
74
|
+
selector['_type'] = { '$in' => @model.type_selector }
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -5,8 +5,14 @@ module MongoModel
|
|
5
5
|
module Types
|
6
6
|
class Time < Object
|
7
7
|
def cast(value)
|
8
|
-
|
9
|
-
|
8
|
+
case value
|
9
|
+
when ::Array
|
10
|
+
base = ::Time.zone ? ::Time.zone : ::Time
|
11
|
+
base.local(*value)
|
12
|
+
else
|
13
|
+
time = value.to_time
|
14
|
+
time.change(:usec => (time.usec / 1000.0).floor * 1000)
|
15
|
+
end
|
10
16
|
rescue
|
11
17
|
nil
|
12
18
|
end
|
data/lib/mongomodel/version.rb
CHANGED
data/mongomodel.gemspec
CHANGED
@@ -14,12 +14,12 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.required_rubygems_version = ">= 1.3.6"
|
15
15
|
s.rubyforge_project = "mongomodel"
|
16
16
|
|
17
|
-
s.add_dependency "activesupport", "~> 3.0.
|
18
|
-
s.add_dependency "activemodel",
|
19
|
-
s.add_dependency "mongo",
|
17
|
+
s.add_dependency "activesupport", "~> 3.0.3"
|
18
|
+
s.add_dependency "activemodel", "~> 3.0.3"
|
19
|
+
s.add_dependency "mongo", "~> 1.1.2"
|
20
20
|
|
21
21
|
s.add_development_dependency "bundler", ">= 1.0.0"
|
22
|
-
s.add_development_dependency "rspec",
|
22
|
+
s.add_development_dependency "rspec", "= 1.3.0"
|
23
23
|
|
24
24
|
s.files = `git ls-files`.split("\n")
|
25
25
|
s.require_path = 'lib'
|
@@ -39,10 +39,17 @@ module MongoModel
|
|
39
39
|
it { should respond_to(:to_param) }
|
40
40
|
|
41
41
|
specify "to_param should return nil if subject.persisted? is false" do
|
42
|
+
subject.stub!(:to_key).and_return([1])
|
42
43
|
subject.stub!(:persisted?).and_return(false)
|
43
44
|
subject.to_param.should be_nil
|
44
45
|
end
|
45
46
|
|
47
|
+
# == Responds to <tt>valid?</tt>
|
48
|
+
#
|
49
|
+
# Returns a boolean that specifies whether the object is in a valid or invalid
|
50
|
+
# state.
|
51
|
+
it { should respond_to_boolean(:valid?) }
|
52
|
+
|
46
53
|
# == Responds to <tt>persisted?</tt>
|
47
54
|
#
|
48
55
|
# Returns a boolean that specifies whether the object has been persisted yet.
|
@@ -70,26 +77,6 @@ module MongoModel
|
|
70
77
|
model_name.plural.should be_a_kind_of(String)
|
71
78
|
end
|
72
79
|
|
73
|
-
# ## Old
|
74
|
-
#
|
75
|
-
# # valid?
|
76
|
-
# # ------
|
77
|
-
# #
|
78
|
-
# # Returns a boolean that specifies whether the object is in a valid or invalid
|
79
|
-
# # state.
|
80
|
-
# it { should respond_to_boolean(:valid?) }
|
81
|
-
#
|
82
|
-
# # new_record?
|
83
|
-
# # -----------
|
84
|
-
# #
|
85
|
-
# # Returns a boolean that specifies whether the object has been persisted yet.
|
86
|
-
# # This is used when calculating the URL for an object. If the object is
|
87
|
-
# # not persisted, a form for that object, for instance, will be POSTed to the
|
88
|
-
# # collection. If it is persisted, a form for the object will put PUTed to the
|
89
|
-
# # URL for the object.
|
90
|
-
# it { should respond_to_boolean(:new_record?) }
|
91
|
-
# it { should respond_to_boolean(:destroyed?) }
|
92
|
-
|
93
80
|
# == Errors Testing
|
94
81
|
#
|
95
82
|
# Returns an object that has :[] and :full_messages defined on it. See below
|
@@ -168,6 +168,58 @@ module MongoModel
|
|
168
168
|
subject { Book.find(book.id) }
|
169
169
|
it_should_behave_like "accessing and manipulating a has_many :by => :foreign_key association"
|
170
170
|
end
|
171
|
+
|
172
|
+
describe "with :dependent => :destroy option" do
|
173
|
+
define_class(:Book, Document) do
|
174
|
+
has_many :chapters, :by => :foreign_key, :dependent => :destroy
|
175
|
+
end
|
176
|
+
|
177
|
+
subject { Book.create!(:chapters => [chapter1, chapter2, chapter3]) }
|
178
|
+
|
179
|
+
context "when the parent object is destroyed" do
|
180
|
+
it "should call destroy on the child objects" do
|
181
|
+
chapter1.should_receive(:destroy)
|
182
|
+
chapter2.should_receive(:destroy)
|
183
|
+
chapter3.should_receive(:destroy)
|
184
|
+
|
185
|
+
subject.destroy
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should remove the child objects from their collection" do
|
189
|
+
subject.destroy
|
190
|
+
|
191
|
+
Chapter.exists?(chapter1.id).should be_false
|
192
|
+
Chapter.exists?(chapter2.id).should be_false
|
193
|
+
Chapter.exists?(chapter3.id).should be_false
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "with :dependent => :delete option" do
|
199
|
+
define_class(:Book, Document) do
|
200
|
+
has_many :chapters, :by => :foreign_key, :dependent => :delete
|
201
|
+
end
|
202
|
+
|
203
|
+
subject { Book.create!(:chapters => [chapter1, chapter2, chapter3]) }
|
204
|
+
|
205
|
+
context "when the parent object is destroyed" do
|
206
|
+
it "should not call destroy on the child objects" do
|
207
|
+
chapter1.should_not_receive(:destroy)
|
208
|
+
chapter2.should_not_receive(:destroy)
|
209
|
+
chapter3.should_not_receive(:destroy)
|
210
|
+
|
211
|
+
subject.destroy
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should remove the child objects from their collection" do
|
215
|
+
subject.destroy
|
216
|
+
|
217
|
+
Chapter.exists?(chapter1.id).should be_false
|
218
|
+
Chapter.exists?(chapter2.id).should be_false
|
219
|
+
Chapter.exists?(chapter3.id).should be_false
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
171
223
|
end
|
172
224
|
end
|
173
225
|
|
@@ -187,6 +187,74 @@ module MongoModel
|
|
187
187
|
|
188
188
|
it_should_behave_like "accessing and manipulating a has_many :by => :ids association"
|
189
189
|
end
|
190
|
+
|
191
|
+
describe "with :dependent => :destroy option" do
|
192
|
+
define_class(:Book, described_class) do
|
193
|
+
has_many :chapters, :by => :ids, :dependent => :destroy
|
194
|
+
end
|
195
|
+
|
196
|
+
if specing?(Document)
|
197
|
+
subject { Book.create!(:chapters => [chapter1, chapter2, chapter3]) }
|
198
|
+
else
|
199
|
+
define_class(:Bookshelf, Document) do
|
200
|
+
property :book, Book
|
201
|
+
end
|
202
|
+
let(:book) { Book.new(:chapters => [chapter1, chapter2, chapter3]) }
|
203
|
+
subject { Bookshelf.create!(:book => book) }
|
204
|
+
end
|
205
|
+
|
206
|
+
context "when the parent object is destroyed" do
|
207
|
+
it "should call destroy on the child objects" do
|
208
|
+
chapter1.should_receive(:destroy)
|
209
|
+
chapter2.should_receive(:destroy)
|
210
|
+
chapter3.should_receive(:destroy)
|
211
|
+
|
212
|
+
subject.destroy
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should remove the child objects from their collection" do
|
216
|
+
subject.destroy
|
217
|
+
|
218
|
+
Chapter.exists?(chapter1.id).should be_false
|
219
|
+
Chapter.exists?(chapter2.id).should be_false
|
220
|
+
Chapter.exists?(chapter3.id).should be_false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "with :dependent => :delete option" do
|
226
|
+
define_class(:Book, described_class) do
|
227
|
+
has_many :chapters, :by => :ids, :dependent => :delete
|
228
|
+
end
|
229
|
+
|
230
|
+
if specing?(Document)
|
231
|
+
subject { Book.create!(:chapters => [chapter1, chapter2, chapter3]) }
|
232
|
+
else
|
233
|
+
define_class(:Bookshelf, Document) do
|
234
|
+
property :book, Book
|
235
|
+
end
|
236
|
+
let(:book) { Book.new(:chapters => [chapter1, chapter2, chapter3]) }
|
237
|
+
subject { Bookshelf.create!(:book => book) }
|
238
|
+
end
|
239
|
+
|
240
|
+
context "when the parent object is destroyed" do
|
241
|
+
it "should not call destroy on the child objects" do
|
242
|
+
chapter1.should_not_receive(:destroy)
|
243
|
+
chapter2.should_not_receive(:destroy)
|
244
|
+
chapter3.should_not_receive(:destroy)
|
245
|
+
|
246
|
+
subject.destroy
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should remove the child objects from their collection" do
|
250
|
+
subject.destroy
|
251
|
+
|
252
|
+
Chapter.exists?(chapter1.id).should be_false
|
253
|
+
Chapter.exists?(chapter2.id).should be_false
|
254
|
+
Chapter.exists?(chapter3.id).should be_false
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
190
258
|
end
|
191
259
|
end
|
192
260
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MongoModel
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
5
|
+
define_class(:TestDocument, described_class) do
|
6
|
+
property :timestamp, Time
|
7
|
+
property :datestamp, Date
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { TestDocument.new }
|
11
|
+
|
12
|
+
describe "multiparameter assignment" do
|
13
|
+
context "setting a timestamp" do
|
14
|
+
it "should combine and assign parameters as Time" do
|
15
|
+
subject.attributes = {
|
16
|
+
"timestamp(1i)" => "2009",
|
17
|
+
"timestamp(2i)" => "10",
|
18
|
+
"timestamp(3i)" => "5",
|
19
|
+
"timestamp(4i)" => "14",
|
20
|
+
"timestamp(5i)" => "35"
|
21
|
+
}
|
22
|
+
|
23
|
+
subject.timestamp.should == Time.local(2009, 10, 5, 14, 35)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "setting a datestamp" do
|
28
|
+
it "should combine and assign parameters as Date" do
|
29
|
+
subject.attributes = {
|
30
|
+
"datestamp(1i)" => "2008",
|
31
|
+
"datestamp(2i)" => "4",
|
32
|
+
"datestamp(3i)" => "9"
|
33
|
+
}
|
34
|
+
|
35
|
+
subject.datestamp.should == Date.new(2008, 4, 9)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -6,6 +6,7 @@ module MongoModel
|
|
6
6
|
define_class(:Article, Document) do
|
7
7
|
property :title, String
|
8
8
|
property :age, Integer
|
9
|
+
property :position, Array
|
9
10
|
end
|
10
11
|
|
11
12
|
def subclass(klass)
|
@@ -41,12 +42,14 @@ module MongoModel
|
|
41
42
|
before(:each) do
|
42
43
|
Article.index :title, :unique => true
|
43
44
|
Article.index :age => :descending
|
45
|
+
Article.index :position => :geo2d, :min => -100, :max => 100
|
44
46
|
end
|
45
47
|
|
46
48
|
it "should create indexes on the collection" do
|
47
49
|
Article.collection.should_receive(:create_index).with(:_type)
|
48
50
|
Article.collection.should_receive(:create_index).with(:title, :unique => true)
|
49
51
|
Article.collection.should_receive(:create_index).with([[:age, Mongo::DESCENDING]])
|
52
|
+
Article.collection.should_receive(:create_index).with([[:position, Mongo::GEO2D]], :min => -100, :max => 100)
|
50
53
|
Article.ensure_indexes!
|
51
54
|
end
|
52
55
|
|
@@ -108,6 +111,14 @@ module MongoModel
|
|
108
111
|
Index.new(:title => :ascending, :age => :descending).to_args.should == [[[:age, Mongo::DESCENDING], [:title, Mongo::ASCENDING]]]
|
109
112
|
end
|
110
113
|
|
114
|
+
it "should convert geospatial index with no options" do
|
115
|
+
Index.new(:position => :geo2d).to_args.should == [[[:position, Mongo::GEO2D]]]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should convert geospatial index with min/max options" do
|
119
|
+
Index.new(:position => :geo2d, :min => -50, :max => 50).to_args.should == [[[:position, Mongo::GEO2D]], { :min => -50, :max => 50 }]
|
120
|
+
end
|
121
|
+
|
111
122
|
it "should be equal to an equivalent index" do
|
112
123
|
Index.new(:title).should == Index.new(:title)
|
113
124
|
Index.new(:title, :age).should == Index.new(:title => :ascending, :age => :ascending)
|
@@ -17,6 +17,7 @@ module MongoModel
|
|
17
17
|
define_class(:Event, Document)
|
18
18
|
define_class(:SpecialEvent, :Event)
|
19
19
|
define_class(:VerySpecialEvent, :SpecialEvent)
|
20
|
+
define_class(:SuperSpecialEvent, :VerySpecialEvent)
|
20
21
|
|
21
22
|
let(:missing) do
|
22
23
|
e = Event.new
|
@@ -29,6 +30,7 @@ module MongoModel
|
|
29
30
|
@event = Event.create!
|
30
31
|
@special = SpecialEvent.create!
|
31
32
|
@very_special = VerySpecialEvent.create!
|
33
|
+
@super_special = SuperSpecialEvent.create!
|
32
34
|
@missing = missing
|
33
35
|
end
|
34
36
|
|
@@ -59,14 +61,17 @@ module MongoModel
|
|
59
61
|
|
60
62
|
describe "loading documents" do
|
61
63
|
it "should load all documents from root class" do
|
62
|
-
Event.all.should include(@event, @special, @very_special, @missing)
|
64
|
+
Event.all.should include(@event, @special, @very_special, @super_special, @missing)
|
63
65
|
end
|
64
66
|
|
65
67
|
it "should only load subclass documents from subclass" do
|
66
|
-
SpecialEvent.all.should include(@special, @very_special)
|
68
|
+
SpecialEvent.all.should include(@special, @very_special, @super_special)
|
67
69
|
SpecialEvent.all.should_not include(@event, @missing)
|
68
70
|
|
69
|
-
VerySpecialEvent.all.should
|
71
|
+
VerySpecialEvent.all.should include(@very_special, @super_special)
|
72
|
+
VerySpecialEvent.all.should_not include(@event, @special, @missing)
|
73
|
+
|
74
|
+
SuperSpecialEvent.all.should == [@super_special]
|
70
75
|
end
|
71
76
|
end
|
72
77
|
end
|
@@ -20,6 +20,7 @@ module MongoModel
|
|
20
20
|
it "should be created from symbol methods" do
|
21
21
|
:age.gt.should == MongoOperator.new(:age, :gt)
|
22
22
|
:date.lte.should == MongoOperator.new(:date, :lte)
|
23
|
+
:position.near.should == MongoOperator.new(:position, :near)
|
23
24
|
end
|
24
25
|
|
25
26
|
it "should be equal within a hash" do
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongomodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 3
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
7
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
8
|
+
- 11
|
9
|
+
version: 0.2.11
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Sam Pohlenz
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-
|
17
|
+
date: 2010-11-22 00:00:00 +10:30
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,12 +25,11 @@ dependencies:
|
|
26
25
|
requirements:
|
27
26
|
- - ~>
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 7
|
30
28
|
segments:
|
31
29
|
- 3
|
32
30
|
- 0
|
33
|
-
-
|
34
|
-
version: 3.0.
|
31
|
+
- 3
|
32
|
+
version: 3.0.3
|
35
33
|
type: :runtime
|
36
34
|
version_requirements: *id001
|
37
35
|
- !ruby/object:Gem::Dependency
|
@@ -42,12 +40,11 @@ dependencies:
|
|
42
40
|
requirements:
|
43
41
|
- - ~>
|
44
42
|
- !ruby/object:Gem::Version
|
45
|
-
hash: 7
|
46
43
|
segments:
|
47
44
|
- 3
|
48
45
|
- 0
|
49
|
-
-
|
50
|
-
version: 3.0.
|
46
|
+
- 3
|
47
|
+
version: 3.0.3
|
51
48
|
type: :runtime
|
52
49
|
version_requirements: *id002
|
53
50
|
- !ruby/object:Gem::Dependency
|
@@ -58,12 +55,11 @@ dependencies:
|
|
58
55
|
requirements:
|
59
56
|
- - ~>
|
60
57
|
- !ruby/object:Gem::Version
|
61
|
-
hash: 25
|
62
58
|
segments:
|
63
59
|
- 1
|
64
|
-
-
|
65
|
-
-
|
66
|
-
version: 1.
|
60
|
+
- 1
|
61
|
+
- 2
|
62
|
+
version: 1.1.2
|
67
63
|
type: :runtime
|
68
64
|
version_requirements: *id003
|
69
65
|
- !ruby/object:Gem::Dependency
|
@@ -74,7 +70,6 @@ dependencies:
|
|
74
70
|
requirements:
|
75
71
|
- - ">="
|
76
72
|
- !ruby/object:Gem::Version
|
77
|
-
hash: 23
|
78
73
|
segments:
|
79
74
|
- 1
|
80
75
|
- 0
|
@@ -90,7 +85,6 @@ dependencies:
|
|
90
85
|
requirements:
|
91
86
|
- - "="
|
92
87
|
- !ruby/object:Gem::Version
|
93
|
-
hash: 27
|
94
88
|
segments:
|
95
89
|
- 1
|
96
90
|
- 3
|
@@ -132,6 +126,7 @@ files:
|
|
132
126
|
- lib/mongomodel/concerns/attribute_methods.rb
|
133
127
|
- lib/mongomodel/concerns/attribute_methods/before_type_cast.rb
|
134
128
|
- lib/mongomodel/concerns/attribute_methods/dirty.rb
|
129
|
+
- lib/mongomodel/concerns/attribute_methods/multi_parameter_assignment.rb
|
135
130
|
- lib/mongomodel/concerns/attribute_methods/protected.rb
|
136
131
|
- lib/mongomodel/concerns/attribute_methods/query.rb
|
137
132
|
- lib/mongomodel/concerns/attribute_methods/read.rb
|
@@ -202,6 +197,7 @@ files:
|
|
202
197
|
- spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb
|
203
198
|
- spec/mongomodel/concerns/attribute_methods/before_type_cast_spec.rb
|
204
199
|
- spec/mongomodel/concerns/attribute_methods/dirty_spec.rb
|
200
|
+
- spec/mongomodel/concerns/attribute_methods/multi_parameter_assignment_spec.rb
|
205
201
|
- spec/mongomodel/concerns/attribute_methods/protected_spec.rb
|
206
202
|
- spec/mongomodel/concerns/attribute_methods/query_spec.rb
|
207
203
|
- spec/mongomodel/concerns/attribute_methods/read_spec.rb
|
@@ -261,7 +257,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
261
257
|
requirements:
|
262
258
|
- - ">="
|
263
259
|
- !ruby/object:Gem::Version
|
264
|
-
hash: 3
|
265
260
|
segments:
|
266
261
|
- 0
|
267
262
|
version: "0"
|
@@ -270,7 +265,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
265
|
requirements:
|
271
266
|
- - ">="
|
272
267
|
- !ruby/object:Gem::Version
|
273
|
-
hash: 23
|
274
268
|
segments:
|
275
269
|
- 1
|
276
270
|
- 3
|