foreigner 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6267757603fa3cc7daf2d7c9fb0cd5c3c0b5de57
4
- data.tar.gz: 59ba0f7366cbfbbc9c315c6a4f3141c7ccefd887
3
+ metadata.gz: 7afaa93e891d1a95c671e0703a70c4fbae46eb84
4
+ data.tar.gz: 8eb8278d682088f6b57aef3962abb224bc4b8727
5
5
  SHA512:
6
- metadata.gz: 0692abaca90f8bf9afe7a1b034062741d1cb6288816f7491aac15e566392121338dab6814bd94adf3dabb0a1acd0262d1f62af938cc34030947be52ca26f3736
7
- data.tar.gz: 17fd7783a3127006a0a2284f4c82ea4960fd81115c6a56c9fff3a2159952cf18b8aac1f7f8da1842e8f514308f87b38c6b0acc2e3e5b4dac7421e25fab3da8b6
6
+ metadata.gz: 254c861fe2c7299e1157dc186f25e0649a4724095e6eebde0dcf94b223169f08e4ea3730d97cdca6d67308640138c3f2e92bf07b84d87c67b8a30490236e2133
7
+ data.tar.gz: a6f39ca36faf91a20d61b76326c47b360891b98adbc3685c02be6d3f52ec070bc74006f84d3d261b0f472462de44e1dd465b2dd7ecea7780ecb6cdfd91fcf60b
data/README.md CHANGED
@@ -13,46 +13,46 @@ The following adapters are supported:
13
13
 
14
14
  Add the following to your Gemfile:
15
15
  ```ruby
16
- gem 'foreigner'
16
+ gem 'foreigner'
17
17
  ```
18
18
  ## API Examples
19
19
 
20
20
  Foreigner adds two methods to migrations.
21
21
 
22
22
  * `add_foreign_key(from_table, to_table, options)`
23
- * `remove_foreign_key(from_table, options)`
23
+ * `remove_foreign_key(from_table, to_table, options)`
24
24
 
25
25
  (Options are documented in `connection_adapters/abstract/schema_statements.rb`):
26
26
 
27
27
  For example, given the following model:
28
28
  ```ruby
29
- class Comment < ActiveRecord::Base
30
- belongs_to :post
31
- end
29
+ class Comment < ActiveRecord::Base
30
+ belongs_to :post
31
+ end
32
32
 
33
- class Post < ActiveRecord::Base
34
- has_many :comments, dependent: :delete_all
35
- end
33
+ class Post < ActiveRecord::Base
34
+ has_many :comments, dependent: :delete_all
35
+ end
36
36
  ```
37
37
  You should add a foreign key in your migration:
38
38
  ```ruby
39
- add_foreign_key(:comments, :posts)
39
+ add_foreign_key(:comments, :posts)
40
40
  ```
41
41
  The `:dependent` option can be moved from the `has_many` definition to the foreign key:
42
42
  ```ruby
43
- add_foreign_key(:comments, :posts, dependent: :delete)
43
+ add_foreign_key(:comments, :posts, dependent: :delete)
44
44
  ```
45
45
  If the column is named `article_id` instead of `post_id`, use the `:column` option:
46
46
  ```ruby
47
- add_foreign_key(:comments, :posts, column: 'article_id')
47
+ add_foreign_key(:comments, :posts, column: 'article_id')
48
48
  ```
49
49
  A name can be specified for the foreign key constraint:
50
50
  ```ruby
51
- add_foreign_key(:comments, :posts, name: 'comment_article_foreign_key')
51
+ add_foreign_key(:comments, :posts, name: 'comment_article_foreign_key')
52
52
  ```
53
53
  The `:column` and `:name` options create a foreign key with a custom name. In order to remove it you need to specify `:name`:
54
54
  ```ruby
55
- remove_foreign_key(:comments, name: 'comment_article_foreign_key')
55
+ remove_foreign_key(:comments, name: 'comment_article_foreign_key')
56
56
  ```
57
57
  ## Change Table Methods
58
58
 
@@ -60,29 +60,29 @@ Foreigner adds extra methods to `create_table` and `change_table`.
60
60
 
61
61
  Create a new table with a foreign key:
62
62
  ```ruby
63
- create_table :products do |t|
64
- t.string :name
65
- t.integer :factory_id
66
- t.foreign_key :factories
67
- end
63
+ create_table :products do |t|
64
+ t.string :name
65
+ t.integer :factory_id
66
+ t.foreign_key :factories
67
+ end
68
68
  ```
69
69
  Add a missing foreign key to comments:
70
70
  ```ruby
71
- change_table :comments do |t|
72
- t.foreign_key :posts, dependent: :delete
73
- end
71
+ change_table :comments do |t|
72
+ t.foreign_key :posts, dependent: :delete
73
+ end
74
74
  ```
75
75
  Remove an unwanted foreign key:
76
76
  ```ruby
77
- change_table :comments do |t|
78
- t.remove_foreign_key :users
79
- end
77
+ change_table :comments do |t|
78
+ t.remove_foreign_key :users
79
+ end
80
80
  ```
81
81
  ## Database-specific options
82
82
 
83
83
  Database-specific options will never be supported by foreigner. You can add them using `:options`:
84
84
  ```ruby
85
- add_foreign_key(:comments, :posts, options: 'ON UPDATE DEFERRED')
85
+ add_foreign_key(:comments, :posts, options: 'ON UPDATE DEFERRED')
86
86
  ```
87
87
  ## Foreigner Add-ons
88
88
 
@@ -28,6 +28,7 @@ Foreigner::Adapter.register 'mysql', 'foreigner/connection_adapters/mysql_adapte
28
28
  Foreigner::Adapter.register 'mysql2', 'foreigner/connection_adapters/mysql2_adapter'
29
29
  Foreigner::Adapter.register 'jdbcmysql', 'foreigner/connection_adapters/mysql2_adapter'
30
30
  Foreigner::Adapter.register 'postgresql', 'foreigner/connection_adapters/postgresql_adapter'
31
+ Foreigner::Adapter.register 'postgis', 'foreigner/connection_adapters/postgresql_adapter'
31
32
  Foreigner::Adapter.register 'jdbcpostgresql', 'foreigner/connection_adapters/postgresql_adapter'
32
33
  Foreigner::Adapter.register 'sqlite3', 'foreigner/connection_adapters/noop_adapter'
33
34
 
@@ -26,12 +26,13 @@ module Foreigner
26
26
  fk_info.map do |row|
27
27
  options = {column: row['column'], name: row['name'], primary_key: row['primary_key']}
28
28
 
29
- if create_table_info =~ /CONSTRAINT #{quote_column_name(row['name'])} FOREIGN KEY .* REFERENCES .* ON DELETE (CASCADE|SET NULL|RESTRICT)/
30
- options[:dependent] = case $1
29
+ if create_table_info =~ /CONSTRAINT #{quote_column_name(row['name'])} FOREIGN KEY .* REFERENCES .*\)( ON DELETE (CASCADE|SET NULL|RESTRICT))? ?(.*)$/
30
+ options[:dependent] = case $2
31
31
  when 'CASCADE' then :delete
32
32
  when 'SET NULL' then :nullify
33
33
  when 'RESTRICT' then :restrict
34
34
  end
35
+ options[:options] = $3 # e.g. ON UPDATE ...
35
36
  end
36
37
  ForeignKeyDefinition.new(table_name, row['to_table'], options)
37
38
  end
@@ -3,9 +3,19 @@ module Foreigner
3
3
  module PostgreSQLAdapter
4
4
  include Foreigner::ConnectionAdapters::Sql2003
5
5
 
6
+ DEPENDENCY_CODE_ACTIONS = {'c' => 'CASCADE', 'n' => 'SET NULL', 'r' => 'RESTRICT', 'd' => 'SET DEFAULT'}
7
+
6
8
  def foreign_keys(table_name)
7
9
  fk_info = select_all %{
8
- SELECT t2.relname AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confdeltype AS dependency
10
+ SELECT t2.relname AS to_table
11
+ , a1.attname AS column
12
+ , a2.attname AS primary_key
13
+ , c.conname AS name
14
+ , c.confdeltype AS dependency
15
+ , c.confupdtype AS update_dependency
16
+ , c.condeferrable AS deferrable
17
+ , c.condeferred AS deferred
18
+ #{", c.convalidated AS valid" if postgresql_version >= 90100}
9
19
  FROM pg_constraint c
10
20
  JOIN pg_class t1 ON c.conrelid = t1.oid
11
21
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -22,11 +32,23 @@ module Foreigner
22
32
  options = {column: row['column'], name: row['name'], primary_key: row['primary_key']}
23
33
 
24
34
  options[:dependent] = case row['dependency']
35
+ # NO ACTION is the default
36
+ # SET DEFAULT is handled below, since it is postgres-specific
25
37
  when 'c' then :delete
26
38
  when 'n' then :nullify
27
39
  when 'r' then :restrict
28
40
  end
29
41
 
42
+ extras = []
43
+ extras << "ON DELETE SET DEFAULT" if row['dependency'] == 'd'
44
+ if update_action = DEPENDENCY_CODE_ACTIONS[row['update_dependency']]
45
+ extras << "ON UPDATE #{update_action}"
46
+ end
47
+ extras << 'DEFERRABLE' if row['deferrable'] == 't'
48
+ extras << 'INITIALLY DEFERRED' if row['deferred'] == 't'
49
+ extras << 'NOT VALID' if row['valid'] == 'f'
50
+ options[:options] = extras.join(" ")
51
+
30
52
  ForeignKeyDefinition.new(table_name, row['to_table'], options)
31
53
  end
32
54
  end
@@ -35,4 +57,4 @@ module Foreigner
35
57
  end
36
58
 
37
59
  Foreigner::Adapter.safe_include :JdbcAdapter, Foreigner::ConnectionAdapters::PostgreSQLAdapter
38
- Foreigner::Adapter.safe_include :PostgreSQLAdapter, Foreigner::ConnectionAdapters::PostgreSQLAdapter
60
+ Foreigner::Adapter.safe_include :PostgreSQLAdapter, Foreigner::ConnectionAdapters::PostgreSQLAdapter
@@ -8,20 +8,19 @@ module Foreigner
8
8
  def drop_table(*args)
9
9
  options = args.extract_options!
10
10
  if options[:force]
11
- disable_referential_integrity { super }
11
+ disable_referential_integrity { super(*(args.dup << options)) }
12
12
  else
13
- super
13
+ super(*(args.dup << options))
14
14
  end
15
15
  end
16
16
 
17
17
  def foreign_key_exists?(from_table, options)
18
18
  foreign_key_name = decipher_foreign_key_name(from_table, options)
19
-
20
19
  foreign_keys(from_table).any? { |fk| fk.name == foreign_key_name }
21
20
  end
22
21
 
23
22
  def add_foreign_key(from_table, to_table, options = {})
24
- sql = "ALTER TABLE #{quote_table_name(from_table)} #{add_foreign_key_sql(from_table, to_table, options)}"
23
+ sql = "ALTER TABLE #{quote_proper_table_name(from_table)} #{add_foreign_key_sql(from_table, to_table, options)}"
25
24
  execute(sql)
26
25
  end
27
26
 
@@ -31,28 +30,35 @@ module Foreigner
31
30
  primary_key = options[:primary_key] || "id"
32
31
  dependency = dependency_sql(options[:dependent])
33
32
 
34
- proper_name = proper_table_name(to_table)
35
-
36
33
  sql =
37
34
  "ADD CONSTRAINT #{quote_column_name(foreign_key_name)} " +
38
35
  "FOREIGN KEY (#{quote_column_name(column)}) " +
39
- "REFERENCES #{quote_table_name(proper_name)}(#{primary_key})"
36
+ "REFERENCES #{quote_proper_table_name(to_table)}(#{primary_key})"
40
37
  sql << " #{dependency}" if dependency.present?
41
38
  sql << " #{options[:options]}" if options[:options]
42
39
 
43
40
  sql
44
41
  end
45
42
 
46
- def proper_table_name(to_table)
43
+ def quote_proper_table_name(table)
44
+ quote_table_name(proper_table_name(table))
45
+ end
46
+
47
+ def proper_table_name(table)
48
+ # This will normally be a no-op, but prevents the table from being wrapped twice:
49
+ table = Foreigner::SchemaDumper::ClassMethods.remove_prefix_and_suffix(table)
47
50
  if ActiveRecord::Migration.instance_methods(false).include? :proper_table_name
48
- ActiveRecord::Migration.new.proper_table_name(to_table)
51
+ ActiveRecord::Migration.new.proper_table_name(table, options = {
52
+ table_name_prefix: ActiveRecord::Base.table_name_prefix,
53
+ table_name_suffix: ActiveRecord::Base.table_name_suffix
54
+ })
49
55
  else
50
- ActiveRecord::Migrator.proper_table_name(to_table)
56
+ ActiveRecord::Migrator.proper_table_name(table)
51
57
  end
52
58
  end
53
59
 
54
60
  def remove_foreign_key(table, options)
55
- execute "ALTER TABLE #{quote_table_name(table)} #{remove_foreign_key_sql(table, options)}"
61
+ execute "ALTER TABLE #{quote_proper_table_name(table)} #{remove_foreign_key_sql(table, options)}"
56
62
  end
57
63
 
58
64
  def remove_foreign_key_sql(table, options)
@@ -21,13 +21,23 @@ module Foreigner
21
21
  if foreign_key.options[:dependent].present?
22
22
  statement_parts << ('dependent: ' + foreign_key.options[:dependent].inspect)
23
23
  end
24
+ if foreign_key.options[:options].present?
25
+ statement_parts << ('options: ' + foreign_key.options[:options].inspect)
26
+ end
24
27
 
25
28
  statement_parts.join(', ')
26
29
  end
27
30
 
28
- def remove_prefix_and_suffix(table)
29
- table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
31
+ def remove_prefix_and_suffix(table_name)
32
+ table_name = table_name.to_s
33
+ pre, suff = ActiveRecord::Base.table_name_prefix, ActiveRecord::Base.table_name_suffix
34
+ if table_name.start_with?(pre) && table_name.end_with?(suff)
35
+ table_name[pre.size..-(suff.size + 1)]
36
+ else
37
+ table_name
38
+ end
30
39
  end
40
+ module_function :remove_prefix_and_suffix
31
41
  end
32
42
 
33
43
  def tables_with_foreign_keys(stream)
@@ -7,7 +7,7 @@ class Foreigner::ConnectionAdapters::TableDefinitionsTest < ActiveSupport::TestC
7
7
 
8
8
  test "foreign_key used once" do
9
9
  definition = TestDefinition.new
10
- definition.foreign_key :poops, and: :one;
10
+ definition.foreign_key :poops, and: :one
11
11
  assert_equal [{ and: :one }], definition.foreign_keys[:poops]
12
12
  end
13
13
 
@@ -18,4 +18,4 @@ class Foreigner::ConnectionAdapters::TableDefinitionsTest < ActiveSupport::TestC
18
18
  assert_equal [{ column: :from_id }, { column: :to_id }],
19
19
  definition.foreign_keys[:nodes]
20
20
  end
21
- end
21
+ end
@@ -2,9 +2,10 @@ require 'helper'
2
2
 
3
3
  class Foreigner::MysqlAdapterTest < Foreigner::UnitTest
4
4
  test 'warning' do
5
+ skip unless respond_to?(:capture) # < not available until 3.1.x
5
6
  output = capture(:stdout) do
6
7
  require 'foreigner/connection_adapters/mysql_adapter'
7
8
  end
8
9
  assert_match /WARNING: Please upgrade to mysql2. The old mysql adapter is not supported by foreigner./, output
9
10
  end
10
- end
11
+ end
@@ -10,6 +10,16 @@ class Foreigner::Sql2003Test < Foreigner::UnitTest
10
10
  @adapter = TestAdapter.new
11
11
  end
12
12
 
13
+ teardown do
14
+ ActiveRecord::Base.table_name_prefix = ''
15
+ ActiveRecord::Base.table_name_suffix = ''
16
+ end
17
+
18
+ def add_table_prefix_and_suffix
19
+ ActiveRecord::Base.table_name_prefix = 'pre_'
20
+ ActiveRecord::Base.table_name_suffix = '_suff'
21
+ end
22
+
13
23
  test 'drop_table without force' do
14
24
  @adapter.drop_table 'shoes'
15
25
  assert !@adapter.instance_variable_get(:@disable_referential_integrity)
@@ -39,6 +49,14 @@ class Foreigner::Sql2003Test < Foreigner::UnitTest
39
49
  )
40
50
  end
41
51
 
52
+ test 'add_without_options with prefix and suffix' do
53
+ add_table_prefix_and_suffix
54
+ assert_equal(
55
+ "ALTER TABLE `pre_employees_suff` ADD CONSTRAINT `employees_company_id_fk` FOREIGN KEY (`company_id`) REFERENCES `pre_companies_suff`(id)",
56
+ @adapter.add_foreign_key(:employees, :companies)
57
+ )
58
+ end
59
+
42
60
  test 'add_with_name' do
43
61
  assert_equal(
44
62
  "ALTER TABLE `employees` ADD CONSTRAINT `favorite_company_fk` FOREIGN KEY (`company_id`) REFERENCES `companies`(id)",
@@ -60,6 +78,14 @@ class Foreigner::Sql2003Test < Foreigner::UnitTest
60
78
  )
61
79
  end
62
80
 
81
+ test 'add_with_column_and_name with prefix and suffix' do
82
+ add_table_prefix_and_suffix
83
+ assert_equal(
84
+ "ALTER TABLE `pre_employees_suff` ADD CONSTRAINT `favorite_company_fk` FOREIGN KEY (`last_employer_id`) REFERENCES `pre_companies_suff`(id)",
85
+ @adapter.add_foreign_key(:employees, :companies, column: 'last_employer_id', name: 'favorite_company_fk')
86
+ )
87
+ end
88
+
63
89
  test 'add_with_delete_dependency' do
64
90
  assert_equal(
65
91
  "ALTER TABLE `employees` ADD CONSTRAINT `employees_company_id_fk` FOREIGN KEY (`company_id`) REFERENCES `companies`(id) " +
@@ -99,6 +125,14 @@ class Foreigner::Sql2003Test < Foreigner::UnitTest
99
125
  )
100
126
  end
101
127
 
128
+ test 'remove_by_table with prefix and suffix' do
129
+ add_table_prefix_and_suffix
130
+ assert_equal(
131
+ "ALTER TABLE `pre_suppliers_suff` DROP CONSTRAINT `suppliers_company_id_fk`",
132
+ @adapter.remove_foreign_key(:suppliers, :companies)
133
+ )
134
+ end
135
+
102
136
  test 'remove_by_name' do
103
137
  assert_equal(
104
138
  "ALTER TABLE `suppliers` DROP CONSTRAINT `belongs_to_supplier`",
@@ -112,4 +146,4 @@ class Foreigner::Sql2003Test < Foreigner::UnitTest
112
146
  @adapter.remove_foreign_key(:suppliers, column: "ship_to_id")
113
147
  )
114
148
  end
115
- end
149
+ end
@@ -1,11 +1,20 @@
1
1
  require 'helper'
2
2
 
3
- ActiveRecord::Migration::CommandRecorder.class_eval do
4
- include ::Foreigner::Migration::CommandRecorder
3
+ if defined?(ActiveRecord::Migration::CommandRecorder)
4
+ ActiveRecord::Migration::CommandRecorder.class_eval do
5
+ include ::Foreigner::Migration::CommandRecorder
6
+ end
5
7
  end
6
8
 
7
9
  class Foreigner::CommandRecorderTest < Foreigner::UnitTest
10
+
11
+ def revert_exists?
12
+ defined?(ActiveRecord::Migration::CommandRecorder) &&
13
+ ActiveRecord::Migration::CommandRecorder.instance_methods.include?(:revert)
14
+ end
15
+
8
16
  setup do
17
+ skip unless revert_exists?
9
18
  @recorder = ActiveRecord::Migration::CommandRecorder.new
10
19
  end
11
20
 
@@ -48,4 +57,4 @@ class Foreigner::CommandRecorderTest < Foreigner::UnitTest
48
57
  # @recorder.inverse
49
58
  # end
50
59
  end
51
- end
60
+ end
@@ -32,6 +32,7 @@ class Foreigner::SchemaDumperTest < Foreigner::UnitTest
32
32
  assert_dump "add_foreign_key \"foos\", \"bars\", name: \"lulz\", primary_key: \"uuid\"", Foreigner::ConnectionAdapters::ForeignKeyDefinition.new('foos', 'bars', column: 'bar_id', primary_key: 'uuid', name: 'lulz')
33
33
  assert_dump "add_foreign_key \"foos\", \"bars\", name: \"lulz\", dependent: :delete", Foreigner::ConnectionAdapters::ForeignKeyDefinition.new('foos', 'bars', column: 'bar_id', primary_key: 'id', name: 'lulz', dependent: :delete)
34
34
  assert_dump "add_foreign_key \"foos\", \"bars\", name: \"lulz\", column: \"mamma_id\"", Foreigner::ConnectionAdapters::ForeignKeyDefinition.new('foos', 'bars', column: 'mamma_id', primary_key: 'id', name: 'lulz')
35
+ assert_dump "add_foreign_key \"foos\", \"bars\", name: \"lulz\", options: \"YOLO MAYBE DB-SPECIFIC!\"", Foreigner::ConnectionAdapters::ForeignKeyDefinition.new('foos', 'bars', column: 'bar_id', primary_key: 'id', name: 'lulz', options: "YOLO MAYBE DB-SPECIFIC!")
35
36
  end
36
37
 
37
38
  test 'all tables' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreigner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Higgins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-06 00:00:00.000000000 Z
11
+ date: 2014-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  description: Adds helpers to migrations and dumps foreign keys to schema.rb
28
56
  email: developer@matthewhiggins.com
29
57
  executables: []
@@ -60,7 +88,8 @@ files:
60
88
  - test/foreigner/schema_dumper_test.rb
61
89
  - test/helper.rb
62
90
  homepage: http://github.com/matthuhiggins/foreigner
63
- licenses: []
91
+ licenses:
92
+ - MIT
64
93
  metadata: {}
65
94
  post_install_message:
66
95
  rdoc_options: []
@@ -78,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
107
  version: 1.3.5
79
108
  requirements: []
80
109
  rubyforge_project: foreigner
81
- rubygems_version: 2.2.0
110
+ rubygems_version: 2.2.2
82
111
  signing_key:
83
112
  specification_version: 4
84
113
  summary: Foreign Keys for Rails