dm-constraints 0.10.2 → 1.0.0.rc1
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/.gitignore +37 -0
- data/Gemfile +141 -0
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/dm-constraints.gemspec +35 -14
- data/lib/dm-constraints/adapters/dm-abstract-adapter.rb +22 -0
- data/lib/dm-constraints/adapters/dm-do-adapter.rb +189 -0
- data/lib/dm-constraints/adapters/dm-mysql-adapter.rb +40 -0
- data/lib/dm-constraints/adapters/dm-oracle-adapter.rb +13 -0
- data/lib/dm-constraints/adapters/dm-postgres-adapter.rb +13 -0
- data/lib/dm-constraints/adapters/dm-sqlite-adapter.rb +22 -0
- data/lib/dm-constraints/adapters/dm-sqlserver-adapter.rb +13 -0
- data/lib/dm-constraints/migrations.rb +26 -240
- data/lib/dm-constraints/relationships.rb +41 -0
- data/lib/dm-constraints.rb +64 -38
- data/spec/integration/constraints_spec.rb +29 -7
- data/spec/isolated/require_after_setup_spec.rb +36 -0
- data/spec/isolated/require_before_setup_spec.rb +36 -0
- data/spec/isolated/require_spec.rb +14 -0
- data/spec/spec_helper.rb +12 -49
- data/tasks/local_gemfile.rake +18 -0
- data/tasks/spec.rake +0 -3
- metadata +67 -26
@@ -1,265 +1,48 @@
|
|
1
|
+
require 'dm-migrations/auto_migration'
|
2
|
+
|
1
3
|
module DataMapper
|
2
4
|
module Constraints
|
3
5
|
module Migrations
|
4
|
-
module SingletonMethods
|
5
|
-
def self.included(base)
|
6
|
-
# TODO: figure out how to make this work without AMC
|
7
|
-
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
8
|
-
alias_method :auto_migrate_down_without_constraints!, :auto_migrate_down!
|
9
|
-
alias_method :auto_migrate_down!, :auto_migrate_down_with_constraints!
|
10
|
-
|
11
|
-
alias_method :auto_migrate_up_without_constraints!, :auto_migrate_up!
|
12
|
-
alias_method :auto_migrate_up!, :auto_migrate_up_with_constraints!
|
13
|
-
RUBY
|
14
|
-
end
|
15
|
-
|
16
|
-
def auto_migrate_down_with_constraints!(repository_name = nil)
|
17
|
-
repository_execute(:auto_migrate_down_with_constraints!, repository_name)
|
18
|
-
auto_migrate_down_without_constraints!(repository_name)
|
19
|
-
end
|
20
|
-
|
21
|
-
def auto_migrate_up_with_constraints!(repository_name = nil)
|
22
|
-
auto_migrate_up_without_constraints!(repository_name)
|
23
|
-
repository_execute(:auto_migrate_up_with_constraints!, repository_name)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
module DataObjectsAdapter
|
28
|
-
##
|
29
|
-
# Determine if a constraint exists for a table
|
30
|
-
#
|
31
|
-
# @param storage_name [Symbol]
|
32
|
-
# name of table to check constraint on
|
33
|
-
# @param constraint_name [~String]
|
34
|
-
# name of constraint to check for
|
35
|
-
#
|
36
|
-
# @return [Boolean]
|
37
|
-
#
|
38
|
-
# @api private
|
39
|
-
def constraint_exists?(storage_name, constraint_name)
|
40
|
-
statement = <<-SQL.compress_lines
|
41
|
-
SELECT COUNT(*)
|
42
|
-
FROM "information_schema"."table_constraints"
|
43
|
-
WHERE "constraint_type" = 'FOREIGN KEY'
|
44
|
-
AND "table_schema" = ?
|
45
|
-
AND "table_name" = ?
|
46
|
-
AND "constraint_name" = ?
|
47
|
-
SQL
|
48
|
-
|
49
|
-
select(statement, schema_name, storage_name, constraint_name).first > 0
|
50
|
-
end
|
51
|
-
|
52
|
-
##
|
53
|
-
# Create the constraint for a relationship
|
54
|
-
#
|
55
|
-
# @param relationship [Relationship]
|
56
|
-
# the relationship to create the constraint for
|
57
|
-
#
|
58
|
-
# @return [true, false]
|
59
|
-
# true if creating the constraints was successful
|
60
|
-
#
|
61
|
-
# @api semipublic
|
62
|
-
def create_relationship_constraint(relationship)
|
63
|
-
return false unless valid_relationship_for_constraint?(relationship)
|
64
|
-
|
65
|
-
source_model = relationship.source_model
|
66
|
-
source_table = source_model.storage_name(name)
|
67
|
-
source_key = relationship.source_key
|
68
|
-
|
69
|
-
constraint_name = constraint_name(source_table, relationship.name)
|
70
|
-
return false if constraint_exists?(source_table, constraint_name)
|
71
|
-
|
72
|
-
constraint_type = case relationship.inverse.constraint
|
73
|
-
when :protect then 'NO ACTION'
|
74
|
-
when :destroy, :destroy! then 'CASCADE'
|
75
|
-
when :set_nil then 'SET NULL'
|
76
|
-
end
|
77
|
-
|
78
|
-
return false if constraint_type.nil?
|
79
|
-
|
80
|
-
storage_name = relationship.source_model.storage_name(name)
|
81
|
-
reference_storage_name = relationship.target_model.storage_name(name)
|
82
|
-
|
83
|
-
foreign_keys = relationship.source_key.map { |p| property_to_column_name(p, false) }
|
84
|
-
reference_keys = relationship.target_key.map { |p| property_to_column_name(p, false) }
|
85
|
-
|
86
|
-
execute(create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys))
|
87
|
-
end
|
88
|
-
|
89
|
-
##
|
90
|
-
# Remove the constraint for a relationship
|
91
|
-
#
|
92
|
-
# @param relationship [Relationship]
|
93
|
-
# the relationship to remove the constraint for
|
94
|
-
#
|
95
|
-
# @return [true, false]
|
96
|
-
# true if destroying the constraint was successful
|
97
|
-
#
|
98
|
-
# @api semipublic
|
99
|
-
def destroy_relationship_constraint(relationship)
|
100
|
-
return false unless valid_relationship_for_constraint?(relationship)
|
101
|
-
|
102
|
-
source_model = relationship.source_model
|
103
|
-
source_table = source_model.storage_name(name)
|
104
6
|
|
105
|
-
|
106
|
-
return false unless constraint_exists?(source_table, constraint_name)
|
107
|
-
|
108
|
-
execute(destroy_constraints_statement(source_table, constraint_name))
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
7
|
+
module SingletonMethods
|
112
8
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# repository as the adapter is connected to.
|
119
|
-
#
|
120
|
-
# @param relationship [Relationship]
|
121
|
-
# the relationship to check
|
122
|
-
#
|
123
|
-
# @return [true, false]
|
124
|
-
# true if a constraint can be established for relationship
|
125
|
-
#
|
126
|
-
# @api private
|
127
|
-
def valid_relationship_for_constraint?(relationship)
|
128
|
-
return false unless relationship.source_repository_name == name || relationship.source_repository_name.nil?
|
129
|
-
return false unless relationship.target_repository_name == name || relationship.target_repository_name.nil?
|
130
|
-
return false unless relationship.kind_of?(Associations::ManyToOne::Relationship)
|
131
|
-
true
|
9
|
+
def auto_migrate!(repository_name = nil)
|
10
|
+
repository_execute(:auto_migrate_down_constraints!, repository_name)
|
11
|
+
descendants = super
|
12
|
+
repository_execute(:auto_migrate_up_constraints!, repository_name)
|
13
|
+
descendants
|
132
14
|
end
|
133
15
|
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
##
|
138
|
-
# Generates the SQL statement to create a constraint
|
139
|
-
#
|
140
|
-
# @param constraint_name [String]
|
141
|
-
# name of the foreign key constraint
|
142
|
-
# @param constraint_type [String]
|
143
|
-
# type of foreign key constraint to add to the table
|
144
|
-
# @param storage_name [String]
|
145
|
-
# name of table to constrain
|
146
|
-
# @param foreign_keys [Array[String]]
|
147
|
-
# columns in the table that refer to foreign table
|
148
|
-
# @param reference_storage_name [String]
|
149
|
-
# table the foreign key refers to
|
150
|
-
# @param reference_storage_name [Array[String]]
|
151
|
-
# columns the foreign table that are referred to
|
152
|
-
#
|
153
|
-
# @return [String]
|
154
|
-
# SQL DDL Statement to create a constraint
|
155
|
-
#
|
156
|
-
# @api private
|
157
|
-
def create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys)
|
158
|
-
<<-SQL.compress_lines
|
159
|
-
ALTER TABLE #{quote_name(storage_name)}
|
160
|
-
ADD CONSTRAINT #{quote_name(constraint_name)}
|
161
|
-
FOREIGN KEY (#{foreign_keys.join(', ')})
|
162
|
-
REFERENCES #{quote_name(reference_storage_name)} (#{reference_keys.join(', ')})
|
163
|
-
ON DELETE #{constraint_type}
|
164
|
-
ON UPDATE #{constraint_type}
|
165
|
-
SQL
|
166
|
-
end
|
167
|
-
|
168
|
-
##
|
169
|
-
# Generates the SQL statement to destroy a constraint
|
170
|
-
#
|
171
|
-
# @param storage_name [String]
|
172
|
-
# name of table to constrain
|
173
|
-
# @param constraint_name [String]
|
174
|
-
# name of foreign key constraint
|
175
|
-
#
|
176
|
-
# @return [String]
|
177
|
-
# SQL DDL Statement to destroy a constraint
|
178
|
-
#
|
179
|
-
# @api private
|
180
|
-
def destroy_constraints_statement(storage_name, constraint_name)
|
181
|
-
<<-SQL.compress_lines
|
182
|
-
ALTER TABLE #{quote_name(storage_name)}
|
183
|
-
DROP CONSTRAINT #{quote_name(constraint_name)}
|
184
|
-
SQL
|
185
|
-
end
|
16
|
+
private
|
186
17
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
# @param storage_name [String]
|
191
|
-
# name of table to constrain
|
192
|
-
# @param relationships_name [String]
|
193
|
-
# name of the relationship to constrain
|
194
|
-
#
|
195
|
-
# @return [String]
|
196
|
-
# name of the constraint
|
197
|
-
#
|
198
|
-
# @api private
|
199
|
-
def constraint_name(storage_name, relationship_name)
|
200
|
-
identifier = "#{storage_name}_#{relationship_name}"[0, self.class::IDENTIFIER_MAX_LENGTH - 3]
|
201
|
-
"#{identifier}_fk"
|
202
|
-
end
|
18
|
+
def auto_migrate_down!(repository_name = nil)
|
19
|
+
repository_execute(:auto_migrate_down_constraints!, repository_name)
|
20
|
+
super
|
203
21
|
end
|
204
22
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
module SQL
|
210
|
-
private
|
211
|
-
|
212
|
-
##
|
213
|
-
# MySQL specific query to drop a foreign key
|
214
|
-
#
|
215
|
-
# @param storage_name [String]
|
216
|
-
# name of table to constrain
|
217
|
-
# @param constraint_name [String]
|
218
|
-
# name of foreign key constraint
|
219
|
-
#
|
220
|
-
# @return [String]
|
221
|
-
# SQL DDL Statement to destroy a constraint
|
222
|
-
#
|
223
|
-
# @api private
|
224
|
-
def destroy_constraints_statement(storage_name, constraint_name)
|
225
|
-
<<-SQL.compress_lines
|
226
|
-
ALTER TABLE #{quote_name(storage_name)}
|
227
|
-
DROP FOREIGN KEY #{quote_name(constraint_name)}
|
228
|
-
SQL
|
229
|
-
end
|
23
|
+
def auto_migrate_up!(repository_name = nil)
|
24
|
+
descendants = super
|
25
|
+
repository_execute(:auto_migrate_up_constraints!, repository_name)
|
26
|
+
descendants
|
230
27
|
end
|
231
28
|
|
232
|
-
include SQL
|
233
29
|
end
|
234
30
|
|
235
|
-
module
|
236
|
-
def constraint_exists?(*)
|
237
|
-
false
|
238
|
-
end
|
239
|
-
|
240
|
-
def create_relationship_constraint(*)
|
241
|
-
false
|
242
|
-
end
|
31
|
+
module Model
|
243
32
|
|
244
|
-
|
245
|
-
false
|
246
|
-
end
|
247
|
-
end
|
33
|
+
private
|
248
34
|
|
249
|
-
|
250
|
-
def auto_migrate_down_with_constraints!(repository_name = self.repository_name)
|
35
|
+
def auto_migrate_down_constraints!(repository_name = self.repository_name)
|
251
36
|
return unless storage_exists?(repository_name)
|
252
37
|
return if self.respond_to?(:is_remixable?) && self.is_remixable?
|
253
38
|
execute_each_relationship(:destroy_relationship_constraint, repository_name)
|
254
39
|
end
|
255
40
|
|
256
|
-
def
|
41
|
+
def auto_migrate_up_constraints!(repository_name = self.repository_name)
|
257
42
|
return if self.respond_to?(:is_remixable?) && self.is_remixable?
|
258
43
|
execute_each_relationship(:create_relationship_constraint, repository_name)
|
259
44
|
end
|
260
45
|
|
261
|
-
private
|
262
|
-
|
263
46
|
def execute_each_relationship(method, repository_name)
|
264
47
|
adapter = DataMapper.repository(repository_name).adapter
|
265
48
|
return unless adapter.respond_to?(method)
|
@@ -268,7 +51,10 @@ module DataMapper
|
|
268
51
|
adapter.send(method, relationship)
|
269
52
|
end
|
270
53
|
end
|
54
|
+
|
271
55
|
end
|
272
|
-
|
273
|
-
|
274
|
-
|
56
|
+
|
57
|
+
end # module Migrations
|
58
|
+
|
59
|
+
end # module Constraints
|
60
|
+
end # module DataMapper
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-constraints/delete_constraint'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
class OneToMany::Relationship
|
8
|
+
|
9
|
+
include DataMapper::Hook
|
10
|
+
include Constraints::DeleteConstraint
|
11
|
+
|
12
|
+
OPTIONS << :constraint
|
13
|
+
|
14
|
+
attr_reader :constraint
|
15
|
+
|
16
|
+
# initialize is a private method in Relationship
|
17
|
+
# and private methods can not be "advised" (hooked into)
|
18
|
+
# in extlib.
|
19
|
+
with_changed_method_visibility(:initialize, :private, :public) do
|
20
|
+
before :initialize, :add_constraint_option
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class ManyToMany::Relationship
|
26
|
+
|
27
|
+
OPTIONS << :constraint
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# TODO: document
|
32
|
+
# @api semipublic
|
33
|
+
chainable do
|
34
|
+
def one_to_many_options
|
35
|
+
super.merge(:constraint => @constraint)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/dm-constraints.rb
CHANGED
@@ -1,41 +1,21 @@
|
|
1
|
-
require 'dm-
|
1
|
+
require 'dm-core'
|
2
|
+
|
2
3
|
require 'dm-constraints/migrations'
|
4
|
+
require 'dm-constraints/delete_constraint'
|
5
|
+
require 'dm-constraints/relationships'
|
6
|
+
require 'dm-constraints/adapters/dm-abstract-adapter'
|
3
7
|
|
4
8
|
module DataMapper
|
5
|
-
module Associations
|
6
|
-
class OneToMany::Relationship
|
7
|
-
include Extlib::Hook
|
8
|
-
include Constraints::DeleteConstraint
|
9
|
-
|
10
|
-
OPTIONS << :constraint
|
11
|
-
|
12
|
-
attr_reader :constraint
|
13
|
-
|
14
|
-
# initialize is a private method in Relationship
|
15
|
-
# and private methods can not be "advised" (hooked into)
|
16
|
-
# in extlib.
|
17
|
-
with_changed_method_visibility(:initialize, :private, :public) do
|
18
|
-
before :initialize, :add_constraint_option
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class ManyToMany::Relationship
|
23
|
-
|
24
|
-
OPTIONS << :constraint
|
25
9
|
|
26
|
-
|
10
|
+
extend Constraints::Migrations::SingletonMethods
|
27
11
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def one_to_many_options
|
32
|
-
super.merge(:constraint => @constraint)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
12
|
+
module Model
|
13
|
+
extend DataMapper::Constraints::Migrations::Model
|
14
|
+
append_extensions DataMapper::Constraints::Migrations::Model
|
36
15
|
end
|
37
16
|
|
38
17
|
module Constraints
|
18
|
+
|
39
19
|
include DeleteConstraint
|
40
20
|
|
41
21
|
module ClassMethods
|
@@ -57,15 +37,61 @@ module DataMapper
|
|
57
37
|
RUBY
|
58
38
|
end
|
59
39
|
|
40
|
+
def self.include_constraint_api
|
41
|
+
DataMapper::Repository.adapters.values.each do |adapter|
|
42
|
+
DataMapper::Adapters.include_constraint_api(ActiveSupport::Inflector.demodulize(adapter.class.name))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
60
46
|
Model.append_inclusions self
|
61
|
-
end
|
62
47
|
|
63
|
-
module
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
48
|
+
end # module Constraints
|
49
|
+
|
50
|
+
module Adapters
|
51
|
+
|
52
|
+
class AbstractAdapter
|
53
|
+
include DataMapper::Constraints::Adapters::AbstractAdapter
|
54
|
+
end
|
55
|
+
|
56
|
+
extend Chainable
|
57
|
+
|
58
|
+
class << self
|
59
|
+
|
60
|
+
def include_constraint_api(const_name)
|
61
|
+
require constraint_extensions(const_name)
|
62
|
+
if Constraints::Adapters.const_defined?(const_name)
|
63
|
+
adapter = const_get(const_name)
|
64
|
+
adapter.send(:include, constraint_module(const_name))
|
65
|
+
end
|
66
|
+
rescue LoadError
|
67
|
+
# do nothing
|
68
|
+
end
|
69
|
+
|
70
|
+
def constraint_module(const_name)
|
71
|
+
Constraints::Adapters.const_get(const_name)
|
68
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# @api private
|
77
|
+
def constraint_extensions(const_name)
|
78
|
+
name = adapter_name(const_name)
|
79
|
+
name = 'do' if name == 'dataobjects'
|
80
|
+
"dm-constraints/adapters/dm-#{name}-adapter"
|
81
|
+
end
|
82
|
+
|
69
83
|
end
|
70
|
-
|
71
|
-
|
84
|
+
|
85
|
+
extendable do
|
86
|
+
# @api private
|
87
|
+
def const_added(const_name)
|
88
|
+
include_constraint_api(const_name)
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end # module Adapters
|
94
|
+
|
95
|
+
Constraints.include_constraint_api
|
96
|
+
|
97
|
+
end # module DataMapper
|
@@ -1,11 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
describe 'DataMapper::Constraints', "(with #{DataMapper::Spec.adapter_name})" do
|
4
|
+
supported_by :all do
|
5
5
|
before :all do
|
6
|
-
@
|
7
|
-
@
|
6
|
+
@in_memory = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter)
|
7
|
+
@yaml = defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
|
8
8
|
|
9
|
+
@skip = @in_memory || @yaml
|
10
|
+
end
|
11
|
+
|
12
|
+
before :all do
|
9
13
|
class ::Article
|
10
14
|
include DataMapper::Resource
|
11
15
|
|
@@ -72,9 +76,11 @@ ADAPTERS.each do |name, connection_uri|
|
|
72
76
|
@comment = @author.comments.create(:body => 'So true!')
|
73
77
|
end
|
74
78
|
|
75
|
-
|
76
|
-
|
77
|
-
|
79
|
+
supported_by :postgres, :mysql do
|
80
|
+
it 'should not be able to create related objects with a failing foreign key constraint' do
|
81
|
+
article = Article.create(:title => 'Man on the Moon')
|
82
|
+
lambda { Comment.create(:body => 'So true!', :article_id => article.id + 1) }.should raise_error(DataObjects::IntegrityError)
|
83
|
+
end
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
@@ -180,6 +186,10 @@ ADAPTERS.each do |name, connection_uri|
|
|
180
186
|
end
|
181
187
|
|
182
188
|
describe 'many-to-many associations' do
|
189
|
+
before do
|
190
|
+
pending 'The adapter does not support m:m associations yet' if @skip
|
191
|
+
end
|
192
|
+
|
183
193
|
before do
|
184
194
|
@author = Author.create(:first_name => 'John', :last_name => 'Doe')
|
185
195
|
@another_author = Author.create(:first_name => 'Joe', :last_name => 'Smith')
|
@@ -275,6 +285,10 @@ ADAPTERS.each do |name, connection_uri|
|
|
275
285
|
end
|
276
286
|
|
277
287
|
describe 'many-to-many associations' do
|
288
|
+
before do
|
289
|
+
pending 'The adapter does not support m:m associations yet' if @skip
|
290
|
+
end
|
291
|
+
|
278
292
|
before do
|
279
293
|
@article = Article.create(:title => 'Man on the Moon')
|
280
294
|
@other_article = Article.create(:title => 'Dolly cloned')
|
@@ -370,6 +384,10 @@ ADAPTERS.each do |name, connection_uri|
|
|
370
384
|
end
|
371
385
|
|
372
386
|
describe 'many-to-many associations' do
|
387
|
+
before do
|
388
|
+
pending 'The adapter does not support m:m associations yet' if @skip
|
389
|
+
end
|
390
|
+
|
373
391
|
before do
|
374
392
|
@article = Article.create(:title => 'Man on the Moon')
|
375
393
|
@other_article = Article.create(:title => 'Dolly cloned')
|
@@ -538,6 +556,10 @@ ADAPTERS.each do |name, connection_uri|
|
|
538
556
|
end
|
539
557
|
|
540
558
|
describe 'many-to-many associations' do
|
559
|
+
before do
|
560
|
+
pending 'The adapter does not support m:m associations yet' if @skip
|
561
|
+
end
|
562
|
+
|
541
563
|
before do
|
542
564
|
@article = Article.create(:title => 'Man on the Moon')
|
543
565
|
@other_article = Article.create(:title => 'Dolly cloned')
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'isolated/require_spec'
|
3
|
+
require 'dm-core/spec/setup'
|
4
|
+
require 'dm-core/spec/lib/adapter_helpers'
|
5
|
+
|
6
|
+
# To really test this behavior, this spec needs to be run in isolation and not
|
7
|
+
# as part of the typical rake spec run, which requires dm-transactions upfront
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
config.extend(DataMapper::Spec::Adapters::Helpers)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "require 'dm-constraints' after calling DataMapper.setup" do
|
14
|
+
|
15
|
+
before(:all) do
|
16
|
+
|
17
|
+
@adapter = DataMapper::Spec.adapter
|
18
|
+
require 'dm-constraints'
|
19
|
+
|
20
|
+
class ::Person
|
21
|
+
include DataMapper::Resource
|
22
|
+
property :id, Serial
|
23
|
+
has n, :tasks
|
24
|
+
end
|
25
|
+
|
26
|
+
class ::Task
|
27
|
+
include DataMapper::Resource
|
28
|
+
property :id, Serial
|
29
|
+
belongs_to :person
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
it_should_behave_like "require 'dm-constraints'"
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'isolated/require_spec'
|
3
|
+
require 'dm-core/spec/setup'
|
4
|
+
require 'dm-core/spec/lib/adapter_helpers'
|
5
|
+
|
6
|
+
# To really test this behavior, this spec needs to be run in isolation and not
|
7
|
+
# as part of the typical rake spec run, which requires dm-transactions upfront
|
8
|
+
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
config.extend(DataMapper::Spec::Adapters::Helpers)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "require 'dm-constraints' before calling DataMapper.setup" do
|
14
|
+
|
15
|
+
before(:all) do
|
16
|
+
|
17
|
+
require 'dm-constraints'
|
18
|
+
@adapter = DataMapper::Spec.adapter
|
19
|
+
|
20
|
+
class ::Person
|
21
|
+
include DataMapper::Resource
|
22
|
+
property :id, Serial
|
23
|
+
has n, :tasks
|
24
|
+
end
|
25
|
+
|
26
|
+
class ::Task
|
27
|
+
include DataMapper::Resource
|
28
|
+
property :id, Serial
|
29
|
+
belongs_to :person
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
it_should_behave_like "require 'dm-constraints'"
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
shared_examples_for "require 'dm-constraints'" do
|
2
|
+
|
3
|
+
it "should include the constraint api in the DataMapper namespace" do
|
4
|
+
DataMapper::Model.respond_to?(:auto_migrate_down_constraints!, true).should be_true
|
5
|
+
DataMapper::Model.respond_to?(:auto_migrate_up_constraints!, true).should be_true
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should include the constraint api into the adapter" do
|
9
|
+
@adapter.respond_to?(:constraint_exists? ).should be_true
|
10
|
+
@adapter.respond_to?(:create_relationship_constraint ).should be_true
|
11
|
+
@adapter.respond_to?(:destroy_relationship_constraint).should be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|