dm-core 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Gemfile +1 -1
  2. data/LICENSE +1 -1
  3. data/README.rdoc +1 -1
  4. data/Rakefile +3 -4
  5. data/VERSION +1 -1
  6. data/dm-core.gemspec +7 -5
  7. data/lib/dm-core.rb +44 -0
  8. data/lib/dm-core/adapters.rb +1 -1
  9. data/lib/dm-core/adapters/abstract_adapter.rb +16 -0
  10. data/lib/dm-core/collection.rb +2 -2
  11. data/lib/dm-core/model.rb +64 -53
  12. data/lib/dm-core/model/property.rb +14 -6
  13. data/lib/dm-core/model/relationship.rb +10 -18
  14. data/lib/dm-core/property.rb +10 -10
  15. data/lib/dm-core/query.rb +8 -18
  16. data/lib/dm-core/resource.rb +3 -11
  17. data/lib/dm-core/resource/state.rb +13 -16
  18. data/lib/dm-core/resource/state/dirty.rb +11 -1
  19. data/lib/dm-core/resource/state/transient.rb +9 -1
  20. data/lib/dm-core/spec/lib/adapter_helpers.rb +5 -0
  21. data/lib/dm-core/spec/shared/adapter_spec.rb +2 -0
  22. data/lib/dm-core/spec/shared/resource_spec.rb +0 -31
  23. data/lib/dm-core/version.rb +1 -1
  24. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +2 -0
  25. data/spec/public/associations/many_to_many_spec.rb +2 -1
  26. data/spec/public/associations/many_to_one_spec.rb +1 -0
  27. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +1 -0
  28. data/spec/public/associations/one_to_many_spec.rb +2 -0
  29. data/spec/public/associations/one_to_one_spec.rb +2 -0
  30. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +1 -0
  31. data/spec/public/collection_spec.rb +2 -0
  32. data/spec/public/finalize_spec.rb +34 -0
  33. data/spec/public/model/hook_spec.rb +1 -0
  34. data/spec/public/model/property_spec.rb +1 -0
  35. data/spec/public/model/relationship_spec.rb +22 -0
  36. data/spec/public/model_spec.rb +138 -3
  37. data/spec/public/property/discriminator_spec.rb +1 -0
  38. data/spec/public/property/object_spec.rb +1 -0
  39. data/spec/public/property_spec.rb +13 -4
  40. data/spec/public/resource_spec.rb +1 -0
  41. data/spec/public/sel_spec.rb +2 -0
  42. data/spec/public/shared/collection_shared_spec.rb +0 -45
  43. data/spec/public/shared/finder_shared_spec.rb +110 -0
  44. data/spec/public/shared/property_shared_spec.rb +1 -1
  45. data/spec/rcov.opts +1 -1
  46. data/spec/semipublic/associations/many_to_many_spec.rb +3 -0
  47. data/spec/semipublic/associations/many_to_one_spec.rb +2 -0
  48. data/spec/semipublic/associations/one_to_many_spec.rb +2 -0
  49. data/spec/semipublic/associations/one_to_one_spec.rb +2 -0
  50. data/spec/semipublic/associations/relationship_spec.rb +6 -0
  51. data/spec/semipublic/query/conditions/comparison_spec.rb +3 -0
  52. data/spec/semipublic/query/conditions/operation_spec.rb +1 -0
  53. data/spec/semipublic/query/path_spec.rb +2 -0
  54. data/spec/semipublic/query_spec.rb +2 -3
  55. data/spec/semipublic/resource/state/clean_spec.rb +2 -1
  56. data/spec/semipublic/resource/state/deleted_spec.rb +2 -1
  57. data/spec/semipublic/resource/state/dirty_spec.rb +42 -20
  58. data/spec/semipublic/resource/state/immutable_spec.rb +7 -1
  59. data/spec/semipublic/resource/state/transient_spec.rb +69 -40
  60. data/spec/semipublic/resource/state_spec.rb +72 -66
  61. data/spec/semipublic/shared/property_shared_spec.rb +1 -0
  62. data/spec/semipublic/shared/resource_shared_spec.rb +1 -0
  63. data/spec/spec_helper.rb +0 -10
  64. data/tasks/spec.rake +3 -0
  65. metadata +9 -7
@@ -21,6 +21,10 @@ share_examples_for 'Finder Interface' do
21
21
  pending if @skip
22
22
  end
23
23
 
24
+ it 'should be Enumerable' do
25
+ @articles.should be_kind_of(Enumerable)
26
+ end
27
+
24
28
  [ :[], :slice ].each do |method|
25
29
  it { @articles.should respond_to(method) }
26
30
 
@@ -880,6 +884,71 @@ share_examples_for 'Finder Interface' do
880
884
  end
881
885
  end
882
886
 
887
+ it { @articles.should respond_to(:each) }
888
+
889
+ describe '#each' do
890
+ subject { @articles.each(&block) }
891
+
892
+ let(:yields) { [] }
893
+ let(:block) { lambda { |resource| yields << resource } }
894
+
895
+ before do
896
+ @copy = @articles.kind_of?(Class) ? @articles : @articles.dup
897
+ @copy.to_a
898
+ end
899
+
900
+ it { should equal(@articles) }
901
+
902
+ it { method(:subject).should change { yields.dup }.from([]).to(@copy.to_a) }
903
+ end
904
+
905
+ it { @articles.should respond_to(:fetch) }
906
+
907
+ describe '#fetch' do
908
+ subject { @articles.fetch(*args, &block) }
909
+
910
+ let(:block) { nil }
911
+
912
+ context 'with a valid index and no default' do
913
+ let(:args) { [ 0 ] }
914
+
915
+ before do
916
+ @copy = @articles.kind_of?(Class) ? @articles : @articles.dup
917
+ @copy.to_a
918
+ end
919
+
920
+ should_not_be_a_kicker
921
+
922
+ it { should be_kind_of(DataMapper::Resource) }
923
+
924
+ it { should == @copy.entries.fetch(*args) }
925
+ end
926
+
927
+ context 'with an invalid index and no default' do
928
+ let(:args) { [ 42 ] }
929
+
930
+ it { method(:subject).should raise_error(IndexError) }
931
+ end
932
+
933
+ context 'with an invalid index and a default' do
934
+ let(:default) { mock('Default') }
935
+ let(:args) { [ 42, default ] }
936
+
937
+ it { should equal(default) }
938
+ end
939
+
940
+ context 'with an invalid index and a block default' do
941
+ let(:yields) { [] }
942
+ let(:default) { mock('Default') }
943
+ let(:block) { lambda { |index| yields << index; default } }
944
+ let(:args) { [ 42 ] }
945
+
946
+ it { should equal(default) }
947
+
948
+ it { method(:subject).should change { yields.dup }.from([]).to([ 42 ]) }
949
+ end
950
+ end
951
+
883
952
  it { @articles.should respond_to(:first) }
884
953
 
885
954
  describe '#first' do
@@ -1200,6 +1269,16 @@ share_examples_for 'Finder Interface' do
1200
1269
  it 'should should be the last Resource in the Collection matching the query' do
1201
1270
  @resource.should == @article
1202
1271
  end
1272
+
1273
+ it 'should not update the original query order' do
1274
+ collection = @articles.all(:order => [ :title ])
1275
+ original_order = collection.query.order[0].dup
1276
+ last = collection.last(:content => 'Sample')
1277
+
1278
+ last.should == @resource
1279
+
1280
+ collection.query.order[0].should == original_order
1281
+ end
1203
1282
  end
1204
1283
 
1205
1284
  describe 'with a limit specified' do
@@ -1269,6 +1348,37 @@ share_examples_for 'Finder Interface' do
1269
1348
  end
1270
1349
  end
1271
1350
 
1351
+ it { @articles.should respond_to(:values_at) }
1352
+
1353
+ describe '#values_at' do
1354
+ subject { @articles.values_at(*args) }
1355
+
1356
+ before :all do
1357
+ @copy = @articles.kind_of?(Class) ? @articles : @articles.dup
1358
+ @copy.to_a
1359
+ end
1360
+
1361
+ context 'with positive offset' do
1362
+ let(:args) { [ 0 ] }
1363
+
1364
+ should_not_be_a_kicker
1365
+
1366
+ it { should be_kind_of(Array) }
1367
+
1368
+ it { should == @copy.entries.values_at(*args) }
1369
+ end
1370
+
1371
+ describe 'with negative offset' do
1372
+ let(:args) { [ -1 ] }
1373
+
1374
+ should_not_be_a_kicker
1375
+
1376
+ it { should be_kind_of(Array) }
1377
+
1378
+ it { should == @copy.entries.values_at(*args) }
1379
+ end
1380
+ end
1381
+
1272
1382
  it 'should respond to a belongs_to relationship method with #method_missing' do
1273
1383
  pending_if 'Model#method_missing should delegate to relationships', @articles.kind_of?(Class) do
1274
1384
  @articles.should respond_to(:original)
@@ -108,7 +108,7 @@ share_examples_for 'A public Property' do
108
108
  [true, false].each do |value|
109
109
  describe "when created with :#{method} => #{value}" do
110
110
  before :all do
111
- opt = method.to_s.sub('?', '').to_sym
111
+ opt = method.to_s.chomp('?').to_sym
112
112
  @property = @type.new(@model, @name, opt => value)
113
113
  end
114
114
 
data/spec/rcov.opts CHANGED
@@ -1,4 +1,4 @@
1
- --exclude "spec"
1
+ --exclude "spec,^/"
2
2
  --sort coverage
3
3
  --callsites
4
4
  --xrefs
@@ -50,6 +50,7 @@ describe 'Many to Many Associations' do
50
50
  end
51
51
  end
52
52
 
53
+
53
54
  @article_model = Blog::Article
54
55
  @author_model = Blog::Author
55
56
 
@@ -61,6 +62,8 @@ describe 'Many to Many Associations' do
61
62
  @subject_without_default = @article_model.has(n, :without_default, 'Author', :through => :without_default_join, :via => :author)
62
63
  @subject_with_default = @article_model.has(n, :with_default, 'Author', :through => :with_default_join, :via => :author, :default => @default_value)
63
64
  @subject_with_default_callable = @article_model.has(n, :with_default_callable, 'Author', :through => :with_default_callable_join, :via => :author, :default => lambda { |resource, relationship| @default_value_callable })
65
+
66
+ DataMapper.finalize
64
67
  end
65
68
 
66
69
  supported_by :all do
@@ -32,6 +32,8 @@ describe 'Many to One Associations' do
32
32
 
33
33
  @default_value.with_default = nil
34
34
  @default_value.with_default_callable = nil
35
+
36
+ DataMapper.finalize
35
37
  end
36
38
 
37
39
  supported_by :all do
@@ -32,6 +32,8 @@ describe 'One to Many Associations' do
32
32
  @subject_without_default = @article_model.has(n, :without_default, @author_model, :child_key => [ :without_default_id ])
33
33
  @subject_with_default = @article_model.has(n, :with_default, @author_model, :child_key => [ :with_default_id ], :default => @default_value)
34
34
  @subject_with_default_callable = @article_model.has(n, :with_default_callable, @author_model, :child_key => [ :with_default_callable_id ], :default => lambda { |resource, relationship| @default_value_callable })
35
+
36
+ DataMapper.finalize
35
37
  end
36
38
 
37
39
  supported_by :all do
@@ -30,6 +30,8 @@ describe 'One to One Associations' do
30
30
  @subject_without_default = @article_model.has(1, :without_default, @author_model, :child_key => [ :without_default_id ])
31
31
  @subject_with_default = @article_model.has(1, :with_default, @author_model, :child_key => [ :with_default_id ], :default => @default_value)
32
32
  @subject_with_default_callable = @article_model.has(1, :with_default_callable, @author_model, :child_key => [ :with_default_callable_id ], :default => lambda { |resource, relationship| @default_value_callable })
33
+
34
+ DataMapper.finalize
33
35
  end
34
36
 
35
37
  supported_by :all do
@@ -38,6 +38,7 @@ describe DataMapper::Associations::Relationship do
38
38
  # TODO: move this to spec/public/model/relationship_spec.rb
39
39
  @article_relationship.child_repository_name.should == :default
40
40
  @article_relationship.parent_repository_name.should be_nil
41
+ DataMapper.finalize
41
42
  end
42
43
 
43
44
  it 'should return the inverted relationships' do
@@ -58,6 +59,7 @@ describe DataMapper::Associations::Relationship do
58
59
  # TODO: move this to spec/public/model/relationship_spec.rb
59
60
  @article_relationship.child_repository_name.should == :default
60
61
  @article_relationship.parent_repository_name.should be_nil
62
+ DataMapper.finalize
61
63
  end
62
64
 
63
65
  it 'should return the inverted relationships' do
@@ -78,6 +80,7 @@ describe DataMapper::Associations::Relationship do
78
80
  # TODO: move this to spec/public/model/relationship_spec.rb
79
81
  @article_relationship.child_repository_name.should == :default
80
82
  @article_relationship.parent_repository_name.should == :default
83
+ DataMapper.finalize
81
84
  end
82
85
 
83
86
  it 'should return the inverted relationships' do
@@ -98,6 +101,7 @@ describe DataMapper::Associations::Relationship do
98
101
 
99
102
  # after Relationship#inverse to ensure no match
100
103
  @expected = @comment_model.belongs_to(:article)
104
+ DataMapper.finalize
101
105
  end
102
106
 
103
107
  it 'should return a Relationship' do
@@ -129,6 +133,7 @@ describe DataMapper::Associations::Relationship do
129
133
 
130
134
  # after Relationship#inverse to ensure no match
131
135
  @expected = @article_model.has(n, :comments)
136
+ DataMapper.finalize
132
137
  end
133
138
 
134
139
  it 'should return a Relationship' do
@@ -156,6 +161,7 @@ describe DataMapper::Associations::Relationship do
156
161
  describe '#valid?' do
157
162
  before :all do
158
163
  @relationship = @article_model.has(n, :comments)
164
+ DataMapper.finalize
159
165
  end
160
166
 
161
167
  supported_by :all do
@@ -14,6 +14,8 @@ shared_examples_for 'DataMapper::Query::Conditions::AbstractComparison' do
14
14
  end
15
15
  end
16
16
 
17
+ DataMapper.finalize
18
+
17
19
  @model = Blog::Article
18
20
  end
19
21
 
@@ -270,6 +272,7 @@ describe DataMapper::Query::Conditions::Comparison do
270
272
  property :title, String, :required => true
271
273
  end
272
274
  end
275
+ DataMapper.finalize
273
276
 
274
277
  @model = Blog::Article
275
278
  end
@@ -47,6 +47,7 @@ shared_examples_for 'DataMapper::Query::Conditions::AbstractOperation' do
47
47
  property :title, String, :required => true
48
48
  end
49
49
  end
50
+ DataMapper.finalize
50
51
 
51
52
  @model = Blog::Article
52
53
  end
@@ -24,6 +24,7 @@ describe DataMapper::Query::Path do
24
24
  @relationship = Author.relationships[:articles]
25
25
  @relationships = [ @relationship ]
26
26
  @property = Article.properties[:title]
27
+ DataMapper.finalize
27
28
  end
28
29
 
29
30
  it { DataMapper::Query::Path.should respond_to(:new) }
@@ -103,6 +104,7 @@ describe DataMapper::Query::Path do
103
104
  @property = Article.properties[:title]
104
105
 
105
106
  @path = DataMapper::Query::Path.new(@relationships)
107
+ DataMapper.finalize
106
108
  end
107
109
 
108
110
  it { @path.should respond_to(:==) }
@@ -1306,9 +1306,8 @@ describe DataMapper::Query do
1306
1306
  property :id, Serial
1307
1307
  end
1308
1308
 
1309
- # TODO: figure out how to remove these
1310
- User.send(:assert_valid)
1311
- Other.send(:assert_valid)
1309
+ # finalize the models
1310
+ DataMapper.finalize
1312
1311
 
1313
1312
  @repository = DataMapper::Repository.new(:default)
1314
1313
  @model = User
@@ -13,6 +13,7 @@ describe DataMapper::Resource::State::Clean do
13
13
  belongs_to :parent, self, :required => false
14
14
  has n, :children, self, :inverse => :parent
15
15
  end
16
+ DataMapper.finalize
16
17
 
17
18
  @model = Author
18
19
  end
@@ -25,7 +26,7 @@ describe DataMapper::Resource::State::Clean do
25
26
  end
26
27
 
27
28
  after do
28
- @resource.destroy
29
+ @model.destroy!
29
30
  end
30
31
 
31
32
  [ :commit, :rollback ].each do |method|
@@ -14,6 +14,7 @@ describe DataMapper::Resource::State::Deleted do
14
14
  has n, :children, self, :inverse => :parent
15
15
  end
16
16
 
17
+ DataMapper.finalize
17
18
  @model = Author
18
19
  end
19
20
 
@@ -24,7 +25,7 @@ describe DataMapper::Resource::State::Deleted do
24
25
  end
25
26
 
26
27
  after do
27
- @resource.destroy
28
+ @model.destroy!
28
29
  end
29
30
 
30
31
  describe '#commit' do
@@ -14,11 +14,15 @@ describe DataMapper::Resource::State::Dirty do
14
14
  has n, :children, self, :inverse => :parent
15
15
  end
16
16
 
17
+ DataMapper.finalize
18
+
17
19
  @model = Author
18
20
  end
19
21
 
20
22
  before do
21
- @resource = @model.create(:name => 'Dan Kubb')
23
+ @parent = @model.create(:name => 'Jane Doe')
24
+
25
+ @resource = @model.create(:id => 2, :name => 'Dan Kubb', :parent => @parent)
22
26
  @resource.attributes = { :name => 'John Doe' }
23
27
 
24
28
  @state = @resource.persisted_state
@@ -26,35 +30,53 @@ describe DataMapper::Resource::State::Dirty do
26
30
  end
27
31
 
28
32
  after do
29
- @resource.destroy
30
- end
31
-
32
- after do
33
- @model.all.destroy!
33
+ @model.destroy!
34
34
  end
35
35
 
36
36
  describe '#commit' do
37
37
  subject { @state.commit }
38
38
 
39
39
  supported_by :all do
40
- before do
41
- @new_id = @resource.id = @resource.id.succ
42
- end
40
+ context 'with valid attributes' do
41
+ let(:state) { @state }
43
42
 
44
- it 'should return a Clean state' do
45
- should eql(DataMapper::Resource::State::Clean.new(@resource))
46
- end
43
+ before do
44
+ @new_id = @resource.id = @resource.id.succ
45
+ end
46
+
47
+ it 'should return a Clean state' do
48
+ should eql(DataMapper::Resource::State::Clean.new(@resource))
49
+ end
50
+
51
+ it 'should set the child key if the parent key changes' do
52
+ original_id = @parent.id
53
+ @parent.update(:id => 42).should be(true)
54
+ method(:subject).should change(@resource, :parent_id).from(original_id.to_s).to('42')
55
+ end
56
+
57
+ it 'should update the resource' do
58
+ subject
59
+ @model.get!(*@resource.key).should == @resource
60
+ end
47
61
 
48
- it 'should update the resource' do
49
- subject
50
- @model.get!(*@resource.key).should == @resource
62
+ it 'should update the resource to the identity map if the key changed' do
63
+ identity_map = @resource.repository.identity_map(@model)
64
+ identity_map.should == { @resource.key => @resource }
65
+ subject
66
+ identity_map.should == { [ @new_id ] => @resource }
67
+ end
51
68
  end
52
69
 
53
- it 'should update the resource to the identity map if the key changed' do
54
- identity_map = @resource.repository.identity_map(@model)
55
- identity_map.should == { @resource.key => @resource }
56
- subject
57
- identity_map.should == { [ @new_id ] => @resource }
70
+ context 'with invalid attributes' do
71
+ before do
72
+ @resource.coding = 'yes'
73
+ end
74
+
75
+ it { should equal(@state) }
76
+
77
+ it 'should update the resource to the identity map if the key changed' do
78
+ method(:subject).should_not change { @resource.repository.identity_map(@model).dup }
79
+ end
58
80
  end
59
81
  end
60
82
  end
@@ -13,11 +13,13 @@ describe DataMapper::Resource::State::Immutable do
13
13
  belongs_to :parent, self, :required => false
14
14
  end
15
15
 
16
+ DataMapper.finalize
17
+
16
18
  @model = Author
17
19
  end
18
20
 
19
21
  before do
20
- @parent = @model.create(:name => 'John Doe')
22
+ @parent = @model.create(:name => 'John Doe')
21
23
 
22
24
  @resource = @model.create(:name => 'Dan Kubb', :parent => @parent)
23
25
  @resource = @model.first(@model.key.zip(@resource.key).to_hash.merge(:fields => [ :name, :parent_id ]))
@@ -26,6 +28,10 @@ describe DataMapper::Resource::State::Immutable do
26
28
  @state.should be_kind_of(DataMapper::Resource::State::Immutable)
27
29
  end
28
30
 
31
+ after do
32
+ @model.destroy!
33
+ end
34
+
29
35
  describe '#commit' do
30
36
  subject { @state.commit }
31
37