dm-core 1.0.0.rc2 → 1.0.0.rc3

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.
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