mongomodel 0.1
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/LICENSE +22 -0
- data/README.md +34 -0
- data/Rakefile +47 -0
- data/bin/console +45 -0
- data/lib/mongomodel.rb +92 -0
- data/lib/mongomodel/attributes/mongo.rb +40 -0
- data/lib/mongomodel/attributes/store.rb +30 -0
- data/lib/mongomodel/attributes/typecasting.rb +51 -0
- data/lib/mongomodel/concerns/abstract_class.rb +17 -0
- data/lib/mongomodel/concerns/activemodel.rb +11 -0
- data/lib/mongomodel/concerns/associations.rb +103 -0
- data/lib/mongomodel/concerns/associations/base/association.rb +33 -0
- data/lib/mongomodel/concerns/associations/base/definition.rb +56 -0
- data/lib/mongomodel/concerns/associations/base/proxy.rb +58 -0
- data/lib/mongomodel/concerns/associations/belongs_to.rb +68 -0
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +159 -0
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +175 -0
- data/lib/mongomodel/concerns/attribute_methods.rb +55 -0
- data/lib/mongomodel/concerns/attribute_methods/before_type_cast.rb +29 -0
- data/lib/mongomodel/concerns/attribute_methods/dirty.rb +35 -0
- data/lib/mongomodel/concerns/attribute_methods/protected.rb +127 -0
- data/lib/mongomodel/concerns/attribute_methods/query.rb +22 -0
- data/lib/mongomodel/concerns/attribute_methods/read.rb +29 -0
- data/lib/mongomodel/concerns/attribute_methods/write.rb +29 -0
- data/lib/mongomodel/concerns/attributes.rb +85 -0
- data/lib/mongomodel/concerns/callbacks.rb +294 -0
- data/lib/mongomodel/concerns/logging.rb +15 -0
- data/lib/mongomodel/concerns/pretty_inspect.rb +29 -0
- data/lib/mongomodel/concerns/properties.rb +69 -0
- data/lib/mongomodel/concerns/record_status.rb +42 -0
- data/lib/mongomodel/concerns/timestamps.rb +32 -0
- data/lib/mongomodel/concerns/validations.rb +38 -0
- data/lib/mongomodel/concerns/validations/associated.rb +46 -0
- data/lib/mongomodel/document.rb +20 -0
- data/lib/mongomodel/document/callbacks.rb +46 -0
- data/lib/mongomodel/document/dynamic_finders.rb +88 -0
- data/lib/mongomodel/document/finders.rb +82 -0
- data/lib/mongomodel/document/indexes.rb +91 -0
- data/lib/mongomodel/document/optimistic_locking.rb +48 -0
- data/lib/mongomodel/document/persistence.rb +143 -0
- data/lib/mongomodel/document/scopes.rb +161 -0
- data/lib/mongomodel/document/validations.rb +68 -0
- data/lib/mongomodel/document/validations/uniqueness.rb +78 -0
- data/lib/mongomodel/embedded_document.rb +42 -0
- data/lib/mongomodel/locale/en.yml +55 -0
- data/lib/mongomodel/support/collection.rb +109 -0
- data/lib/mongomodel/support/configuration.rb +35 -0
- data/lib/mongomodel/support/core_extensions.rb +10 -0
- data/lib/mongomodel/support/exceptions.rb +25 -0
- data/lib/mongomodel/support/mongo_options.rb +177 -0
- data/lib/mongomodel/support/types.rb +35 -0
- data/lib/mongomodel/support/types/array.rb +11 -0
- data/lib/mongomodel/support/types/boolean.rb +25 -0
- data/lib/mongomodel/support/types/custom.rb +38 -0
- data/lib/mongomodel/support/types/date.rb +20 -0
- data/lib/mongomodel/support/types/float.rb +13 -0
- data/lib/mongomodel/support/types/hash.rb +18 -0
- data/lib/mongomodel/support/types/integer.rb +13 -0
- data/lib/mongomodel/support/types/object.rb +21 -0
- data/lib/mongomodel/support/types/string.rb +9 -0
- data/lib/mongomodel/support/types/symbol.rb +9 -0
- data/lib/mongomodel/support/types/time.rb +12 -0
- data/lib/mongomodel/version.rb +3 -0
- data/spec/mongomodel/attributes/store_spec.rb +273 -0
- data/spec/mongomodel/concerns/activemodel_spec.rb +61 -0
- data/spec/mongomodel/concerns/associations/belongs_to_spec.rb +153 -0
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +165 -0
- data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +192 -0
- data/spec/mongomodel/concerns/attribute_methods/before_type_cast_spec.rb +46 -0
- data/spec/mongomodel/concerns/attribute_methods/dirty_spec.rb +131 -0
- data/spec/mongomodel/concerns/attribute_methods/protected_spec.rb +86 -0
- data/spec/mongomodel/concerns/attribute_methods/query_spec.rb +27 -0
- data/spec/mongomodel/concerns/attribute_methods/read_spec.rb +52 -0
- data/spec/mongomodel/concerns/attribute_methods/write_spec.rb +43 -0
- data/spec/mongomodel/concerns/attributes_spec.rb +152 -0
- data/spec/mongomodel/concerns/callbacks_spec.rb +90 -0
- data/spec/mongomodel/concerns/logging_spec.rb +20 -0
- data/spec/mongomodel/concerns/pretty_inspect_spec.rb +68 -0
- data/spec/mongomodel/concerns/properties_spec.rb +29 -0
- data/spec/mongomodel/concerns/timestamps_spec.rb +170 -0
- data/spec/mongomodel/concerns/validations_spec.rb +159 -0
- data/spec/mongomodel/document/callbacks_spec.rb +80 -0
- data/spec/mongomodel/document/dynamic_finders_spec.rb +183 -0
- data/spec/mongomodel/document/finders_spec.rb +231 -0
- data/spec/mongomodel/document/indexes_spec.rb +121 -0
- data/spec/mongomodel/document/optimistic_locking_spec.rb +57 -0
- data/spec/mongomodel/document/persistence_spec.rb +319 -0
- data/spec/mongomodel/document/scopes_spec.rb +204 -0
- data/spec/mongomodel/document/validations/uniqueness_spec.rb +217 -0
- data/spec/mongomodel/document/validations_spec.rb +132 -0
- data/spec/mongomodel/document_spec.rb +74 -0
- data/spec/mongomodel/embedded_document_spec.rb +66 -0
- data/spec/mongomodel/mongomodel_spec.rb +33 -0
- data/spec/mongomodel/support/collection_spec.rb +248 -0
- data/spec/mongomodel/support/mongo_options_spec.rb +295 -0
- data/spec/mongomodel/support/property_spec.rb +83 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/specdoc.opts +6 -0
- data/spec/support/callbacks.rb +44 -0
- data/spec/support/helpers/define_class.rb +24 -0
- data/spec/support/helpers/specs_for.rb +11 -0
- data/spec/support/matchers/be_a_subclass_of.rb +5 -0
- data/spec/support/matchers/respond_to_boolean.rb +17 -0
- data/spec/support/matchers/run_callbacks.rb +20 -0
- data/spec/support/models.rb +23 -0
- data/spec/support/time.rb +6 -0
- metadata +232 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(EmbeddedDocument) do
|
|
5
|
+
describe "callbacks" do
|
|
6
|
+
define_class(:ChildDocument, EmbeddedDocument) do
|
|
7
|
+
include MongoModel::CallbackHelpers
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
define_class(:ParentDocument, Document) do
|
|
11
|
+
property :child, ChildDocument
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:child) { ChildDocument.new }
|
|
15
|
+
let(:parent) { ParentDocument.create!(:child => child) }
|
|
16
|
+
|
|
17
|
+
it "should run each type of callback when initializing" do
|
|
18
|
+
instance = ChildDocument.new
|
|
19
|
+
instance.should run_callbacks(:after_initialize)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should run each type of callback on find" do
|
|
23
|
+
instance = ParentDocument.find(parent.id).child
|
|
24
|
+
instance.should run_callbacks(:after_initialize, :after_find)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should run each type of callback when validating a new document" do
|
|
28
|
+
instance = ChildDocument.new
|
|
29
|
+
instance.valid?
|
|
30
|
+
instance.should run_callbacks(:after_initialize, :before_validation, :after_validation)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should run each type of callback when validating an existing document" do
|
|
34
|
+
instance = ParentDocument.find(parent.id).child
|
|
35
|
+
instance.valid?
|
|
36
|
+
instance.should run_callbacks(:after_initialize, :after_find, :before_validation, :after_validation)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should run each type of callback when creating a document" do
|
|
40
|
+
instance = ParentDocument.create!(:child => ChildDocument.new)
|
|
41
|
+
instance.child.should run_callbacks(:after_initialize, :before_validation, :after_validation, :before_save, :before_create, :after_create, :after_save)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should run each type of callback when saving an existing document" do
|
|
45
|
+
instance = ParentDocument.find(parent.id)
|
|
46
|
+
instance.save
|
|
47
|
+
instance.child.should run_callbacks(:after_initialize, :after_find, :before_validation, :after_validation, :before_save, :before_update, :after_update, :after_save)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should run each type of callback when destroying a document" do
|
|
51
|
+
instance = ParentDocument.find(parent.id)
|
|
52
|
+
instance.destroy
|
|
53
|
+
instance.child.should run_callbacks(:after_initialize, :after_find, :before_destroy, :after_destroy)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should not run destroy callbacks when deleting a document" do
|
|
57
|
+
instance = ParentDocument.find(parent.id)
|
|
58
|
+
instance.delete
|
|
59
|
+
instance.child.should run_callbacks(:after_initialize, :after_find)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
[ :before_save, :before_create ].each do |callback|
|
|
64
|
+
context "#{callback} callback return false" do
|
|
65
|
+
define_class(:ChildDocument, EmbeddedDocument) do
|
|
66
|
+
send(callback) { false }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
define_class(:ParentDocument, Document) do
|
|
70
|
+
property :child, ChildDocument
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
let(:child) { ChildDocument.new }
|
|
74
|
+
let(:parent) { ParentDocument.new(:child => child) }
|
|
75
|
+
|
|
76
|
+
describe "#save" do
|
|
77
|
+
it "should return false" do
|
|
78
|
+
parent.save.should be_false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "#save!" do
|
|
83
|
+
it "should raise a MongoModel::DocumentNotSaved exception" do
|
|
84
|
+
lambda { parent.save! }.should raise_error(MongoModel::DocumentNotSaved)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
|
5
|
+
describe "logging" do
|
|
6
|
+
define_class(:TestDocument, described_class)
|
|
7
|
+
|
|
8
|
+
let(:logger) { mock('logger') }
|
|
9
|
+
before(:all) { MongoModel.logger = logger }
|
|
10
|
+
|
|
11
|
+
it "should have a logger reader on the class" do
|
|
12
|
+
TestDocument.logger.should == logger
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should have a logger reader on the instance" do
|
|
16
|
+
TestDocument.new.logger.should == logger
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
|
5
|
+
describe "#inspect" do
|
|
6
|
+
context "on base class" do
|
|
7
|
+
it "should return class name" do
|
|
8
|
+
described_class.inspect.should == described_class.name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "on subclasses" do
|
|
13
|
+
context "without properties" do
|
|
14
|
+
define_class(:TestDocument, described_class)
|
|
15
|
+
|
|
16
|
+
it "should return class name" do
|
|
17
|
+
TestDocument.inspect.should == 'TestDocument()'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "with properties" do
|
|
22
|
+
define_class(:TestDocument, described_class) do
|
|
23
|
+
property :name, String
|
|
24
|
+
property :age, Integer
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should return class name and property definitions" do
|
|
28
|
+
TestDocument.inspect.should == 'TestDocument(name: String, age: Integer)'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
specs_for(Document) do
|
|
36
|
+
describe "#inspect" do
|
|
37
|
+
context "on subclass instances" do
|
|
38
|
+
define_class(:TestDocument, Document) do
|
|
39
|
+
property :name, String
|
|
40
|
+
property :age, Integer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
subject { TestDocument.new(:id => 'abc-123', :name => 'Doc name', :age => 54) }
|
|
44
|
+
|
|
45
|
+
it "should return class name and property values" do
|
|
46
|
+
subject.inspect.should == '#<TestDocument id: abc-123, name: "Doc name", age: 54>'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
specs_for(EmbeddedDocument) do
|
|
53
|
+
describe "#inspect" do
|
|
54
|
+
context "on subclass instances" do
|
|
55
|
+
define_class(:TestDocument, EmbeddedDocument) do
|
|
56
|
+
property :name, String
|
|
57
|
+
property :age, Integer
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
subject { TestDocument.new(:name => 'Doc name', :age => 54) }
|
|
61
|
+
|
|
62
|
+
it "should return class name and property values" do
|
|
63
|
+
subject.inspect.should == '#<TestDocument name: "Doc name", age: 54>'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
|
5
|
+
define_class(:Person, described_class) do
|
|
6
|
+
property :name, String
|
|
7
|
+
property :age, Integer, :default => 21
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
define_class(:SkilledPerson, :Person) do
|
|
11
|
+
property :skill, String
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should have a populated hash of properties" do
|
|
15
|
+
Person.properties.should include({
|
|
16
|
+
:name => Properties::Property.new(:name, String),
|
|
17
|
+
:age => Properties::Property.new(:age, Integer, :default => 21)
|
|
18
|
+
})
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should extend properties in subclasses" do
|
|
22
|
+
SkilledPerson.properties.should include({
|
|
23
|
+
:name => Properties::Property.new(:name, String),
|
|
24
|
+
:age => Properties::Property.new(:age, Integer, :default => 21),
|
|
25
|
+
:skill => Properties::Property.new(:skill, String)
|
|
26
|
+
})
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
|
5
|
+
describe "#timestamps!" do
|
|
6
|
+
define_class(:TestDocument, described_class) do
|
|
7
|
+
timestamps!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should define a Time property updated_at" do
|
|
11
|
+
TestDocument.properties.should include(:updated_at)
|
|
12
|
+
TestDocument.properties[:updated_at].type.should == Time
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should define a Time property created_at" do
|
|
16
|
+
TestDocument.properties.should include(:created_at)
|
|
17
|
+
TestDocument.properties[:created_at].type.should == Time
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "with updated_at property" do
|
|
22
|
+
define_class(:TestDocument, described_class) do
|
|
23
|
+
property :updated_at, Time
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if specing?(EmbeddedDocument)
|
|
27
|
+
define_class(:ParentDocument, Document) do
|
|
28
|
+
property :child, TestDocument
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
let(:parent) { ParentDocument.new(:child => subject) }
|
|
32
|
+
let(:doc) { parent }
|
|
33
|
+
else
|
|
34
|
+
let(:doc) { subject }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subject { TestDocument.new }
|
|
38
|
+
|
|
39
|
+
before(:each) do
|
|
40
|
+
@now = Time.now.utc
|
|
41
|
+
Time.stub!(:now).and_return(@now)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should set the updated_at property to the current time when saved" do
|
|
45
|
+
doc.save
|
|
46
|
+
subject.updated_at.should == @now
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "with updated_on property" do
|
|
51
|
+
define_class(:TestDocument, described_class) do
|
|
52
|
+
property :updated_on, Date
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if specing?(EmbeddedDocument)
|
|
56
|
+
define_class(:ParentDocument, Document) do
|
|
57
|
+
property :child, TestDocument
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
let(:parent) { ParentDocument.new(:child => subject) }
|
|
61
|
+
let(:doc) { parent }
|
|
62
|
+
else
|
|
63
|
+
let(:doc) { subject }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
subject { TestDocument.new }
|
|
67
|
+
|
|
68
|
+
it "should set the updated_on property to the current date when saved" do
|
|
69
|
+
doc.save
|
|
70
|
+
subject.updated_on.should == Date.today
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context "with created_at property" do
|
|
75
|
+
define_class(:TestDocument, described_class) do
|
|
76
|
+
property :created_at, Time
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if specing?(EmbeddedDocument)
|
|
80
|
+
define_class(:ParentDocument, Document) do
|
|
81
|
+
property :child, TestDocument
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
let(:parent) { ParentDocument.new(:child => subject) }
|
|
85
|
+
let(:doc) { parent }
|
|
86
|
+
else
|
|
87
|
+
let(:doc) { subject }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
subject { TestDocument.new }
|
|
91
|
+
|
|
92
|
+
before(:each) do
|
|
93
|
+
@now = Time.now.utc
|
|
94
|
+
Time.stub!(:now).and_return(@now)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should set the created_at property to the current time when created" do
|
|
98
|
+
doc.save
|
|
99
|
+
subject.created_at.should == @now
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should not change the created_at property when updated" do
|
|
103
|
+
@next = 1.day.from_now
|
|
104
|
+
|
|
105
|
+
Time.stub!(:now).and_return(@now)
|
|
106
|
+
|
|
107
|
+
doc.save
|
|
108
|
+
|
|
109
|
+
Time.stub!(:now).and_return(@next)
|
|
110
|
+
|
|
111
|
+
doc.save
|
|
112
|
+
subject.created_at.should == @now
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should preserve created_at attribute when set explicitly" do
|
|
116
|
+
@a_year_ago = 1.year.ago
|
|
117
|
+
|
|
118
|
+
subject.created_at = @a_year_ago
|
|
119
|
+
doc.save
|
|
120
|
+
|
|
121
|
+
subject.created_at.should == @a_year_ago
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context "with created_on property" do
|
|
126
|
+
define_class(:TestDocument, described_class) do
|
|
127
|
+
property :created_on, Date
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if specing?(EmbeddedDocument)
|
|
131
|
+
define_class(:ParentDocument, Document) do
|
|
132
|
+
property :child, TestDocument
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
let(:parent) { ParentDocument.new(:child => subject) }
|
|
136
|
+
let(:doc) { parent }
|
|
137
|
+
else
|
|
138
|
+
let(:doc) { subject }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
subject { TestDocument.new }
|
|
142
|
+
|
|
143
|
+
it "should set the created_on property to the current date when created" do
|
|
144
|
+
doc.save
|
|
145
|
+
subject.created_on.should == Date.today
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "should not change the created_on property when updated" do
|
|
149
|
+
doc.save
|
|
150
|
+
|
|
151
|
+
@today = Date.today
|
|
152
|
+
@tomorrow = 1.day.from_now
|
|
153
|
+
|
|
154
|
+
Time.stub!(:now).and_return(@tomorrow)
|
|
155
|
+
|
|
156
|
+
doc.save
|
|
157
|
+
subject.created_on.should == @today
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should preserve created_on attribute when set explicitly" do
|
|
161
|
+
@a_year_ago = 1.year.ago.to_date
|
|
162
|
+
|
|
163
|
+
subject.created_on = @a_year_ago
|
|
164
|
+
doc.save
|
|
165
|
+
|
|
166
|
+
subject.created_on.should == @a_year_ago
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
module ValidationHelpers
|
|
5
|
+
def clear_validations!
|
|
6
|
+
reset_callbacks(:validate)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
specs_for(Document, EmbeddedDocument) do
|
|
11
|
+
describe "validations" do
|
|
12
|
+
define_class(:TestDocument, described_class) do
|
|
13
|
+
property :title, String
|
|
14
|
+
validates_presence_of :title
|
|
15
|
+
|
|
16
|
+
extend MongoModel::ValidationHelpers
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if specing?(EmbeddedDocument)
|
|
20
|
+
define_class(:ParentDocument, Document) do
|
|
21
|
+
property :child, TestDocument
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:child) { TestDocument.new }
|
|
25
|
+
let(:parent) { ParentDocument.new(:child => child) }
|
|
26
|
+
let(:doc) { parent }
|
|
27
|
+
|
|
28
|
+
subject { parent.child }
|
|
29
|
+
else
|
|
30
|
+
let(:doc) { TestDocument.new }
|
|
31
|
+
|
|
32
|
+
subject { doc }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should have an errors collection" do
|
|
36
|
+
subject.errors.should be_an_instance_of(ActiveModel::Errors)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "when validations are not met" do
|
|
40
|
+
before(:each) { subject.valid? }
|
|
41
|
+
|
|
42
|
+
it { should_not be_valid }
|
|
43
|
+
it { should have(1).error }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "validation on create" do
|
|
47
|
+
before(:each) do
|
|
48
|
+
TestDocument.clear_validations!
|
|
49
|
+
TestDocument.validates_presence_of :title, :on => :create
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "new document" do
|
|
53
|
+
it { should_not be_valid }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "existing document" do
|
|
57
|
+
before(:each) do
|
|
58
|
+
subject.title = 'Valid title'
|
|
59
|
+
doc.save!
|
|
60
|
+
subject.title = nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it { should be_valid }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "validation on update" do
|
|
68
|
+
before(:each) do
|
|
69
|
+
TestDocument.clear_validations!
|
|
70
|
+
TestDocument.validates_presence_of :title, :on => :update
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "new document" do
|
|
74
|
+
it { should be_valid }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context "existing document" do
|
|
78
|
+
before(:each) do
|
|
79
|
+
subject.title = 'Valid title'
|
|
80
|
+
doc.save!
|
|
81
|
+
subject.title = nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it { should_not be_valid }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "validation shortcuts" do
|
|
90
|
+
define_class(:TestDocument, described_class)
|
|
91
|
+
|
|
92
|
+
describe ":required => true" do
|
|
93
|
+
it "should add a validates_presence_of validation" do
|
|
94
|
+
TestDocument.should_receive(:validates_presence_of).with(:title)
|
|
95
|
+
TestDocument.property :title, String, :required => true
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe ":format => /regex/" do
|
|
100
|
+
it "should add a validates_format_of validation" do
|
|
101
|
+
TestDocument.should_receive(:validates_format_of).with(:email, /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i)
|
|
102
|
+
TestDocument.property :email, String, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
specs_for(EmbeddedDocument) do
|
|
109
|
+
describe "validations" do
|
|
110
|
+
define_class(:ChildDocument, EmbeddedDocument) do
|
|
111
|
+
property :title, String
|
|
112
|
+
validates_presence_of :title
|
|
113
|
+
|
|
114
|
+
extend MongoModel::ValidationHelpers
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
define_class(:ParentDocument, Document) do
|
|
118
|
+
property :child, ChildDocument
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
let(:child) { ChildDocument.new }
|
|
122
|
+
let(:parent) { ParentDocument.new(:child => child) }
|
|
123
|
+
|
|
124
|
+
subject { parent.child }
|
|
125
|
+
|
|
126
|
+
context "child document is invalid" do
|
|
127
|
+
before(:each) { parent.valid? }
|
|
128
|
+
|
|
129
|
+
specify "parent should be invalid" do
|
|
130
|
+
parent.should_not be_valid
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
specify "parent should have error on child" do
|
|
134
|
+
parent.should have(1).error
|
|
135
|
+
parent.errors[:child].should_not be_nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
specify "child should be invalid" do
|
|
139
|
+
child.should_not be_valid
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
specify "child should have error" do
|
|
143
|
+
child.errors[:title].should_not be_nil
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context "parent has no child" do
|
|
148
|
+
before(:each) do
|
|
149
|
+
parent.child = nil
|
|
150
|
+
parent.valid?
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
specify "parent should be valid" do
|
|
154
|
+
parent.should be_valid
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|