dm-constraints 1.1.0 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|