dm-constraints 0.9.11 → 0.10.0
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/{History.txt → History.rdoc} +4 -2
- data/Manifest.txt +3 -5
- data/{README.txt → README.rdoc} +0 -0
- data/Rakefile +2 -3
- data/lib/dm-constraints/delete_constraint.rb +37 -106
- data/lib/dm-constraints/migrations.rb +274 -0
- data/lib/dm-constraints/version.rb +1 -1
- data/lib/dm-constraints.rb +27 -49
- data/spec/integration/constraints_spec.rb +380 -339
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +50 -12
- data/tasks/install.rb +1 -1
- data/tasks/spec.rb +4 -4
- metadata +15 -24
- data/lib/dm-constraints/data_objects_adapter.rb +0 -197
- data/lib/dm-constraints/mysql_adapter.rb +0 -50
- data/lib/dm-constraints/postgres_adapter.rb +0 -32
data/Manifest.txt
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
History.
|
1
|
+
History.rdoc
|
2
2
|
LICENSE
|
3
3
|
Manifest.txt
|
4
|
-
README.
|
4
|
+
README.rdoc
|
5
5
|
Rakefile
|
6
6
|
TODO
|
7
7
|
lib/dm-constraints.rb
|
8
|
-
lib/dm-constraints/data_objects_adapter.rb
|
9
8
|
lib/dm-constraints/delete_constraint.rb
|
10
|
-
lib/dm-constraints/
|
11
|
-
lib/dm-constraints/postgres_adapter.rb
|
9
|
+
lib/dm-constraints/migrations.rb
|
12
10
|
lib/dm-constraints/version.rb
|
13
11
|
spec/integration/constraints_spec.rb
|
14
12
|
spec/spec.opts
|
data/{README.txt → README.rdoc}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
require 'rubygems'
|
3
2
|
|
4
3
|
ROOT = Pathname(__FILE__).dirname.expand_path
|
5
4
|
JRUBY = RUBY_PLATFORM =~ /java/
|
@@ -14,10 +13,10 @@ GEM_NAME = 'dm-constraints'
|
|
14
13
|
GEM_VERSION = DataMapper::Constraints::VERSION
|
15
14
|
GEM_DEPENDENCIES = [['dm-core', GEM_VERSION]]
|
16
15
|
GEM_CLEAN = %w[ log pkg coverage ]
|
17
|
-
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.
|
16
|
+
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.rdoc LICENSE TODO History.rdoc ] }
|
18
17
|
|
19
18
|
PROJECT_NAME = 'datamapper'
|
20
|
-
PROJECT_URL = "http://github.com/
|
19
|
+
PROJECT_URL = "http://github.com/datamapper/dm-more/tree/master/#{GEM_NAME}"
|
21
20
|
PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin constraining relationships'
|
22
21
|
|
23
22
|
[ ROOT, ROOT.parent ].each do |dir|
|
@@ -1,13 +1,12 @@
|
|
1
1
|
module DataMapper
|
2
2
|
module Constraints
|
3
3
|
module DeleteConstraint
|
4
|
-
|
5
4
|
def self.included(base)
|
6
5
|
base.extend(ClassMethods)
|
7
6
|
end
|
8
7
|
|
9
8
|
module ClassMethods
|
10
|
-
|
9
|
+
CONSTRAINT_OPTIONS = [ :protect, :destroy, :destroy!, :set_nil, :skip ].to_set.freeze
|
11
10
|
|
12
11
|
##
|
13
12
|
# Checks that the constraint type is appropriate to the relationship
|
@@ -23,26 +22,25 @@ module DataMapper
|
|
23
22
|
# @return [nil]
|
24
23
|
#
|
25
24
|
# @api semi-public
|
26
|
-
def check_delete_constraint_type(cardinality, name,
|
27
|
-
|
28
|
-
options[:constraint] ||= nil
|
29
|
-
constraint_type = options[:constraint]
|
30
|
-
return if constraint_type.nil?
|
25
|
+
def check_delete_constraint_type(cardinality, name, *args)
|
26
|
+
options = extract_options(args)
|
31
27
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
28
|
+
return unless options.key?(:constraint)
|
29
|
+
|
30
|
+
constraint = options[:constraint]
|
36
31
|
|
37
|
-
|
38
|
-
raise ArgumentError, "
|
32
|
+
unless CONSTRAINT_OPTIONS.include?(constraint)
|
33
|
+
raise ArgumentError, ":constraint option must be one of #{CONSTRAINT_OPTIONS.to_a.join(', ')}"
|
39
34
|
end
|
40
35
|
|
41
|
-
|
42
|
-
|
36
|
+
# XXX: is any constraint valid with a :through relationship?
|
37
|
+
if constraint == :set_nil && options.key?(:through)
|
38
|
+
raise ArgumentError, 'Constraint type :set_nil is not valid for relationships using :through'
|
43
39
|
end
|
44
40
|
end
|
45
41
|
|
42
|
+
private
|
43
|
+
|
46
44
|
##
|
47
45
|
# Temporarily changes the visibility of a method so a block can be evaluated against it
|
48
46
|
#
|
@@ -62,38 +60,25 @@ module DataMapper
|
|
62
60
|
def with_changed_method_visibility(method, from_visibility, to_visibility, &block)
|
63
61
|
send(to_visibility, method)
|
64
62
|
yield
|
63
|
+
ensure
|
65
64
|
send(from_visibility, method)
|
66
65
|
end
|
67
|
-
|
68
66
|
end
|
69
67
|
|
70
68
|
##
|
71
|
-
#
|
69
|
+
# Adds the delete constraint options to a relationship
|
72
70
|
#
|
73
|
-
# @param params [*ARGS] Arguments passed to Relationship#initialize
|
74
|
-
#
|
75
|
-
# @notes This takes *params because it runs before the initializer for Relationships and RelationshipChains
|
76
|
-
# which have different method signatures
|
71
|
+
# @param params [*ARGS] Arguments passed to Relationship#initialize
|
77
72
|
#
|
78
73
|
# @return [nil]
|
79
74
|
#
|
80
75
|
# @api semi-public
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
if opts.is_a?(Hash)
|
85
|
-
#if it is a chain, set the constraint on the 1:M near relationship(anonymous)
|
86
|
-
if self.is_a?(DataMapper::Associations::RelationshipChain)
|
87
|
-
opts = params.last
|
88
|
-
near_rel = opts[:parent_model].relationships[opts[:near_relationship_name]]
|
89
|
-
near_rel.options[:constraint] = opts[:constraint]
|
90
|
-
near_rel.instance_variable_set "@delete_constraint", opts[:constraint]
|
91
|
-
end
|
92
|
-
|
93
|
-
@delete_constraint = params.last[:constraint]
|
94
|
-
end
|
76
|
+
def add_constraint_option(name, child_model, parent_model, options = {})
|
77
|
+
@constraint = options.fetch(:constraint, :protect) || :skip
|
95
78
|
end
|
96
79
|
|
80
|
+
private
|
81
|
+
|
97
82
|
##
|
98
83
|
# Checks delete constraints prior to destroying a dm resource or collection
|
99
84
|
#
|
@@ -101,86 +86,32 @@ module DataMapper
|
|
101
86
|
#
|
102
87
|
# @notes
|
103
88
|
# - It only considers a relationship's constraints if this is the parent model (ie a child shouldn't delete a parent)
|
104
|
-
# -
|
89
|
+
# - Many to Many Relationships are skipped, as they are evaluated by their underlying 1:M relationships
|
105
90
|
#
|
106
91
|
# @returns [nil]
|
107
92
|
#
|
108
93
|
# @api semi-public
|
109
94
|
def check_delete_constraints
|
110
|
-
|
111
|
-
|
112
|
-
next
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
95
|
+
relationships.each_value do |relationship|
|
96
|
+
next unless relationship.respond_to?(:constraint)
|
97
|
+
next unless association = relationship.get(self)
|
98
|
+
|
99
|
+
delete_allowed = case constraint = relationship.constraint
|
100
|
+
when :protect
|
101
|
+
Array(association).empty?
|
102
|
+
when :destroy, :destroy!
|
103
|
+
association.send(constraint)
|
104
|
+
when :set_nil
|
105
|
+
Array(association).all? do |resource|
|
106
|
+
resource.update(relationship.inverse => nil)
|
107
|
+
end
|
108
|
+
when :skip
|
109
|
+
true # do nothing
|
122
110
|
end
|
123
|
-
end # relationships
|
124
|
-
end # check_delete_constraints
|
125
111
|
|
126
|
-
|
127
|
-
# Performs the meat of the check_delete_constraints method for a collection of resources
|
128
|
-
#
|
129
|
-
# @param rel [DataMapper::Associations::Relationship] relationship being evaluated
|
130
|
-
#
|
131
|
-
# @param children [~DataMapper::Collection] child records to constrain
|
132
|
-
#
|
133
|
-
# @see #check_delete_constraints
|
134
|
-
#
|
135
|
-
# @api semi-public
|
136
|
-
def check_collection_delete_constraints(rel, children)
|
137
|
-
case rel.delete_constraint
|
138
|
-
when nil, :protect
|
139
|
-
unless children.empty?
|
140
|
-
DataMapper.logger.error("Could not delete #{self.class} a child #{children.first.class} exists")
|
141
|
-
throw(:halt,false)
|
142
|
-
end
|
143
|
-
when :destroy
|
144
|
-
children.each{|child| child.destroy}
|
145
|
-
when :destroy!
|
146
|
-
children.destroy!
|
147
|
-
when :set_nil
|
148
|
-
children.each do |child|
|
149
|
-
child.class.many_to_one_relationships.each do |mto_rel|
|
150
|
-
child.send("#{mto_rel.name}=", nil) if child.send(mto_rel.name).eql?(self)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
##
|
157
|
-
# Performs the meat of check_delete_constraints method for a single resource
|
158
|
-
#
|
159
|
-
# @param rel [DataMapper::Associations::Relationship] the relationship to evaluate
|
160
|
-
#
|
161
|
-
# @param child [~DataMapper::Model] the model to constrain
|
162
|
-
#
|
163
|
-
# @see #check_delete_constraints
|
164
|
-
#
|
165
|
-
# @api semi-public
|
166
|
-
def check_resource_delete_constraints(rel, child)
|
167
|
-
case rel.delete_constraint
|
168
|
-
when nil, :protect
|
169
|
-
unless child.nil?
|
170
|
-
DataMapper.logger.error("Could not delete #{self.class} a child #{child.class} exists")
|
171
|
-
throw(:halt,false)
|
172
|
-
end
|
173
|
-
when :destroy
|
174
|
-
child.destroy
|
175
|
-
when :destroy!
|
176
|
-
#not supported in dm-master, an exception should have been raised on class load
|
177
|
-
when :set_nil
|
178
|
-
child.class.many_to_one_relationships.each do |mto_rel|
|
179
|
-
child.send("#{mto_rel.name}=", nil) if child.send(mto_rel.name).eql?(self)
|
180
|
-
end
|
112
|
+
throw(:halt, false) unless delete_allowed
|
181
113
|
end
|
182
114
|
end
|
183
|
-
|
184
115
|
end # DeleteConstraint
|
185
116
|
end # Constraints
|
186
117
|
end # DataMapper
|
@@ -0,0 +1,274 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Constraints
|
3
|
+
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
|
+
query(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
|
+
|
105
|
+
constraint_name = constraint_name(source_table, relationship.name)
|
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
|
112
|
+
|
113
|
+
##
|
114
|
+
# Check to see if the relationship's constraints can be used
|
115
|
+
#
|
116
|
+
# Only one-to-one, one-to-many, and many-to-many relationships
|
117
|
+
# can be used for constraints. They must also be in the same
|
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
|
132
|
+
end
|
133
|
+
|
134
|
+
module SQL
|
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
|
186
|
+
|
187
|
+
##
|
188
|
+
# generates a unique constraint name given a table and a relationships
|
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
|
203
|
+
end
|
204
|
+
|
205
|
+
include SQL
|
206
|
+
end
|
207
|
+
|
208
|
+
module MysqlAdapter
|
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
|
230
|
+
end
|
231
|
+
|
232
|
+
include SQL
|
233
|
+
end
|
234
|
+
|
235
|
+
module Sqlite3Adapter
|
236
|
+
def constraint_exists?(*)
|
237
|
+
false
|
238
|
+
end
|
239
|
+
|
240
|
+
def create_relationship_constraint(*)
|
241
|
+
false
|
242
|
+
end
|
243
|
+
|
244
|
+
def destroy_relationship_constraint(*)
|
245
|
+
false
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
module Model
|
250
|
+
def auto_migrate_down_with_constraints!(repository_name = self.repository_name)
|
251
|
+
return unless storage_exists?(repository_name)
|
252
|
+
return if self.respond_to?(:is_remixable?) && self.is_remixable?
|
253
|
+
execute_each_relationship(:destroy_relationship_constraint, repository_name)
|
254
|
+
end
|
255
|
+
|
256
|
+
def auto_migrate_up_with_constraints!(repository_name = self.repository_name)
|
257
|
+
return if self.respond_to?(:is_remixable?) && self.is_remixable?
|
258
|
+
execute_each_relationship(:create_relationship_constraint, repository_name)
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
def execute_each_relationship(method, repository_name)
|
264
|
+
adapter = DataMapper.repository(repository_name).adapter
|
265
|
+
return unless adapter.respond_to?(method)
|
266
|
+
|
267
|
+
relationships(repository_name).each_value do |relationship|
|
268
|
+
adapter.send(method, relationship)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
data/lib/dm-constraints.rb
CHANGED
@@ -1,58 +1,42 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
|
5
|
-
# Add all external dependencies for the plugin here
|
6
|
-
gem 'dm-core', '0.9.11'
|
7
|
-
require 'dm-core'
|
8
|
-
|
9
|
-
# Require plugin-files
|
10
|
-
require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'data_objects_adapter'
|
11
|
-
require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'postgres_adapter'
|
12
|
-
require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'mysql_adapter'
|
13
|
-
require Pathname(__FILE__).dirname.expand_path / 'dm-constraints' / 'delete_constraint'
|
1
|
+
require 'dm-constraints/delete_constraint'
|
2
|
+
require 'dm-constraints/migrations'
|
3
|
+
require 'dm-constraints/version'
|
14
4
|
|
15
5
|
module DataMapper
|
16
6
|
module Associations
|
17
|
-
class
|
7
|
+
class OneToMany::Relationship
|
18
8
|
include Extlib::Hook
|
19
|
-
include
|
9
|
+
include Constraints::DeleteConstraint
|
20
10
|
|
21
|
-
attr_reader :delete_constraint
|
22
11
|
OPTIONS << :constraint
|
23
12
|
|
13
|
+
attr_reader :constraint
|
14
|
+
|
24
15
|
# initialize is a private method in Relationship
|
25
16
|
# and private methods can not be "advised" (hooked into)
|
26
17
|
# in extlib.
|
27
18
|
with_changed_method_visibility(:initialize, :private, :public) do
|
28
|
-
before :initialize, :
|
19
|
+
before :initialize, :add_constraint_option
|
29
20
|
end
|
30
21
|
end
|
31
|
-
end
|
32
|
-
end
|
33
22
|
|
34
|
-
|
35
|
-
module Associations
|
36
|
-
class Relationship
|
37
|
-
include Extlib::Hook
|
38
|
-
include DataMapper::Constraints::DeleteConstraint
|
23
|
+
class ManyToMany::Relationship
|
39
24
|
|
40
|
-
attr_reader :delete_constraint
|
41
25
|
OPTIONS << :constraint
|
42
26
|
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
|
47
|
-
|
27
|
+
private
|
28
|
+
|
29
|
+
# TODO: document
|
30
|
+
# @api semipublic
|
31
|
+
chainable do
|
32
|
+
def one_to_many_options
|
33
|
+
super.merge(:constraint => @constraint)
|
34
|
+
end
|
48
35
|
end
|
49
36
|
end
|
50
37
|
end
|
51
|
-
end
|
52
38
|
|
53
|
-
module DataMapper
|
54
39
|
module Constraints
|
55
|
-
|
56
40
|
include DeleteConstraint
|
57
41
|
|
58
42
|
module ClassMethods
|
@@ -65,28 +49,22 @@ module DataMapper
|
|
65
49
|
#
|
66
50
|
def self.included(model)
|
67
51
|
model.extend(ClassMethods)
|
68
|
-
model.class_eval
|
52
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
69
53
|
before_class_method :has, :check_delete_constraint_type
|
70
|
-
|
54
|
+
|
55
|
+
if instance_methods.any? { |m| m.to_sym == :destroy }
|
71
56
|
before :destroy, :check_delete_constraints
|
72
57
|
end
|
73
|
-
|
58
|
+
RUBY
|
74
59
|
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
class AutoMigrator
|
79
|
-
include Extlib::Hook
|
80
|
-
include DataMapper::Constraints::DataObjectsAdapter::Migration
|
81
60
|
end
|
82
61
|
|
83
|
-
module
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
PostgresAdapter.send :include, DataMapper::Constraints::PostgresAdapter::SQL
|
62
|
+
module Migrations
|
63
|
+
constants.each do |const_name|
|
64
|
+
if Constraints::Migrations.const_defined?(const_name)
|
65
|
+
mod = const_get(const_name)
|
66
|
+
mod.send(:include, Constraints::Migrations.const_get(const_name))
|
67
|
+
end
|
90
68
|
end
|
91
69
|
end
|
92
70
|
end
|