dm-constraints 1.1.0 → 1.2.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/Gemfile +8 -8
- data/README.rdoc +23 -21
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/dm-constraints.gemspec +36 -39
- data/lib/data_mapper/constraints/adapters/abstract_adapter.rb +36 -0
- data/lib/{dm-constraints/adapters/dm-do-adapter.rb → data_mapper/constraints/adapters/do_adapter.rb} +48 -39
- data/lib/data_mapper/constraints/adapters/extension.rb +49 -0
- data/lib/{dm-constraints/adapters/dm-mysql-adapter.rb → data_mapper/constraints/adapters/mysql_adapter.rb} +2 -8
- data/lib/{dm-constraints/adapters/dm-oracle-adapter.rb → data_mapper/constraints/adapters/oracle_adapter.rb} +9 -2
- data/lib/{dm-constraints/adapters/dm-postgres-adapter.rb → data_mapper/constraints/adapters/postgres_adapter.rb} +1 -1
- data/lib/{dm-constraints/adapters/dm-sqlite-adapter.rb → data_mapper/constraints/adapters/sqlite_adapter.rb} +4 -0
- data/lib/{dm-constraints/adapters/dm-sqlserver-adapter.rb → data_mapper/constraints/adapters/sqlserver_adapter.rb} +1 -1
- data/lib/data_mapper/constraints/migrations/model.rb +36 -0
- data/lib/data_mapper/constraints/migrations/relationship.rb +42 -0
- data/lib/data_mapper/constraints/migrations/singleton_methods.rb +48 -0
- data/lib/data_mapper/constraints/relationship/many_to_many.rb +50 -0
- data/lib/data_mapper/constraints/relationship/one_to_many.rb +83 -0
- data/lib/data_mapper/constraints/resource.rb +34 -0
- data/lib/dm-constraints.rb +12 -88
- data/spec/integration/constraints_spec.rb +4 -2
- data/spec/isolated/require_spec.rb +7 -4
- metadata +79 -44
- data/lib/dm-constraints/adapters/dm-abstract-adapter.rb +0 -22
- data/lib/dm-constraints/delete_constraint.rb +0 -115
- data/lib/dm-constraints/migrations.rb +0 -60
- data/lib/dm-constraints/relationships.rb +0 -41
data/Gemfile
CHANGED
@@ -5,26 +5,26 @@ source 'http://rubygems.org'
|
|
5
5
|
SOURCE = ENV.fetch('SOURCE', :git).to_sym
|
6
6
|
REPO_POSTFIX = SOURCE == :path ? '' : '.git'
|
7
7
|
DATAMAPPER = SOURCE == :path ? Pathname(__FILE__).dirname.parent : 'http://github.com/datamapper'
|
8
|
-
DM_VERSION = '~> 1.
|
9
|
-
DO_VERSION = '~> 0.10.
|
8
|
+
DM_VERSION = '~> 1.2.0.rc1'
|
9
|
+
DO_VERSION = '~> 0.10.6'
|
10
10
|
DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]
|
11
11
|
|
12
12
|
gem 'dm-core', DM_VERSION, SOURCE => "#{DATAMAPPER}/dm-core#{REPO_POSTFIX}"
|
13
13
|
|
14
14
|
group :development do
|
15
15
|
|
16
|
-
gem 'jeweler', '~> 1.
|
17
|
-
gem 'rake', '~> 0.
|
18
|
-
gem 'rspec', '~> 1.3.
|
16
|
+
gem 'jeweler', '~> 1.6.4'
|
17
|
+
gem 'rake', '~> 0.9.2'
|
18
|
+
gem 'rspec', '~> 1.3.2'
|
19
19
|
|
20
20
|
end
|
21
21
|
|
22
22
|
platforms :mri_18 do
|
23
23
|
group :quality do
|
24
24
|
|
25
|
-
gem 'rcov', '~> 0.9.
|
26
|
-
gem 'yard', '~> 0.
|
27
|
-
gem 'yardstick', '~> 0.
|
25
|
+
gem 'rcov', '~> 0.9.10'
|
26
|
+
gem 'yard', '~> 0.7.2'
|
27
|
+
gem 'yardstick', '~> 0.4'
|
28
28
|
|
29
29
|
end
|
30
30
|
end
|
data/README.rdoc
CHANGED
@@ -33,24 +33,26 @@ By default a relationship will PROTECT its children.
|
|
33
33
|
|
34
34
|
=== Examples
|
35
35
|
|
36
|
-
# 1:M Example
|
37
|
-
class
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
36
|
+
# 1:M Example
|
37
|
+
class Post
|
38
|
+
has n, :comments
|
39
|
+
# equivalent to:
|
40
|
+
# has n, :comments, :constraint => :protect
|
41
|
+
end
|
42
|
+
|
43
|
+
# M:M Example
|
44
|
+
class Article
|
45
|
+
has n, :tags, :through => Resource, :constraint => :destroy
|
46
|
+
end
|
47
|
+
|
48
|
+
class Tags
|
49
|
+
has n, :articles, :through => Resource, :constraint => :destroy
|
50
|
+
end
|
51
|
+
|
52
|
+
# Intermediary constraints for relationships using :through => Resource
|
53
|
+
# are automatically inherited from the M:M relationship.
|
54
|
+
|
55
|
+
# 1:1 Example
|
56
|
+
class User
|
57
|
+
has 1, :address, :constraint => :protect
|
58
|
+
end
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0.rc1
|
data/dm-constraints.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "1.
|
7
|
+
s.name = "dm-constraints"
|
8
|
+
s.version = "1.2.0.rc1"
|
9
9
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Dirkjan Bussink"]
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
12
|
+
s.date = "2011-09-09"
|
13
|
+
s.description = "DataMapper plugin constraining relationships"
|
14
|
+
s.email = "d.bussink [a] gmail [d] com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
17
|
"README.rdoc"
|
@@ -23,17 +23,21 @@ Gem::Specification.new do |s|
|
|
23
23
|
"Rakefile",
|
24
24
|
"VERSION",
|
25
25
|
"dm-constraints.gemspec",
|
26
|
+
"lib/data_mapper/constraints/adapters/abstract_adapter.rb",
|
27
|
+
"lib/data_mapper/constraints/adapters/do_adapter.rb",
|
28
|
+
"lib/data_mapper/constraints/adapters/extension.rb",
|
29
|
+
"lib/data_mapper/constraints/adapters/mysql_adapter.rb",
|
30
|
+
"lib/data_mapper/constraints/adapters/oracle_adapter.rb",
|
31
|
+
"lib/data_mapper/constraints/adapters/postgres_adapter.rb",
|
32
|
+
"lib/data_mapper/constraints/adapters/sqlite_adapter.rb",
|
33
|
+
"lib/data_mapper/constraints/adapters/sqlserver_adapter.rb",
|
34
|
+
"lib/data_mapper/constraints/migrations/model.rb",
|
35
|
+
"lib/data_mapper/constraints/migrations/relationship.rb",
|
36
|
+
"lib/data_mapper/constraints/migrations/singleton_methods.rb",
|
37
|
+
"lib/data_mapper/constraints/relationship/many_to_many.rb",
|
38
|
+
"lib/data_mapper/constraints/relationship/one_to_many.rb",
|
39
|
+
"lib/data_mapper/constraints/resource.rb",
|
26
40
|
"lib/dm-constraints.rb",
|
27
|
-
"lib/dm-constraints/adapters/dm-abstract-adapter.rb",
|
28
|
-
"lib/dm-constraints/adapters/dm-do-adapter.rb",
|
29
|
-
"lib/dm-constraints/adapters/dm-mysql-adapter.rb",
|
30
|
-
"lib/dm-constraints/adapters/dm-oracle-adapter.rb",
|
31
|
-
"lib/dm-constraints/adapters/dm-postgres-adapter.rb",
|
32
|
-
"lib/dm-constraints/adapters/dm-sqlite-adapter.rb",
|
33
|
-
"lib/dm-constraints/adapters/dm-sqlserver-adapter.rb",
|
34
|
-
"lib/dm-constraints/delete_constraint.rb",
|
35
|
-
"lib/dm-constraints/migrations.rb",
|
36
|
-
"lib/dm-constraints/relationships.rb",
|
37
41
|
"spec/integration/constraints_spec.rb",
|
38
42
|
"spec/isolated/require_after_setup_spec.rb",
|
39
43
|
"spec/isolated/require_before_setup_spec.rb",
|
@@ -45,38 +49,31 @@ Gem::Specification.new do |s|
|
|
45
49
|
"tasks/yard.rake",
|
46
50
|
"tasks/yardstick.rake"
|
47
51
|
]
|
48
|
-
s.homepage =
|
52
|
+
s.homepage = "http://github.com/datamapper/dm-constraints"
|
49
53
|
s.require_paths = ["lib"]
|
50
|
-
s.rubyforge_project =
|
51
|
-
s.rubygems_version =
|
52
|
-
s.summary =
|
53
|
-
s.test_files = [
|
54
|
-
"spec/integration/constraints_spec.rb",
|
55
|
-
"spec/isolated/require_after_setup_spec.rb",
|
56
|
-
"spec/isolated/require_before_setup_spec.rb",
|
57
|
-
"spec/isolated/require_spec.rb",
|
58
|
-
"spec/spec_helper.rb"
|
59
|
-
]
|
54
|
+
s.rubyforge_project = "datamapper"
|
55
|
+
s.rubygems_version = "1.8.10"
|
56
|
+
s.summary = "DataMapper plugin constraining relationships"
|
60
57
|
|
61
58
|
if s.respond_to? :specification_version then
|
62
59
|
s.specification_version = 3
|
63
60
|
|
64
61
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
-
s.add_runtime_dependency(%q<dm-core>, ["~> 1.
|
66
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.
|
67
|
-
s.add_development_dependency(%q<rake>, ["~> 0.
|
68
|
-
s.add_development_dependency(%q<rspec>, ["~> 1.3.
|
62
|
+
s.add_runtime_dependency(%q<dm-core>, ["~> 1.2.0.rc1"])
|
63
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
64
|
+
s.add_development_dependency(%q<rake>, ["~> 0.9.2"])
|
65
|
+
s.add_development_dependency(%q<rspec>, ["~> 1.3.2"])
|
69
66
|
else
|
70
|
-
s.add_dependency(%q<dm-core>, ["~> 1.
|
71
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
72
|
-
s.add_dependency(%q<rake>, ["~> 0.
|
73
|
-
s.add_dependency(%q<rspec>, ["~> 1.3.
|
67
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0.rc1"])
|
68
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
69
|
+
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
70
|
+
s.add_dependency(%q<rspec>, ["~> 1.3.2"])
|
74
71
|
end
|
75
72
|
else
|
76
|
-
s.add_dependency(%q<dm-core>, ["~> 1.
|
77
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
78
|
-
s.add_dependency(%q<rake>, ["~> 0.
|
79
|
-
s.add_dependency(%q<rspec>, ["~> 1.3.
|
73
|
+
s.add_dependency(%q<dm-core>, ["~> 1.2.0.rc1"])
|
74
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
75
|
+
s.add_dependency(%q<rake>, ["~> 0.9.2"])
|
76
|
+
s.add_dependency(%q<rspec>, ["~> 1.3.2"])
|
80
77
|
end
|
81
78
|
end
|
82
79
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "data_mapper/constraints/adapters/extension"
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
module Constraints
|
5
|
+
module Adapters
|
6
|
+
module AbstractAdapter
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
def constraint_exists?(*)
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def create_relationship_constraint(*)
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def destroy_relationship_constraint(*)
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
end # module AbstractAdapter
|
24
|
+
end # module Adapters
|
25
|
+
end # module Constraints
|
26
|
+
|
27
|
+
Adapters::AbstractAdapter.class_eval do
|
28
|
+
include Constraints::Adapters::AbstractAdapter
|
29
|
+
end
|
30
|
+
|
31
|
+
Adapters::AbstractAdapter.descendants.each do |adapter_class|
|
32
|
+
const_name = DataMapper::Inflector.demodulize(adapter_class.name)
|
33
|
+
Adapters.include_constraint_api(const_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
end # module DataMapper
|
data/lib/{dm-constraints/adapters/dm-do-adapter.rb → data_mapper/constraints/adapters/do_adapter.rb}
RENAMED
@@ -40,28 +40,35 @@ module DataMapper
|
|
40
40
|
def create_relationship_constraint(relationship)
|
41
41
|
return false unless valid_relationship_for_constraint?(relationship)
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
source_storage_name = relationship.source_model.storage_name(name)
|
44
|
+
target_storage_name = relationship.target_model.storage_name(name)
|
45
|
+
constraint_name = constraint_name(source_storage_name, relationship.name)
|
46
46
|
|
47
|
-
|
48
|
-
return false if constraint_exists?(source_table, constraint_name)
|
47
|
+
return false if constraint_exists?(source_storage_name, constraint_name)
|
49
48
|
|
50
|
-
constraint_type =
|
49
|
+
constraint_type =
|
50
|
+
case relationship.inverse.constraint
|
51
51
|
when :protect then 'NO ACTION'
|
52
|
+
# TODO: support :cascade as an option:
|
53
|
+
# (destroy doesn't communicate the UPDATE constraint)
|
52
54
|
when :destroy, :destroy! then 'CASCADE'
|
53
55
|
when :set_nil then 'SET NULL'
|
54
|
-
|
56
|
+
end
|
55
57
|
|
56
58
|
return false if constraint_type.nil?
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
+
source_keys = relationship.source_key.map { |p| property_to_column_name(p, false) }
|
61
|
+
target_keys = relationship.target_key.map { |p| property_to_column_name(p, false) }
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
+
create_constraints_statement = create_constraints_statement(
|
64
|
+
constraint_name,
|
65
|
+
constraint_type,
|
66
|
+
source_storage_name,
|
67
|
+
source_keys,
|
68
|
+
target_storage_name,
|
69
|
+
target_keys)
|
63
70
|
|
64
|
-
execute(create_constraints_statement
|
71
|
+
execute(create_constraints_statement)
|
65
72
|
end
|
66
73
|
|
67
74
|
##
|
@@ -77,16 +84,18 @@ module DataMapper
|
|
77
84
|
def destroy_relationship_constraint(relationship)
|
78
85
|
return false unless valid_relationship_for_constraint?(relationship)
|
79
86
|
|
80
|
-
|
81
|
-
|
87
|
+
storage_name = relationship.source_model.storage_name(name)
|
88
|
+
constraint_name = constraint_name(storage_name, relationship.name)
|
89
|
+
|
90
|
+
return false unless constraint_exists?(storage_name, constraint_name)
|
82
91
|
|
83
|
-
|
84
|
-
|
92
|
+
destroy_constraints_statement =
|
93
|
+
destroy_constraints_statement(storage_name, constraint_name)
|
85
94
|
|
86
|
-
execute(destroy_constraints_statement
|
95
|
+
execute(destroy_constraints_statement)
|
87
96
|
end
|
88
97
|
|
89
|
-
|
98
|
+
private
|
90
99
|
|
91
100
|
##
|
92
101
|
# Check to see if the relationship's constraints can be used
|
@@ -110,34 +119,34 @@ module DataMapper
|
|
110
119
|
end
|
111
120
|
|
112
121
|
module SQL
|
113
|
-
private
|
114
122
|
|
115
|
-
|
123
|
+
private
|
124
|
+
|
116
125
|
# Generates the SQL statement to create a constraint
|
117
126
|
#
|
118
|
-
# @param
|
127
|
+
# @param [String] constraint_name
|
119
128
|
# name of the foreign key constraint
|
120
|
-
# @param
|
121
|
-
# type of
|
122
|
-
# @param
|
123
|
-
# name of table to
|
124
|
-
# @param
|
125
|
-
# columns in
|
126
|
-
# @param
|
127
|
-
# table the
|
128
|
-
# @param
|
129
|
-
# columns the
|
129
|
+
# @param [String] constraint_type
|
130
|
+
# type of constraint to ALTER source_storage_name with
|
131
|
+
# @param [String] source_storage_name
|
132
|
+
# name of table to ALTER with constraint
|
133
|
+
# @param [Array(String)] source_keys
|
134
|
+
# columns in source_storage_name that refer to foreign table
|
135
|
+
# @param [String] target_storage_name
|
136
|
+
# target table of the constraint
|
137
|
+
# @param [Array(String)] target_keys
|
138
|
+
# columns the target table that are referred to
|
130
139
|
#
|
131
140
|
# @return [String]
|
132
141
|
# SQL DDL Statement to create a constraint
|
133
142
|
#
|
134
143
|
# @api private
|
135
|
-
def create_constraints_statement(
|
144
|
+
def create_constraints_statement(constraint_name, constraint_type, source_storage_name, source_keys, target_storage_name, target_keys)
|
136
145
|
DataMapper::Ext::String.compress_lines(<<-SQL)
|
137
|
-
ALTER TABLE #{quote_name(
|
146
|
+
ALTER TABLE #{quote_name(source_storage_name)}
|
138
147
|
ADD CONSTRAINT #{quote_name(constraint_name)}
|
139
|
-
FOREIGN KEY (#{
|
140
|
-
REFERENCES #{quote_name(
|
148
|
+
FOREIGN KEY (#{source_keys.join(', ')})
|
149
|
+
REFERENCES #{quote_name(target_storage_name)} (#{target_keys.join(', ')})
|
141
150
|
ON DELETE #{constraint_type}
|
142
151
|
ON UPDATE #{constraint_type}
|
143
152
|
SQL
|
@@ -146,9 +155,9 @@ module DataMapper
|
|
146
155
|
##
|
147
156
|
# Generates the SQL statement to destroy a constraint
|
148
157
|
#
|
149
|
-
# @param
|
158
|
+
# @param [String] storage_name
|
150
159
|
# name of table to constrain
|
151
|
-
# @param
|
160
|
+
# @param [String] constraint_name
|
152
161
|
# name of foreign key constraint
|
153
162
|
#
|
154
163
|
# @return [String]
|
@@ -165,9 +174,9 @@ module DataMapper
|
|
165
174
|
##
|
166
175
|
# generates a unique constraint name given a table and a relationships
|
167
176
|
#
|
168
|
-
# @param
|
177
|
+
# @param [String] storage_name
|
169
178
|
# name of table to constrain
|
170
|
-
# @param
|
179
|
+
# @param [String] relationship_name
|
171
180
|
# name of the relationship to constrain
|
172
181
|
#
|
173
182
|
# @return [String]
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Constraints
|
3
|
+
module Adapters
|
4
|
+
module Extension
|
5
|
+
# Include the corresponding Constraints module into a adapter class
|
6
|
+
#
|
7
|
+
# @param [Symbol] const_name
|
8
|
+
# demodulized name of the adapter class to include corresponding
|
9
|
+
# constraints module into
|
10
|
+
#
|
11
|
+
# TODO: come up with a better way to include modules
|
12
|
+
# into all currently loaded and subsequently loaded Adapters
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def include_constraint_api(const_name)
|
16
|
+
require constraint_extensions(const_name)
|
17
|
+
|
18
|
+
if Constraints::Adapters.const_defined?(const_name)
|
19
|
+
adapter = const_get(const_name)
|
20
|
+
constraint_module = Constraints::Adapters.const_get(const_name)
|
21
|
+
adapter.class_eval { include constraint_module }
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
# Silently ignore the fact that no adapter extensions could be required
|
25
|
+
# This means that the adapter in use doesn't support constraints
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def constraint_extensions(const_name)
|
32
|
+
name = adapter_name(const_name)
|
33
|
+
name = 'do' if name == 'dataobjects'
|
34
|
+
"data_mapper/constraints/adapters/#{name}_adapter"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
def const_added(const_name)
|
39
|
+
include_constraint_api(const_name)
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
end # module Extension
|
44
|
+
end # module Adapters
|
45
|
+
end # module Constraints
|
46
|
+
|
47
|
+
Adapters.extend Constraints::Adapters::Extension
|
48
|
+
|
49
|
+
end # module DataMapper
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'data_mapper/constraints/adapters/do_adapter'
|
2
2
|
|
3
3
|
module DataMapper
|
4
4
|
module Constraints
|
@@ -14,13 +14,7 @@ module DataMapper
|
|
14
14
|
##
|
15
15
|
# MySQL specific query to drop a foreign key
|
16
16
|
#
|
17
|
-
# @
|
18
|
-
# name of table to constrain
|
19
|
-
# @param constraint_name [String]
|
20
|
-
# name of foreign key constraint
|
21
|
-
#
|
22
|
-
# @return [String]
|
23
|
-
# SQL DDL Statement to destroy a constraint
|
17
|
+
# @see DataMapper::Constraints::Adapters::DataObjectsAdapter#destroy_constraints_statement
|
24
18
|
#
|
25
19
|
# @api private
|
26
20
|
def destroy_constraints_statement(storage_name, constraint_name)
|