sam-dm-core 0.9.6
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/.autotest +26 -0
- data/CONTRIBUTING +51 -0
- data/FAQ +92 -0
- data/History.txt +145 -0
- data/MIT-LICENSE +22 -0
- data/Manifest.txt +125 -0
- data/QUICKLINKS +12 -0
- data/README.txt +143 -0
- data/Rakefile +30 -0
- data/SPECS +63 -0
- data/TODO +1 -0
- data/lib/dm-core.rb +224 -0
- data/lib/dm-core/adapters.rb +4 -0
- data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
- data/lib/dm-core/adapters/data_objects_adapter.rb +707 -0
- data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
- data/lib/dm-core/adapters/postgres_adapter.rb +188 -0
- data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
- data/lib/dm-core/associations.rb +199 -0
- data/lib/dm-core/associations/many_to_many.rb +147 -0
- data/lib/dm-core/associations/many_to_one.rb +107 -0
- data/lib/dm-core/associations/one_to_many.rb +309 -0
- data/lib/dm-core/associations/one_to_one.rb +61 -0
- data/lib/dm-core/associations/relationship.rb +218 -0
- data/lib/dm-core/associations/relationship_chain.rb +81 -0
- data/lib/dm-core/auto_migrations.rb +113 -0
- data/lib/dm-core/collection.rb +638 -0
- data/lib/dm-core/dependency_queue.rb +31 -0
- data/lib/dm-core/hook.rb +11 -0
- data/lib/dm-core/identity_map.rb +45 -0
- data/lib/dm-core/is.rb +16 -0
- data/lib/dm-core/logger.rb +232 -0
- data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
- data/lib/dm-core/migrator.rb +29 -0
- data/lib/dm-core/model.rb +471 -0
- data/lib/dm-core/naming_conventions.rb +84 -0
- data/lib/dm-core/property.rb +673 -0
- data/lib/dm-core/property_set.rb +162 -0
- data/lib/dm-core/query.rb +625 -0
- data/lib/dm-core/repository.rb +159 -0
- data/lib/dm-core/resource.rb +637 -0
- data/lib/dm-core/scope.rb +58 -0
- data/lib/dm-core/support.rb +7 -0
- data/lib/dm-core/support/array.rb +13 -0
- data/lib/dm-core/support/assertions.rb +8 -0
- data/lib/dm-core/support/errors.rb +23 -0
- data/lib/dm-core/support/kernel.rb +7 -0
- data/lib/dm-core/support/symbol.rb +41 -0
- data/lib/dm-core/transaction.rb +267 -0
- data/lib/dm-core/type.rb +160 -0
- data/lib/dm-core/type_map.rb +80 -0
- data/lib/dm-core/types.rb +19 -0
- data/lib/dm-core/types/boolean.rb +7 -0
- data/lib/dm-core/types/discriminator.rb +34 -0
- data/lib/dm-core/types/object.rb +24 -0
- data/lib/dm-core/types/paranoid_boolean.rb +34 -0
- data/lib/dm-core/types/paranoid_datetime.rb +33 -0
- data/lib/dm-core/types/serial.rb +9 -0
- data/lib/dm-core/types/text.rb +10 -0
- data/lib/dm-core/version.rb +3 -0
- data/script/all +5 -0
- data/script/performance.rb +203 -0
- data/script/profile.rb +87 -0
- data/spec/integration/association_spec.rb +1371 -0
- data/spec/integration/association_through_spec.rb +203 -0
- data/spec/integration/associations/many_to_many_spec.rb +449 -0
- data/spec/integration/associations/many_to_one_spec.rb +163 -0
- data/spec/integration/associations/one_to_many_spec.rb +151 -0
- data/spec/integration/auto_migrations_spec.rb +398 -0
- data/spec/integration/collection_spec.rb +1069 -0
- data/spec/integration/data_objects_adapter_spec.rb +32 -0
- data/spec/integration/dependency_queue_spec.rb +58 -0
- data/spec/integration/model_spec.rb +127 -0
- data/spec/integration/mysql_adapter_spec.rb +85 -0
- data/spec/integration/postgres_adapter_spec.rb +731 -0
- data/spec/integration/property_spec.rb +233 -0
- data/spec/integration/query_spec.rb +506 -0
- data/spec/integration/repository_spec.rb +57 -0
- data/spec/integration/resource_spec.rb +475 -0
- data/spec/integration/sqlite3_adapter_spec.rb +352 -0
- data/spec/integration/sti_spec.rb +208 -0
- data/spec/integration/strategic_eager_loading_spec.rb +138 -0
- data/spec/integration/transaction_spec.rb +75 -0
- data/spec/integration/type_spec.rb +271 -0
- data/spec/lib/logging_helper.rb +18 -0
- data/spec/lib/mock_adapter.rb +27 -0
- data/spec/lib/model_loader.rb +91 -0
- data/spec/lib/publicize_methods.rb +28 -0
- data/spec/models/vehicles.rb +34 -0
- data/spec/models/zoo.rb +47 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
- data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
- data/spec/unit/adapters/data_objects_adapter_spec.rb +628 -0
- data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
- data/spec/unit/associations/many_to_many_spec.rb +17 -0
- data/spec/unit/associations/many_to_one_spec.rb +152 -0
- data/spec/unit/associations/one_to_many_spec.rb +393 -0
- data/spec/unit/associations/one_to_one_spec.rb +7 -0
- data/spec/unit/associations/relationship_spec.rb +71 -0
- data/spec/unit/associations_spec.rb +242 -0
- data/spec/unit/auto_migrations_spec.rb +111 -0
- data/spec/unit/collection_spec.rb +182 -0
- data/spec/unit/data_mapper_spec.rb +35 -0
- data/spec/unit/identity_map_spec.rb +126 -0
- data/spec/unit/is_spec.rb +80 -0
- data/spec/unit/migrator_spec.rb +33 -0
- data/spec/unit/model_spec.rb +339 -0
- data/spec/unit/naming_conventions_spec.rb +36 -0
- data/spec/unit/property_set_spec.rb +83 -0
- data/spec/unit/property_spec.rb +753 -0
- data/spec/unit/query_spec.rb +530 -0
- data/spec/unit/repository_spec.rb +93 -0
- data/spec/unit/resource_spec.rb +626 -0
- data/spec/unit/scope_spec.rb +142 -0
- data/spec/unit/transaction_spec.rb +493 -0
- data/spec/unit/type_map_spec.rb +114 -0
- data/spec/unit/type_spec.rb +119 -0
- data/tasks/ci.rb +68 -0
- data/tasks/dm.rb +63 -0
- data/tasks/doc.rb +20 -0
- data/tasks/gemspec.rb +23 -0
- data/tasks/hoe.rb +46 -0
- data/tasks/install.rb +20 -0
- metadata +216 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
if ADAPTER
|
4
|
+
describe 'through-associations' do
|
5
|
+
before :all do
|
6
|
+
repository(ADAPTER) do
|
7
|
+
class Tag
|
8
|
+
include DataMapper::Resource
|
9
|
+
def self.default_repository_name
|
10
|
+
ADAPTER
|
11
|
+
end
|
12
|
+
|
13
|
+
property :id, Serial
|
14
|
+
property :title, String
|
15
|
+
property :voided, Boolean, :default => false
|
16
|
+
|
17
|
+
has n, :taggings
|
18
|
+
|
19
|
+
has n, :relationships
|
20
|
+
has n, :related_posts, :through => :relationships, :class_name => 'Post', :child_key => [:post_id]
|
21
|
+
|
22
|
+
has n, :posts, :through => :taggings
|
23
|
+
end
|
24
|
+
|
25
|
+
class Tagging
|
26
|
+
include DataMapper::Resource
|
27
|
+
def self.default_repository_name
|
28
|
+
ADAPTER
|
29
|
+
end
|
30
|
+
|
31
|
+
property :id, Serial
|
32
|
+
property :title, String
|
33
|
+
|
34
|
+
belongs_to :post
|
35
|
+
belongs_to :tag
|
36
|
+
end
|
37
|
+
|
38
|
+
class Post
|
39
|
+
include DataMapper::Resource
|
40
|
+
def self.default_repository_name
|
41
|
+
ADAPTER
|
42
|
+
end
|
43
|
+
|
44
|
+
property :id, Serial
|
45
|
+
property :title, String
|
46
|
+
|
47
|
+
has n, :taggings
|
48
|
+
has n, :tags, :through => :taggings
|
49
|
+
|
50
|
+
has n, :relationships
|
51
|
+
has n, :related_posts,
|
52
|
+
:through => :relationships,
|
53
|
+
:child_key => [:post_id],
|
54
|
+
:class_name => "Post"
|
55
|
+
|
56
|
+
has n, :void_tags,
|
57
|
+
:through => :taggings,
|
58
|
+
:child_key => [:post_id],
|
59
|
+
:class_name => "Tag",
|
60
|
+
:remote_relationship_name => :tag,
|
61
|
+
Post.taggings.tag.voided => true
|
62
|
+
end
|
63
|
+
|
64
|
+
class Relationship
|
65
|
+
include DataMapper::Resource
|
66
|
+
def self.default_repository_name
|
67
|
+
ADAPTER
|
68
|
+
end
|
69
|
+
|
70
|
+
property :id, Serial
|
71
|
+
belongs_to :post
|
72
|
+
belongs_to :related_post, :class_name => "Post", :child_key => [:related_post_id]
|
73
|
+
end
|
74
|
+
|
75
|
+
[Post, Tag, Tagging, Relationship].each do |descendant|
|
76
|
+
descendant.auto_migrate!(ADAPTER)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '(sample data)' do
|
82
|
+
before(:each) do
|
83
|
+
post = Post.create(:title => "Entry")
|
84
|
+
another_post = Post.create(:title => "Another")
|
85
|
+
|
86
|
+
crappy = Tagging.new
|
87
|
+
post.taggings << crappy
|
88
|
+
post.save
|
89
|
+
|
90
|
+
crap = Tag.create(:title => "crap")
|
91
|
+
crap.taggings << crappy
|
92
|
+
crap.save
|
93
|
+
|
94
|
+
crappier = Tagging.new
|
95
|
+
post.taggings << crappier
|
96
|
+
post.save
|
97
|
+
|
98
|
+
crapz = Tag.create(:title => "crapz", :voided => true)
|
99
|
+
crapz.taggings << crappier
|
100
|
+
crapz.save
|
101
|
+
|
102
|
+
goody = Tagging.new
|
103
|
+
another_post.taggings << goody
|
104
|
+
another_post.save
|
105
|
+
|
106
|
+
good = Tag.create(:title => "good")
|
107
|
+
good.taggings << goody
|
108
|
+
good.save
|
109
|
+
|
110
|
+
relation = Relationship.new(:related_post_id => another_post.id)
|
111
|
+
post.relationships << relation
|
112
|
+
post.save
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should return the right children for has n => belongs_to relationships' do
|
116
|
+
Post.first.tags.select do |tag|
|
117
|
+
tag.title == 'crap'
|
118
|
+
end.size.should == 1
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should return the right children for has n => belongs_to self-referential relationships' do
|
122
|
+
Post.first.related_posts.select do |post|
|
123
|
+
post.title == 'Another'
|
124
|
+
end.size.should == 1
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should handle all()' do
|
128
|
+
related_posts = Post.first.related_posts
|
129
|
+
related_posts.all.object_id.should == related_posts.object_id
|
130
|
+
related_posts.all(:id => 2).first.should == Post.get!(2)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should handle first()' do
|
134
|
+
post = Post.get!(2)
|
135
|
+
related_posts = Post.first.related_posts
|
136
|
+
related_posts.first.should == post
|
137
|
+
related_posts.first(10).should == [ post ]
|
138
|
+
related_posts.first(:id => 2).should == post
|
139
|
+
related_posts.first(10, :id => 2).map { |r| r.id }.should == [post.id]
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should handle get()' do
|
143
|
+
post = Post.get!(2)
|
144
|
+
related_posts = Post.first.related_posts
|
145
|
+
related_posts.get(2).should == post
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should proxy object should be frozen' do
|
149
|
+
Post.first.related_posts.should be_frozen
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should respect tagging with conditions" do
|
153
|
+
post = Post.get(1)
|
154
|
+
post.tags.size
|
155
|
+
post.tags.select{ |t| t.voided == true }.size.should == 1
|
156
|
+
post.void_tags.size.should == 1
|
157
|
+
post.void_tags.all?{ |t| t.voided == true }.should be_true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "Saved Tag, Post, Tagging" do
|
162
|
+
before(:each) do
|
163
|
+
@tag = Tag.create
|
164
|
+
@post = Post.create
|
165
|
+
@tagging = Tagging.create(
|
166
|
+
:tag => @tag,
|
167
|
+
:post => @post
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should get posts of a tag" do
|
172
|
+
@tag.posts.should == [@post]
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should get tags of a post" do
|
176
|
+
@post.tags.should == [@tag]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "In-memory Tag, Post, Tagging" do
|
181
|
+
before(:each) do
|
182
|
+
@tag = Tag.new
|
183
|
+
@post = Post.new
|
184
|
+
@tagging = Tagging.new(
|
185
|
+
:tag => @tag,
|
186
|
+
:post => @post
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should get posts of a tag" do
|
191
|
+
pending("DataMapper does not yet support in-memory associations") do
|
192
|
+
@tag.posts.should == [@post]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should get tags of a post" do
|
197
|
+
pending("DataMapper does not yet support in-memory associations") do
|
198
|
+
@post.tags.should == [@tag]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,449 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe DataMapper::Associations::ManyToMany::Proxy do
|
4
|
+
before :all do
|
5
|
+
class Editor
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
def self.default_repository_name; ADAPTER end
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :name, String
|
12
|
+
|
13
|
+
has n, :books, :through => Resource
|
14
|
+
end
|
15
|
+
|
16
|
+
Object.send(:remove_const, :Book) if defined?(Book)
|
17
|
+
|
18
|
+
class Book
|
19
|
+
include DataMapper::Resource
|
20
|
+
|
21
|
+
def self.default_repository_name; ADAPTER end
|
22
|
+
|
23
|
+
property :id, Serial
|
24
|
+
property :title, String
|
25
|
+
|
26
|
+
has n, :editors, :through => Resource
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
before do
|
31
|
+
[ Book, Editor, BookEditor ].each { |k| k.auto_migrate! }
|
32
|
+
|
33
|
+
repository(ADAPTER) do
|
34
|
+
book_1 = Book.create(:title => "Dubliners")
|
35
|
+
book_2 = Book.create(:title => "Portrait of the Artist as a Young Man")
|
36
|
+
book_3 = Book.create(:title => "Ulysses")
|
37
|
+
|
38
|
+
editor_1 = Editor.create(:name => "Jon Doe")
|
39
|
+
editor_2 = Editor.create(:name => "Jane Doe")
|
40
|
+
|
41
|
+
BookEditor.create(:book => book_1, :editor => editor_1)
|
42
|
+
BookEditor.create(:book => book_2, :editor => editor_1)
|
43
|
+
BookEditor.create(:book => book_1, :editor => editor_2)
|
44
|
+
|
45
|
+
@parent = book_3
|
46
|
+
@association = @parent.editors
|
47
|
+
@other = [ editor_1 ]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should provide #replace" do
|
52
|
+
@association.should respond_to(:replace)
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#replace" do
|
56
|
+
it "should remove the resource from the collection" do
|
57
|
+
@association.should have(0).entries
|
58
|
+
@association.replace(@other)
|
59
|
+
@association.should == @other
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not automatically save that the resource was removed from the association" do
|
63
|
+
@association.replace(@other)
|
64
|
+
@parent.reload.should have(0).editors
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return the association" do
|
68
|
+
@association.replace(@other).object_id.should == @association.object_id
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should add the new resources so they will be saved when saving the parent" do
|
72
|
+
@association.replace(@other)
|
73
|
+
@association.should == @other
|
74
|
+
@parent.save
|
75
|
+
@association.reload.should == @other
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should instantiate the remote model if passed an array of hashes" do
|
79
|
+
@association.replace([ { :name => "Jim Smith" } ])
|
80
|
+
other = [ Editor.first(:name => "Jim Smith") ]
|
81
|
+
other.first.should_not be_nil
|
82
|
+
@association.should == other
|
83
|
+
@parent.save
|
84
|
+
@association.reload.should == other
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should correctly link records" do
|
89
|
+
Book.get(1).should have(2).editors
|
90
|
+
Book.get(2).should have(1).editors
|
91
|
+
Book.get(3).should have(0).editors
|
92
|
+
Editor.get(1).should have(2).books
|
93
|
+
Editor.get(2).should have(1).books
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be able to have associated objects manually added" do
|
97
|
+
book = Book.get(3)
|
98
|
+
book.should have(0).editors
|
99
|
+
|
100
|
+
be = BookEditor.new(:book_id => book.id, :editor_id => 2)
|
101
|
+
book.book_editors << be
|
102
|
+
book.save
|
103
|
+
|
104
|
+
book.reload.should have(1).editors
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should automatically added necessary through class" do
|
108
|
+
book = Book.get(3)
|
109
|
+
book.should have(0).editors
|
110
|
+
|
111
|
+
book.editors << Editor.get(1)
|
112
|
+
book.editors << Editor.new(:name => "Jimmy John")
|
113
|
+
book.save
|
114
|
+
|
115
|
+
book.reload.should have(2).editors
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should react correctly to a new record" do
|
119
|
+
book = Book.new(:title => "Finnegan's Wake")
|
120
|
+
editor = Editor.get(2)
|
121
|
+
book.should have(0).editors
|
122
|
+
editor.should have(1).books
|
123
|
+
|
124
|
+
book.editors << editor
|
125
|
+
book.save
|
126
|
+
|
127
|
+
book.reload.should have(1).editors
|
128
|
+
editor.reload.should have(2).books
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be able to delete intermediate model" do
|
132
|
+
book = Book.get(1)
|
133
|
+
book.should have(2).book_editors
|
134
|
+
book.should have(2).editors
|
135
|
+
|
136
|
+
be = BookEditor.get(1,1)
|
137
|
+
book.book_editors.delete(be)
|
138
|
+
book.save
|
139
|
+
|
140
|
+
book.reload
|
141
|
+
book.should have(1).book_editors
|
142
|
+
book.should have(1).editors
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should be clearable" do
|
146
|
+
repository(ADAPTER) do
|
147
|
+
book = Book.get(2)
|
148
|
+
book.should have(1).book_editors
|
149
|
+
book.should have(1).editors
|
150
|
+
|
151
|
+
book.editors.clear
|
152
|
+
book.save
|
153
|
+
|
154
|
+
book.reload
|
155
|
+
book.should have(0).book_editors
|
156
|
+
book.should have(0).editors
|
157
|
+
end
|
158
|
+
repository(ADAPTER) do
|
159
|
+
Book.get(2).should have(0).editors
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should be able to delete one object" do
|
164
|
+
book = Book.get(1)
|
165
|
+
book.should have(2).book_editors
|
166
|
+
book.should have(2).editors
|
167
|
+
|
168
|
+
editor = book.editors.first
|
169
|
+
book.editors.delete(editor)
|
170
|
+
book.save
|
171
|
+
|
172
|
+
book.reload
|
173
|
+
book.should have(1).book_editors
|
174
|
+
book.should have(1).editors
|
175
|
+
editor.reload.books.should_not include(book)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should be destroyable" do
|
179
|
+
pending "cannot destroy a collection yet" do
|
180
|
+
book = Book.get(2)
|
181
|
+
book.should have(1).editors
|
182
|
+
|
183
|
+
book.editors.destroy
|
184
|
+
book.save
|
185
|
+
|
186
|
+
book.reload
|
187
|
+
book.should have(0).editors
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "with natural keys" do
|
192
|
+
before :all do
|
193
|
+
class Author
|
194
|
+
include DataMapper::Resource
|
195
|
+
|
196
|
+
def self.default_repository_name; ADAPTER end
|
197
|
+
|
198
|
+
property :name, String, :key => true
|
199
|
+
|
200
|
+
has n, :books, :through => Resource
|
201
|
+
end
|
202
|
+
|
203
|
+
class Book
|
204
|
+
has n, :authors, :through => Resource
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
before do
|
209
|
+
[ Author, AuthorBook ].each { |k| k.auto_migrate! }
|
210
|
+
|
211
|
+
@author = Author.create(:name => "James Joyce")
|
212
|
+
|
213
|
+
@book_1 = Book.get!(1)
|
214
|
+
@book_2 = Book.get!(2)
|
215
|
+
@book_3 = Book.get!(3)
|
216
|
+
|
217
|
+
AuthorBook.create(:book => @book_1, :author => @author)
|
218
|
+
AuthorBook.create(:book => @book_2, :author => @author)
|
219
|
+
AuthorBook.create(:book => @book_3, :author => @author)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should have a join resource where the natural key is a property" do
|
223
|
+
AuthorBook.properties[:author_name].primitive.should == String
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should have a join resource where every property is part of the key" do
|
227
|
+
AuthorBook.key.should == AuthorBook.properties.to_a
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should correctly link records" do
|
231
|
+
@author.should have(3).books
|
232
|
+
@book_1.should have(1).authors
|
233
|
+
@book_2.should have(1).authors
|
234
|
+
@book_3.should have(1).authors
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "When join model has non-serial (integer) natural keys." do
|
239
|
+
before :all do
|
240
|
+
class Tag
|
241
|
+
include DataMapper::Resource
|
242
|
+
|
243
|
+
def self.default_repository_name; ADAPTER end
|
244
|
+
|
245
|
+
property :id, Serial
|
246
|
+
property :name, String, :size => 128
|
247
|
+
|
248
|
+
has n, :book_taggings
|
249
|
+
has n, :books, :through => :book_taggings
|
250
|
+
end
|
251
|
+
|
252
|
+
class BookTagging
|
253
|
+
include DataMapper::Resource
|
254
|
+
|
255
|
+
def self.default_repository_name; ADAPTER end
|
256
|
+
|
257
|
+
property :book_id, Integer, :key => true
|
258
|
+
property :tag_id, Integer, :key => true
|
259
|
+
|
260
|
+
belongs_to :book
|
261
|
+
belongs_to :tag
|
262
|
+
end
|
263
|
+
|
264
|
+
class Book
|
265
|
+
has n, :book_taggings
|
266
|
+
has n, :tags, :through => :book_taggings
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
before do
|
271
|
+
[ Tag, BookTagging ].each { |k| k.auto_migrate! }
|
272
|
+
|
273
|
+
@tag_1 = Tag.create(:name => "good")
|
274
|
+
@tag_2 = Tag.create(:name => "long")
|
275
|
+
|
276
|
+
@book_1 = Book.get!(1)
|
277
|
+
@book_2 = Book.get!(2)
|
278
|
+
@book_3 = Book.get!(3)
|
279
|
+
|
280
|
+
BookTagging.create(:book => @book_2, :tag => @tag_1)
|
281
|
+
BookTagging.create(:book => @book_2, :tag => @tag_2)
|
282
|
+
BookTagging.create(:book => @book_3, :tag => @tag_2)
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should fetch all tags for a book" do
|
286
|
+
@book_1.tags.should have(0).tags
|
287
|
+
@book_2.tags.should have(2).tags
|
288
|
+
@book_3.tags.should have(1).tags
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should allow for adding an association using the << operator" do
|
292
|
+
@book_1.book_taggings << @tag_1
|
293
|
+
@book_1.tags.should have(0).tags
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "with renamed associations" do
|
298
|
+
before :all do
|
299
|
+
class Singer
|
300
|
+
include DataMapper::Resource
|
301
|
+
|
302
|
+
def self.default_repository_name; ADAPTER end
|
303
|
+
|
304
|
+
property :id, Serial
|
305
|
+
property :name, String
|
306
|
+
|
307
|
+
has n, :tunes, :through => Resource, :class_name => 'Song'
|
308
|
+
end
|
309
|
+
|
310
|
+
class Song
|
311
|
+
include DataMapper::Resource
|
312
|
+
|
313
|
+
def self.default_repository_name; ADAPTER end
|
314
|
+
|
315
|
+
property :id, Serial
|
316
|
+
property :title, String
|
317
|
+
|
318
|
+
has n, :performers, :through => Resource, :class_name => 'Singer'
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
before do
|
323
|
+
[ Singer, Song, SingerSong ].each { |k| k.auto_migrate! }
|
324
|
+
|
325
|
+
song_1 = Song.create(:title => "Dubliners")
|
326
|
+
song_2 = Song.create(:title => "Portrait of the Artist as a Young Man")
|
327
|
+
song_3 = Song.create(:title => "Ulysses")
|
328
|
+
|
329
|
+
singer_1 = Singer.create(:name => "Jon Doe")
|
330
|
+
singer_2 = Singer.create(:name => "Jane Doe")
|
331
|
+
|
332
|
+
SingerSong.create(:song => song_1, :singer => singer_1)
|
333
|
+
SingerSong.create(:song => song_2, :singer => singer_1)
|
334
|
+
SingerSong.create(:song => song_1, :singer => singer_2)
|
335
|
+
|
336
|
+
@parent = song_3
|
337
|
+
@association = @parent.performers
|
338
|
+
@other = [ singer_1 ]
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should provide #replace" do
|
342
|
+
@association.should respond_to(:replace)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should correctly link records" do
|
346
|
+
Song.get(1).should have(2).performers
|
347
|
+
Song.get(2).should have(1).performers
|
348
|
+
Song.get(3).should have(0).performers
|
349
|
+
Singer.get(1).should have(2).tunes
|
350
|
+
Singer.get(2).should have(1).tunes
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should be able to have associated objects manually added" do
|
354
|
+
song = Song.get(3)
|
355
|
+
song.should have(0).performers
|
356
|
+
|
357
|
+
be = SingerSong.new(:song_id => song.id, :singer_id => 2)
|
358
|
+
song.singer_songs << be
|
359
|
+
song.save
|
360
|
+
|
361
|
+
song.reload.should have(1).performers
|
362
|
+
end
|
363
|
+
|
364
|
+
it "should automatically added necessary through class" do
|
365
|
+
song = Song.get(3)
|
366
|
+
song.should have(0).performers
|
367
|
+
|
368
|
+
song.performers << Singer.get(1)
|
369
|
+
song.performers << Singer.new(:name => "Jimmy John")
|
370
|
+
song.save
|
371
|
+
|
372
|
+
song.reload.should have(2).performers
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should react correctly to a new record" do
|
376
|
+
song = Song.new(:title => "Finnegan's Wake")
|
377
|
+
singer = Singer.get(2)
|
378
|
+
song.should have(0).performers
|
379
|
+
singer.should have(1).tunes
|
380
|
+
|
381
|
+
song.performers << singer
|
382
|
+
song.save
|
383
|
+
|
384
|
+
song.reload.should have(1).performers
|
385
|
+
singer.reload.should have(2).tunes
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should be able to delete intermediate model" do
|
389
|
+
song = Song.get(1)
|
390
|
+
song.should have(2).singer_songs
|
391
|
+
song.should have(2).performers
|
392
|
+
|
393
|
+
be = SingerSong.get(1,1)
|
394
|
+
song.singer_songs.delete(be)
|
395
|
+
song.save
|
396
|
+
|
397
|
+
song.reload
|
398
|
+
song.should have(1).singer_songs
|
399
|
+
song.should have(1).performers
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should be clearable" do
|
403
|
+
repository(ADAPTER) do
|
404
|
+
song = Song.get(2)
|
405
|
+
song.should have(1).singer_songs
|
406
|
+
song.should have(1).performers
|
407
|
+
|
408
|
+
song.performers.clear
|
409
|
+
song.save
|
410
|
+
|
411
|
+
song.reload
|
412
|
+
song.should have(0).singer_songs
|
413
|
+
song.should have(0).performers
|
414
|
+
end
|
415
|
+
repository(ADAPTER) do
|
416
|
+
Song.get(2).should have(0).performers
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
it "should be able to delete one object" do
|
421
|
+
song = Song.get(1)
|
422
|
+
song.should have(2).singer_songs
|
423
|
+
song.should have(2).performers
|
424
|
+
|
425
|
+
editor = song.performers.first
|
426
|
+
song.performers.delete(editor)
|
427
|
+
song.save
|
428
|
+
|
429
|
+
song.reload
|
430
|
+
song.should have(1).singer_songs
|
431
|
+
song.should have(1).performers
|
432
|
+
editor.reload.tunes.should_not include(song)
|
433
|
+
end
|
434
|
+
|
435
|
+
it "should be destroyable" do
|
436
|
+
pending "cannot destroy a collection yet" do
|
437
|
+
song = Song.get(2)
|
438
|
+
song.should have(1).performers
|
439
|
+
|
440
|
+
song.performers.destroy
|
441
|
+
song.save
|
442
|
+
|
443
|
+
song.reload
|
444
|
+
song.should have(0).performers
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|