schema_plus 1.2.0 → 1.3.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +29 -0
  4. data/README.md +19 -0
  5. data/gemfiles/rails-3.2/Gemfile.mysql +7 -1
  6. data/gemfiles/rails-3.2/Gemfile.mysql2 +7 -1
  7. data/gemfiles/rails-3.2/Gemfile.postgresql +7 -1
  8. data/gemfiles/rails-3.2/Gemfile.sqlite3 +7 -1
  9. data/gemfiles/rails-4.0/Gemfile.mysql2 +7 -1
  10. data/gemfiles/rails-4.0/Gemfile.postgresql +7 -1
  11. data/gemfiles/rails-4.0/Gemfile.sqlite3 +7 -1
  12. data/gemfiles/rails-edge/Gemfile.mysql2 +7 -1
  13. data/gemfiles/rails-edge/Gemfile.postgresql +7 -1
  14. data/gemfiles/rails-edge/Gemfile.sqlite3 +7 -1
  15. data/lib/schema_plus/active_record/column_options_handler.rb +2 -2
  16. data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +124 -57
  17. data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +9 -2
  18. data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +30 -10
  19. data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +55 -30
  20. data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +1 -0
  21. data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +10 -7
  22. data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +7 -16
  23. data/lib/schema_plus/active_record/migration/command_recorder.rb +88 -0
  24. data/lib/schema_plus/active_record/schema_dumper.rb +1 -1
  25. data/lib/schema_plus/version.rb +1 -1
  26. data/lib/schema_plus.rb +10 -1
  27. data/runspecs +24 -5
  28. data/spec/column_definition_spec.rb +69 -9
  29. data/spec/index_definition_spec.rb +25 -0
  30. data/spec/index_spec.rb +1 -1
  31. data/spec/migration_spec.rb +76 -11
  32. data/spec/schema_dumper_spec.rb +19 -5
  33. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 57e14aaa2266c620a64f9807c8eda09e72d7f87a
4
- data.tar.gz: a2a284774dd0bd02b0ff0c21421a79278e93813b
3
+ metadata.gz: f36351daeeb9863e7b0344d565b2f67125ed9cea
4
+ data.tar.gz: cb88da4066f4db2f2dfb35f018cc38d82cf6aebc
5
5
  SHA512:
6
- metadata.gz: 51859b083b8f9e245733efad553563b7dd53d569705f70b8fe371c95987b90775b6efde32f033dea70232d629345c6128e5aaf1d5f7b8b1494036286f3c12f4e
7
- data.tar.gz: 9818a2241a8d90f4c5898af00ddb37a34c33011518b70c771533fec90ef63d9d8042ce9ab8850bfd59db45658813f1d118ac4f529396c40336c3be90ead10813
6
+ metadata.gz: f7f2ab813dcf8bd3c02c780337fefc2ae2c5cd793dee59b1cecc76d1706749918bf534685d3d53b7c31bce4989b56e906d292ff1c254f26e9f80d56ac450aca3
7
+ data.tar.gz: 79c000eaa5312482df33ec2d09638d93038ba213320172eb6a029a077efbb41164996cc344a8f3325b06d4cb7702a9aaa3153eeba8f65acf9a9e33e2bc80a651
data/.gitignore CHANGED
@@ -13,6 +13,9 @@ tmtags
13
13
  ## VIM
14
14
  .*.sw?
15
15
 
16
+ ## RubyMine
17
+ /.idea/
18
+
16
19
  ## PROJECT::GENERAL
17
20
  coverage
18
21
  rdoc
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  rvm:
2
2
  - 1.9.3
3
3
  - 2.0.0
4
+ - jruby
4
5
  gemfile:
5
6
  - gemfiles/rails-3.2/Gemfile.postgresql
6
7
  - gemfiles/rails-3.2/Gemfile.sqlite3
@@ -26,3 +27,31 @@ matrix:
26
27
  - gemfile: gemfiles/rails-edge/Gemfile.postgresql
27
28
  - gemfile: gemfiles/rails-edge/Gemfile.sqlite3
28
29
  - gemfile: gemfiles/rails-edge/Gemfile.mysql2
30
+ exclude:
31
+ - rvm: jruby
32
+ gemfile: gemfiles/rails-3.2/Gemfile.sqlite3
33
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
34
+ - rvm: jruby
35
+ gemfile: gemfiles/rails-3.2/Gemfile.mysql
36
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
37
+ - rvm: jruby
38
+ gemfile: gemfiles/rails-3.2/Gemfile.mysql2
39
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
40
+ - rvm: jruby
41
+ gemfile: gemfiles/rails-4.0/Gemfile.postgresql
42
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
43
+ - rvm: jruby
44
+ gemfile: gemfiles/rails-4.0/Gemfile.sqlite3
45
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
46
+ - rvm: jruby
47
+ gemfile: gemfiles/rails-4.0/Gemfile.mysql2
48
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
49
+ - rvm: jruby
50
+ gemfile: gemfiles/rails-edge/Gemfile.postgresql
51
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
52
+ - rvm: jruby
53
+ gemfile: gemfiles/rails-edge/Gemfile.sqlite3
54
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
55
+ - rvm: jruby
56
+ gemfile: gemfiles/rails-edge/Gemfile.mysql2
57
+ env: 'POSTGRES_DB_USER=postgres MYSQL_DB_USER=""'
data/README.md CHANGED
@@ -22,6 +22,10 @@ SchemaPlus supports all combinations of:
22
22
  3.2), or SQLite3 (using sqlite3 >= 3.7.7 which has foreign key support)
23
23
  * MRI Ruby 1.9.3 or 2.0.0
24
24
 
25
+ And also supports:
26
+
27
+ * jruby with Rails 3.2 and PostgreSQL
28
+
25
29
 
26
30
  Note: As of version 1.0.0, SchemaPlus no longer supports Rails 2.3, 3.0 and
27
31
  3.1, and also no longer supports MRI Ruby 1.8.7; the last version
@@ -118,6 +122,8 @@ SchemaPlus also tidies some index-related behavior:
118
122
  * If you rename a table, indexes named using rails' automatic naming
119
123
  convention will be renamed correspondingly.
120
124
 
125
+ * `remove_index` now accepts an `:if_exists` option to prevent errors from attempting to remove non-existent indexes.
126
+
121
127
 
122
128
  ### Foreign Key Constraints
123
129
 
@@ -290,6 +296,19 @@ of foreign key constraints, you can re-enable it:
290
296
 
291
297
  ## Release notes:
292
298
 
299
+ ### Master branch (to be released)
300
+
301
+ * (nothing new yet)
302
+
303
+ ### 1.3.0
304
+
305
+ * Added :if_exists option for remove_index
306
+ * Initial jruby support (rails 3.2, postgresql), due to efforts of [@donv](https://github.com/donv)
307
+ * Preliminatry groundwork for rails 4.1, due to efforts of [@tovodeverett](https://github.com/tovodeverett)
308
+ * Bug fix for change_table
309
+ * Bug fix for schema_dump postgresql non-btree indexes
310
+ * Bug fix regarding expressions that cast non-string columns to strings in a lower()
311
+
293
312
  ### 1.2.0
294
313
  * Now works with rails 4, due to efforts of [@tovodeverett](https://github.com/tovodeverett)
295
314
  * Test against MRI ruby 2.0.0, no longer test against 1.9.2
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "mysql", "~> 2.8.1"
4
+ platform :ruby do
5
+ gem "mysql", "~> 2.8.1"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "mysql2"
4
+ platform :ruby do
5
+ gem "mysql2"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "pg"
4
+ platform :ruby do
5
+ gem "pg"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcpostgresql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "sqlite3"
4
+ platform :ruby do
5
+ gem "sqlite3"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "mysql2"
4
+ platform :ruby do
5
+ gem "mysql2"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "pg"
4
+ platform :ruby do
5
+ gem "pg"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcpostgresql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "sqlite3"
4
+ platform :ruby do
5
+ gem "sqlite3"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "mysql2"
4
+ platform :ruby do
5
+ gem "mysql2"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "pg"
4
+ platform :ruby do
5
+ gem "pg"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcpostgresql-adapter'
10
+ end
@@ -1,4 +1,10 @@
1
1
  require "pathname"
2
2
  eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
3
 
4
- gem "sqlite3"
4
+ platform :ruby do
5
+ gem "sqlite3"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
10
+ end
@@ -73,7 +73,7 @@ module SchemaPlus::ActiveRecord
73
73
  def remove_foreign_key_if_exists(table_name, column_name) #:nodoc:
74
74
  foreign_keys = ActiveRecord::Base.connection.foreign_keys(table_name.to_s) rescue [] # no fks if table_name doesn't exist
75
75
  fk = foreign_keys.detect { |fk| fk.table_name == table_name.to_s && fk.column_names == Array(column_name).collect(&:to_s) }
76
- remove_foreign_key(table_name, fk.name) if fk
76
+ remove_foreign_key(table_name, fk.column_names, fk.references_table_name, fk.references_column_names) if fk
77
77
  end
78
78
 
79
79
 
@@ -86,7 +86,7 @@ module SchemaPlus::ActiveRecord
86
86
 
87
87
  def remove_auto_index_if_exists(table_name, column_name)
88
88
  name = auto_index_name(table_name, column_name)
89
- remove_index(table_name, :name => name) if index_exists?(table_name, column_name, :name => name)
89
+ remove_index(table_name, :name => name, :column => column_name, :if_exists => true)
90
90
  end
91
91
 
92
92
  def auto_index_name(table_name, column_name)
@@ -13,6 +13,7 @@ module SchemaPlus
13
13
  module AbstractAdapter
14
14
  def self.included(base) #:nodoc:
15
15
  base.alias_method_chain :initialize, :schema_plus
16
+ base.alias_method_chain :remove_index, :schema_plus
16
17
  end
17
18
 
18
19
  def initialize_with_schema_plus(*args) #:nodoc:
@@ -31,6 +32,13 @@ module SchemaPlus
31
32
  # for this.
32
33
  adapter_module = SchemaPlus::ActiveRecord::ConnectionAdapters.const_get(adapter)
33
34
  self.class.send(:include, adapter_module) unless self.class.include?(adapter_module)
35
+
36
+ if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r >= "4.1".to_r
37
+ self.class.const_get(:SchemaCreation).send(:include, adapter_module.const_get(:AddColumnOptions))
38
+ else
39
+ self.class.send(:include, adapter_module.const_get(:AddColumnOptions))
40
+ end
41
+
34
42
  extend(SchemaPlus::ActiveRecord::ForeignKeys)
35
43
  end
36
44
 
@@ -57,17 +65,56 @@ module SchemaPlus
57
65
  # it's created. If you're using Sqlite3, this method will raise an
58
66
  # error.)
59
67
  def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
60
- foreign_key = ForeignKeyDefinition.new(options[:name] || ForeignKeyDefinition.default_name(table_name, column_names), table_name, column_names, ::ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
61
- execute "ALTER TABLE #{quote_table_name(table_name)} ADD #{foreign_key.to_sql}"
68
+ foreign_key_sql = add_foreign_key_sql(table_name, column_names, references_table_name, references_column_names, options)
69
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{foreign_key_sql}"
70
+ end
71
+
72
+ # called directly by AT's bulk_change_table, for migration
73
+ # change_table :name, :bulk => true { ... }
74
+ def add_foreign_key_sql(table_name, column_names, references_table_name, references_column_names, options = {}) #:nodoc:
75
+ foreign_key = _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options)
76
+ "ADD #{foreign_key.to_sql}"
77
+ end
78
+
79
+ def _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {}) #:nodoc:
80
+ ForeignKeyDefinition.new(options[:name] || ForeignKeyDefinition.default_name(table_name, column_names), table_name, column_names, ::ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
62
81
  end
63
82
 
64
83
  # Remove a foreign key constraint
65
84
  #
85
+ # Arguments are the same as for add_foreign_key, or by name:
86
+ #
87
+ # remove_foreign_key table_name, column_names, references_table_name, references_column_names
88
+ # remove_foreign_key name: constraint_name
89
+ #
66
90
  # (NOTE: Sqlite3 does not support altering a table to remove
67
91
  # foreign-key constraints. If you're using Sqlite3, this method will
68
92
  # raise an error.)
69
- def remove_foreign_key(table_name, foreign_key_name)
70
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{foreign_key_name}"
93
+ def remove_foreign_key(table_name, *args)
94
+ case sql = remove_foreign_key_sql(table_name, *args)
95
+ when String then execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
96
+ end
97
+ end
98
+
99
+ def remove_foreign_key_sql(table_name, *args)
100
+ column_names, references_table_name, references_column_names, options = args
101
+ options ||= {}
102
+ foreign_key_name = case
103
+ when args.length == 1
104
+ case args[0]
105
+ when Hash then args[0][:name]
106
+ else args[0]
107
+ end
108
+ else
109
+ test_fk = _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options)
110
+ if foreign_keys(table_name).detect { |fk| fk == test_fk }
111
+ test_fk.name
112
+ else
113
+ raise "SchemaPlus: no foreign key constraint found on #{table_name.inspect} matching #{args.inspect}" unless options[:if_exists]
114
+ nil
115
+ end
116
+ end
117
+ foreign_key_name ? "DROP CONSTRAINT #{foreign_key_name}" : [] # hack -- return empty array rather than nil, so that result will disappear when caller flattens but doesn't compact
71
118
  end
72
119
 
73
120
  # Extends rails' drop_table to include these options:
@@ -82,6 +129,14 @@ module SchemaPlus
82
129
  execute sql
83
130
  end
84
131
 
132
+ # Extends rails' remove_index to include this options:
133
+ # :if_exists
134
+ def remove_index_with_schema_plus(table_name, options={})
135
+ return if options.delete(:if_exists) and not index_name_exists?(table_name, options[:name] || index_name(table_name, options), false)
136
+ options.delete(:column) if options[:name] and ::ActiveRecord::VERSION::MAJOR < 4
137
+ remove_index_without_schema_plus(table_name, options)
138
+ end
139
+
85
140
  # called from individual adpaters, after renaming table from old
86
141
  # name to
87
142
  def rename_indexes_and_foreign_keys(oldname, newname) #:nodoc:
@@ -113,26 +168,73 @@ module SchemaPlus
113
168
  false
114
169
  end
115
170
 
116
- def add_column_options!(sql, options)
117
- if options_include_default?(options)
118
- default = options[:default]
119
- if default.is_a? Hash
120
- value = default[:value]
121
- expr = sql_for_function(default[:expr]) || default[:expr] if default[:expr]
122
- else
123
- value = default
124
- expr = sql_for_function(default)
125
- end
126
- if expr
127
- raise ArgumentError, "Invalid default expression" unless default_expr_valid?(expr)
128
- sql << " DEFAULT #{expr}"
171
+ module AddColumnOptions
172
+ def self.included(base) #:nodoc:
173
+ base.alias_method_chain :add_column_options!, :schema_plus
174
+ end
175
+
176
+ def add_column_options_with_schema_plus!(sql, options)
177
+ if options_include_default?(options)
178
+ default = options[:default]
179
+
180
+ if default.is_a? Hash
181
+ value = default[:value]
182
+ expr = sql_for_function(default[:expr]) || default[:expr] if default[:expr]
183
+ else
184
+ value = default
185
+ expr = sql_for_function(default)
186
+ end
187
+
188
+ if expr
189
+ raise ArgumentError, "Invalid default expression" unless default_expr_valid?(expr)
190
+ sql << " DEFAULT #{expr}"
191
+ # must explicitly check for :null to allow change_column to work on migrations
192
+ if options[:null] == false
193
+ sql << " NOT NULL"
194
+ end
195
+ else
196
+ add_column_options_without_schema_plus!(sql, options.merge(default: value))
197
+ end
129
198
  else
130
- sql << " DEFAULT #{quote(value, options[:column])}" unless value.nil?
199
+ add_column_options_without_schema_plus!(sql, options)
131
200
  end
132
201
  end
133
- # must explicitly check for :null to allow change_column to work on migrations
134
- if options[:null] == false
135
- sql << " NOT NULL"
202
+
203
+ #####################################################################
204
+ #
205
+ # The functions below here are abstract; each subclass should
206
+ # define them all. Defining them here only for reference.
207
+
208
+ # (abstract) Return true if the passed expression can be used as a column
209
+ # default value. (For most databases the specific expression
210
+ # doesn't matter, and the adapter's function would return a
211
+ # constant true if default expressions are supported or false if
212
+ # they're not.)
213
+ def default_expr_valid?(expr) raise "Internal Error: Connection adapter didn't override abstract function"; end
214
+
215
+ # (abstract) Return SQL definition for a given canonical function_name symbol.
216
+ # Currently, the only function to support is :now, which should
217
+ # return a DATETIME object for the current time.
218
+ def sql_for_function(function_name) raise "Internal Error: Connection adapter didn't override abstract function"; end
219
+ end
220
+
221
+ module VisitTableDefinition
222
+ def self.included(base) #:nodoc:
223
+ base.alias_method_chain :visit_TableDefinition, :schema_plus
224
+ end
225
+
226
+ def visit_TableDefinition_with_schema_plus(o) #:nodoc:
227
+ create_sql = visit_TableDefinition_without_schema_plus(o)
228
+ last_chunk = ") #{o.options}"
229
+
230
+ unless create_sql.end_with?(last_chunk)
231
+ raise "Internal Error: Can't find '#{last_chunk}' at end of '#{create_sql}' - Rails internals have changed!"
232
+ end
233
+
234
+ unless o.foreign_keys.empty?
235
+ create_sql[create_sql.size - last_chunk.size, 0] = ', ' + o.foreign_keys.map(&:to_sql) * ', '
236
+ end
237
+ create_sql
136
238
  end
137
239
  end
138
240
 
@@ -141,7 +243,7 @@ module SchemaPlus
141
243
  # The functions below here are abstract; each subclass should
142
244
  # define them all. Defining them here only for reference.
143
245
  #
144
-
246
+
145
247
  # (abstract) Returns the names of all views, as an array of strings
146
248
  def views(name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end
147
249
 
@@ -157,41 +259,6 @@ module SchemaPlus
157
259
  # (abstract) Return the ForeignKeyDefinition objects for foreign key
158
260
  # constraints defined on other tables that reference this table
159
261
  def reverse_foreign_keys(table_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end
160
-
161
- # (abstract) Return true if the passed expression can be used as a column
162
- # default value. (For most databases the specific expression
163
- # doesn't matter, and the adapter's function would return a
164
- # constant true if default expressions are supported or false if
165
- # they're not.)
166
- def default_expr_valid?(expr) raise "Internal Error: Connection adapter didn't override abstract function"; end
167
-
168
- # (abstract) Return SQL definition for a given canonical function_name symbol.
169
- # Currently, the only function to support is :now, which should
170
- # return a DATETIME object for the current time.
171
- def sql_for_function(function_name) raise "Internal Error: Connection adapter didn't override abstract function"; end
172
-
173
-
174
- if ::ActiveRecord::VERSION::MAJOR.to_i >= 4
175
- module SchemaCreation
176
- def self.included(base) #:nodoc:
177
- base.alias_method_chain :visit_TableDefinition, :schema_plus
178
- end
179
-
180
- def visit_TableDefinition_with_schema_plus(o) #:nodoc:
181
- create_sql = visit_TableDefinition_without_schema_plus(o)
182
- last_chunk = ") #{o.options}"
183
-
184
- unless create_sql.end_with?(last_chunk)
185
- raise "Internal Error: Can't find '#{last_chunk}' at end of '#{create_sql}' - Rails internals have changed!"
186
- end
187
-
188
- unless o.foreign_keys.empty?
189
- create_sql[create_sql.size - last_chunk.size, 0] = ', ' + o.foreign_keys.map(&:to_sql) * ', '
190
- end
191
- create_sql
192
- end
193
- end
194
- end
195
262
  end
196
263
  end
197
264
  end
@@ -49,9 +49,9 @@ module SchemaPlus
49
49
  def initialize(name, table_name, column_names, references_table_name, references_column_names, on_update = nil, on_delete = nil, deferrable = nil)
50
50
  @name = name
51
51
  @table_name = unquote(table_name)
52
- @column_names = unquote(column_names)
52
+ @column_names = unquote(Array.wrap(column_names))
53
53
  @references_table_name = unquote(references_table_name)
54
- @references_column_names = unquote(references_column_names)
54
+ @references_column_names = unquote(Array.wrap(references_column_names))
55
55
  @on_update = on_update
56
56
  @on_delete = on_delete
57
57
  @deferrable = deferrable
@@ -124,6 +124,13 @@ module SchemaPlus
124
124
  table_name.to_s.gsub(/[.]/, '_')
125
125
  end
126
126
 
127
+ def ==(other) # note equality test ignores :name and options
128
+ [:table_name,
129
+ :column_names,
130
+ :references_table_name,
131
+ :references_column_names
132
+ ].all? { |attr| self.send(attr) == other.send(attr) }
133
+ end
127
134
  end
128
135
  end
129
136
  end
@@ -27,11 +27,15 @@ module SchemaPlus
27
27
  tables_without_schema_plus(name, *args) - views(name)
28
28
  end
29
29
 
30
- def remove_column_with_schema_plus(table_name, column_name)
30
+ def remove_column_with_schema_plus(table_name, column_name, type=nil, options={})
31
31
  foreign_keys(table_name).select { |foreign_key| foreign_key.column_names.include?(column_name.to_s) }.each do |foreign_key|
32
32
  remove_foreign_key(table_name, foreign_key.name)
33
33
  end
34
- remove_column_without_schema_plus(table_name, column_name)
34
+ if ::ActiveRecord::VERSION::MAJOR.to_i >= 4
35
+ remove_column_without_schema_plus(table_name, column_name, type, options)
36
+ else
37
+ remove_column_without_schema_plus(table_name, column_name)
38
+ end
35
39
  end
36
40
 
37
41
  def rename_table_with_schema_plus(oldname, newname)
@@ -59,8 +63,22 @@ module SchemaPlus
59
63
  super
60
64
  end
61
65
 
62
- def remove_foreign_key(table_name, foreign_key_name, options = {})
63
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP FOREIGN KEY #{foreign_key_name}"
66
+ def remove_index_sql(table_name, options)
67
+ return [] if options.delete(:if_exists) and not index_exists?(table_name, options)
68
+ super
69
+ end
70
+
71
+ def remove_foreign_key_sql(table_name, *args)
72
+ case ret = super
73
+ when String then ret.sub(/DROP CONSTRAINT/, 'DROP FOREIGN KEY')
74
+ else ret
75
+ end
76
+ end
77
+
78
+ def remove_foreign_key(table_name, *args)
79
+ case sql = remove_foreign_key_sql(table_name, *args)
80
+ when String then execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
81
+ end
64
82
  end
65
83
 
66
84
  def foreign_keys(table_name, name = nil)
@@ -147,13 +165,15 @@ module SchemaPlus
147
165
  sql
148
166
  end
149
167
 
150
- def default_expr_valid?(expr)
151
- false # only the TIMESTAMP column accepts SQL column defaults and rails uses DATETIME
152
- end
168
+ module AddColumnOptions
169
+ def default_expr_valid?(expr)
170
+ false # only the TIMESTAMP column accepts SQL column defaults and rails uses DATETIME
171
+ end
153
172
 
154
- def sql_for_function(function)
155
- case function
156
- when :now then 'CURRENT_TIMESTAMP'
173
+ def sql_for_function(function)
174
+ case function
175
+ when :now then 'CURRENT_TIMESTAMP'
176
+ end
157
177
  end
158
178
  end
159
179