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,61 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# Specs ported from ActiveModel::Lint::Tests
|
|
4
|
+
module MongoModel
|
|
5
|
+
specs_for(Document, EmbeddedDocument) do
|
|
6
|
+
define_class(:TestModel, described_class)
|
|
7
|
+
|
|
8
|
+
subject { TestModel.new.to_model }
|
|
9
|
+
|
|
10
|
+
# valid?
|
|
11
|
+
# ------
|
|
12
|
+
#
|
|
13
|
+
# Returns a boolean that specifies whether the object is in a valid or invalid
|
|
14
|
+
# state.
|
|
15
|
+
it { should respond_to_boolean(:valid?) }
|
|
16
|
+
|
|
17
|
+
# new_record?
|
|
18
|
+
# -----------
|
|
19
|
+
#
|
|
20
|
+
# Returns a boolean that specifies whether the object has been persisted yet.
|
|
21
|
+
# This is used when calculating the URL for an object. If the object is
|
|
22
|
+
# not persisted, a form for that object, for instance, will be POSTed to the
|
|
23
|
+
# collection. If it is persisted, a form for the object will put PUTed to the
|
|
24
|
+
# URL for the object.
|
|
25
|
+
it { should respond_to_boolean(:new_record?) }
|
|
26
|
+
it { should respond_to_boolean(:destroyed?) }
|
|
27
|
+
|
|
28
|
+
# errors
|
|
29
|
+
# ------
|
|
30
|
+
#
|
|
31
|
+
# Returns an object that has :[] and :full_messages defined on it. See below
|
|
32
|
+
# for more details.
|
|
33
|
+
describe "errors" do
|
|
34
|
+
it { should respond_to(:errors) }
|
|
35
|
+
|
|
36
|
+
# Returns an Array of Strings that are the errors for the attribute in
|
|
37
|
+
# question. If localization is used, the Strings should be localized
|
|
38
|
+
# for the current locale. If no error is present, this method should
|
|
39
|
+
# return an empty Array.
|
|
40
|
+
describe "#[]" do
|
|
41
|
+
it "should return an Array" do
|
|
42
|
+
subject.errors[:hello].should be_an(Array)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns an Array of all error messages for the object. Each message
|
|
47
|
+
# should contain information about the field, if applicable.
|
|
48
|
+
describe "#full_messages" do
|
|
49
|
+
it "should return an Array" do
|
|
50
|
+
subject.errors.full_messages.should be_an(Array)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#model_name" do
|
|
56
|
+
it "should return an ActiveModel::Name object" do
|
|
57
|
+
TestModel.model_name.should == ActiveModel::Name.new('TestModel')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
shared_examples_for "assigning correct class to belongs_to association" do
|
|
5
|
+
define_class(:User, Document)
|
|
6
|
+
define_class(:SpecialUser, :User)
|
|
7
|
+
|
|
8
|
+
let(:user) { User.create! }
|
|
9
|
+
let(:special_user) { SpecialUser.create! }
|
|
10
|
+
|
|
11
|
+
subject { Article.new }
|
|
12
|
+
|
|
13
|
+
context "when uninitialized" do
|
|
14
|
+
it "should be nil" do
|
|
15
|
+
subject.user.should be_nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should be settable" do
|
|
19
|
+
subject.user = user
|
|
20
|
+
subject.user.should == user
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "setting a subclass type" do
|
|
24
|
+
it "should set successfully" do
|
|
25
|
+
subject.user = special_user
|
|
26
|
+
subject.user.should == special_user
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "when loading from database" do
|
|
32
|
+
subject { Article.new(:user => user) }
|
|
33
|
+
|
|
34
|
+
if specing?(EmbeddedDocument)
|
|
35
|
+
define_class(:ArticleParent, Document) do
|
|
36
|
+
property :article, Article
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
let(:parent) { ArticleParent.create!(:article => subject) }
|
|
40
|
+
let(:reloaded) { ArticleParent.find(parent.id).article }
|
|
41
|
+
else
|
|
42
|
+
before(:each) { subject.save! }
|
|
43
|
+
let(:reloaded) { Article.find(subject.id) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should access the user through the association" do
|
|
47
|
+
reloaded.user.should == user
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should allow the user to be reloaded" do
|
|
51
|
+
reloaded.user.inspect
|
|
52
|
+
reloaded.user.loaded?.should be_true
|
|
53
|
+
|
|
54
|
+
reloaded.user(true)
|
|
55
|
+
reloaded.user.loaded?.should be_false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "setting a subclass type" do
|
|
59
|
+
subject { Article.new(:user => special_user) }
|
|
60
|
+
|
|
61
|
+
it "should load successfully" do
|
|
62
|
+
reloaded.user.should == special_user
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
specs_for(Document, EmbeddedDocument) do
|
|
69
|
+
describe "belongs_to association" do
|
|
70
|
+
define_class(:Article, described_class) do
|
|
71
|
+
belongs_to :user
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it_should_behave_like "assigning correct class to belongs_to association"
|
|
75
|
+
|
|
76
|
+
describe "setting a different class type" do
|
|
77
|
+
define_class(:NonUser, Document)
|
|
78
|
+
|
|
79
|
+
let(:non_user) { NonUser.create! }
|
|
80
|
+
|
|
81
|
+
it "should raise a AssociationTypeMismatch exception" do
|
|
82
|
+
lambda { subject.user = non_user }.should raise_error(AssociationTypeMismatch, "expected instance of User but got NonUser")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe "#build_user" do
|
|
87
|
+
subject { Article.new }
|
|
88
|
+
|
|
89
|
+
let(:user) { subject.build_user(:id => '123') }
|
|
90
|
+
|
|
91
|
+
it "should return a new unsaved user with the given attributes" do
|
|
92
|
+
user.should be_an_instance_of(User)
|
|
93
|
+
user.should be_a_new_record
|
|
94
|
+
user.id.should == '123'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe "#create_user" do
|
|
99
|
+
subject { Article.new }
|
|
100
|
+
|
|
101
|
+
it "should return a new saved user with the given attributes" do
|
|
102
|
+
user = subject.create_user(:id => '123')
|
|
103
|
+
user.should be_an_instance_of(User)
|
|
104
|
+
user.should_not be_a_new_record
|
|
105
|
+
user.id.should == '123'
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe "polymorphic belongs_to association" do
|
|
111
|
+
define_class(:Article, described_class) do
|
|
112
|
+
belongs_to :user, :polymorphic => true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
define_class(:NonUser, Document)
|
|
116
|
+
|
|
117
|
+
let(:non_user) { NonUser.create! }
|
|
118
|
+
|
|
119
|
+
it_should_behave_like "assigning correct class to belongs_to association"
|
|
120
|
+
|
|
121
|
+
describe "setting a different class type" do
|
|
122
|
+
it "should set successfully" do
|
|
123
|
+
subject.user = non_user
|
|
124
|
+
subject.user.should == non_user
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "when loading from database" do
|
|
129
|
+
subject { Article.new(:user => user) }
|
|
130
|
+
|
|
131
|
+
if specing?(EmbeddedDocument)
|
|
132
|
+
define_class(:ArticleParent, Document) do
|
|
133
|
+
property :article, Article
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
let(:parent) { ArticleParent.create!(:article => subject) }
|
|
137
|
+
let(:reloaded) { ArticleParent.find(parent.id).article }
|
|
138
|
+
else
|
|
139
|
+
before(:each) { subject.save! }
|
|
140
|
+
let(:reloaded) { Article.find(subject.id) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe "setting a different class type" do
|
|
144
|
+
subject { Article.new(:user => non_user) }
|
|
145
|
+
|
|
146
|
+
it "should load successfully" do
|
|
147
|
+
reloaded.user.should == non_user
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(Document) do
|
|
5
|
+
describe "has_many association" do
|
|
6
|
+
define_class(:Book, Document) do
|
|
7
|
+
has_many :chapters
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should default to :by => :foreign_key" do
|
|
11
|
+
Book.associations[:chapters].should be_a(Associations::HasManyByForeignKey)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "has_many :by => :foreign_key association" do
|
|
16
|
+
define_class(:Chapter, Document) do
|
|
17
|
+
belongs_to :book
|
|
18
|
+
end
|
|
19
|
+
define_class(:IllustratedChapter, :Chapter)
|
|
20
|
+
define_class(:Book, Document) do
|
|
21
|
+
has_many :chapters, :by => :foreign_key
|
|
22
|
+
end
|
|
23
|
+
define_class(:NonChapter, Document)
|
|
24
|
+
|
|
25
|
+
let(:chapter1) { Chapter.create!(:id => '1') }
|
|
26
|
+
let(:chapter2) { IllustratedChapter.create!(:id => '2') }
|
|
27
|
+
let(:chapter3) { Chapter.create!(:id => '3') }
|
|
28
|
+
let(:nonchapter) { NonChapter.create! }
|
|
29
|
+
|
|
30
|
+
context "when uninitialized" do
|
|
31
|
+
subject { Book.new }
|
|
32
|
+
|
|
33
|
+
it "should be empty" do
|
|
34
|
+
subject.chapters.should be_empty
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
shared_examples_for "accessing and manipulating a has_many :by => :foreign_key association" do
|
|
39
|
+
it "should access chapters" do
|
|
40
|
+
subject.chapters.should include(chapter1, chapter2)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should access chapter ids through association" do
|
|
44
|
+
subject.chapters.ids.should include(chapter1.id, chapter2.id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should add chapters with <<" do
|
|
48
|
+
subject.chapters << chapter3
|
|
49
|
+
subject.chapters.should include(chapter1, chapter2, chapter3)
|
|
50
|
+
chapter3.book.should == subject
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should add/change chapters with []=" do
|
|
54
|
+
subject.chapters[2] = chapter3
|
|
55
|
+
subject.chapters.should include(chapter1, chapter2, chapter3)
|
|
56
|
+
chapter3.book.should == subject
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should add chapters with concat" do
|
|
60
|
+
subject.chapters.concat([chapter3])
|
|
61
|
+
subject.chapters.should include(chapter1, chapter2, chapter3)
|
|
62
|
+
chapter3.book.should == subject
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should insert chapters" do
|
|
66
|
+
subject.chapters.insert(1, chapter3)
|
|
67
|
+
subject.chapters.should include(chapter1, chapter2, chapter3)
|
|
68
|
+
chapter3.book.should == subject
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# it "should replace chapters" do
|
|
72
|
+
# subject.chapters.replace([chapter2, chapter3])
|
|
73
|
+
# subject.chapters.should == [chapter2, chapter3]
|
|
74
|
+
# subject.chapter_ids.should == [chapter2.id, chapter3.id]
|
|
75
|
+
# end
|
|
76
|
+
|
|
77
|
+
it "should add chapters with push" do
|
|
78
|
+
subject.chapters.push(chapter3)
|
|
79
|
+
subject.chapters.should include(chapter1, chapter2, chapter3)
|
|
80
|
+
chapter3.book.should == subject
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should add chapters with unshift" do
|
|
84
|
+
subject.chapters.unshift(chapter3)
|
|
85
|
+
subject.chapters.should include(chapter3, chapter1, chapter2)
|
|
86
|
+
chapter3.book.should == subject
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# it "should clear chapters" do
|
|
90
|
+
# subject.chapters.clear
|
|
91
|
+
# subject.chapters.should be_empty
|
|
92
|
+
# subject.chapter_ids.should be_empty
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# it "should remove chapters with delete" do
|
|
96
|
+
# subject.chapters.delete(chapter1)
|
|
97
|
+
# subject.chapters.should == [chapter2]
|
|
98
|
+
# subject.chapter_ids.should == [chapter2.id]
|
|
99
|
+
# end
|
|
100
|
+
#
|
|
101
|
+
# it "should remove chapters with delete_at" do
|
|
102
|
+
# subject.chapters.delete_at(0)
|
|
103
|
+
# subject.chapters.should == [chapter2]
|
|
104
|
+
# subject.chapter_ids.should == [chapter2.id]
|
|
105
|
+
# end
|
|
106
|
+
#
|
|
107
|
+
# it "should remove chapters with delete_if" do
|
|
108
|
+
# subject.chapters.delete_if { |c| c.id == chapter1.id }
|
|
109
|
+
# subject.chapters.should == [chapter2]
|
|
110
|
+
# subject.chapter_ids.should == [chapter2.id]
|
|
111
|
+
# end
|
|
112
|
+
|
|
113
|
+
it "should build a chapter" do
|
|
114
|
+
chapter4 = subject.chapters.build(:id => '4')
|
|
115
|
+
subject.chapters.should include(chapter1, chapter2, chapter4)
|
|
116
|
+
|
|
117
|
+
chapter4.should be_a_new_record
|
|
118
|
+
chapter4.id.should == '4'
|
|
119
|
+
chapter4.book.should == subject
|
|
120
|
+
chapter4.book_id.should == subject.id
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should create a chapter" do
|
|
124
|
+
chapter4 = subject.chapters.create(:id => '4')
|
|
125
|
+
subject.chapters.should == [chapter1, chapter2, chapter4]
|
|
126
|
+
|
|
127
|
+
chapter4.should_not be_a_new_record
|
|
128
|
+
chapter4.id.should == '4'
|
|
129
|
+
chapter4.book.should == subject
|
|
130
|
+
chapter4.book_id.should == subject.id
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should find chapters" do
|
|
134
|
+
# Create bogus chapters
|
|
135
|
+
Chapter.create!(:id => '999')
|
|
136
|
+
Chapter.create!(:id => '998')
|
|
137
|
+
|
|
138
|
+
result = subject.chapters.find(:all, :order => :id.desc)
|
|
139
|
+
result.should == [chapter2, chapter1]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context "with chapters set" do
|
|
144
|
+
subject { Book.new(:chapters => [chapter1, chapter2]) }
|
|
145
|
+
it_should_behave_like "accessing and manipulating a has_many :by => :foreign_key association"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context "when loaded from database" do
|
|
149
|
+
let(:book) { Book.create!(:chapters => [chapter1, chapter2]) }
|
|
150
|
+
subject { Book.find(book.id) }
|
|
151
|
+
it_should_behave_like "accessing and manipulating a has_many :by => :foreign_key association"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
specs_for(EmbeddedDocument) do
|
|
157
|
+
describe "defining a has_many :by => :foreign_key association" do
|
|
158
|
+
define_class(:Book, EmbeddedDocument)
|
|
159
|
+
|
|
160
|
+
it "should raise an exception" do
|
|
161
|
+
lambda { Book.has_many :chapters, :by => :foreign_key }.should raise_error
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MongoModel
|
|
4
|
+
specs_for(EmbeddedDocument) do
|
|
5
|
+
describe "has_many association" do
|
|
6
|
+
define_class(:Book, EmbeddedDocument) do
|
|
7
|
+
has_many :chapters
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should default to :by => :ids" do
|
|
11
|
+
Book.associations[:chapters].should be_a(Associations::HasManyByIds)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
specs_for(Document, EmbeddedDocument) do
|
|
17
|
+
describe "has_many :by => :ids association" do
|
|
18
|
+
define_class(:Chapter, Document)
|
|
19
|
+
define_class(:IllustratedChapter, :Chapter)
|
|
20
|
+
define_class(:Book, described_class) do
|
|
21
|
+
has_many :chapters, :by => :ids
|
|
22
|
+
end
|
|
23
|
+
define_class(:NonChapter, Document)
|
|
24
|
+
|
|
25
|
+
let(:chapter1) { Chapter.create!(:id => '1') }
|
|
26
|
+
let(:chapter2) { IllustratedChapter.create!(:id => '2') }
|
|
27
|
+
let(:chapter3) { Chapter.create!(:id => '3') }
|
|
28
|
+
let(:nonchapter) { NonChapter.create! }
|
|
29
|
+
|
|
30
|
+
context "when uninitialized" do
|
|
31
|
+
subject { Book.new }
|
|
32
|
+
|
|
33
|
+
it "should be empty" do
|
|
34
|
+
subject.chapters.should be_empty
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should have an empty ids array" do
|
|
38
|
+
subject.chapter_ids.should be_empty
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
shared_examples_for "accessing and manipulating a has_many :by => :ids association" do
|
|
43
|
+
it "should access chapters" do
|
|
44
|
+
subject.chapters.should == [chapter1, chapter2]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should access chapter ids through association" do
|
|
48
|
+
subject.chapters.ids.should == [chapter1.id, chapter2.id]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should have chapter ids" do
|
|
52
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should add chapters with <<" do
|
|
56
|
+
subject.chapters << chapter3
|
|
57
|
+
subject.chapters.should == [chapter1, chapter2, chapter3]
|
|
58
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter3.id]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should add/change chapters with []=" do
|
|
62
|
+
subject.chapters[2] = chapter3
|
|
63
|
+
subject.chapters.should == [chapter1, chapter2, chapter3]
|
|
64
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter3.id]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should add chapters with concat" do
|
|
68
|
+
subject.chapters.concat([chapter3])
|
|
69
|
+
subject.chapters.should == [chapter1, chapter2, chapter3]
|
|
70
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter3.id]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should insert chapters" do
|
|
74
|
+
subject.chapters.insert(1, chapter3)
|
|
75
|
+
subject.chapters.should == [chapter1, chapter3, chapter2]
|
|
76
|
+
subject.chapter_ids.should == [chapter1.id, chapter3.id, chapter2.id]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should replace chapters" do
|
|
80
|
+
subject.chapters.replace([chapter2, chapter3])
|
|
81
|
+
subject.chapters.should == [chapter2, chapter3]
|
|
82
|
+
subject.chapter_ids.should == [chapter2.id, chapter3.id]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should add chapters with push" do
|
|
86
|
+
subject.chapters.push(chapter3)
|
|
87
|
+
subject.chapters.should == [chapter1, chapter2, chapter3]
|
|
88
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter3.id]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should add chapters with unshift" do
|
|
92
|
+
subject.chapters.unshift(chapter3)
|
|
93
|
+
subject.chapters.should == [chapter3, chapter1, chapter2]
|
|
94
|
+
subject.chapter_ids.should == [chapter3.id, chapter1.id, chapter2.id]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should clear chapters" do
|
|
98
|
+
subject.chapters.clear
|
|
99
|
+
subject.chapters.should be_empty
|
|
100
|
+
subject.chapter_ids.should be_empty
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should remove chapters with delete" do
|
|
104
|
+
subject.chapters.delete(chapter1)
|
|
105
|
+
subject.chapters.should == [chapter2]
|
|
106
|
+
subject.chapter_ids.should == [chapter2.id]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should remove chapters with delete_at" do
|
|
110
|
+
subject.chapters.delete_at(0)
|
|
111
|
+
subject.chapters.should == [chapter2]
|
|
112
|
+
subject.chapter_ids.should == [chapter2.id]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should remove chapters with delete_if" do
|
|
116
|
+
subject.chapters.delete_if { |c| c.id == chapter1.id }
|
|
117
|
+
subject.chapters.should == [chapter2]
|
|
118
|
+
subject.chapter_ids.should == [chapter2.id]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should build a chapter" do
|
|
122
|
+
chapter4 = subject.chapters.build(:id => '4')
|
|
123
|
+
subject.chapters.should == [chapter1, chapter2, chapter4]
|
|
124
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter4.id]
|
|
125
|
+
|
|
126
|
+
chapter4.should be_a_new_record
|
|
127
|
+
chapter4.id.should == '4'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "should create a chapter" do
|
|
131
|
+
chapter4 = subject.chapters.create(:id => '4')
|
|
132
|
+
subject.chapters.should == [chapter1, chapter2, chapter4]
|
|
133
|
+
subject.chapter_ids.should == [chapter1.id, chapter2.id, chapter4.id]
|
|
134
|
+
|
|
135
|
+
chapter4.should_not be_a_new_record
|
|
136
|
+
chapter4.id.should == '4'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should find chapters" do
|
|
140
|
+
# Create bogus chapters
|
|
141
|
+
Chapter.create!(:id => '999')
|
|
142
|
+
Chapter.create!(:id => '998')
|
|
143
|
+
|
|
144
|
+
result = subject.chapters.find(:all, :order => :id.desc)
|
|
145
|
+
result.should == [chapter2, chapter1]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "adding a non-chapter" do
|
|
149
|
+
def self.should_raise(message, &block)
|
|
150
|
+
it "should raise an AsssociationTypeMismatch error when #{message}" do
|
|
151
|
+
lambda { instance_eval(&block) }.should raise_error(AssociationTypeMismatch, "expected instance of Chapter but got NonChapter")
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
should_raise("assigning an array containing non-chapters") { subject.chapters = [nonchapter] }
|
|
156
|
+
should_raise("adding a non-chapter using <<") { subject.chapters << nonchapter }
|
|
157
|
+
should_raise("adding non-chapters with concat") { subject.chapters.concat([nonchapter]) }
|
|
158
|
+
should_raise("inserting chapters") { subject.chapters.insert(1, nonchapter) }
|
|
159
|
+
should_raise("replacing chapters") { subject.chapters.replace([nonchapter]) }
|
|
160
|
+
should_raise("addding chapters with push") { subject.chapters.push(nonchapter) }
|
|
161
|
+
should_raise("addding chapters with unshift") { subject.chapters.unshift(nonchapter) }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
context "with chapters set" do
|
|
166
|
+
subject { Book.new(:chapters => [chapter1, chapter2]) }
|
|
167
|
+
it_should_behave_like "accessing and manipulating a has_many :by => :ids association"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
context "with chapter ids set" do
|
|
171
|
+
subject { Book.new(:chapter_ids => [chapter1.id, chapter2.id]) }
|
|
172
|
+
it_should_behave_like "accessing and manipulating a has_many :by => :ids association"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
context "when loaded from database" do
|
|
176
|
+
if specing?(Document)
|
|
177
|
+
let(:book) { Book.create!(:chapter_ids => [chapter1.id, chapter2.id]) }
|
|
178
|
+
subject { Book.find(book.id) }
|
|
179
|
+
else
|
|
180
|
+
define_class(:Bookshelf, Document) do
|
|
181
|
+
property :book, Book
|
|
182
|
+
end
|
|
183
|
+
let(:book) { Book.new(:chapter_ids => [chapter1.id, chapter2.id]) }
|
|
184
|
+
let(:shelf) { Bookshelf.create!(:book => book) }
|
|
185
|
+
subject { Bookshelf.find(shelf.id).book }
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it_should_behave_like "accessing and manipulating a has_many :by => :ids association"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|