crnixon-mongomapper 0.2.0 → 0.3.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.
- data/.gitignore +1 -0
- data/History +48 -0
- data/README.rdoc +5 -3
- data/Rakefile +6 -4
- data/VERSION +1 -1
- data/bin/mmconsole +56 -0
- data/lib/mongomapper.rb +29 -18
- data/lib/mongomapper/associations.rb +53 -38
- data/lib/mongomapper/associations/base.rb +53 -20
- data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
- data/lib/mongomapper/associations/many_documents_as_proxy.rb +27 -0
- data/lib/mongomapper/associations/many_documents_proxy.rb +103 -0
- data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +6 -8
- data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongomapper/associations/{array_proxy.rb → many_proxy.rb} +1 -1
- data/lib/mongomapper/associations/proxy.rb +24 -21
- data/lib/mongomapper/callbacks.rb +1 -1
- data/lib/mongomapper/document.rb +160 -74
- data/lib/mongomapper/dynamic_finder.rb +38 -0
- data/lib/mongomapper/embedded_document.rb +154 -105
- data/lib/mongomapper/finder_options.rb +11 -7
- data/lib/mongomapper/key.rb +15 -21
- data/lib/mongomapper/pagination.rb +52 -0
- data/lib/mongomapper/rails_compatibility/document.rb +15 -0
- data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
- data/lib/mongomapper/serialization.rb +1 -1
- data/lib/mongomapper/serializers/json_serializer.rb +15 -0
- data/lib/mongomapper/support.rb +30 -0
- data/mongomapper.gemspec +87 -46
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +45 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +253 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -0
- data/test/functional/associations/test_many_proxy.rb +295 -0
- data/test/functional/test_associations.rb +47 -0
- data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
- data/test/functional/test_document.rb +952 -0
- data/test/functional/test_pagination.rb +81 -0
- data/test/functional/test_rails_compatibility.rb +30 -0
- data/test/functional/test_validations.rb +172 -0
- data/test/models.rb +169 -0
- data/test/test_helper.rb +7 -2
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_association_base.rb +144 -0
- data/test/unit/test_document.rb +123 -0
- data/test/unit/test_embedded_document.rb +526 -0
- data/test/{test_finder_options.rb → unit/test_finder_options.rb} +36 -1
- data/test/{test_key.rb → unit/test_key.rb} +59 -12
- data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
- data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +34 -0
- data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
- data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
- metadata +81 -43
- data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
- data/lib/mongomapper/rails_compatibility.rb +0 -23
- data/test/serializers/test_json_serializer.rb +0 -104
- data/test/test_associations.rb +0 -174
- data/test/test_document.rb +0 -944
- data/test/test_embedded_document.rb +0 -253
- data/test/test_rails_compatibility.rb +0 -29
@@ -0,0 +1,34 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class BelongsToPolymorphicProxy < Proxy
|
4
|
+
def replace(doc)
|
5
|
+
if doc
|
6
|
+
doc.save if doc.new?
|
7
|
+
id, type = doc.id, doc.class.name
|
8
|
+
end
|
9
|
+
|
10
|
+
@owner.send("#{@association.foreign_key}=", id)
|
11
|
+
@owner.send("#{@association.type_key_name}=", type)
|
12
|
+
reset
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
def find_target
|
17
|
+
if proxy_id && proxy_class
|
18
|
+
proxy_class.find_by_id(proxy_id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def proxy_id
|
23
|
+
@proxy_id ||= @owner.send(@association.foreign_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def proxy_class
|
27
|
+
@proxy_class ||= begin
|
28
|
+
klass = @owner.send(@association.type_key_name)
|
29
|
+
klass && klass.constantize
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,26 +1,22 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Associations
|
3
3
|
class BelongsToProxy < Proxy
|
4
|
-
def replace(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
v.save if v.new?
|
9
|
-
@owner.__send__(:write_attribute, ref_id, v.id)
|
10
|
-
else
|
11
|
-
@owner.__send__(:write_attribute, ref_id, nil)
|
4
|
+
def replace(doc)
|
5
|
+
if doc
|
6
|
+
doc.save if doc.new?
|
7
|
+
id = doc.id
|
12
8
|
end
|
13
9
|
|
14
|
-
|
10
|
+
@owner.send("#{@association.foreign_key}=", id)
|
11
|
+
reset
|
15
12
|
end
|
16
13
|
|
17
14
|
protected
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
def find_target
|
16
|
+
if association_id = @owner.send(@association.foreign_key)
|
17
|
+
@association.klass.find_by_id(association_id)
|
18
|
+
end
|
22
19
|
end
|
23
|
-
end
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class ManyDocumentsAsProxy < ManyDocumentsProxy
|
4
|
+
protected
|
5
|
+
def scoped_conditions
|
6
|
+
{as_type_name => @owner.class.name, as_id_name => @owner.id}
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply_scope(doc)
|
10
|
+
ensure_owner_saved
|
11
|
+
|
12
|
+
doc.send("#{as_type_name}=", @owner.class.name)
|
13
|
+
doc.send("#{as_id_name}=", @owner.id)
|
14
|
+
|
15
|
+
doc
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_type_name
|
19
|
+
@as_type_name ||= @association.options[:as].to_s + "_type"
|
20
|
+
end
|
21
|
+
|
22
|
+
def as_id_name
|
23
|
+
@as_id_name ||= @association.options[:as].to_s + "_id"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class ManyDocumentsProxy < Proxy
|
4
|
+
delegate :klass, :to => :@association
|
5
|
+
|
6
|
+
def find(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
klass.find(*args << scoped_options(options))
|
9
|
+
end
|
10
|
+
|
11
|
+
def paginate(options)
|
12
|
+
klass.paginate(scoped_options(options))
|
13
|
+
end
|
14
|
+
|
15
|
+
def all(options={})
|
16
|
+
find(:all, scoped_options(options))
|
17
|
+
end
|
18
|
+
|
19
|
+
def first(options={})
|
20
|
+
find(:first, scoped_options(options))
|
21
|
+
end
|
22
|
+
|
23
|
+
def last(options={})
|
24
|
+
find(:last, scoped_options(options))
|
25
|
+
end
|
26
|
+
|
27
|
+
def count(conditions={})
|
28
|
+
klass.count(conditions.deep_merge(scoped_conditions))
|
29
|
+
end
|
30
|
+
|
31
|
+
def replace(docs)
|
32
|
+
@target.map(&:destroy) if load_target
|
33
|
+
docs.each { |doc| apply_scope(doc).save }
|
34
|
+
reset
|
35
|
+
end
|
36
|
+
|
37
|
+
def <<(*docs)
|
38
|
+
ensure_owner_saved
|
39
|
+
flatten_deeper(docs).each { |doc| apply_scope(doc).save }
|
40
|
+
reset
|
41
|
+
end
|
42
|
+
alias_method :push, :<<
|
43
|
+
alias_method :concat, :<<
|
44
|
+
|
45
|
+
def build(attrs={})
|
46
|
+
doc = klass.new(attrs)
|
47
|
+
apply_scope(doc)
|
48
|
+
doc
|
49
|
+
end
|
50
|
+
|
51
|
+
def create(attrs={})
|
52
|
+
doc = klass.new(attrs)
|
53
|
+
apply_scope(doc).save
|
54
|
+
doc
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroy_all(conditions={})
|
58
|
+
all(:conditions => conditions).map(&:destroy)
|
59
|
+
reset
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete_all(conditions={})
|
63
|
+
klass.delete_all(conditions.deep_merge(scoped_conditions))
|
64
|
+
reset
|
65
|
+
end
|
66
|
+
|
67
|
+
def nullify
|
68
|
+
criteria = FinderOptions.to_mongo_criteria(scoped_conditions)
|
69
|
+
all(criteria).each do |doc|
|
70
|
+
doc.update_attributes self.foreign_key => nil
|
71
|
+
end
|
72
|
+
reset
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
def scoped_conditions
|
77
|
+
{self.foreign_key => @owner.id}
|
78
|
+
end
|
79
|
+
|
80
|
+
def scoped_options(options)
|
81
|
+
options.deep_merge({:conditions => scoped_conditions})
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_target
|
85
|
+
find(:all)
|
86
|
+
end
|
87
|
+
|
88
|
+
def ensure_owner_saved
|
89
|
+
@owner.save if @owner.new?
|
90
|
+
end
|
91
|
+
|
92
|
+
def apply_scope(doc)
|
93
|
+
ensure_owner_saved
|
94
|
+
doc.send("#{self.foreign_key}=", @owner.id)
|
95
|
+
doc
|
96
|
+
end
|
97
|
+
|
98
|
+
def foreign_key
|
99
|
+
@association.options[:foreign_key] || @owner.class.name.underscore.gsub("/", "_") + "_id"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Associations
|
3
|
+
class ManyEmbeddedPolymorphicProxy < Proxy
|
4
|
+
def replace(v)
|
5
|
+
@_values = v.map do |doc_or_hash|
|
6
|
+
if doc_or_hash.kind_of?(EmbeddedDocument)
|
7
|
+
doc = doc_or_hash
|
8
|
+
{@association.type_key_name => doc.class.name}.merge(doc.attributes)
|
9
|
+
else
|
10
|
+
doc_or_hash
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def find_target
|
19
|
+
(@_values || []).map do |hash|
|
20
|
+
polymorphic_class(hash).new(hash)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def polymorphic_class(doc)
|
25
|
+
if class_name = doc[@association.type_key_name]
|
26
|
+
class_name.constantize
|
27
|
+
else
|
28
|
+
@association.klass
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Associations
|
3
|
-
class
|
3
|
+
class ManyEmbeddedProxy < Proxy
|
4
4
|
def replace(v)
|
5
5
|
@_values = v.map { |e| e.kind_of?(EmbeddedDocument) ? e.attributes : e }
|
6
|
-
|
7
|
-
|
8
|
-
reload_target
|
6
|
+
reset
|
9
7
|
end
|
10
8
|
|
11
9
|
protected
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def find_target
|
11
|
+
(@_values || []).map do |e|
|
12
|
+
@association.klass.new(e)
|
13
|
+
end
|
15
14
|
end
|
16
|
-
end
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -1,16 +1,11 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Associations
|
3
|
-
class Proxy
|
3
|
+
class Proxy < BasicObject
|
4
4
|
attr_reader :owner, :association
|
5
|
-
|
6
|
-
instance_methods.each do |m|
|
7
|
-
undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/
|
8
|
-
end
|
9
|
-
|
5
|
+
|
10
6
|
def initialize(owner, association)
|
11
|
-
@owner= owner
|
7
|
+
@owner = owner
|
12
8
|
@association = association
|
13
|
-
|
14
9
|
reset
|
15
10
|
end
|
16
11
|
|
@@ -38,23 +33,31 @@ module MongoMapper
|
|
38
33
|
end
|
39
34
|
|
40
35
|
protected
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
36
|
+
def method_missing(method, *args)
|
37
|
+
if load_target
|
38
|
+
if block_given?
|
39
|
+
@target.send(method, *args) { |*block_args| yield(*block_args) }
|
40
|
+
else
|
41
|
+
@target.send(method, *args)
|
42
|
+
end
|
47
43
|
end
|
48
44
|
end
|
49
|
-
end
|
50
45
|
|
51
|
-
|
52
|
-
|
53
|
-
|
46
|
+
def load_target
|
47
|
+
@target ||= find_target
|
48
|
+
end
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
50
|
+
def find_target
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
# Array#flatten has problems with recursive arrays. Going one level
|
55
|
+
# deeper solves the majority of the problems.
|
56
|
+
def flatten_deeper(array)
|
57
|
+
array.collect do |element|
|
58
|
+
(element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
|
59
|
+
end.flatten
|
60
|
+
end
|
58
61
|
end
|
59
62
|
end
|
60
63
|
end
|
@@ -90,7 +90,7 @@ module MongoMapper
|
|
90
90
|
def callback(method)
|
91
91
|
result = run_callbacks(method) { |result, object| false == result }
|
92
92
|
|
93
|
-
if result != false &&
|
93
|
+
if result != false && respond_to?(method)
|
94
94
|
result = send(method)
|
95
95
|
end
|
96
96
|
|
data/lib/mongomapper/document.rb
CHANGED
@@ -9,12 +9,8 @@ module MongoMapper
|
|
9
9
|
include Observing
|
10
10
|
include Callbacks
|
11
11
|
include SaveWithValidation
|
12
|
-
include RailsCompatibility
|
12
|
+
include RailsCompatibility::Document
|
13
13
|
extend ClassMethods
|
14
|
-
|
15
|
-
key :_id, String
|
16
|
-
key :created_at, Time
|
17
|
-
key :updated_at, Time
|
18
14
|
end
|
19
15
|
|
20
16
|
descendants << model
|
@@ -23,7 +19,7 @@ module MongoMapper
|
|
23
19
|
def self.descendants
|
24
20
|
@descendants ||= Set.new
|
25
21
|
end
|
26
|
-
|
22
|
+
|
27
23
|
module ClassMethods
|
28
24
|
def find(*args)
|
29
25
|
options = args.extract_options!
|
@@ -32,10 +28,23 @@ module MongoMapper
|
|
32
28
|
when :first then find_first(options)
|
33
29
|
when :last then find_last(options)
|
34
30
|
when :all then find_every(options)
|
35
|
-
else find_from_ids(args)
|
31
|
+
else find_from_ids(args, options)
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
35
|
+
def paginate(options)
|
36
|
+
per_page = options.delete(:per_page)
|
37
|
+
page = options.delete(:page)
|
38
|
+
total_entries = count(options[:conditions] || {})
|
39
|
+
collection = Pagination::PaginationProxy.new(total_entries, page, per_page)
|
40
|
+
|
41
|
+
options[:limit] = collection.limit
|
42
|
+
options[:offset] = collection.offset
|
43
|
+
|
44
|
+
collection.subject = find_every(options)
|
45
|
+
collection
|
46
|
+
end
|
47
|
+
|
39
48
|
def first(options={})
|
40
49
|
find_first(options)
|
41
50
|
end
|
@@ -49,18 +58,19 @@ module MongoMapper
|
|
49
58
|
end
|
50
59
|
|
51
60
|
def find_by_id(id)
|
52
|
-
|
61
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => id)
|
62
|
+
if doc = collection.find_first(criteria)
|
53
63
|
new(doc)
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
57
|
-
# TODO: remove the rescuing when ruby driver works correctly
|
58
67
|
def count(conditions={})
|
59
68
|
collection.count(FinderOptions.to_mongo_criteria(conditions))
|
60
69
|
end
|
61
70
|
|
62
71
|
def create(*docs)
|
63
72
|
instances = []
|
73
|
+
docs = [{}] if docs.blank?
|
64
74
|
docs.flatten.each do |attrs|
|
65
75
|
doc = new(attrs); doc.save
|
66
76
|
instances << doc
|
@@ -84,11 +94,13 @@ module MongoMapper
|
|
84
94
|
end
|
85
95
|
|
86
96
|
def delete(*ids)
|
87
|
-
|
97
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => ids.flatten)
|
98
|
+
collection.remove(criteria)
|
88
99
|
end
|
89
100
|
|
90
101
|
def delete_all(conditions={})
|
91
|
-
|
102
|
+
criteria = FinderOptions.to_mongo_criteria(conditions)
|
103
|
+
collection.remove(criteria)
|
92
104
|
end
|
93
105
|
|
94
106
|
def destroy(*ids)
|
@@ -125,11 +137,18 @@ module MongoMapper
|
|
125
137
|
end
|
126
138
|
@collection
|
127
139
|
end
|
128
|
-
|
140
|
+
|
141
|
+
def timestamps!
|
142
|
+
key :created_at, Time
|
143
|
+
key :updated_at, Time
|
144
|
+
|
145
|
+
class_eval { before_save :update_timestamps }
|
146
|
+
end
|
147
|
+
|
129
148
|
def validates_uniqueness_of(*args)
|
130
149
|
add_validations(args, MongoMapper::Validations::ValidatesUniquenessOf)
|
131
150
|
end
|
132
|
-
|
151
|
+
|
133
152
|
def validates_exclusion_of(*args)
|
134
153
|
add_validations(args, MongoMapper::Validations::ValidatesExclusionOf)
|
135
154
|
end
|
@@ -137,59 +156,125 @@ module MongoMapper
|
|
137
156
|
def validates_inclusion_of(*args)
|
138
157
|
add_validations(args, MongoMapper::Validations::ValidatesInclusionOf)
|
139
158
|
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
def method_missing(method, *args)
|
162
|
+
finder = DynamicFinder.new(self, method)
|
163
|
+
|
164
|
+
if finder.valid?
|
165
|
+
meta_def(finder.options[:method]) do |*args|
|
166
|
+
find_with_args(args, finder.options)
|
167
|
+
end
|
168
|
+
|
169
|
+
send(finder.options[:method], *args)
|
170
|
+
else
|
171
|
+
super
|
172
|
+
end
|
173
|
+
end
|
140
174
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
175
|
+
private
|
176
|
+
def find_every(options)
|
177
|
+
criteria, options = FinderOptions.new(options).to_a
|
178
|
+
collection.find(criteria, options).to_a.map { |doc| new(doc) }
|
179
|
+
end
|
146
180
|
|
147
|
-
|
148
|
-
|
149
|
-
|
181
|
+
def find_first(options)
|
182
|
+
options.merge!(:limit => 1)
|
183
|
+
find_every({:order => '$natural asc'}.merge(options))[0]
|
184
|
+
end
|
150
185
|
|
151
|
-
|
152
|
-
|
153
|
-
|
186
|
+
def find_last(options)
|
187
|
+
options.merge!(:limit => 1)
|
188
|
+
options[:order] = invert_order_clause(options)
|
189
|
+
find_every(options)[0]
|
190
|
+
#find_every({:order => '$natural desc'}.merge(invert_order_clause(options)))[0]
|
191
|
+
end
|
154
192
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
193
|
+
def invert_order_clause(options)
|
194
|
+
return '$natural desc' unless options[:order]
|
195
|
+
options[:order].split(',').map do |order_segment|
|
196
|
+
if order_segment =~ /\sasc/i
|
197
|
+
order_segment.sub /\sasc/i, ' desc'
|
198
|
+
elsif order_segment =~ /\sdesc/i
|
199
|
+
order_segment.sub /\sdesc/i, ' asc'
|
200
|
+
else
|
201
|
+
"#{order_segment.strip} desc"
|
202
|
+
end
|
203
|
+
end.join(',')
|
161
204
|
end
|
162
|
-
end
|
163
205
|
|
164
|
-
|
165
|
-
|
206
|
+
def find_some(ids, options={})
|
207
|
+
documents = find_every(options.deep_merge(:conditions => {'_id' => ids}))
|
208
|
+
if ids.size == documents.size
|
209
|
+
documents
|
210
|
+
else
|
211
|
+
raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{documents.size}, but was expecting #{ids.size}"
|
212
|
+
end
|
213
|
+
end
|
166
214
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
when 1
|
171
|
-
find_by_id(ids[0]) || raise(DocumentNotFound, "Document with id of #{ids[0]} does not exist in collection named #{collection.name}")
|
215
|
+
def find_one(id, options={})
|
216
|
+
if doc = find_every(options.deep_merge(:conditions => {:_id => id})).first
|
217
|
+
doc
|
172
218
|
else
|
173
|
-
|
219
|
+
raise DocumentNotFound, "Document with id of #{id} does not exist in collection named #{collection.name}"
|
220
|
+
end
|
174
221
|
end
|
175
|
-
end
|
176
222
|
|
177
|
-
|
178
|
-
|
179
|
-
|
223
|
+
def find_from_ids(ids, options={})
|
224
|
+
ids = ids.flatten.compact.uniq
|
225
|
+
|
226
|
+
case ids.size
|
227
|
+
when 0
|
228
|
+
raise(DocumentNotFound, "Couldn't find without an ID")
|
229
|
+
when 1
|
230
|
+
find_one(ids[0], options)
|
231
|
+
else
|
232
|
+
find_some(ids, options)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def find_with_args(args, options)
|
237
|
+
attributes, = {}
|
238
|
+
find_options = args.extract_options!.deep_merge(:conditions => attributes)
|
239
|
+
|
240
|
+
options[:attribute_names].each_with_index do |attr, index|
|
241
|
+
attributes[attr] = args[index]
|
242
|
+
end
|
243
|
+
|
244
|
+
result = find(options[:finder], find_options)
|
245
|
+
|
246
|
+
if result.nil?
|
247
|
+
if options[:bang]
|
248
|
+
raise DocumentNotFound, "Couldn't find Document with #{attributes.inspect} in collection named #{collection.name}"
|
249
|
+
end
|
250
|
+
|
251
|
+
if options[:instantiator]
|
252
|
+
self.send(options[:instantiator], attributes)
|
253
|
+
end
|
254
|
+
else
|
255
|
+
result
|
256
|
+
end
|
180
257
|
end
|
181
258
|
|
182
|
-
|
183
|
-
|
259
|
+
def update_single(id, attrs)
|
260
|
+
if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
|
261
|
+
raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
|
262
|
+
end
|
184
263
|
|
185
|
-
|
186
|
-
|
187
|
-
|
264
|
+
doc = find(id)
|
265
|
+
doc.update_attributes(attrs)
|
266
|
+
doc
|
267
|
+
end
|
268
|
+
|
269
|
+
def update_multiple(docs)
|
270
|
+
unless docs.is_a?(Hash)
|
271
|
+
raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
|
272
|
+
end
|
273
|
+
|
274
|
+
instances = []
|
275
|
+
docs.each_pair { |id, attrs| instances << update(id, attrs) }
|
276
|
+
instances
|
188
277
|
end
|
189
|
-
instances = []
|
190
|
-
docs.each_pair { |id, attrs| instances << update(id, attrs) }
|
191
|
-
instances
|
192
|
-
end
|
193
278
|
end
|
194
279
|
|
195
280
|
module InstanceMethods
|
@@ -198,13 +283,13 @@ module MongoMapper
|
|
198
283
|
end
|
199
284
|
|
200
285
|
def new?
|
201
|
-
read_attribute('_id').blank? ||
|
286
|
+
read_attribute('_id').blank? || using_custom_id?
|
202
287
|
end
|
203
288
|
|
204
289
|
def save
|
205
290
|
create_or_update
|
206
291
|
end
|
207
|
-
|
292
|
+
|
208
293
|
def save!
|
209
294
|
create_or_update || raise(DocumentNotValid.new(self))
|
210
295
|
end
|
@@ -212,20 +297,14 @@ module MongoMapper
|
|
212
297
|
def update_attributes(attrs={})
|
213
298
|
self.attributes = attrs
|
214
299
|
save
|
215
|
-
self
|
216
300
|
end
|
217
301
|
|
218
302
|
def destroy
|
219
|
-
|
220
|
-
freeze
|
221
|
-
end
|
303
|
+
return false if frozen?
|
222
304
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
def id
|
228
|
-
read_attribute('_id')
|
305
|
+
criteria = FinderOptions.to_mongo_criteria(:_id => id)
|
306
|
+
collection.remove(criteria) unless new?
|
307
|
+
freeze
|
229
308
|
end
|
230
309
|
|
231
310
|
private
|
@@ -233,29 +312,36 @@ module MongoMapper
|
|
233
312
|
result = new? ? create : update
|
234
313
|
result != false
|
235
314
|
end
|
236
|
-
|
315
|
+
|
237
316
|
def create
|
238
|
-
|
239
|
-
update_timestamps
|
317
|
+
assign_id
|
240
318
|
save_to_collection
|
241
319
|
end
|
320
|
+
|
321
|
+
def assign_id
|
322
|
+
if read_attribute(:_id).blank?
|
323
|
+
write_attribute(:_id, XGen::Mongo::Driver::ObjectID.new.to_s)
|
324
|
+
end
|
325
|
+
end
|
242
326
|
|
243
327
|
def update
|
244
|
-
update_timestamps
|
245
328
|
save_to_collection
|
246
329
|
end
|
247
330
|
|
331
|
+
# collection.save returns mongoid
|
248
332
|
def save_to_collection
|
249
|
-
|
333
|
+
clear_custom_id_flag
|
334
|
+
collection.save(attributes)
|
250
335
|
end
|
251
336
|
|
252
337
|
def update_timestamps
|
253
|
-
|
254
|
-
write_attribute('
|
338
|
+
now = Time.now.utc
|
339
|
+
write_attribute('created_at', now) if new?
|
340
|
+
write_attribute('updated_at', now)
|
255
341
|
end
|
256
|
-
|
257
|
-
def
|
258
|
-
|
342
|
+
|
343
|
+
def clear_custom_id_flag
|
344
|
+
@using_custom_id = nil
|
259
345
|
end
|
260
346
|
end
|
261
347
|
end # Document
|