schema_auto_foreign_keys 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecord::Schema do
4
+
5
+ let(:connection) { ActiveRecord::Base.connection }
6
+
7
+ context "defining with auto_index and auto_create" do
8
+
9
+ before(:each) do
10
+ with_fk_config(auto_create: true, auto_index: true) do
11
+ ActiveRecord::Schema.define do
12
+
13
+ create_table :users, :force => :cascade do
14
+ end
15
+
16
+ create_table :colors, :force => :cascade do
17
+ end
18
+
19
+ create_table :shoes, :force => :cascade do
20
+ end
21
+
22
+ create_table :posts, :force => true do |t|
23
+ t.integer :user_id, :references => :users, :index => true
24
+ t.integer :shoe_id, :references => :shoes # should not have an index (except mysql)
25
+ t.integer :color_id # should not have a foreign key nor index
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ it "creates only explicity added indexes" do
32
+ expected = SchemaDev::Rspec::Helpers.mysql? ? 2 : 1
33
+ expect(connection.tables.collect { |table| connection.indexes(table) }.flatten.size).to eq(expected)
34
+ end
35
+
36
+ it "should create only explicity added foriegn keys" do
37
+ expect(connection.tables.collect { |table| connection.foreign_keys(table) }.flatten.size).to eq(2)
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'simplecov'
2
+ require 'simplecov-gem-profile'
3
+ SimpleCov.start "gem"
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+
8
+ require 'rspec'
9
+ require 'active_record'
10
+ require 'schema_auto_foreign_keys'
11
+ require 'schema_dev/rspec'
12
+
13
+ SchemaDev::Rspec.setup
14
+
15
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
16
+
17
+ RSpec.configure do |config|
18
+ config.include(SchemaPlus::Matchers)
19
+ config.warnings = true
20
+ config.around(:each) do |example|
21
+ with_fk_config(auto_create: true, auto_index: true) do
22
+ ActiveRecord::Migration.suppress_messages do
23
+ example.run
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def with_fk_config(opts={}, &block)
30
+ save = opts.keys.map{|key| [key, SchemaPlus::ForeignKeys.config.send(key)]}.to_h
31
+ begin
32
+ SchemaPlus::ForeignKeys.config.update_attributes(opts)
33
+ yield
34
+ ensure
35
+ SchemaPlus::ForeignKeys.config.update_attributes(save)
36
+ end
37
+ end
38
+
39
+ def with_fk_auto_create(value = true, &block)
40
+ with_fk_config(:auto_create => value, &block)
41
+ end
42
+
43
+ def define_schema(&block)
44
+ ActiveRecord::Schema.define do
45
+ connection.tables.each do |table|
46
+ drop_table table, force: :cascade
47
+ end
48
+ instance_eval &block
49
+ end
50
+ end
51
+
52
+ SimpleCov.command_name "[ruby#{RUBY_VERSION}-activerecord#{::ActiveRecord.version}-#{ActiveRecord::Base.connection.adapter_name}]"
@@ -0,0 +1,2 @@
1
+ require 'support/matchers/reference'
2
+ require 'support/matchers/have_index'
@@ -0,0 +1,60 @@
1
+ module SchemaPlus::Matchers
2
+
3
+ class HaveIndex
4
+
5
+ def initialize(expectation, options = {})
6
+ set_required_columns(expectation)
7
+ @unique = options.delete(:unique)
8
+ @name = options.delete(:name)
9
+ end
10
+
11
+ def matches?(model)
12
+ @too_many = nil
13
+ @model = model
14
+ indexes = @model.indexes.select { |index| index.columns.to_set == @required_columns }
15
+ if indexes.length > 1
16
+ @too_many = indexes.length
17
+ return false
18
+ end
19
+ index = indexes.first
20
+ return index && (@unique ? index.unique : true) && (@name ? index.name == @name.to_s : true)
21
+ end
22
+
23
+ def failure_message(should_not = false)
24
+ invert = should_not ? "not to" : "to"
25
+ what = ""
26
+ what += "unique " if @unique
27
+ what += "named '{@name}'" if @name
28
+ msg = "Expected #{@model.table_name} #{invert} contain one #{what}index on #{@required_columns.entries.inspect}"
29
+ msg += "; found #{@too_many} indexes" if @too_many
30
+ msg
31
+ end
32
+
33
+ def failure_message_when_negated
34
+ failure_message(true)
35
+ end
36
+
37
+ def on(expectation)
38
+ set_required_columns(expectation)
39
+ self
40
+ end
41
+
42
+ private
43
+ def set_required_columns(expectation)
44
+ @required_columns = Array(expectation).collect(&:to_s).to_set
45
+ end
46
+
47
+ end
48
+
49
+ def have_index(*expectation)
50
+ options = expectation.extract_options!
51
+ HaveIndex.new(expectation, options)
52
+ end
53
+
54
+ def have_unique_index(*expectation)
55
+ options = expectation.extract_options!
56
+ options[:unique] = true
57
+ HaveIndex.new(expectation, options)
58
+ end
59
+
60
+ end
@@ -0,0 +1,79 @@
1
+ module SchemaPlus::Matchers
2
+
3
+ class Reference
4
+ def initialize(expected)
5
+ @column = @on_update = @on_delete = @deferrable = @name = @to_table = @primary_key = nil
6
+ unless expected.empty?
7
+ @to_table, @primary_key = Array(expected).map(&:to_s)
8
+ end
9
+ end
10
+
11
+ def matches?(model)
12
+ @model = model
13
+ if @to_table
14
+ @result = @model.foreign_keys.select do |fk|
15
+ fk.to_table == @to_table &&
16
+ @primary_key.blank? ? true : fk.primary_key == @primary_key
17
+ end
18
+ else
19
+ @result = @model.foreign_keys
20
+ end
21
+ @result.keep_if {|fk| Array.wrap(fk.column) == @column } if @column
22
+ @result.keep_if {|fk| fk.on_update == @on_update } if @on_update
23
+ @result.keep_if {|fk| fk.on_delete == @on_delete } if @on_delete
24
+ @result.keep_if {|fk| fk.deferrable == @deferrable } if @deferrable
25
+ @result.keep_if {|fk| fk.name == @name } if @name
26
+ !@result.empty?
27
+ end
28
+
29
+ def failure_message(should_not = false)
30
+ target_column = @column.present? ? "(#{Array.wrap(@column).join(', ')})" : ""
31
+ destinantion_column = @to_table ? "#{@to_table}(#{Array.wrap(@primary_key).join(', ')})" : "anything"
32
+ invert = should_not ? 'not' : ''
33
+ msg = "Expected #{@model.table_name}#{target_column} to #{invert} reference #{destinantion_column}"
34
+ with = []
35
+ with << "on_update=#{@on_update.inspect}" if @on_update
36
+ with << "on_delete=#{@on_delete.inspect}" if @on_delete
37
+ with << "deferrable=#{@deferrable.inspect}" if @deferrable
38
+ with << "name=#{@name.inspect}" if @name
39
+ msg += " with #{with.join(" and ")}" if with.any?
40
+ msg
41
+ end
42
+
43
+ def failure_message_when_negated
44
+ failure_message(true)
45
+ end
46
+
47
+ def on(*column)
48
+ @column = column.collect(&:to_s)
49
+ self
50
+ end
51
+
52
+ def on_update(action)
53
+ @on_update = action
54
+ self
55
+ end
56
+
57
+ def deferrable(action)
58
+ @deferrable = action
59
+ self
60
+ end
61
+
62
+ def on_delete(action)
63
+ @on_delete = action
64
+ self
65
+ end
66
+
67
+ def with_name(action)
68
+ @name = action
69
+ self
70
+ end
71
+
72
+ end
73
+
74
+ def reference(*expect)
75
+ Reference.new(expect)
76
+ end
77
+
78
+ end
79
+
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schema_auto_foreign_keys
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ronen barzel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: schema_plus_foreign_keys
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: schema_plus_indexes
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: schema_dev
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov-gem-profile
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: In an ActiveRecord migration, set the default to create a foreign key
126
+ and index for all columns that define relatoins.
127
+ email:
128
+ - ronen@barzel.org
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - gemfiles/Gemfile.base
140
+ - gemfiles/activerecord-4.2.0/Gemfile.base
141
+ - gemfiles/activerecord-4.2.0/Gemfile.mysql2
142
+ - gemfiles/activerecord-4.2.0/Gemfile.postgresql
143
+ - gemfiles/activerecord-4.2.0/Gemfile.sqlite3
144
+ - gemfiles/activerecord-4.2.1/Gemfile.base
145
+ - gemfiles/activerecord-4.2.1/Gemfile.mysql2
146
+ - gemfiles/activerecord-4.2.1/Gemfile.postgresql
147
+ - gemfiles/activerecord-4.2.1/Gemfile.sqlite3
148
+ - lib/schema_auto_foreign_keys.rb
149
+ - lib/schema_auto_foreign_keys/active_record/connection_adapters/sqlite3_adapter.rb
150
+ - lib/schema_auto_foreign_keys/middleware/migration.rb
151
+ - lib/schema_auto_foreign_keys/middleware/schema.rb
152
+ - lib/schema_auto_foreign_keys/version.rb
153
+ - schema_auto_foreign_keys.gemspec
154
+ - schema_dev.yml
155
+ - spec/migration_spec.rb
156
+ - spec/schema_spec.rb
157
+ - spec/spec_helper.rb
158
+ - spec/support/matchers/automatic_foreign_key_matchers.rb
159
+ - spec/support/matchers/have_index.rb
160
+ - spec/support/matchers/reference.rb
161
+ homepage: https://github.com/SchemaPlus/schema_auto_foreign_keys
162
+ licenses:
163
+ - MIT
164
+ metadata: {}
165
+ post_install_message:
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubyforge_project:
181
+ rubygems_version: 2.2.2
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Automatically define foreign key constraints in ActiveRecord
185
+ test_files:
186
+ - spec/migration_spec.rb
187
+ - spec/schema_spec.rb
188
+ - spec/spec_helper.rb
189
+ - spec/support/matchers/automatic_foreign_key_matchers.rb
190
+ - spec/support/matchers/have_index.rb
191
+ - spec/support/matchers/reference.rb