foreigner 1.6.1 → 1.7.0

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