dm-core 0.9.2

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 (101) hide show
  1. data/CHANGELOG +144 -0
  2. data/FAQ +74 -0
  3. data/MIT-LICENSE +22 -0
  4. data/QUICKLINKS +12 -0
  5. data/README +143 -0
  6. data/lib/dm-core.rb +213 -0
  7. data/lib/dm-core/adapters.rb +4 -0
  8. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  9. data/lib/dm-core/adapters/data_objects_adapter.rb +701 -0
  10. data/lib/dm-core/adapters/mysql_adapter.rb +132 -0
  11. data/lib/dm-core/adapters/postgres_adapter.rb +179 -0
  12. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  13. data/lib/dm-core/associations.rb +172 -0
  14. data/lib/dm-core/associations/many_to_many.rb +138 -0
  15. data/lib/dm-core/associations/many_to_one.rb +101 -0
  16. data/lib/dm-core/associations/one_to_many.rb +275 -0
  17. data/lib/dm-core/associations/one_to_one.rb +61 -0
  18. data/lib/dm-core/associations/relationship.rb +116 -0
  19. data/lib/dm-core/associations/relationship_chain.rb +74 -0
  20. data/lib/dm-core/auto_migrations.rb +64 -0
  21. data/lib/dm-core/collection.rb +604 -0
  22. data/lib/dm-core/hook.rb +11 -0
  23. data/lib/dm-core/identity_map.rb +45 -0
  24. data/lib/dm-core/is.rb +16 -0
  25. data/lib/dm-core/logger.rb +233 -0
  26. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  27. data/lib/dm-core/migrator.rb +29 -0
  28. data/lib/dm-core/model.rb +399 -0
  29. data/lib/dm-core/naming_conventions.rb +52 -0
  30. data/lib/dm-core/property.rb +611 -0
  31. data/lib/dm-core/property_set.rb +158 -0
  32. data/lib/dm-core/query.rb +590 -0
  33. data/lib/dm-core/repository.rb +159 -0
  34. data/lib/dm-core/resource.rb +618 -0
  35. data/lib/dm-core/scope.rb +35 -0
  36. data/lib/dm-core/support.rb +7 -0
  37. data/lib/dm-core/support/array.rb +13 -0
  38. data/lib/dm-core/support/assertions.rb +8 -0
  39. data/lib/dm-core/support/errors.rb +23 -0
  40. data/lib/dm-core/support/kernel.rb +7 -0
  41. data/lib/dm-core/support/symbol.rb +41 -0
  42. data/lib/dm-core/transaction.rb +267 -0
  43. data/lib/dm-core/type.rb +160 -0
  44. data/lib/dm-core/type_map.rb +80 -0
  45. data/lib/dm-core/types.rb +19 -0
  46. data/lib/dm-core/types/boolean.rb +7 -0
  47. data/lib/dm-core/types/discriminator.rb +32 -0
  48. data/lib/dm-core/types/object.rb +20 -0
  49. data/lib/dm-core/types/paranoid_boolean.rb +23 -0
  50. data/lib/dm-core/types/paranoid_datetime.rb +22 -0
  51. data/lib/dm-core/types/serial.rb +9 -0
  52. data/lib/dm-core/types/text.rb +10 -0
  53. data/spec/integration/association_spec.rb +1215 -0
  54. data/spec/integration/association_through_spec.rb +150 -0
  55. data/spec/integration/associations/many_to_many_spec.rb +171 -0
  56. data/spec/integration/associations/many_to_one_spec.rb +123 -0
  57. data/spec/integration/associations/one_to_many_spec.rb +66 -0
  58. data/spec/integration/auto_migrations_spec.rb +398 -0
  59. data/spec/integration/collection_spec.rb +1015 -0
  60. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  61. data/spec/integration/model_spec.rb +68 -0
  62. data/spec/integration/mysql_adapter_spec.rb +85 -0
  63. data/spec/integration/postgres_adapter_spec.rb +732 -0
  64. data/spec/integration/property_spec.rb +224 -0
  65. data/spec/integration/query_spec.rb +376 -0
  66. data/spec/integration/repository_spec.rb +57 -0
  67. data/spec/integration/resource_spec.rb +324 -0
  68. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  69. data/spec/integration/sti_spec.rb +185 -0
  70. data/spec/integration/transaction_spec.rb +75 -0
  71. data/spec/integration/type_spec.rb +149 -0
  72. data/spec/lib/mock_adapter.rb +27 -0
  73. data/spec/spec_helper.rb +112 -0
  74. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  75. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  76. data/spec/unit/adapters/data_objects_adapter_spec.rb +627 -0
  77. data/spec/unit/adapters/postgres_adapter_spec.rb +125 -0
  78. data/spec/unit/associations/many_to_many_spec.rb +14 -0
  79. data/spec/unit/associations/many_to_one_spec.rb +138 -0
  80. data/spec/unit/associations/one_to_many_spec.rb +385 -0
  81. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  82. data/spec/unit/associations/relationship_spec.rb +67 -0
  83. data/spec/unit/associations_spec.rb +205 -0
  84. data/spec/unit/auto_migrations_spec.rb +110 -0
  85. data/spec/unit/collection_spec.rb +174 -0
  86. data/spec/unit/data_mapper_spec.rb +21 -0
  87. data/spec/unit/identity_map_spec.rb +126 -0
  88. data/spec/unit/is_spec.rb +80 -0
  89. data/spec/unit/migrator_spec.rb +33 -0
  90. data/spec/unit/model_spec.rb +339 -0
  91. data/spec/unit/naming_conventions_spec.rb +28 -0
  92. data/spec/unit/property_set_spec.rb +96 -0
  93. data/spec/unit/property_spec.rb +447 -0
  94. data/spec/unit/query_spec.rb +485 -0
  95. data/spec/unit/repository_spec.rb +93 -0
  96. data/spec/unit/resource_spec.rb +557 -0
  97. data/spec/unit/scope_spec.rb +131 -0
  98. data/spec/unit/transaction_spec.rb +493 -0
  99. data/spec/unit/type_map_spec.rb +114 -0
  100. data/spec/unit/type_spec.rb +119 -0
  101. metadata +187 -0
@@ -0,0 +1,150 @@
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
+
8
+ class Tag
9
+ include DataMapper::Resource
10
+ def self.default_repository_name
11
+ ADAPTER
12
+ end
13
+
14
+ property :id, Serial
15
+ property :title, String
16
+ property :voided, Boolean, :default => false
17
+
18
+ has n, :taggings
19
+
20
+ has n, :relationships
21
+ has n, :related_posts, :through => :relationships, :class_name => 'Post'
22
+
23
+ has n, :posts, :through => :taggings
24
+ end
25
+
26
+ class Tagging
27
+ include DataMapper::Resource
28
+ def self.default_repository_name
29
+ ADAPTER
30
+ end
31
+
32
+ property :id, Serial
33
+ property :title, String
34
+
35
+ belongs_to :post
36
+ belongs_to :tag
37
+ end
38
+
39
+ class Post
40
+ include DataMapper::Resource
41
+ def self.default_repository_name
42
+ ADAPTER
43
+ end
44
+
45
+ property :id, Serial
46
+ property :title, String
47
+
48
+ has n, :taggings
49
+ has n, :tags, :through => :taggings
50
+
51
+ has n, :relationships
52
+ has n, :related_posts,
53
+ :through => :relationships,
54
+ :class_name => "Post"
55
+
56
+ has n, :void_tags,
57
+ :through => :taggings,
58
+ :class_name => "Tag",
59
+ :remote_relationship_name => :tag,
60
+ Post.taggings.tag.voided => true
61
+ end
62
+
63
+ class Relationship
64
+ include DataMapper::Resource
65
+ def self.default_repository_name
66
+ ADAPTER
67
+ end
68
+
69
+ property :id, Serial
70
+ belongs_to :post
71
+ belongs_to :related_post, :class_name => "Post"
72
+ end
73
+
74
+ [Post, Tag, Tagging, Relationship].each do |descendant|
75
+ descendant.auto_migrate!(ADAPTER)
76
+ end
77
+
78
+ post = Post.create(:title => "Entry")
79
+ another_post = Post.create(:title => "Another")
80
+
81
+ crappy = Tagging.new
82
+ post.taggings << crappy
83
+ post.save
84
+
85
+ crap = Tag.create(:title => "crap")
86
+ crap.taggings << crappy
87
+ crap.save
88
+
89
+ crappier = Tagging.new
90
+ post.taggings << crappier
91
+ post.save
92
+
93
+ crapz = Tag.create(:title => "crapz", :voided => true)
94
+ crapz.taggings << crappier
95
+ crapz.save
96
+
97
+ goody = Tagging.new
98
+ another_post.taggings << goody
99
+ another_post.save
100
+
101
+ good = Tag.create(:title => "good")
102
+ good.taggings << goody
103
+ good.save
104
+
105
+ relation = Relationship.new(:related_post => another_post)
106
+ post.relationships << relation
107
+ post.save
108
+ end
109
+ end
110
+
111
+ it 'should return the right children for has n => belongs_to relationships' do
112
+ Post.first.tags.select do |tag|
113
+ tag.title == 'crap'
114
+ end.size.should == 1
115
+ end
116
+
117
+ it 'should return the right children for has n => belongs_to self-referential relationships' do
118
+ Post.first.related_posts.select do |post|
119
+ post.title == 'Another'
120
+ end.size.should == 1
121
+ end
122
+
123
+ it 'should handle all()' do
124
+ related_posts = Post.first.related_posts
125
+ related_posts.all.object_id.should == related_posts.object_id
126
+ related_posts.all(:id => 2).first.should == Post.get!(2)
127
+ end
128
+
129
+ it 'should handle first()' do
130
+ post = Post.get!(2)
131
+ related_posts = Post.first.related_posts
132
+ related_posts.first.should == post
133
+ related_posts.first(10).should == [ post ]
134
+ related_posts.first(:id => 2).should == post
135
+ related_posts.first(10, :id => 2).map { |r| r.id }.should == [ post.id ]
136
+ end
137
+
138
+ it 'should proxy object should be frozen' do
139
+ Post.first.related_posts.should be_frozen
140
+ end
141
+
142
+ it "should respect tagging with conditions" do
143
+ post = Post.get(1)
144
+ post.tags.size
145
+ post.tags.select{|t| t.voided == true}.size.should == 1
146
+ post.void_tags.size.should == 1
147
+ post.void_tags.all?{|t| t.voided == true}.should be_true
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,171 @@
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
+ class Book
17
+ include DataMapper::Resource
18
+
19
+ def self.default_repository_name; ADAPTER end
20
+
21
+ property :id, Serial
22
+ property :title, String
23
+
24
+ has n, :editors, :through => Resource
25
+ end
26
+
27
+ [ Book, Editor, BookEditor ].each { |k| k.auto_migrate! }
28
+
29
+ repository(ADAPTER) do
30
+ book_1 = Book.create(:title => "Dubliners")
31
+ book_2 = Book.create(:title => "Portrait of the Artist as a Young Man")
32
+ book_3 = Book.create(:title => "Ulysses")
33
+
34
+ editor_1 = Editor.create(:name => "Jon Doe")
35
+ editor_2 = Editor.create(:name => "Jane Doe")
36
+
37
+ BookEditor.create(:book => book_1, :editor => editor_1)
38
+ BookEditor.create(:book => book_2, :editor => editor_1)
39
+ BookEditor.create(:book => book_1, :editor => editor_2)
40
+ end
41
+ end
42
+
43
+ it "should correctly link records" do
44
+ Editor.get(1).books.size.should == 2
45
+ Editor.get(2).books.size.should == 1
46
+ Book.get(1).editors.size.should == 2
47
+ Book.get(2).editors.size.should == 1
48
+ end
49
+
50
+ it "should be able to have associated objects manually added" do
51
+ book = Book.get(3)
52
+ # book.editors.size.should == 0
53
+
54
+ be = BookEditor.new(:book_id => book.id, :editor_id => 2)
55
+ book.book_editors << be
56
+ book.save
57
+
58
+ book.reload
59
+ book.editors.size.should == 1
60
+ end
61
+
62
+ it "should automatically added necessary through class" do
63
+ book = Book.get(3)
64
+ book.editors << Editor.get(1)
65
+ book.editors << Editor.new(:name => "Jimmy John")
66
+ book.save
67
+ book.editors.size.should == 3
68
+ end
69
+
70
+ it "should react correctly to a new record" do
71
+ book = Book.new(:title => "Finnegan's Wake")
72
+ book.editors << Editor.get(2)
73
+ book.save
74
+ book.editors.size.should == 1
75
+ Editor.get(2).books.size.should == 3
76
+ end
77
+
78
+ it "should be able to delete intermediate model" do
79
+ book = Book.get(3)
80
+ be = BookEditor.get(3,1)
81
+ book.book_editors.delete(be)
82
+ book.save
83
+ book.reload
84
+ book = Book.get(3)
85
+ book.book_editors.size.should == 2
86
+ book.editors.size.should == 2
87
+ end
88
+
89
+ it "should be clearable" do
90
+ repository(ADAPTER) do
91
+ book = Book.get(2)
92
+ book.editors.size.should == 1
93
+ book.editors.clear
94
+ book.save
95
+ book.reload
96
+ book.book_editors.size.should == 0
97
+ book.editors.size.should == 0
98
+ end
99
+ repository(ADAPTER) do
100
+ Book.get(2).editors.size.should == 0
101
+ end
102
+ end
103
+
104
+ it "should be able to delete one object" do
105
+ book = Book.get(1)
106
+ editor = book.editors.first
107
+
108
+ book.editors.size.should == 2
109
+ book.editors.delete(editor)
110
+ book.book_editors.size.should == 1
111
+ book.editors.size.should == 1
112
+ book.save
113
+ book.reload
114
+ Editor.get(1).books.should_not include(book)
115
+ end
116
+
117
+ it "should be destroyable" do
118
+ pending 'cannot destroy a collection yet' do
119
+ book = Book.get(3)
120
+ book.editors.destroy
121
+ book.save
122
+ book.reload
123
+ book.editors.size.should == 0
124
+ end
125
+ end
126
+
127
+ describe 'with natural keys' do
128
+ before :all do
129
+ class Author
130
+ include DataMapper::Resource
131
+
132
+ def self.default_repository_name; ADAPTER end
133
+
134
+ property :name, String, :key => true
135
+
136
+ has n, :books, :through => Resource
137
+ end
138
+
139
+ class Book
140
+ has n, :authors, :through => Resource
141
+ end
142
+
143
+ [ Author, AuthorBook ].each { |k| k.auto_migrate! }
144
+
145
+ @author = Author.create(:name => 'James Joyce')
146
+
147
+ @book_1 = Book.get!(1)
148
+ @book_2 = Book.get!(2)
149
+ @book_3 = Book.get!(3)
150
+
151
+ AuthorBook.create(:book => @book_1, :author => @author)
152
+ AuthorBook.create(:book => @book_2, :author => @author)
153
+ AuthorBook.create(:book => @book_3, :author => @author)
154
+ end
155
+
156
+ it 'should have a join resource where the natural key is a property' do
157
+ AuthorBook.properties[:author_name].primitive.should == String
158
+ end
159
+
160
+ it 'should have a join resource where every property is part of the key' do
161
+ AuthorBook.key.should == AuthorBook.properties.to_a
162
+ end
163
+
164
+ it 'should correctly link records' do
165
+ @author.books.should have(3).entries
166
+ @book_1.authors.should have(1).entries
167
+ @book_2.authors.should have(1).entries
168
+ @book_3.authors.should have(1).entries
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,123 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ if ADAPTER
4
+ module ManyToOneSpec
5
+ class Parent
6
+ include DataMapper::Resource
7
+
8
+ def self.default_repository_name
9
+ ADAPTER
10
+ end
11
+
12
+ property :id, Serial
13
+ property :name, String
14
+ end
15
+
16
+ class Child
17
+ include DataMapper::Resource
18
+
19
+ def self.default_repository_name
20
+ ADAPTER
21
+ end
22
+
23
+ property :id, Serial
24
+ property :name, String
25
+
26
+ belongs_to :parent, :class_name => 'ManyToOneSpec::Parent'
27
+ end
28
+ end
29
+
30
+ describe DataMapper::Associations::ManyToOne::Proxy do
31
+ before do
32
+ ManyToOneSpec::Parent.auto_migrate!
33
+ ManyToOneSpec::Child.auto_migrate!
34
+
35
+ @parent = ManyToOneSpec::Parent.create(:name => 'parent')
36
+ @child = ManyToOneSpec::Child.create(:name => 'child', :parent => @parent)
37
+ @other = ManyToOneSpec::Parent.create(:name => 'other parent')
38
+ @association = @child.parent
39
+ end
40
+
41
+ describe '#replace' do
42
+ it 'should remove the resource from the collection' do
43
+ @association.should == @parent
44
+ @association.replace(@other)
45
+ @association.should == @other
46
+ end
47
+
48
+ it 'should not automatically save that the resource was removed from the association' do
49
+ @association.replace(@other)
50
+ @child.reload.parent.should == @parent
51
+ end
52
+
53
+ it 'should return the association' do
54
+ @association.replace(@other).object_id.should == @association.object_id
55
+ end
56
+ end
57
+
58
+ describe '#save' do
59
+ describe 'when the parent is nil' do
60
+ before do
61
+ @association.replace(nil)
62
+ end
63
+
64
+ it 'should not save the parent' do
65
+ @association.save
66
+ end
67
+
68
+ it 'should return false' do
69
+ @association.save.should == false
70
+ end
71
+ end
72
+
73
+ describe 'when the parent is not a new record' do
74
+ before do
75
+ @parent.should_not be_new_record
76
+ end
77
+
78
+ it 'should not save the parent' do
79
+ @parent.should_not_receive(:save)
80
+ @association.save
81
+ end
82
+
83
+ it 'should return true' do
84
+ @association.save.should == true
85
+ end
86
+ end
87
+
88
+ describe 'when the parent is a new record' do
89
+ before do
90
+ @parent = ManyToOneSpec::Parent.new(:name => 'unsaved parent')
91
+ @parent.should be_new_record
92
+ @association.replace(@parent)
93
+ end
94
+
95
+ it 'should save the parent' do
96
+ @association.save
97
+ @parent.should_not be_new_record
98
+ end
99
+
100
+ it 'should return the result of the save' do
101
+ @association.save.should == true
102
+ end
103
+ end
104
+ end
105
+
106
+ describe '#reload' do
107
+ before do
108
+ @child.parent_id.should == @parent.id
109
+ @association.replace(@other)
110
+ end
111
+
112
+ it 'should not change the foreign key in the child' do
113
+ @child.parent_id.should == @other.id
114
+ @association.reload
115
+ @child.parent_id.should == @other.id
116
+ end
117
+
118
+ it 'should return self' do
119
+ @association.reload.object_id.should == @association.object_id
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+ require 'pp'
3
+ describe "OneToMany" do
4
+ before(:all) do
5
+ class Team
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, :players
14
+ end
15
+
16
+ class Player
17
+ include DataMapper::Resource
18
+
19
+ def self.default_repository_name; ADAPTER end
20
+
21
+ property :id, Serial
22
+ property :name, String
23
+
24
+ belongs_to :team
25
+ end
26
+
27
+ [Team, Player].each { |k| k.auto_migrate!(ADAPTER) }
28
+
29
+ Team.create!(:name => "Cowboys")
30
+ end
31
+
32
+ it "unsaved parent model should accept array of hashes for association" do
33
+ players = [{ :name => "Brett Favre" }, { :name => "Reggie White" }]
34
+
35
+ team = Team.new(:name => "Packers", :players => players)
36
+ team.players.zip(players) do |player, values|
37
+ player.should be_an_instance_of(Player)
38
+ values.each { |k, v| player.send(k).should == v }
39
+ end
40
+
41
+ players = team.players
42
+ team.save
43
+
44
+ repository(ADAPTER) do
45
+ Team.get(2).players.should == players
46
+ end
47
+ end
48
+
49
+ it "saved parent model should accept array of hashes for association" do
50
+ players = [{ :name => "Troy Aikman" }, { :name => "Chad Hennings" }]
51
+
52
+ team = Team.get(1)
53
+ team.players = players
54
+ team.players.zip(players) do |player, values|
55
+ player.should be_an_instance_of(Player)
56
+ values.each { |k, v| player.send(k).should == v }
57
+ end
58
+
59
+ players = team.players
60
+ team.save
61
+
62
+ repository(ADAPTER) do
63
+ Team.get(1).players.should == players
64
+ end
65
+ end
66
+ end