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.
Files changed (27) hide show
  1. data/Gemfile +8 -8
  2. data/README.rdoc +23 -21
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/dm-constraints.gemspec +36 -39
  6. data/lib/data_mapper/constraints/adapters/abstract_adapter.rb +36 -0
  7. data/lib/{dm-constraints/adapters/dm-do-adapter.rb → data_mapper/constraints/adapters/do_adapter.rb} +48 -39
  8. data/lib/data_mapper/constraints/adapters/extension.rb +49 -0
  9. data/lib/{dm-constraints/adapters/dm-mysql-adapter.rb → data_mapper/constraints/adapters/mysql_adapter.rb} +2 -8
  10. data/lib/{dm-constraints/adapters/dm-oracle-adapter.rb → data_mapper/constraints/adapters/oracle_adapter.rb} +9 -2
  11. data/lib/{dm-constraints/adapters/dm-postgres-adapter.rb → data_mapper/constraints/adapters/postgres_adapter.rb} +1 -1
  12. data/lib/{dm-constraints/adapters/dm-sqlite-adapter.rb → data_mapper/constraints/adapters/sqlite_adapter.rb} +4 -0
  13. data/lib/{dm-constraints/adapters/dm-sqlserver-adapter.rb → data_mapper/constraints/adapters/sqlserver_adapter.rb} +1 -1
  14. data/lib/data_mapper/constraints/migrations/model.rb +36 -0
  15. data/lib/data_mapper/constraints/migrations/relationship.rb +42 -0
  16. data/lib/data_mapper/constraints/migrations/singleton_methods.rb +48 -0
  17. data/lib/data_mapper/constraints/relationship/many_to_many.rb +50 -0
  18. data/lib/data_mapper/constraints/relationship/one_to_many.rb +83 -0
  19. data/lib/data_mapper/constraints/resource.rb +34 -0
  20. data/lib/dm-constraints.rb +12 -88
  21. data/spec/integration/constraints_spec.rb +4 -2
  22. data/spec/isolated/require_spec.rb +7 -4
  23. metadata +79 -44
  24. data/lib/dm-constraints/adapters/dm-abstract-adapter.rb +0 -22
  25. data/lib/dm-constraints/delete_constraint.rb +0 -115
  26. data/lib/dm-constraints/migrations.rb +0 -60
  27. 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.1.0'
9
- DO_VERSION = '~> 0.10.2'
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.5.2'
17
- gem 'rake', '~> 0.8.7'
18
- gem 'rspec', '~> 1.3.1'
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.9'
26
- gem 'yard', '~> 0.6'
27
- gem 'yardstick', '~> 0.2'
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
@@ -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 Farmer
38
- has n, :pigs #equivalent to: has n, :pigs, :constraint => :protect
39
- end
40
-
41
- # M:M Example
42
- class Articles
43
- has n, :tags, :through => Resource, :constraint => :destroy
44
- end
45
-
46
- class Tags
47
- has n, :articles, :through => Resource, :constraint => :destroy
48
- end
49
-
50
- # Intermediary constraints for relationships using :through => Resource
51
- # are automatically inherited from the M:M relationship.
52
-
53
- # 1:1 Example
54
- class Farmer
55
- has 1, :beloved_sheep, :constraint => :protect
56
- end
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
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
 
4
4
  begin
5
- gem 'jeweler', '~> 1.5.2'
5
+ gem 'jeweler', '~> 1.6.4'
6
6
  require 'jeweler'
7
7
 
8
8
  Jeweler::Tasks.new do |gem|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0.rc1
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{dm-constraints}
8
- s.version = "1.1.0"
7
+ s.name = "dm-constraints"
8
+ s.version = "1.2.0.rc1"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
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 = %q{2011-03-16}
13
- s.description = %q{DataMapper plugin constraining relationships}
14
- s.email = %q{d.bussink [a] gmail [d] com}
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 = %q{http://github.com/datamapper/dm-constraints}
52
+ s.homepage = "http://github.com/datamapper/dm-constraints"
49
53
  s.require_paths = ["lib"]
50
- s.rubyforge_project = %q{datamapper}
51
- s.rubygems_version = %q{1.6.2}
52
- s.summary = %q{DataMapper plugin constraining relationships}
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.1.0"])
66
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
67
- s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
68
- s.add_development_dependency(%q<rspec>, ["~> 1.3.1"])
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.1.0"])
71
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
72
- s.add_dependency(%q<rake>, ["~> 0.8.7"])
73
- s.add_dependency(%q<rspec>, ["~> 1.3.1"])
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.1.0"])
77
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
78
- s.add_dependency(%q<rake>, ["~> 0.8.7"])
79
- s.add_dependency(%q<rspec>, ["~> 1.3.1"])
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
@@ -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
- source_model = relationship.source_model
44
- source_table = source_model.storage_name(name)
45
- source_key = relationship.source_key
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
- constraint_name = constraint_name(source_table, relationship.name)
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 = case relationship.inverse.constraint
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
- end
56
+ end
55
57
 
56
58
  return false if constraint_type.nil?
57
59
 
58
- storage_name = relationship.source_model.storage_name(name)
59
- reference_storage_name = relationship.target_model.storage_name(name)
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
- foreign_keys = relationship.source_key.map { |p| property_to_column_name(p, false) }
62
- reference_keys = relationship.target_key.map { |p| property_to_column_name(p, false) }
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(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys))
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
- source_model = relationship.source_model
81
- source_table = source_model.storage_name(name)
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
- constraint_name = constraint_name(source_table, relationship.name)
84
- return false unless constraint_exists?(source_table, constraint_name)
92
+ destroy_constraints_statement =
93
+ destroy_constraints_statement(storage_name, constraint_name)
85
94
 
86
- execute(destroy_constraints_statement(source_table, constraint_name))
95
+ execute(destroy_constraints_statement)
87
96
  end
88
97
 
89
- private
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 constraint_name [String]
127
+ # @param [String] constraint_name
119
128
  # name of the foreign key constraint
120
- # @param constraint_type [String]
121
- # type of foreign key constraint to add to the table
122
- # @param storage_name [String]
123
- # name of table to constrain
124
- # @param foreign_keys [Array[String]]
125
- # columns in the table that refer to foreign table
126
- # @param reference_storage_name [String]
127
- # table the foreign key refers to
128
- # @param reference_storage_name [Array[String]]
129
- # columns the foreign table that are referred to
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(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys)
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(storage_name)}
146
+ ALTER TABLE #{quote_name(source_storage_name)}
138
147
  ADD CONSTRAINT #{quote_name(constraint_name)}
139
- FOREIGN KEY (#{foreign_keys.join(', ')})
140
- REFERENCES #{quote_name(reference_storage_name)} (#{reference_keys.join(', ')})
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 storage_name [String]
158
+ # @param [String] storage_name
150
159
  # name of table to constrain
151
- # @param constraint_name [String]
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 storage_name [String]
177
+ # @param [String] storage_name
169
178
  # name of table to constrain
170
- # @param relationships_name [String]
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 'dm-constraints/adapters/dm-do-adapter'
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
- # @param storage_name [String]
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)