dm-core 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CONTRIBUTING +51 -0
- data/FAQ +26 -8
- data/Manifest.txt +2 -0
- data/Rakefile +2 -1
- data/lib/dm-core.rb +8 -3
- data/lib/dm-core/adapters/abstract_adapter.rb +2 -2
- data/lib/dm-core/adapters/data_objects_adapter.rb +18 -4
- data/lib/dm-core/adapters/mysql_adapter.rb +8 -4
- data/lib/dm-core/adapters/postgres_adapter.rb +12 -3
- data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
- data/lib/dm-core/associations.rb +1 -0
- data/lib/dm-core/associations/many_to_one.rb +7 -1
- data/lib/dm-core/associations/one_to_many.rb +1 -1
- data/lib/dm-core/associations/relationship.rb +14 -13
- data/lib/dm-core/auto_migrations.rb +57 -5
- data/lib/dm-core/collection.rb +2 -1
- data/lib/dm-core/logger.rb +5 -6
- data/lib/dm-core/model.rb +17 -2
- data/lib/dm-core/naming_conventions.rb +57 -25
- data/lib/dm-core/property.rb +6 -5
- data/lib/dm-core/query.rb +20 -16
- data/lib/dm-core/resource.rb +24 -7
- data/lib/dm-core/version.rb +1 -1
- data/script/performance.rb +2 -1
- data/script/profile.rb +1 -0
- data/spec/integration/association_spec.rb +131 -81
- data/spec/integration/association_through_spec.rb +87 -42
- data/spec/integration/associations/many_to_many_spec.rb +76 -16
- data/spec/integration/associations/many_to_one_spec.rb +6 -1
- data/spec/integration/associations/one_to_many_spec.rb +69 -0
- data/spec/integration/collection_spec.rb +7 -0
- data/spec/integration/query_spec.rb +24 -7
- data/spec/integration/resource_spec.rb +59 -10
- data/spec/integration/sti_spec.rb +23 -0
- data/spec/models/zoo.rb +2 -3
- data/spec/spec_helper.rb +9 -1
- data/spec/unit/adapters/postgres_adapter_spec.rb +9 -1
- data/spec/unit/associations/many_to_one_spec.rb +6 -0
- data/spec/unit/auto_migrations_spec.rb +2 -1
- data/spec/unit/naming_conventions_spec.rb +26 -18
- data/spec/unit/property_spec.rb +3 -4
- data/spec/unit/resource_spec.rb +19 -22
- data/tasks/gemspec.rb +23 -0
- data/tasks/hoe.rb +9 -1
- metadata +6 -14
@@ -4,7 +4,6 @@ if ADAPTER
|
|
4
4
|
describe 'through-associations' do
|
5
5
|
before :all do
|
6
6
|
repository(ADAPTER) do
|
7
|
-
|
8
7
|
class Tag
|
9
8
|
include DataMapper::Resource
|
10
9
|
def self.default_repository_name
|
@@ -53,11 +52,11 @@ if ADAPTER
|
|
53
52
|
:through => :relationships,
|
54
53
|
:class_name => "Post"
|
55
54
|
|
56
|
-
has n,
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
55
|
+
has n, :void_tags,
|
56
|
+
:through => :taggings,
|
57
|
+
:class_name => "Tag",
|
58
|
+
:remote_relationship_name => :tag,
|
59
|
+
Post.taggings.tag.voided => true
|
61
60
|
end
|
62
61
|
|
63
62
|
class Relationship
|
@@ -74,7 +73,11 @@ if ADAPTER
|
|
74
73
|
[Post, Tag, Tagging, Relationship].each do |descendant|
|
75
74
|
descendant.auto_migrate!(ADAPTER)
|
76
75
|
end
|
76
|
+
end
|
77
|
+
end
|
77
78
|
|
79
|
+
describe '(sample data)' do
|
80
|
+
before(:each) do
|
78
81
|
post = Post.create(:title => "Entry")
|
79
82
|
another_post = Post.create(:title => "Another")
|
80
83
|
|
@@ -106,51 +109,93 @@ if ADAPTER
|
|
106
109
|
post.relationships << relation
|
107
110
|
post.save
|
108
111
|
end
|
109
|
-
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
it 'should return the right children for has n => belongs_to relationships' do
|
114
|
+
Post.first.tags.select do |tag|
|
115
|
+
tag.title == 'crap'
|
116
|
+
end.size.should == 1
|
117
|
+
end
|
116
118
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
it 'should return the right children for has n => belongs_to self-referential relationships' do
|
120
|
+
Post.first.related_posts.select do |post|
|
121
|
+
post.title == 'Another'
|
122
|
+
end.size.should == 1
|
123
|
+
end
|
122
124
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
125
|
+
it 'should handle all()' do
|
126
|
+
related_posts = Post.first.related_posts
|
127
|
+
related_posts.all.object_id.should == related_posts.object_id
|
128
|
+
related_posts.all(:id => 2).first.should == Post.get!(2)
|
129
|
+
end
|
128
130
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
131
|
+
it 'should handle first()' do
|
132
|
+
post = Post.get!(2)
|
133
|
+
related_posts = Post.first.related_posts
|
134
|
+
related_posts.first.should == post
|
135
|
+
related_posts.first(10).should == [ post ]
|
136
|
+
related_posts.first(:id => 2).should == post
|
137
|
+
related_posts.first(10, :id => 2).map { |r| r.id }.should == [post.id]
|
138
|
+
end
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
140
|
+
it 'should handle get()' do
|
141
|
+
post = Post.get!(2)
|
142
|
+
related_posts = Post.first.related_posts
|
143
|
+
related_posts.get(2).should == post
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should proxy object should be frozen' do
|
147
|
+
Post.first.related_posts.should be_frozen
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should respect tagging with conditions" do
|
151
|
+
post = Post.get(1)
|
152
|
+
post.tags.size
|
153
|
+
post.tags.select{ |t| t.voided == true }.size.should == 1
|
154
|
+
post.void_tags.size.should == 1
|
155
|
+
post.void_tags.all?{ |t| t.voided == true }.should be_true
|
156
|
+
end
|
142
157
|
end
|
143
158
|
|
144
|
-
|
145
|
-
|
159
|
+
describe "Saved Tag, Post, Tagging" do
|
160
|
+
before(:each) do
|
161
|
+
@tag = Tag.create
|
162
|
+
@post = Post.create
|
163
|
+
@tagging = Tagging.create(
|
164
|
+
:tag => @tag,
|
165
|
+
:post => @post
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should get posts of a tag" do
|
170
|
+
@tag.posts.should == [@post]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should get tags of a post" do
|
174
|
+
@post.tags.should == [@tag]
|
175
|
+
end
|
146
176
|
end
|
147
177
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
178
|
+
describe "In-memory Tag, Post, Tagging" do
|
179
|
+
before(:each) do
|
180
|
+
@tag = Tag.new
|
181
|
+
@post = Post.new
|
182
|
+
@tagging = Tagging.new(
|
183
|
+
:tag => @tag,
|
184
|
+
:post => @post
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should get posts of a tag" do
|
189
|
+
pending("DataMapper does not yet support in-memory associations") do
|
190
|
+
@tag.posts.should == [@post]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should get tags of a post" do
|
195
|
+
pending("DataMapper does not yet support in-memory associations") do
|
196
|
+
@post.tags.should == [@tag]
|
197
|
+
end
|
198
|
+
end
|
154
199
|
end
|
155
200
|
end
|
156
201
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__),
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
|
2
2
|
|
3
3
|
describe DataMapper::Associations::ManyToMany::Proxy do
|
4
4
|
before :all do
|
@@ -48,36 +48,36 @@ describe DataMapper::Associations::ManyToMany::Proxy do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
it
|
51
|
+
it "should provide #replace" do
|
52
52
|
@association.should respond_to(:replace)
|
53
53
|
end
|
54
54
|
|
55
|
-
describe
|
56
|
-
it
|
55
|
+
describe "#replace" do
|
56
|
+
it "should remove the resource from the collection" do
|
57
57
|
@association.should have(0).entries
|
58
58
|
@association.replace(@other)
|
59
59
|
@association.should == @other
|
60
60
|
end
|
61
61
|
|
62
|
-
it
|
62
|
+
it "should not automatically save that the resource was removed from the association" do
|
63
63
|
@association.replace(@other)
|
64
64
|
@parent.reload.should have(0).editors
|
65
65
|
end
|
66
66
|
|
67
|
-
it
|
67
|
+
it "should return the association" do
|
68
68
|
@association.replace(@other).object_id.should == @association.object_id
|
69
69
|
end
|
70
70
|
|
71
|
-
it
|
71
|
+
it "should add the new resources so they will be saved when saving the parent" do
|
72
72
|
@association.replace(@other)
|
73
73
|
@association.should == @other
|
74
74
|
@parent.save
|
75
75
|
@association.reload.should == @other
|
76
76
|
end
|
77
77
|
|
78
|
-
it
|
79
|
-
@association.replace([ { :name =>
|
80
|
-
other = [ Editor.first(:name =>
|
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
81
|
other.first.should_not be_nil
|
82
82
|
@association.should == other
|
83
83
|
@parent.save
|
@@ -176,7 +176,7 @@ describe DataMapper::Associations::ManyToMany::Proxy do
|
|
176
176
|
end
|
177
177
|
|
178
178
|
it "should be destroyable" do
|
179
|
-
pending
|
179
|
+
pending "cannot destroy a collection yet" do
|
180
180
|
book = Book.get(2)
|
181
181
|
book.should have(1).editors
|
182
182
|
|
@@ -188,7 +188,7 @@ describe DataMapper::Associations::ManyToMany::Proxy do
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
-
describe
|
191
|
+
describe "with natural keys" do
|
192
192
|
before :all do
|
193
193
|
class Author
|
194
194
|
include DataMapper::Resource
|
@@ -208,7 +208,7 @@ describe DataMapper::Associations::ManyToMany::Proxy do
|
|
208
208
|
before do
|
209
209
|
[ Author, AuthorBook ].each { |k| k.auto_migrate! }
|
210
210
|
|
211
|
-
@author = Author.create(:name =>
|
211
|
+
@author = Author.create(:name => "James Joyce")
|
212
212
|
|
213
213
|
@book_1 = Book.get!(1)
|
214
214
|
@book_2 = Book.get!(2)
|
@@ -219,19 +219,79 @@ describe DataMapper::Associations::ManyToMany::Proxy do
|
|
219
219
|
AuthorBook.create(:book => @book_3, :author => @author)
|
220
220
|
end
|
221
221
|
|
222
|
-
it
|
222
|
+
it "should have a join resource where the natural key is a property" do
|
223
223
|
AuthorBook.properties[:author_name].primitive.should == String
|
224
224
|
end
|
225
225
|
|
226
|
-
it
|
226
|
+
it "should have a join resource where every property is part of the key" do
|
227
227
|
AuthorBook.key.should == AuthorBook.properties.to_a
|
228
228
|
end
|
229
229
|
|
230
|
-
it
|
230
|
+
it "should correctly link records" do
|
231
231
|
@author.should have(3).books
|
232
232
|
@book_1.should have(1).authors
|
233
233
|
@book_2.should have(1).authors
|
234
234
|
@book_3.should have(1).authors
|
235
235
|
end
|
236
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
|
+
|
237
297
|
end
|
@@ -24,7 +24,7 @@ if ADAPTER
|
|
24
24
|
property :name, String
|
25
25
|
property :type, Discriminator
|
26
26
|
|
27
|
-
belongs_to :parent
|
27
|
+
belongs_to :parent
|
28
28
|
end
|
29
29
|
|
30
30
|
class StepChild < Child
|
@@ -108,6 +108,7 @@ if ADAPTER
|
|
108
108
|
describe 'when the parent is not a new record' do
|
109
109
|
before do
|
110
110
|
@parent.should_not be_new_record
|
111
|
+
@child.should_not be_new_record
|
111
112
|
end
|
112
113
|
|
113
114
|
it 'should not save the parent' do
|
@@ -118,6 +119,10 @@ if ADAPTER
|
|
118
119
|
it 'should return true' do
|
119
120
|
@association.save.should == true
|
120
121
|
end
|
122
|
+
|
123
|
+
it "should return true to the child" do
|
124
|
+
@child.save.should == true
|
125
|
+
end
|
121
126
|
end
|
122
127
|
|
123
128
|
describe 'when the parent is a new record' do
|
@@ -34,6 +34,75 @@ describe "OneToMany" do
|
|
34
34
|
BaseballTeam.create(:name => "Giants")
|
35
35
|
end
|
36
36
|
|
37
|
+
describe "(saved parent, saved child)" do
|
38
|
+
before(:each) do
|
39
|
+
@dc_united = Team.create
|
40
|
+
@emilio = Player.create(:team => @dc_united)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "child association should return parent" do
|
44
|
+
@emilio.team.should == @dc_united
|
45
|
+
end
|
46
|
+
|
47
|
+
it "parent association should return children" do
|
48
|
+
@dc_united.players.should == [@emilio]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "(saved parent, unsaved child)" do
|
53
|
+
before(:each) do
|
54
|
+
@dc_united = Team.create
|
55
|
+
@emilio = Player.new(:team => @dc_united)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "child association should return parent" do
|
59
|
+
@emilio.team.should == @dc_united
|
60
|
+
end
|
61
|
+
|
62
|
+
it "parent association should return children" do
|
63
|
+
pending("DataMapper does not yet support in-memory associations") do
|
64
|
+
@dc_united.players.should == [@emilio]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "(unsaved parent, saved child)" do
|
70
|
+
before(:each) do
|
71
|
+
@dc_united = Team.new
|
72
|
+
@emilio = Player.create(:team => @dc_united)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "child association should return parent" do
|
76
|
+
@emilio.team.should == @dc_united
|
77
|
+
end
|
78
|
+
|
79
|
+
it "parent association should return children" do
|
80
|
+
@dc_united.players.should == [@emilio]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should return true to child.save" do
|
84
|
+
@emilio.should_not be_a_new_record
|
85
|
+
@emilio.save.should be_true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "(unsaved parent, unsaved child)" do
|
90
|
+
before(:each) do
|
91
|
+
@dc_united = Team.new
|
92
|
+
@emilio = Player.new(:team => @dc_united)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "child association should return parent" do
|
96
|
+
@emilio.team.should == @dc_united
|
97
|
+
end
|
98
|
+
|
99
|
+
it "parent association should return children" do
|
100
|
+
pending("DataMapper does not yet support in-memory associations") do
|
101
|
+
@dc_united.players.should == [@emilio]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
37
106
|
it "unsaved parent model should accept array of hashes for association" do
|
38
107
|
players = [{ :name => "Brett Favre" }, { :name => "Reggie White" }]
|
39
108
|
|
@@ -1055,6 +1055,13 @@ if ADAPTER
|
|
1055
1055
|
collection.length.should == 2
|
1056
1056
|
collection.should be_loaded
|
1057
1057
|
end
|
1058
|
+
|
1059
|
+
it "should load lazy columns when using offset" do
|
1060
|
+
repository(ADAPTER) do
|
1061
|
+
zebras = Zebra.all(:offset => 1, :limit => 2)
|
1062
|
+
zebras.first.notes.should_not be_nil
|
1063
|
+
end
|
1064
|
+
end
|
1058
1065
|
end
|
1059
1066
|
end
|
1060
1067
|
end
|
@@ -34,7 +34,7 @@ if ADAPTER
|
|
34
34
|
|
35
35
|
property :id, Serial
|
36
36
|
property :name, String
|
37
|
-
|
37
|
+
property :type, String
|
38
38
|
|
39
39
|
def self.default_repository_name
|
40
40
|
ADAPTER
|
@@ -250,6 +250,23 @@ if ADAPTER
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
+
it "should find by conditions passed in as an array" do
|
254
|
+
repository(ADAPTER) do
|
255
|
+
find = QuerySpec::SailBoat.all(:id => [1,2])
|
256
|
+
find.should_not be_nil
|
257
|
+
find.should have(2).entries
|
258
|
+
|
259
|
+
find = QuerySpec::SailBoat.all(:id.not => [1,2])
|
260
|
+
find.should have(1).entries
|
261
|
+
|
262
|
+
find = QuerySpec::SailBoat.all(:id => [])
|
263
|
+
find.should have(0).entries
|
264
|
+
|
265
|
+
find = QuerySpec::SailBoat.all(:id.not => [])
|
266
|
+
find.should have(3).entries
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
253
270
|
it "should order results" do
|
254
271
|
repository(ADAPTER) do
|
255
272
|
result = QuerySpec::SailBoat.all(:order => [
|
@@ -432,12 +449,12 @@ if ADAPTER
|
|
432
449
|
vehicle.name.should == '10 ton delivery truck'
|
433
450
|
end
|
434
451
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
452
|
+
it "should accept 'id' and 'type' as endpoints on ah DM::QueryPath" do
|
453
|
+
vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.type => 'commercial' )
|
454
|
+
vehicle.name.should == '10 ton delivery truck'
|
455
|
+
vehicle = QuerySpec::Vehicle.first( QuerySpec::Vehicle.factory.region.id => 1 )
|
456
|
+
vehicle.name.should == '10 ton delivery truck'
|
457
|
+
end
|
441
458
|
|
442
459
|
it 'should auto generate the link if a DM::Property from a different resource is in the :fields option'
|
443
460
|
|