automatic_foreign_key 1.0.5 → 1.1.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.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ 1.1
2
+ * create index within foreign key (auto index)
3
+ * global configuration for: auto_index and on_update/on_delete actions
1
4
  1.0.2
2
5
  * rails 3 generator
3
6
  1.0.1
data/README.rdoc CHANGED
@@ -77,6 +77,33 @@ configuration properties:
77
77
  * <code>config.active_record.table_name_prefix</code>
78
78
  * <code>config.active_record.table_name_suffix</code>
79
79
 
80
+ === Auto Indices ===
81
+
82
+ It's very common to create an index on foreign key. You can instruct
83
+ AutomaticForeignKey to add an index after adding foreign key.
84
+
85
+ create_table :orders, :id => false do |t|
86
+ ...
87
+ t.integer :order_id, :index => true
88
+ end
89
+
90
+ If you want to pass some options for index use hash params.
91
+
92
+ create_table :bills, :id => false do |t|
93
+ ...
94
+ t.integer :order_id, :index => { :unique => true, :name => 'foo_index' }
95
+ end
96
+
97
+ That option is useless for MySQL users as their RDBMS adds indices on foreign
98
+ keys by default. However PostgreSQL users may have fun with that feature.
99
+
100
+ === Configuration ===
101
+
102
+ For customization purposes create config/initializers/automatic_foreign_key.rb file:
103
+ AutomaticForeignKey.auto_index = true # create indices on FKs by default
104
+ AutomaticForeignKey.on_update = :cascade # cascade as default on_update action
105
+ AutomaticForeignKey.on_delete = :restrtic # restrict as default on_delete action
106
+
80
107
  === Rails 3 compatibility
81
108
 
82
109
  Automatic foreign key is fully compatibly with Rails 3.
data/Rakefile CHANGED
@@ -7,8 +7,8 @@ begin
7
7
  Jeweler::Tasks.new do |gem|
8
8
  gem.name = "automatic_foreign_key"
9
9
  gem.summary = %Q{Automatically generate foreign-key constraints when creating tables}
10
- gem.description = %Q{Automatic Key Migrations is a gem that automatically generates foreign-key
11
- constraints when creating tables. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.}
10
+ gem.description = %Q{Automatic Foreign Key automatically generates foreign-key
11
+ constraints when creating tables or adding columns. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.}
12
12
  gem.email = "michal.lomnicki@gmail.com"
13
13
  gem.homepage = "http://github.com/mlomnicki/automatic_foreign_key"
14
14
  gem.authors = ["Michał Łomnicki"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.5
1
+ 1.1.0
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{automatic_foreign_key}
8
- s.version = "1.0.5"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Michał Łomnicki"]
12
- s.date = %q{2010-04-06}
13
- s.description = %q{Automatic Key Migrations is a gem that automatically generates foreign-key
14
- constraints when creating tables. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.}
11
+ s.authors = ["Micha\305\202 \305\201omnicki"]
12
+ s.date = %q{2010-06-12}
13
+ s.description = %q{Automatic Foreign Key automatically generates foreign-key
14
+ constraints when creating tables or adding columns. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.}
15
15
  s.email = %q{michal.lomnicki@gmail.com}
16
16
  s.extra_rdoc_files = [
17
17
  "README.rdoc"
@@ -43,7 +43,9 @@ constraints when creating tables. It uses SQL-92 syntax and as such should be co
43
43
  "spec/references_spec.rb",
44
44
  "spec/schema/schema.rb",
45
45
  "spec/spec_helper.rb",
46
- "spec/support/matchers/automatic_foreign_key_matchers.rb"
46
+ "spec/support/matchers/automatic_foreign_key_matchers.rb",
47
+ "spec/support/matchers/have_index.rb",
48
+ "spec/support/matchers/reference.rb"
47
49
  ]
48
50
  s.homepage = %q{http://github.com/mlomnicki/automatic_foreign_key}
49
51
  s.rdoc_options = ["--charset=UTF-8"]
@@ -55,7 +57,9 @@ constraints when creating tables. It uses SQL-92 syntax and as such should be co
55
57
  "spec/connections/postgresql/connection.rb",
56
58
  "spec/connections/mysql/connection.rb",
57
59
  "spec/aaa_create_tables_spec.rb",
60
+ "spec/support/matchers/have_index.rb",
58
61
  "spec/support/matchers/automatic_foreign_key_matchers.rb",
62
+ "spec/support/matchers/reference.rb",
59
63
  "spec/spec_helper.rb",
60
64
  "spec/references_spec.rb",
61
65
  "spec/migration_spec.rb",
@@ -16,6 +16,18 @@ module AutomaticForeignKey
16
16
  end
17
17
 
18
18
  end
19
+
20
+ # Default FK update action
21
+ mattr_accessor :on_update
22
+
23
+ # Default FK delete action
24
+ mattr_accessor :on_delete
25
+
26
+ # Create an index after creating FK (default false)
27
+ mattr_accessor :auto_index
28
+ @@auto_index = nil
29
+
30
+
19
31
  end
20
32
 
21
33
  ActiveRecord::Base.send(:include, AutomaticForeignKey::ActiveRecord::Base)
@@ -11,19 +11,44 @@ module AutomaticForeignKey::ActiveRecord
11
11
  # add_column('comments', 'post_id', :integer)
12
12
  # # creates a column and adds foreign key on posts(id)
13
13
  #
14
+ # add_column('comments', 'post_id', :integer, :on_update => :cascade, :on_delete => :cascade)
15
+ # # creates a column and adds foreign key on posts(id) with cascade actions on update and on delete
16
+ #
17
+ # add_column('comments', 'post_id', :integer, :index => true)
18
+ # # creates a column and adds foreign key on posts(id)
19
+ # # additionally adds index on posts(id)
20
+ #
21
+ # add_column('comments', 'post_id', :integer, :index => { :unique => true, :name => 'comments_post_id_unique_index' }))
22
+ # # creates a column and adds foreign key on posts(id)
23
+ # # additionally adds unique index on posts(id) named comments_post_id_unique_index
24
+ #
14
25
  # add_column('addresses', 'citizen_id', :integer, :references => :users
15
26
  # # creates a column and adds foreign key on users(id)
16
27
  #
17
28
  # add_column('addresses', 'citizen_id', :integer, :references => [:users, :uuid]
18
29
  # # creates a column and adds foreign key on users(uuid)
19
30
  #
20
- # add_column('users', 'verified')
21
- # # just creates a column as usually
22
31
  def add_column(table_name, column_name, type, options = {})
23
32
  super
33
+ index = options.fetch(:index, AutomaticForeignKey.auto_index)
24
34
  references = ActiveRecord::Base.references(table_name, column_name, options)
25
- add_foreign_key(table_name, column_name, references.first, references.last, options) if references
35
+ if references
36
+ set_default_update_and_delete_actions!(options)
37
+ add_foreign_key(table_name, column_name, references.first, references.last, options)
38
+ end
39
+ add_index(table_name, column_name, options_for_index(index)) if index
26
40
  end
41
+
42
+ private
43
+ def options_for_index(index)
44
+ index.is_a?(Hash) ? index : {}
45
+ end
46
+
47
+ def set_default_update_and_delete_actions!(options)
48
+ options[:on_update] = options.fetch(:on_update, AutomaticForeignKey.on_update)
49
+ options[:on_delete] = options.fetch(:on_delete, AutomaticForeignKey.on_delete)
50
+ end
51
+
27
52
  end
28
53
  end
29
54
  end
@@ -68,6 +68,71 @@ describe ActiveRecord::Migration do
68
68
  end
69
69
  end
70
70
 
71
+ it "should create an index if specified" do
72
+ add_column(:post_id, :integer, :index => true) do
73
+ @model.should have_index.on(:post_id)
74
+ end
75
+ end
76
+
77
+ it "should create a unique index if specified" do
78
+ add_column(:post_id, :integer, :index => { :unique => true }) do
79
+ @model.should have_unique_index.on(:post_id)
80
+ end
81
+ end
82
+
83
+ it "should allow custom name for index" do
84
+ index_name = 'comments_post_id_unique_index'
85
+ add_column(:post_id, :integer, :index => { :unique => true, :name => index_name }) do
86
+ @model.should have_unique_index(:name => index_name).on(:post_id)
87
+ end
88
+ end
89
+
90
+ it "should auto-create index if specified in global options" do
91
+ AutomaticForeignKey.auto_index = true
92
+ add_column(:post_id, :integer) do
93
+ @model.should have_index.on(:post_id)
94
+ end
95
+ AutomaticForeignKey.auto_index = false
96
+ end
97
+
98
+ it "should allow to overwrite auto_index options in column definition" do
99
+ AutomaticForeignKey.auto_index = true
100
+ add_column(:post_id, :integer, :index => false) do
101
+ # MySQL creates an index on foreign by default
102
+ # and we can do nothing with that
103
+ unless mysql?
104
+ @model.should_not have_index.on(:post_id)
105
+ end
106
+ end
107
+ AutomaticForeignKey.auto_index = false
108
+ end
109
+
110
+ it "should use default on_update action" do
111
+ AutomaticForeignKey.on_update = :cascade
112
+ add_column(:post_id, :integer) do
113
+ @model.should reference.on(:post_id).on_update(:cascade)
114
+ end
115
+ AutomaticForeignKey.on_update = nil
116
+ end
117
+
118
+ it "should use default on_delete action" do
119
+ AutomaticForeignKey.on_delete = :cascade
120
+ add_column(:post_id, :integer) do
121
+ @model.should reference.on(:post_id).on_delete(:cascade)
122
+ end
123
+ AutomaticForeignKey.on_delete = nil
124
+ end
125
+
126
+ it "should allow to overwrite default actions" do
127
+ AutomaticForeignKey.on_delete = :cascade
128
+ AutomaticForeignKey.on_update = :restrict
129
+ add_column(:post_id, :integer, :on_update => :set_null, :on_delete => :set_null) do
130
+ @model.should reference.on(:post_id).on_delete(:set_null).on_update(:set_null)
131
+ end
132
+ AutomaticForeignKey.on_delete = nil
133
+ end
134
+
135
+ protected
71
136
  def add_column(column_name, *args)
72
137
  table = @model.table_name
73
138
  ActiveRecord::Migration.suppress_messages do
@@ -81,10 +146,14 @@ describe ActiveRecord::Migration do
81
146
  end
82
147
 
83
148
  def foreign_key(model, column)
84
- columns = Array(column)
149
+ columns = Array(column).collect(&:to_s)
85
150
  model.foreign_keys.detect { |fk| fk.table_name == model.table_name && fk.column_names == columns }
86
151
  end
87
-
152
+
153
+ def mysql?
154
+ ActiveRecord::Base.connection.adapter_name == 'MySQL'
155
+ end
156
+
88
157
  def create_table(model, columns_with_options)
89
158
  ActiveRecord::Migration.suppress_messages do
90
159
  ActiveRecord::Migration.create_table model.table_name, :force => true do |t|
@@ -1,52 +1,2 @@
1
- module AutomaticForeignKeyMatchers
2
-
3
- class Reference
4
- def initialize(expected)
5
- @column_names = nil
6
- unless expected.empty?
7
- @references_column_names = Array(expected).collect(&:to_s)
8
- @references_table_name = @references_column_names.shift
9
- end
10
- end
11
-
12
- def matches?(model)
13
- @model = model
14
- if @references_table_name
15
- @result = @model.foreign_keys.select do |fk|
16
- fk.references_table_name == @references_table_name &&
17
- fk.references_column_names == @references_column_names
18
- end
19
- else
20
- @result = @model.foreign_keys
21
- end
22
- if @column_names
23
- @result.any? { |fk| fk.column_names == @column_names }
24
- else
25
- !!@result
26
- end
27
- end
28
-
29
- def failure_message_for_should(should_not = false)
30
- target_column_names = @column_names.present? ? "(#{@column_names.join(', ')})" : ""
31
- destinantion_column_names = @references_table_name ? "#{@references_table_name}(#{@references_column_names.join(', ')})" : "anything"
32
- invert = should_not ? 'not' : ''
33
- "Expected #{@model.table_name}#{target_column_names} #{invert} to reference #{destinantion_column_names}"
34
- end
35
-
36
- def failure_message_for_should_not
37
- failure_message_for_should(true)
38
- end
39
-
40
- def on(*column_names)
41
- @column_names = column_names.collect(&:to_s)
42
- self
43
- end
44
-
45
- end
46
-
47
- def reference(*expect)
48
- Reference.new(expect)
49
- end
50
-
51
- end
52
-
1
+ require 'support/matchers/reference'
2
+ require 'support/matchers/have_index'
@@ -0,0 +1,52 @@
1
+ module AutomaticForeignKeyMatchers
2
+
3
+ class HaveIndex
4
+
5
+ def initialize(expectation, options = {})
6
+ set_required_columns(expectation, options)
7
+ end
8
+
9
+ def matches?(model)
10
+ @model = model
11
+ @model.indexes.any? do |index|
12
+ index.columns.to_set == @required_columns &&
13
+ (@unique ? index.unique : true) &&
14
+ (@name ? index.name == @name.to_s : true)
15
+ end
16
+ end
17
+
18
+ def failure_message_for_should(should_not = false)
19
+ invert = should_not ? "not to" : ""
20
+ "Expected #{@model.table_name} to #{invert} contain index on #{@required_columns.entries.inspect}"
21
+ end
22
+
23
+ def failure_message_for_should_not
24
+ failure_message_for_should(true)
25
+ end
26
+
27
+ def on(expectation)
28
+ set_required_columns(expectation)
29
+ self
30
+ end
31
+
32
+ private
33
+ def set_required_columns(expectation, options = {})
34
+ @required_columns = Array(expectation).collect(&:to_s).to_set
35
+ @unique = options.delete(:unique)
36
+ @name = options.delete(:name)
37
+ end
38
+
39
+ end
40
+
41
+ def have_index(*expectation)
42
+ options = expectation.extract_options!
43
+ HaveIndex.new(expectation, options)
44
+ end
45
+
46
+ def have_unique_index(*expectation)
47
+ options = expectation.extract_options!
48
+ options[:unique] = true
49
+ HaveIndex.new(expectation, options)
50
+ end
51
+
52
+ end
@@ -0,0 +1,66 @@
1
+ module AutomaticForeignKeyMatchers
2
+
3
+ class Reference
4
+ def initialize(expected)
5
+ @column_names = nil
6
+ unless expected.empty?
7
+ @references_column_names = Array(expected).collect(&:to_s)
8
+ @references_table_name = @references_column_names.shift
9
+ end
10
+ end
11
+
12
+ def matches?(model)
13
+ @model = model
14
+ if @references_table_name
15
+ @result = @model.foreign_keys.select do |fk|
16
+ fk.references_table_name == @references_table_name &&
17
+ fk.references_column_names == @references_column_names
18
+ end
19
+ else
20
+ @result = @model.foreign_keys
21
+ end
22
+ if @column_names
23
+ @result.any? do |fk|
24
+ fk.column_names == @column_names &&
25
+ (@on_update ? fk.on_update == @on_update : true) &&
26
+ (@on_delete ? fk.on_delete == @on_delete : true)
27
+ end
28
+ else
29
+ !!@result
30
+ end
31
+ end
32
+
33
+ def failure_message_for_should(should_not = false)
34
+ target_column_names = @column_names.present? ? "(#{@column_names.join(', ')})" : ""
35
+ destinantion_column_names = @references_table_name ? "#{@references_table_name}(#{@references_column_names.join(', ')})" : "anything"
36
+ invert = should_not ? 'not' : ''
37
+ "Expected #{@model.table_name}#{target_column_names} #{invert} to reference #{destinantion_column_names}"
38
+ end
39
+
40
+ def failure_message_for_should_not
41
+ failure_message_for_should(true)
42
+ end
43
+
44
+ def on(*column_names)
45
+ @column_names = column_names.collect(&:to_s)
46
+ self
47
+ end
48
+
49
+ def on_update(action)
50
+ @on_update = action
51
+ self
52
+ end
53
+
54
+ def on_delete(action)
55
+ @on_delete = action
56
+ self
57
+ end
58
+
59
+ end
60
+
61
+ def reference(*expect)
62
+ Reference.new(expect)
63
+ end
64
+
65
+ end
66
+
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
+ - 1
7
8
  - 0
8
- - 5
9
- version: 1.0.5
9
+ version: 1.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - "Micha\xC5\x82 \xC5\x81omnicki"
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-06 00:00:00 +02:00
17
+ date: 2010-06-12 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -46,8 +46,8 @@ dependencies:
46
46
  type: :runtime
47
47
  version_requirements: *id002
48
48
  description: |-
49
- Automatic Key Migrations is a gem that automatically generates foreign-key
50
- constraints when creating tables. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.
49
+ Automatic Foreign Key automatically generates foreign-key
50
+ constraints when creating tables or adding columns. It uses SQL-92 syntax and as such should be compatible with most databases that support foreign-key constraints.
51
51
  email: michal.lomnicki@gmail.com
52
52
  executables: []
53
53
 
@@ -83,6 +83,8 @@ files:
83
83
  - spec/schema/schema.rb
84
84
  - spec/spec_helper.rb
85
85
  - spec/support/matchers/automatic_foreign_key_matchers.rb
86
+ - spec/support/matchers/have_index.rb
87
+ - spec/support/matchers/reference.rb
86
88
  has_rdoc: true
87
89
  homepage: http://github.com/mlomnicki/automatic_foreign_key
88
90
  licenses: []
@@ -118,7 +120,9 @@ test_files:
118
120
  - spec/connections/postgresql/connection.rb
119
121
  - spec/connections/mysql/connection.rb
120
122
  - spec/aaa_create_tables_spec.rb
123
+ - spec/support/matchers/have_index.rb
121
124
  - spec/support/matchers/automatic_foreign_key_matchers.rb
125
+ - spec/support/matchers/reference.rb
122
126
  - spec/spec_helper.rb
123
127
  - spec/references_spec.rb
124
128
  - spec/migration_spec.rb