mongodb_model 0.2.8 → 2.0.0
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/Rakefile +3 -4
- data/lib/mongo/model/assignment.rb +4 -2
- data/lib/mongo/model/callbacks.rb +27 -2
- data/lib/mongo/model/conversion.rb +26 -11
- data/lib/mongo/model/crud.rb +52 -9
- data/lib/mongo/model/{file_model.rb → integration/file_model.rb} +1 -1
- data/lib/mongo/model/{validation → integration/validatable}/uniqueness_validator.rb +2 -2
- data/lib/mongo/model/integration/validatable.rb +41 -0
- data/lib/mongo/model/load.rb +59 -0
- data/lib/mongo/model/model.rb +51 -19
- data/lib/mongo/model/query.rb +1 -1
- data/lib/mongo/model/scope.rb +2 -6
- data/lib/mongo/model/spec.rb +0 -3
- data/lib/mongo/model/support/{types.rb → conversions.rb} +9 -23
- data/lib/mongo/model/support.rb +11 -0
- data/lib/mongo/model/validation.rb +14 -34
- data/lib/mongo/model.rb +1 -46
- data/spec/assignment_spec.rb +2 -2
- data/spec/associations_spec.rb +2 -4
- data/spec/attribute_convertors_spec.rb +9 -9
- data/spec/callbacks_spec.rb +161 -34
- data/spec/conversion_spec.rb +18 -18
- data/spec/crud_spec.rb +79 -81
- data/spec/equality_spec.rb +23 -13
- data/spec/{file_model_spec.rb → integration/file_model_spec.rb} +1 -1
- data/spec/integration/validatable_spec.rb +89 -0
- data/spec/misc_spec.rb +6 -6
- data/spec/query_spec.rb +14 -25
- data/spec/scope_spec.rb +10 -10
- data/spec/spec_helper.rb +19 -3
- data/spec/validation_spec.rb +92 -85
- metadata +16 -12
data/lib/mongo/model.rb
CHANGED
@@ -1,51 +1,6 @@
|
|
1
1
|
require 'mongodb_model/gems'
|
2
2
|
|
3
|
-
require 'validatable'
|
4
|
-
require 'ruby_ext'
|
5
|
-
require 'mongo/object'
|
6
|
-
|
7
|
-
module Mongo::Model; end
|
8
|
-
|
9
|
-
%w(
|
10
|
-
support/types
|
11
|
-
|
12
|
-
db
|
13
|
-
conversion
|
14
|
-
assignment
|
15
|
-
callbacks
|
16
|
-
validation
|
17
|
-
validation/uniqueness_validator
|
18
|
-
crud
|
19
|
-
query
|
20
|
-
query_mixin
|
21
|
-
scope
|
22
|
-
attribute_convertors
|
23
|
-
misc
|
24
|
-
model
|
25
|
-
).each{|f| require "mongo/model/#{f}"}
|
26
|
-
|
27
3
|
module Mongo
|
28
|
-
|
29
|
-
autoload :FileModel, 'mongo/model/file_model'
|
30
|
-
|
31
|
-
inherit \
|
32
|
-
Db,
|
33
|
-
Conversion,
|
34
|
-
Assignment,
|
35
|
-
Callbacks,
|
36
|
-
Validation,
|
37
|
-
Crud,
|
38
|
-
QueryMixin,
|
39
|
-
Scope,
|
40
|
-
AttributeConvertors,
|
41
|
-
Misc
|
42
|
-
end
|
4
|
+
autoload :Model, 'mongo/model/load'
|
43
5
|
end
|
44
6
|
|
45
|
-
Mongo.defaults.merge! \
|
46
|
-
convert_underscore_to_dollar: true,
|
47
|
-
batch_size: 50,
|
48
|
-
multi: true,
|
49
|
-
safe: true
|
50
|
-
|
51
|
-
require 'mongo/model/integration/rails' if defined? Rails
|
data/spec/assignment_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'Attribute assignment' do
|
4
4
|
with_mongo_model
|
5
5
|
|
6
6
|
after{remove_constants :User, :Writer}
|
@@ -70,7 +70,7 @@ describe 'Model callbacks' do
|
|
70
70
|
[u.age, u.posts].should == [20, 12]
|
71
71
|
end
|
72
72
|
|
73
|
-
it '
|
73
|
+
it 'should cast string values' do
|
74
74
|
[
|
75
75
|
Boolean, '1', true,
|
76
76
|
Date, '2011-08-23', Date.parse('2011-08-23'),
|
data/spec/associations_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe 'Associations' do
|
|
5
5
|
|
6
6
|
after{remove_constants :Post, :Comment}
|
7
7
|
|
8
|
-
it "
|
8
|
+
it "should allow to model associations" do
|
9
9
|
class Post
|
10
10
|
inherit Mongo::Model
|
11
11
|
collection :posts
|
@@ -13,7 +13,7 @@ describe 'Associations' do
|
|
13
13
|
attr_accessor :text
|
14
14
|
|
15
15
|
def comments
|
16
|
-
Comment.query({post_id: _id}, {sort: [[:
|
16
|
+
Comment.query({post_id: _id}, {sort: [[:text, -1]]})
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -26,8 +26,6 @@ describe 'Associations' do
|
|
26
26
|
def == o
|
27
27
|
self.class == o.class and [text, post_id] == [o.text, o.post_id]
|
28
28
|
end
|
29
|
-
|
30
|
-
timestamps!
|
31
29
|
end
|
32
30
|
|
33
31
|
post1 = Post.create! text: 'Post 1'
|
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe "Attribute
|
3
|
+
describe "Attribute convertors" do
|
4
4
|
with_mongo_model
|
5
5
|
|
6
6
|
after(:all){remove_constants :TheSample}
|
7
7
|
|
8
8
|
convertors = Mongo::Model::AttributeConvertors::CONVERTORS
|
9
9
|
|
10
|
-
it "
|
10
|
+
it "should convert line of comma-separated tokens to array and backward" do
|
11
11
|
v = ['a', 'b']
|
12
12
|
str_v = 'a, b'
|
13
13
|
convertors[:line][:from_string].call(str_v).should == v
|
14
14
|
convertors[:line][:to_string].call(v).should == str_v
|
15
15
|
end
|
16
16
|
|
17
|
-
it "
|
17
|
+
it "should convert to YAML and backward" do
|
18
18
|
v = {'a' => 'b'}
|
19
19
|
str_v = v.to_yaml.strip
|
20
20
|
|
@@ -22,14 +22,14 @@ describe "Attribute Convertors" do
|
|
22
22
|
convertors[:yaml][:to_string].call(v).should == str_v
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
25
|
+
it "should convert to JSON and backward" do
|
26
26
|
v = {'a' => 'b'}
|
27
27
|
str_v = v.to_json.strip
|
28
28
|
convertors[:json][:from_string].call(str_v).should == v
|
29
29
|
convertors[:json][:to_string].call(v).should == str_v
|
30
30
|
end
|
31
31
|
|
32
|
-
it "
|
32
|
+
it "should generate helper methods if :as_string option provided" do
|
33
33
|
class ::TheSample
|
34
34
|
inherit Mongo::Model
|
35
35
|
|
@@ -48,24 +48,24 @@ describe "Attribute Convertors" do
|
|
48
48
|
|
49
49
|
o = TheSample.new
|
50
50
|
|
51
|
-
#
|
51
|
+
# Get.
|
52
52
|
o.tags_as_string.should == ''
|
53
53
|
o.tags = %w(Java Ruby)
|
54
54
|
o._cache.clear
|
55
55
|
o.tags_as_string.should == 'Java, Ruby'
|
56
56
|
|
57
|
-
#
|
57
|
+
# Set.
|
58
58
|
o.tags_as_string = ''
|
59
59
|
o.tags.should == []
|
60
60
|
o.tags_as_string = 'Java, Ruby'
|
61
61
|
o.tags.should == %w(Java Ruby)
|
62
62
|
|
63
|
-
#
|
63
|
+
# Mass assignment.
|
64
64
|
o.tags = []
|
65
65
|
o.set tags_as_string: 'Java, Ruby'
|
66
66
|
o.tags.should == %w(Java Ruby)
|
67
67
|
|
68
|
-
#
|
68
|
+
# Protection.
|
69
69
|
o.protected_tags = []
|
70
70
|
-> {o.set protected_tags_as_string: 'Java, Ruby'}.should raise_error(/not allowed/)
|
71
71
|
end
|
data/spec/callbacks_spec.rb
CHANGED
@@ -1,60 +1,187 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'Callbacks' do
|
4
4
|
with_mongo_model
|
5
5
|
|
6
|
-
|
6
|
+
describe "basics" do
|
7
|
+
after{remove_constants :Post, :Unit}
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
it "should works in common use case" do
|
10
|
+
class Unit
|
11
|
+
inherit Mongo::Model
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
before_validate :validate_unit
|
14
|
+
after_save :unit_saved
|
14
15
|
|
15
|
-
|
16
|
+
attr_accessor :items
|
17
|
+
embedded :items
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
class Item
|
20
|
+
inherit Mongo::Model
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
before_validate :validate_item
|
23
|
+
after_save :item_saved
|
24
|
+
end
|
22
25
|
end
|
23
|
-
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
item = Unit::Item.new
|
28
|
+
unit = Unit.new
|
29
|
+
unit.items = [item]
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
unit.should_receive(:validate_unit).once.ordered.and_return(nil)
|
32
|
+
unit.should_receive(:unit_saved).once.ordered.and_return(nil)
|
33
|
+
item.should_receive(:validate_item).once.ordered.and_return(nil)
|
34
|
+
item.should_receive(:item_saved).once.ordered.and_return(nil)
|
33
35
|
|
34
|
-
|
36
|
+
db.units.save(unit).should be_true
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
describe 'CRUD' do
|
41
|
+
with_mongo_model
|
42
|
+
|
43
|
+
before do
|
44
|
+
class MainObject
|
45
|
+
inherit Mongo::Model, RSpec::CallbackHelper
|
41
46
|
|
42
|
-
|
47
|
+
attr_accessor :children
|
48
|
+
embedded :children
|
43
49
|
end
|
44
50
|
|
45
|
-
|
46
|
-
|
51
|
+
class EmbeddedObject
|
52
|
+
inherit Mongo::Model, RSpec::CallbackHelper
|
47
53
|
end
|
48
|
-
|
54
|
+
end
|
55
|
+
after{remove_constants :MainObject, :Post}
|
49
56
|
|
50
|
-
|
51
|
-
|
57
|
+
before do
|
58
|
+
@child = EmbeddedObject.new
|
59
|
+
@object = MainObject.new
|
60
|
+
@object.children = [@child]
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should fire create callbacks' do
|
64
|
+
%w(before_validate after_validate before_save before_create after_create after_save).each do |name|
|
65
|
+
@object.should_receive(name).once.ordered.and_return(true)
|
66
|
+
@child.should_receive(name).once.ordered.and_return(true)
|
52
67
|
end
|
68
|
+
|
69
|
+
db.objects.save(@object).should be_true
|
53
70
|
end
|
54
71
|
|
55
|
-
|
56
|
-
|
72
|
+
it 'should fire update callbacks' do
|
73
|
+
@object.dont_watch_callbacks do
|
74
|
+
db.objects.save(@object).should be_true
|
75
|
+
end
|
57
76
|
|
58
|
-
|
77
|
+
%w(before_validate after_validate before_save before_update after_update after_save).each do |name|
|
78
|
+
@object.should_receive(name).once.ordered.and_return(true)
|
79
|
+
@child.should_receive(name).once.ordered.and_return(true)
|
80
|
+
end
|
81
|
+
db.objects.save(@object).should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should fire delete callbacks' do
|
85
|
+
@object.dont_watch_callbacks do
|
86
|
+
db.objects.save(@object).should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
%w(before_validate after_validate before_delete after_delete).each do |name|
|
90
|
+
@object.should_receive(name).once.ordered.and_return(true)
|
91
|
+
@child.should_receive(name).once.ordered.and_return(true)
|
92
|
+
end
|
93
|
+
db.objects.delete(@object).should be_true
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should be able skip callbacks' do
|
97
|
+
%w(before_validate after_validate before_save before_update after_update after_save).each do |name|
|
98
|
+
@object.should_not_receive(name)
|
99
|
+
@child.should_not_receive(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
db.objects.save(@object, callbacks: false).should be_true
|
103
|
+
db.objects.count.should == 1
|
104
|
+
db.objects.save(@object, callbacks: false).should be_true
|
105
|
+
db.objects.count.should == 1
|
106
|
+
db.objects.delete(@object, callbacks: false).should be_true
|
107
|
+
db.objects.count.should == 0
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should be able interrupt CRUD' do
|
111
|
+
%w(before_validate after_validate before_save).each do |name|
|
112
|
+
@object.should_receive(name).and_return true
|
113
|
+
@child.should_receive(name).and_return true
|
114
|
+
end
|
115
|
+
|
116
|
+
@object.should_receive(:before_create).and_return true
|
117
|
+
@child.should_receive(:before_create).and_return false
|
118
|
+
|
119
|
+
db.objects.save(@object).should be_false
|
120
|
+
db.objects.count.should == 0
|
121
|
+
end
|
122
|
+
|
123
|
+
# Rejected.
|
124
|
+
# describe "embedded" do
|
125
|
+
# it 'should fire :delete on detached objects' do
|
126
|
+
# db.objects.save(@object).should be_true
|
127
|
+
# @object.children.clear
|
128
|
+
# @child.should_receive(:before_delete).once.and_return(true)
|
129
|
+
# db.objects.delete(@object).should be_true
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# it 'should fire :delete on deleted objects in update' do
|
133
|
+
# db.objects.save(@object).should be_true
|
134
|
+
# @object.children.clear
|
135
|
+
# @child.should_receive(:before_delete).once.and_return(true)
|
136
|
+
# db.objects.save(@object).should be_true
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# it 'should fire :create on new objects in update' do
|
140
|
+
# db.objects.save(@object).should be_true
|
141
|
+
# child2 = EmbeddedObject.new
|
142
|
+
# @object.children << child2
|
143
|
+
# child2.should_receive(:before_create).once.and_return(true)
|
144
|
+
# child2.should_not_receive(:before_update)
|
145
|
+
# db.objects.save(@object).should be_true
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
|
149
|
+
it "should fire after build callback after building the model" do
|
150
|
+
@object.dont_watch_callbacks do
|
151
|
+
db.objects.save(@object).should be_true
|
152
|
+
end
|
153
|
+
|
154
|
+
MainObject.after_instantiate do |instance|
|
155
|
+
instance.should_receive(:after_build)
|
156
|
+
end
|
157
|
+
EmbeddedObject.after_instantiate do |instance|
|
158
|
+
instance.should_receive(:after_build)
|
159
|
+
end
|
160
|
+
db.objects.first
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should allow to use after build callback to post-process and alter model" do
|
164
|
+
class Post
|
165
|
+
inherit Mongo::Model
|
166
|
+
collection :posts
|
167
|
+
|
168
|
+
class Tags < Array
|
169
|
+
end
|
170
|
+
|
171
|
+
def tags
|
172
|
+
@tags ||= Tags.new
|
173
|
+
end
|
174
|
+
attr_writer :tags
|
175
|
+
|
176
|
+
after_build do |post|
|
177
|
+
post.tags = Tags.new.replace post.tags
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
post = Post.new
|
182
|
+
post.save!
|
183
|
+
|
184
|
+
Post.first.tags.class.should == Post::Tags
|
185
|
+
end
|
59
186
|
end
|
60
187
|
end
|
data/spec/conversion_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'Conversion' do
|
4
4
|
with_mongo_model
|
5
5
|
|
6
6
|
before :all do
|
@@ -40,7 +40,7 @@ describe 'Model callbacks' do
|
|
40
40
|
|
41
41
|
it "should work without arguments" do
|
42
42
|
post = build_post_with_comment
|
43
|
-
post.
|
43
|
+
post.to_hash.should == {
|
44
44
|
'text' => 'StarCraft releasing soon!',
|
45
45
|
'token' => 'secret',
|
46
46
|
'comments' => [
|
@@ -49,32 +49,32 @@ describe 'Model callbacks' do
|
|
49
49
|
}
|
50
50
|
end
|
51
51
|
|
52
|
-
it "only, except
|
52
|
+
it "should accept :only, :except and :methods options" do
|
53
53
|
post = build_post_with_comment
|
54
|
-
post.
|
55
|
-
post.
|
54
|
+
post.to_hash(only: :text).should == {'text' => 'StarCraft releasing soon!'}
|
55
|
+
post.to_hash(except: :token).should == {
|
56
56
|
'text' => 'StarCraft releasing soon!',
|
57
57
|
'comments' => [
|
58
58
|
{'text' => 'Cool!'}
|
59
59
|
]
|
60
60
|
}
|
61
|
-
post.
|
61
|
+
post.to_hash(only: [], methods: :teaser).should == {'teaser' => 'StarCraft r'}
|
62
62
|
end
|
63
63
|
|
64
|
-
it "profiles" do
|
64
|
+
it "should use conversion profiles" do
|
65
65
|
Post.class_eval do
|
66
66
|
profile :public, only: [:text, :comments], methods: :teaser
|
67
67
|
end
|
68
68
|
|
69
69
|
post = build_post_with_comment
|
70
70
|
|
71
|
-
-> {post.
|
71
|
+
-> {post.to_hash(profile: :public)}.should raise_error(/profile :public not defined for Comment/)
|
72
72
|
|
73
73
|
Comment.class_eval do
|
74
74
|
profile :public
|
75
75
|
end
|
76
76
|
|
77
|
-
post.
|
77
|
+
post.to_hash(profile: :public).should == {
|
78
78
|
'text' => 'StarCraft releasing soon!',
|
79
79
|
'teaser' => 'StarCraft r',
|
80
80
|
'comments' => [
|
@@ -82,7 +82,7 @@ describe 'Model callbacks' do
|
|
82
82
|
]
|
83
83
|
}
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
it "should include errors" do
|
87
87
|
Post.class_eval do
|
88
88
|
validates_presence_of :token
|
@@ -90,30 +90,30 @@ describe 'Model callbacks' do
|
|
90
90
|
|
91
91
|
post = Post.new text: 'StarCraft releasing soon!'
|
92
92
|
post.valid?.should be_false
|
93
|
-
|
94
|
-
post.
|
93
|
+
|
94
|
+
post.to_hash.should == {
|
95
95
|
'text' => 'StarCraft releasing soon!',
|
96
96
|
'errors' => {"token" => ["can't be empty"]}
|
97
97
|
}
|
98
|
-
|
99
|
-
post.
|
98
|
+
|
99
|
+
post.to_hash(errors: false).should == {
|
100
100
|
'text' => 'StarCraft releasing soon!'
|
101
101
|
}
|
102
102
|
end
|
103
103
|
|
104
|
-
it "to_json" do
|
104
|
+
it "should convert to to_json" do
|
105
105
|
post = build_post_with_comment
|
106
106
|
rson = mock
|
107
107
|
rson.should_receive(:to_json).and_return(:ok)
|
108
|
-
post.should_receive(:
|
108
|
+
post.should_receive(:to_hash).with(only: :text).and_return(rson)
|
109
109
|
post.to_json(only: :text).should == :ok
|
110
110
|
end
|
111
111
|
|
112
|
-
it "to_xml" do
|
112
|
+
it "should convert to to_xml" do
|
113
113
|
post = build_post_with_comment
|
114
114
|
rson = mock
|
115
115
|
rson.should_receive(:to_xml).and_return(:ok)
|
116
|
-
post.should_receive(:
|
116
|
+
post.should_receive(:to_hash).with(only: :text).and_return(rson)
|
117
117
|
post.to_xml(only: :text).should == :ok
|
118
118
|
end
|
119
119
|
end
|