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.
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)