dm-constraints 0.10.2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,37 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## Rubinius
17
+ *.rbc
18
+
19
+ ## PROJECT::GENERAL
20
+ *.gem
21
+ coverage
22
+ rdoc
23
+ pkg
24
+ tmp
25
+ doc
26
+ log
27
+ .yardoc
28
+ measurements
29
+
30
+ ## BUNDLER
31
+ .bundle
32
+ Gemfile.local
33
+ Gemfile.lock
34
+
35
+ ## PROJECT::SPECIFIC
36
+ spec/db/
37
+ spec/log/
data/Gemfile ADDED
@@ -0,0 +1,141 @@
1
+ # If you're working on more than one datamapper gem at a time, then it's
2
+ # recommended to create a local Gemfile and use this instead of the git
3
+ # sources. This will make sure that you are developing against your
4
+ # other local datamapper sources that you currently work on. Gemfile.local
5
+ # will behave identically to the standard Gemfile apart from the fact that
6
+ # it fetches the datamapper gems from local paths. This means that you can use
7
+ # the same environment variables, like ADAPTER(S) or PLUGIN(S) when running
8
+ # bundle commands. Gemfile.local is added to .gitignore, so you don't need to
9
+ # worry about accidentally checking local development paths into git.
10
+ # In order to create a local Gemfile, all you need to do is run:
11
+ #
12
+ # bundle exec rake local_gemfile
13
+ #
14
+ # This will give you a Gemfile.local file that points to your local clones of
15
+ # the various datamapper gems. It's assumed that all datamapper repo clones
16
+ # reside in the same directory. You can use the Gemfile.local like so for
17
+ # running any bundle command:
18
+ #
19
+ # BUNDLE_GEMFILE=Gemfile.local bundle foo
20
+ #
21
+ # You can also specify which adapter(s) should be part of the bundle by setting
22
+ # an environment variable. This of course also works when using the Gemfile.local
23
+ #
24
+ # bundle foo # dm-sqlite-adapter
25
+ # ADAPTER=mysql bundle foo # dm-mysql-adapter
26
+ # ADAPTERS=sqlite,mysql bundle foo # dm-sqlite-adapter and dm-mysql-adapter
27
+ #
28
+ # Of course you can also use the ADAPTER(S) variable when using the Gemfile.local
29
+ # and running specs against selected adapters.
30
+ #
31
+ # For easily working with adapters supported on your machine, it's recommended
32
+ # that you first install all adapters that you are planning to use or work on
33
+ # by doing something like
34
+ #
35
+ # ADAPTERS=sqlite,mysql,postgres bundle install
36
+ #
37
+ # This will clone the various repositories and make them available to bundler.
38
+ # Once you have them installed you can easily switch between adapters for the
39
+ # various development tasks. Running something like
40
+ #
41
+ # ADAPTER=mysql bundle exec rake spec
42
+ #
43
+ # will make sure that the dm-mysql-adapter is part of the bundle, and will be used
44
+ # when running the specs.
45
+ #
46
+ # You can also specify which plugin(s) should be part of the bundle by setting
47
+ # an environment variable. This also works when using the Gemfile.local
48
+ #
49
+ # bundle foo # dm-migrations
50
+ # PLUGINS=dm-validations bundle foo # dm-migrations and dm-validations
51
+ # PLUGINS=dm-validations,dm-types bundle foo # dm-migrations, dm-validations and dm-types
52
+ #
53
+ # Of course you can combine the PLUGIN(S) and ADAPTER(S) env vars to run specs
54
+ # for certain adapter/plugin combinations.
55
+ #
56
+ # Finally, to speed up running specs and other tasks, it's recommended to run
57
+ #
58
+ # bundle lock
59
+ #
60
+ # after running 'bundle install' for the first time. This will make 'bundle exec' run
61
+ # a lot faster compared to the unlocked version. With an unlocked bundle you would
62
+ # typically just run 'bundle install' from time to time to fetch the latest sources from
63
+ # upstream. When you locked your bundle, you need to run
64
+ #
65
+ # bundle install --relock
66
+ #
67
+ # to make sure to fetch the latest updates and then lock the bundle again. Gemfile.lock
68
+ # is added to the .gitignore file, so you don't need to worry about accidentally checking
69
+ # it into version control.
70
+
71
+ source 'http://rubygems.org'
72
+
73
+ DATAMAPPER = 'git://github.com/datamapper'
74
+ DM_VERSION = '~> 1.0.0.rc1'
75
+
76
+ group :runtime do # Runtime dependencies (as in the gemspec)
77
+
78
+ if ENV['EXTLIB']
79
+ gem 'extlib', '~> 0.9.15', :git => "#{DATAMAPPER}/extlib.git"
80
+ else
81
+ gem 'activesupport', '~> 3.0.0.beta3', :git => 'git://github.com/rails/rails.git', :require => nil
82
+ end
83
+
84
+ gem 'dm-core', DM_VERSION, :git => "#{DATAMAPPER}/dm-core.git"
85
+
86
+ end
87
+
88
+ group(:development) do # Development dependencies (as in the gemspec)
89
+
90
+ gem 'rake', '~> 0.8.7'
91
+ gem 'rspec', '~> 1.3'
92
+ gem 'jeweler', '~> 1.4'
93
+
94
+ end
95
+
96
+ group :quality do # These gems contain rake tasks that check the quality of the source code
97
+
98
+ gem 'metric_fu', '~> 1.3'
99
+ gem 'rcov', '~> 0.9.7'
100
+ gem 'reek', '~> 1.2.7'
101
+ gem 'roodi', '~> 2.1'
102
+ gem 'yard', '~> 0.5'
103
+ gem 'yardstick', '~> 0.1'
104
+
105
+ end
106
+
107
+ group :datamapper do # We need this because we want to pin these dependencies to their git master sources
108
+
109
+ adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
110
+ adapters = adapters.to_s.gsub(',',' ').split(' ') - ['in_memory']
111
+
112
+ unless adapters.empty?
113
+
114
+ DO_VERSION = '~> 0.10.2'
115
+ DM_DO_ADAPTERS = %w[sqlite postgres mysql oracle sqlserver]
116
+
117
+ gem 'data_objects', DO_VERSION, :git => "#{DATAMAPPER}/do.git"
118
+
119
+ adapters.each do |adapter|
120
+ if DM_DO_ADAPTERS.any? { |dm_do_adapter| dm_do_adapter =~ /#{adapter}/ }
121
+ adapter = 'sqlite3' if adapter == 'sqlite'
122
+ gem "do_#{adapter}", DO_VERSION, :git => "#{DATAMAPPER}/do.git"
123
+ end
124
+ end
125
+
126
+ gem 'dm-do-adapter', DM_VERSION, :git => "#{DATAMAPPER}/dm-do-adapter.git"
127
+
128
+ adapters.each do |adapter|
129
+ gem "dm-#{adapter}-adapter", DM_VERSION, :git => "#{DATAMAPPER}/dm-#{adapter}-adapter.git"
130
+ end
131
+
132
+ end
133
+
134
+ plugins = ENV['PLUGINS'] || ENV['PLUGIN']
135
+ plugins = (plugins.to_s.gsub(',',' ').split(' ') + ['dm-migrations']).uniq
136
+
137
+ plugins.each do |plugin|
138
+ gem plugin, DM_VERSION, :git => "#{DATAMAPPER}/#{plugin}.git"
139
+ end
140
+
141
+ end
data/Rakefile CHANGED
@@ -15,10 +15,10 @@ begin
15
15
 
16
16
  gem.rubyforge_project = 'datamapper'
17
17
 
18
- gem.add_dependency 'dm-core', '~> 0.10.2'
18
+ gem.add_dependency 'dm-core', '~> 1.0.0.rc1'
19
+ gem.add_dependency 'dm-migrations', '~> 1.0.0.rc1'
19
20
 
20
- gem.add_development_dependency 'rspec', '~> 1.2.9'
21
- gem.add_development_dependency 'yard', '~> 0.4.0'
21
+ gem.add_development_dependency 'rspec', '~> 1.3'
22
22
  end
23
23
 
24
24
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.2
1
+ 1.0.0.rc1
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dm-constraints}
8
- s.version = "0.10.2"
8
+ s.version = "1.0.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{2009-12-11}
12
+ s.date = %q{2010-05-19}
13
13
  s.description = %q{DataMapper plugin constraining relationships}
14
14
  s.email = %q{d.bussink [a] gmail [d] com}
15
15
  s.extra_rdoc_files = [
@@ -17,19 +17,33 @@ Gem::Specification.new do |s|
17
17
  "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
- "LICENSE",
20
+ ".gitignore",
21
+ "Gemfile",
22
+ "LICENSE",
21
23
  "README.rdoc",
22
24
  "Rakefile",
23
25
  "VERSION",
24
26
  "dm-constraints.gemspec",
25
27
  "lib/dm-constraints.rb",
28
+ "lib/dm-constraints/adapters/dm-abstract-adapter.rb",
29
+ "lib/dm-constraints/adapters/dm-do-adapter.rb",
30
+ "lib/dm-constraints/adapters/dm-mysql-adapter.rb",
31
+ "lib/dm-constraints/adapters/dm-oracle-adapter.rb",
32
+ "lib/dm-constraints/adapters/dm-postgres-adapter.rb",
33
+ "lib/dm-constraints/adapters/dm-sqlite-adapter.rb",
34
+ "lib/dm-constraints/adapters/dm-sqlserver-adapter.rb",
26
35
  "lib/dm-constraints/delete_constraint.rb",
27
36
  "lib/dm-constraints/migrations.rb",
37
+ "lib/dm-constraints/relationships.rb",
28
38
  "spec/integration/constraints_spec.rb",
39
+ "spec/isolated/require_after_setup_spec.rb",
40
+ "spec/isolated/require_before_setup_spec.rb",
41
+ "spec/isolated/require_spec.rb",
29
42
  "spec/rcov.opts",
30
43
  "spec/spec.opts",
31
44
  "spec/spec_helper.rb",
32
45
  "tasks/ci.rake",
46
+ "tasks/local_gemfile.rake",
33
47
  "tasks/metrics.rake",
34
48
  "tasks/spec.rake",
35
49
  "tasks/yard.rake",
@@ -39,26 +53,33 @@ Gem::Specification.new do |s|
39
53
  s.rdoc_options = ["--charset=UTF-8"]
40
54
  s.require_paths = ["lib"]
41
55
  s.rubyforge_project = %q{datamapper}
42
- s.rubygems_version = %q{1.3.5}
56
+ s.rubygems_version = %q{1.3.6}
43
57
  s.summary = %q{DataMapper plugin constraining relationships}
58
+ s.test_files = [
59
+ "spec/integration/constraints_spec.rb",
60
+ "spec/isolated/require_after_setup_spec.rb",
61
+ "spec/isolated/require_before_setup_spec.rb",
62
+ "spec/isolated/require_spec.rb",
63
+ "spec/spec_helper.rb"
64
+ ]
44
65
 
45
66
  if s.respond_to? :specification_version then
46
67
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
68
  s.specification_version = 3
48
69
 
49
70
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
- s.add_runtime_dependency(%q<dm-core>, ["~> 0.10.2"])
51
- s.add_development_dependency(%q<rspec>, ["~> 1.2.9"])
52
- s.add_development_dependency(%q<yard>, ["~> 0.4.0"])
71
+ s.add_runtime_dependency(%q<dm-core>, ["~> 1.0.0.rc1"])
72
+ s.add_runtime_dependency(%q<dm-migrations>, ["~> 1.0.0.rc1"])
73
+ s.add_development_dependency(%q<rspec>, ["~> 1.3"])
53
74
  else
54
- s.add_dependency(%q<dm-core>, ["~> 0.10.2"])
55
- s.add_dependency(%q<rspec>, ["~> 1.2.9"])
56
- s.add_dependency(%q<yard>, ["~> 0.4.0"])
75
+ s.add_dependency(%q<dm-core>, ["~> 1.0.0.rc1"])
76
+ s.add_dependency(%q<dm-migrations>, ["~> 1.0.0.rc1"])
77
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
57
78
  end
58
79
  else
59
- s.add_dependency(%q<dm-core>, ["~> 0.10.2"])
60
- s.add_dependency(%q<rspec>, ["~> 1.2.9"])
61
- s.add_dependency(%q<yard>, ["~> 0.4.0"])
80
+ s.add_dependency(%q<dm-core>, ["~> 1.0.0.rc1"])
81
+ s.add_dependency(%q<dm-migrations>, ["~> 1.0.0.rc1"])
82
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
62
83
  end
63
84
  end
64
85
 
@@ -0,0 +1,22 @@
1
+ module DataMapper
2
+ module Constraints
3
+ module Adapters
4
+
5
+ module AbstractAdapter
6
+
7
+ def constraint_exists?(*)
8
+ false
9
+ end
10
+
11
+ def create_relationship_constraint(*)
12
+ false
13
+ end
14
+
15
+ def destroy_relationship_constraint(*)
16
+ false
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,189 @@
1
+ module DataMapper
2
+ module Constraints
3
+ module Adapters
4
+
5
+ module DataObjectsAdapter
6
+ ##
7
+ # Determine if a constraint exists for a table
8
+ #
9
+ # @param storage_name [Symbol]
10
+ # name of table to check constraint on
11
+ # @param constraint_name [~String]
12
+ # name of constraint to check for
13
+ #
14
+ # @return [Boolean]
15
+ #
16
+ # @api private
17
+ def constraint_exists?(storage_name, constraint_name)
18
+ statement = <<-SQL.compress_lines
19
+ SELECT COUNT(*)
20
+ FROM "information_schema"."table_constraints"
21
+ WHERE "constraint_type" = 'FOREIGN KEY'
22
+ AND "table_schema" = ?
23
+ AND "table_name" = ?
24
+ AND "constraint_name" = ?
25
+ SQL
26
+
27
+ select(statement, schema_name, storage_name, constraint_name).first > 0
28
+ end
29
+
30
+ ##
31
+ # Create the constraint for a relationship
32
+ #
33
+ # @param relationship [Relationship]
34
+ # the relationship to create the constraint for
35
+ #
36
+ # @return [true, false]
37
+ # true if creating the constraints was successful
38
+ #
39
+ # @api semipublic
40
+ def create_relationship_constraint(relationship)
41
+ return false unless valid_relationship_for_constraint?(relationship)
42
+
43
+ source_model = relationship.source_model
44
+ source_table = source_model.storage_name(name)
45
+ source_key = relationship.source_key
46
+
47
+ constraint_name = constraint_name(source_table, relationship.name)
48
+ return false if constraint_exists?(source_table, constraint_name)
49
+
50
+ constraint_type = case relationship.inverse.constraint
51
+ when :protect then 'NO ACTION'
52
+ when :destroy, :destroy! then 'CASCADE'
53
+ when :set_nil then 'SET NULL'
54
+ end
55
+
56
+ return false if constraint_type.nil?
57
+
58
+ storage_name = relationship.source_model.storage_name(name)
59
+ reference_storage_name = relationship.target_model.storage_name(name)
60
+
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
+
64
+ execute(create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys))
65
+ end
66
+
67
+ ##
68
+ # Remove the constraint for a relationship
69
+ #
70
+ # @param relationship [Relationship]
71
+ # the relationship to remove the constraint for
72
+ #
73
+ # @return [true, false]
74
+ # true if destroying the constraint was successful
75
+ #
76
+ # @api semipublic
77
+ def destroy_relationship_constraint(relationship)
78
+ return false unless valid_relationship_for_constraint?(relationship)
79
+
80
+ source_model = relationship.source_model
81
+ source_table = source_model.storage_name(name)
82
+
83
+ constraint_name = constraint_name(source_table, relationship.name)
84
+ return false unless constraint_exists?(source_table, constraint_name)
85
+
86
+ execute(destroy_constraints_statement(source_table, constraint_name))
87
+ end
88
+
89
+ private
90
+
91
+ ##
92
+ # Check to see if the relationship's constraints can be used
93
+ #
94
+ # Only one-to-one, one-to-many, and many-to-many relationships
95
+ # can be used for constraints. They must also be in the same
96
+ # repository as the adapter is connected to.
97
+ #
98
+ # @param relationship [Relationship]
99
+ # the relationship to check
100
+ #
101
+ # @return [true, false]
102
+ # true if a constraint can be established for relationship
103
+ #
104
+ # @api private
105
+ def valid_relationship_for_constraint?(relationship)
106
+ return false unless relationship.source_repository_name == name || relationship.source_repository_name.nil?
107
+ return false unless relationship.target_repository_name == name || relationship.target_repository_name.nil?
108
+ return false unless relationship.kind_of?(Associations::ManyToOne::Relationship)
109
+ true
110
+ end
111
+
112
+ module SQL
113
+ private
114
+
115
+ ##
116
+ # Generates the SQL statement to create a constraint
117
+ #
118
+ # @param constraint_name [String]
119
+ # 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
130
+ #
131
+ # @return [String]
132
+ # SQL DDL Statement to create a constraint
133
+ #
134
+ # @api private
135
+ def create_constraints_statement(storage_name, constraint_name, constraint_type, foreign_keys, reference_storage_name, reference_keys)
136
+ <<-SQL.compress_lines
137
+ ALTER TABLE #{quote_name(storage_name)}
138
+ ADD CONSTRAINT #{quote_name(constraint_name)}
139
+ FOREIGN KEY (#{foreign_keys.join(', ')})
140
+ REFERENCES #{quote_name(reference_storage_name)} (#{reference_keys.join(', ')})
141
+ ON DELETE #{constraint_type}
142
+ ON UPDATE #{constraint_type}
143
+ SQL
144
+ end
145
+
146
+ ##
147
+ # Generates the SQL statement to destroy a constraint
148
+ #
149
+ # @param storage_name [String]
150
+ # name of table to constrain
151
+ # @param constraint_name [String]
152
+ # name of foreign key constraint
153
+ #
154
+ # @return [String]
155
+ # SQL DDL Statement to destroy a constraint
156
+ #
157
+ # @api private
158
+ def destroy_constraints_statement(storage_name, constraint_name)
159
+ <<-SQL.compress_lines
160
+ ALTER TABLE #{quote_name(storage_name)}
161
+ DROP CONSTRAINT #{quote_name(constraint_name)}
162
+ SQL
163
+ end
164
+
165
+ ##
166
+ # generates a unique constraint name given a table and a relationships
167
+ #
168
+ # @param storage_name [String]
169
+ # name of table to constrain
170
+ # @param relationships_name [String]
171
+ # name of the relationship to constrain
172
+ #
173
+ # @return [String]
174
+ # name of the constraint
175
+ #
176
+ # @api private
177
+ def constraint_name(storage_name, relationship_name)
178
+ identifier = "#{storage_name}_#{relationship_name}"[0, self.class::IDENTIFIER_MAX_LENGTH - 3]
179
+ "#{identifier}_fk"
180
+ end
181
+ end
182
+
183
+ include SQL
184
+
185
+ end # module DataObjectsAdapter
186
+
187
+ end # module Adapters
188
+ end # module Constraints
189
+ end # module DataMapper
@@ -0,0 +1,40 @@
1
+ require 'dm-constraints/adapters/dm-do-adapter'
2
+
3
+ module DataMapper
4
+ module Constraints
5
+ module Adapters
6
+
7
+ module MysqlAdapter
8
+
9
+ include DataObjectsAdapter
10
+
11
+ module SQL
12
+ private
13
+
14
+ ##
15
+ # MySQL specific query to drop a foreign key
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
24
+ #
25
+ # @api private
26
+ def destroy_constraints_statement(storage_name, constraint_name)
27
+ <<-SQL.compress_lines
28
+ ALTER TABLE #{quote_name(storage_name)}
29
+ DROP FOREIGN KEY #{quote_name(constraint_name)}
30
+ SQL
31
+ end
32
+ end
33
+
34
+ include SQL
35
+
36
+ end # module MysqlAdapter
37
+
38
+ end # module Adapters
39
+ end # module Constraints
40
+ end # module DataMapper
@@ -0,0 +1,13 @@
1
+ require 'dm-constraints/adapters/dm-do-adapter'
2
+
3
+ module DataMapper
4
+ module Constraints
5
+ module Adapters
6
+
7
+ module OracleAdapter
8
+ include DataObjectsAdapter
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'dm-constraints/adapters/dm-do-adapter'
2
+
3
+ module DataMapper
4
+ module Constraints
5
+ module Adapters
6
+
7
+ module PostgresAdapter
8
+ include DataObjectsAdapter
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module DataMapper
2
+ module Constraints
3
+ module Adapters
4
+
5
+ module SqliteAdapter
6
+
7
+ def constraint_exists?(*)
8
+ false
9
+ end
10
+
11
+ def create_relationship_constraint(*)
12
+ false
13
+ end
14
+
15
+ def destroy_relationship_constraint(*)
16
+ false
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'dm-constraints/adapters/dm-do-adapter'
2
+
3
+ module DataMapper
4
+ module Constraints
5
+ module Adapters
6
+
7
+ module SqlserverAdapter
8
+ include DataObjectsAdapter
9
+ end
10
+
11
+ end
12
+ end
13
+ end