schema_plus 1.8.9 → 2.0.0.pre1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/.travis.yml +1 -47
  4. data/CHANGELOG.md +0 -35
  5. data/README.md +73 -107
  6. data/Rakefile +7 -10
  7. data/TODO.md +51 -0
  8. data/gemfiles/Gemfile.base +2 -0
  9. data/lib/schema_column_plus.rb +7 -0
  10. data/lib/{schema_plus → schema_column_plus}/active_record/connection_adapters/column.rb +13 -11
  11. data/lib/schema_column_plus/middleware/model.rb +22 -0
  12. data/lib/schema_db_default.rb +13 -0
  13. data/lib/{schema_plus → schema_db_default}/active_record/attribute.rb +4 -4
  14. data/lib/schema_db_default/db_default.rb +17 -0
  15. data/lib/schema_db_default/middleware.rb +30 -0
  16. data/lib/schema_default_expr.rb +32 -0
  17. data/lib/schema_default_expr/active_record/connection_adapters/mysql_adapter.rb +17 -0
  18. data/lib/schema_default_expr/active_record/connection_adapters/postgresql_adapter.rb +18 -0
  19. data/lib/schema_default_expr/active_record/connection_adapters/sqlite3_adapter.rb +35 -0
  20. data/lib/schema_default_expr/middleware.rb +54 -0
  21. data/lib/schema_pg_enums.rb +6 -0
  22. data/lib/schema_pg_enums/active_record.rb +69 -0
  23. data/lib/schema_pg_enums/middleware.rb +23 -0
  24. data/lib/schema_plus.rb +17 -45
  25. data/lib/schema_plus/active_record/base.rb +6 -23
  26. data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +80 -181
  27. data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +78 -99
  28. data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +34 -114
  29. data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +16 -370
  30. data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +1 -67
  31. data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +18 -112
  32. data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +14 -116
  33. data/lib/schema_plus/active_record/migration/command_recorder.rb +8 -59
  34. data/lib/schema_plus/middleware/dumper.rb +94 -0
  35. data/lib/schema_plus/middleware/migration.rb +167 -0
  36. data/lib/schema_plus/middleware/model.rb +17 -0
  37. data/lib/schema_plus/version.rb +1 -1
  38. data/lib/schema_plus_tables.rb +15 -0
  39. data/lib/schema_plus_tables/active_record/connection_adapters/abstract_adapter.rb +20 -0
  40. data/lib/schema_plus_tables/active_record/connection_adapters/mysql_adapter.rb +25 -0
  41. data/lib/schema_plus_tables/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  42. data/lib/schema_plus_tables/active_record/connection_adapters/sqlite3_adapter.rb +12 -0
  43. data/lib/schema_views.rb +16 -0
  44. data/lib/schema_views/active_record/connection_adapters/abstract_adapter.rb +41 -0
  45. data/lib/schema_views/active_record/connection_adapters/mysql_adapter.rb +30 -0
  46. data/lib/schema_views/active_record/connection_adapters/postgresql_adapter.rb +31 -0
  47. data/lib/schema_views/active_record/connection_adapters/sqlite3_adapter.rb +18 -0
  48. data/lib/schema_views/middleware.rb +47 -0
  49. data/schema_dev.yml +1 -31
  50. data/schema_plus.gemspec +11 -9
  51. data/spec/foreign_key_definition_spec.rb +7 -7
  52. data/spec/foreign_key_spec.rb +63 -48
  53. data/spec/migration_spec.rb +58 -203
  54. data/spec/named_schemas_spec.rb +5 -88
  55. data/spec/{column_spec.rb → schema_column_plus/column_spec.rb} +26 -48
  56. data/spec/schema_db_default/column_spec.rb +58 -0
  57. data/spec/{column_default_spec.rb → schema_default_expr/column_default_spec.rb} +1 -2
  58. data/spec/schema_default_expr/schema_dumper_spec.rb +116 -0
  59. data/spec/schema_dumper_spec.rb +22 -327
  60. data/spec/{enum_spec.rb → schema_pg_enums/enum_spec.rb} +1 -1
  61. data/spec/schema_pg_enums/schema_dumper_spec.rb +37 -0
  62. data/spec/schema_views/named_schemas_spec.rb +97 -0
  63. data/spec/{views_spec.rb → schema_views/views_spec.rb} +1 -1
  64. data/spec/spec_helper.rb +2 -1
  65. data/spec/support/matchers/reference.rb +11 -12
  66. metadata +104 -57
  67. data/gemfiles/rails-3.2/Gemfile.base +0 -3
  68. data/gemfiles/rails-3.2/Gemfile.mysql +0 -10
  69. data/gemfiles/rails-3.2/Gemfile.mysql2 +0 -10
  70. data/gemfiles/rails-3.2/Gemfile.postgresql +0 -10
  71. data/gemfiles/rails-3.2/Gemfile.sqlite3 +0 -10
  72. data/gemfiles/rails-4.0/Gemfile.base +0 -3
  73. data/gemfiles/rails-4.0/Gemfile.mysql2 +0 -10
  74. data/gemfiles/rails-4.0/Gemfile.postgresql +0 -10
  75. data/gemfiles/rails-4.0/Gemfile.sqlite3 +0 -10
  76. data/gemfiles/rails-4.1/Gemfile.base +0 -3
  77. data/gemfiles/rails-4.1/Gemfile.mysql2 +0 -10
  78. data/gemfiles/rails-4.1/Gemfile.postgresql +0 -10
  79. data/gemfiles/rails-4.1/Gemfile.sqlite3 +0 -10
  80. data/lib/schema_plus/active_record/column_options_handler.rb +0 -117
  81. data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +0 -70
  82. data/lib/schema_plus/active_record/db_default.rb +0 -19
  83. data/lib/schema_plus/active_record/foreign_keys.rb +0 -137
  84. data/lib/schema_plus/active_record/schema_dumper.rb +0 -171
  85. data/lib/schema_plus/railtie.rb +0 -20
  86. data/spec/index_definition_spec.rb +0 -211
  87. data/spec/index_spec.rb +0 -249
@@ -10,38 +10,21 @@ module SchemaPlus
10
10
  end
11
11
 
12
12
  module ClassMethods #:nodoc:
13
- def self.extended(base) #:nodoc:
14
- class << base
15
- alias_method_chain :columns, :schema_plus
16
- alias_method_chain :reset_column_information, :schema_plus
17
- end
18
- end
19
13
 
20
14
  public
21
15
 
22
- def columns_with_schema_plus #:nodoc:
23
- columns = columns_without_schema_plus
24
- columns.each do |column| column.model = self end unless (@schema_plus_extended_columns ||= false)
25
- columns
26
- end
27
-
28
- def reset_column_information_with_schema_plus #:nodoc:
29
- reset_column_information_without_schema_plus
30
- @indexes = @foreign_keys = @schema_plus_extended_columns = nil
31
- end
32
-
33
- # Returns a list of IndexDefinition objects, for each index
34
- # defind on this model's table.
35
- def indexes
36
- @indexes ||= connection.indexes(table_name, "#{name} Indexes")
37
- end
38
-
39
16
  # Returns a list of ForeignKeyDefinition objects, for each foreign
40
17
  # key constraint defined in this model's table
18
+ #
19
+ # (memoized result gets reset in Middleware::Model::ResetColumnInformation)
41
20
  def foreign_keys
42
21
  @foreign_keys ||= connection.foreign_keys(table_name, "#{name} Foreign Keys")
43
22
  end
44
23
 
24
+ def reset_foreign_key_information
25
+ @foreign_keys = nil
26
+ end
27
+
45
28
  # Returns a list of ForeignKeyDefinition objects, for each foreign
46
29
  # key constraint of other tables that refer to this model's table
47
30
  def reverse_foreign_keys
@@ -11,59 +11,6 @@ module SchemaPlus
11
11
  # things are called by ActiveRecord::Base.
12
12
  #
13
13
  module AbstractAdapter
14
- def self.included(base) #:nodoc:
15
- base.alias_method_chain :initialize, :schema_plus
16
- base.alias_method_chain :remove_index, :schema_plus
17
- end
18
-
19
- def initialize_with_schema_plus(*args) #:nodoc:
20
- initialize_without_schema_plus(*args)
21
- adapter = case adapter_name
22
- # name of MySQL adapter depends on mysql gem
23
- # * with mysql gem adapter is named MySQL
24
- # * with mysql2 gem adapter is named Mysql2
25
- # Here we handle this and hopefully futher adapter names
26
- when /^MySQL/i then 'MysqlAdapter'
27
- when 'PostgreSQL', 'PostGIS' then 'PostgresqlAdapter'
28
- when 'SQLite' then 'Sqlite3Adapter'
29
- end
30
- if adapter.nil?
31
- unless adapter_name == 'JDBC' # ARJDBC
32
- ::ActiveRecord::Base.logger.warn "SchemaPlus: Unsupported adapter name #{adapter_name.inspect}. Leaving it alone."
33
- end
34
- return
35
- end
36
- adapter_module = SchemaPlus::ActiveRecord::ConnectionAdapters.const_get(adapter)
37
- self.class.send(:include, adapter_module) unless self.class.include?(adapter_module)
38
-
39
- if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r >= "4.1".to_r
40
- self.class.const_get(:SchemaCreation).send(:include, adapter_module.const_get(:AddColumnOptions))
41
- else
42
- self.class.send(:include, adapter_module.const_get(:AddColumnOptions))
43
- end
44
-
45
- extend(SchemaPlus::ActiveRecord::ForeignKeys)
46
- end
47
-
48
- # Create a view given the SQL definition. Specify :force => true
49
- # to first drop the view if it already exists.
50
- def create_view(view_name, definition, options={})
51
- definition = definition.to_sql if definition.respond_to? :to_sql
52
- if options[:force]
53
- drop_view(view_name, if_exists: true)
54
- end
55
- execute "CREATE VIEW #{quote_table_name(view_name)} AS #{definition}"
56
- end
57
-
58
- # Drop the named view. Specify :if_exists => true
59
- # to fail silently if the view doesn't exist.
60
- def drop_view(view_name, options = {})
61
- sql = "DROP VIEW"
62
- sql += " IF EXISTS" if options[:if_exists]
63
- sql += " #{quote_table_name(view_name)}"
64
- execute sql
65
- end
66
-
67
14
 
68
15
  # Define a foreign key constraint. Valid options are :on_update,
69
16
  # :on_delete, and :deferrable, with values as described at
@@ -73,102 +20,118 @@ module SchemaPlus
73
20
  # constraints; they must be included in the table specification when
74
21
  # it's created. If you're using Sqlite3, this method will raise an
75
22
  # error.)
76
- def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
77
- foreign_key_sql = add_foreign_key_sql(table_name, column_names, references_table_name, references_column_names, options)
78
- execute "ALTER TABLE #{quote_table_name(table_name)} #{foreign_key_sql}"
23
+ def add_foreign_key(*args) # (table_name, column, to_table, primary_key, options = {})
24
+ options = args.extract_options!
25
+ case args.length
26
+ when 2
27
+ from_table, to_table = args
28
+ when 4
29
+ ActiveSupport::Deprecation.warn "4-argument form of add_foreign_key is deprecated. use add_foreign_key(from_table, to_table, options)"
30
+ (from_table, column, to_table, primary_key) = args
31
+ options.merge!(column: column, primary_key: primary_key)
32
+ end
33
+
34
+ options = options.dup
35
+ options[:column] ||= foreign_key_column_for(to_table)
36
+
37
+ foreign_key_sql = add_foreign_key_sql(from_table, to_table, options)
38
+ execute "ALTER TABLE #{quote_table_name(from_table)} #{foreign_key_sql}"
79
39
  end
80
40
 
81
41
  # called directly by AT's bulk_change_table, for migration
82
42
  # change_table :name, :bulk => true { ... }
83
- def add_foreign_key_sql(table_name, column_names, references_table_name, references_column_names, options = {}) #:nodoc:
84
- foreign_key = _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options)
43
+ def add_foreign_key_sql(from_table, to_table, options = {}) #:nodoc:
44
+ foreign_key = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(from_table, AbstractAdapter.proper_table_name(to_table), options)
85
45
  "ADD #{foreign_key.to_sql}"
86
46
  end
87
47
 
88
- def _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {}) #:nodoc:
89
- options.merge!(:column_names => column_names, :references_column_names => references_column_names)
90
- options.reverse_merge!(:name => ForeignKeyDefinition.default_name(table_name, column_names))
91
- ForeignKeyDefinition.new(table_name, AbstractAdapter.proper_table_name(references_table_name), options)
48
+ def _build_foreign_key(from_table, to_table, options = {}) #:nodoc:
49
+ ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(from_table, AbstractAdapter.proper_table_name(to_table), options)
92
50
  end
93
51
 
94
52
  def self.proper_table_name(name)
95
- if ::ActiveRecord::Migration.instance_methods(false).include? :proper_table_name
96
- proper_name = ::ActiveRecord::Migration.new.proper_table_name(name) # Rails >= 4.1
97
- else
98
- proper_name = ::ActiveRecord::Migrator.proper_table_name(name) # Rails <= 4.0 ; Deprecated in 4.1
99
- end
53
+ proper_name = ::ActiveRecord::Migration.new.proper_table_name(name)
100
54
  end
101
55
 
102
56
  # Remove a foreign key constraint
103
57
  #
104
58
  # Arguments are the same as for add_foreign_key, or by name:
105
59
  #
106
- # remove_foreign_key table_name, column_names, references_table_name, references_column_names
107
- # remove_foreign_key name: constraint_name
60
+ # remove_foreign_key table_name, to_table, options
61
+ # remove_foreign_key table_name, name: constraint_name
108
62
  #
109
63
  # (NOTE: Sqlite3 does not support altering a table to remove
110
64
  # foreign-key constraints. If you're using Sqlite3, this method will
111
65
  # raise an error.)
112
- def remove_foreign_key(table_name, *args)
113
- case sql = remove_foreign_key_sql(table_name, *args)
114
- when String then execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
66
+ def remove_foreign_key(*args)
67
+ from_table, to_table, options = normalize_remove_foreign_key_args(*args)
68
+ options[:column] ||= foreign_key_column_for(to_table)
69
+ if sql = remove_foreign_key_sql(from_table, to_table, options)
70
+ execute "ALTER TABLE #{quote_table_name(from_table)} #{sql}"
115
71
  end
116
72
  end
117
73
 
118
- def remove_foreign_key_sql(table_name, *args)
119
- column_names, references_table_name, references_column_names, options = args
120
- options ||= {}
121
- foreign_key_name = case
122
- when args.length == 1
123
- case args[0]
124
- when Hash then args[0][:name]
125
- else args[0]
126
- end
127
- else
128
- test_fk = _build_foreign_key(table_name, column_names, references_table_name, references_column_names, options)
129
- if foreign_keys(table_name).detect { |fk| fk == test_fk }
130
- test_fk.name
131
- else
132
- raise "SchemaPlus: no foreign key constraint found on #{table_name.inspect} matching #{args.inspect}" unless options[:if_exists]
133
- nil
134
- end
135
- end
136
- 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
74
+ def normalize_remove_foreign_key_args(*args)
75
+ options = args.extract_options!
76
+ if options.has_key? :column_names
77
+ ActiveSupport::Deprecation.warn ":column_names option is deprecated, use :column"
78
+ options[:column] = options.delete(:column_names)
79
+ end
80
+ if options.has_key? :references_column_names
81
+ ActiveSupport::Deprecation.warn ":references_column_names option is deprecated, use :primary_key"
82
+ options[:primary_key] = options.delete(:references_column_names)
83
+ end
84
+ if options.has_key? :references_table_name
85
+ ActiveSupport::Deprecation.warn ":references_table_name option is deprecated, use :to_table"
86
+ options[:to_table] = options.delete(:references_table_name)
87
+ end
88
+ case args.length
89
+ when 1
90
+ from_table = args[0]
91
+ when 2
92
+ from_table, to_table = args
93
+ when 3, 4
94
+ ActiveSupport::Deprecation.warn "3- and 4-argument forms of remove_foreign_key are deprecated. use add_foreign_key(from_table, to_table, options)"
95
+ (from_table, column, to_table, primary_key) = args
96
+ options.merge!(column: column, primary_key: primary_key)
97
+ else
98
+ raise ArgumentError, "Wrong number of arguments(args.length) to remove_foreign_key"
99
+ end
100
+ to_table ||= options.delete(:to_table)
101
+ [from_table, to_table, options]
137
102
  end
138
103
 
139
- # Extends rails' drop_table to include these options:
140
- # :cascade
141
- # :if_exists
142
- #
143
- def drop_table(name, options = {})
144
- sql = "DROP TABLE"
145
- sql += " IF EXISTS" if options[:if_exists]
146
- sql += " #{quote_table_name(name)}"
147
- sql += " CASCADE" if options[:cascade]
148
- execute sql
104
+ def get_foreign_key_name(from_table, to_table, options)
105
+ return options[:name] if options[:name]
106
+
107
+ fks = foreign_keys(from_table)
108
+ if fks.detect(&its.name == to_table)
109
+ ActiveSupport::Deprecation.warn "remove_foreign_key(table, name) is deprecated. use remove_foreign_key(table, name: name)"
110
+ return to_table
111
+ end
112
+ test_fk = _build_foreign_key(from_table, to_table, options)
113
+ if fk = fks.detect { |fk| fk.match(test_fk) }
114
+ fk.name
115
+ else
116
+ raise "SchemaPlus: no foreign key constraint found on #{from_table.inspect} matching #{[to_table, options].inspect}" unless options[:if_exists]
117
+ nil
118
+ end
149
119
  end
150
120
 
151
- # Extends rails' remove_index to include this options:
152
- # :if_exists
153
- def remove_index_with_schema_plus(table_name, *args)
154
- options = args.extract_options!
155
- if_exists = options.delete(:if_exists)
156
- options.delete(:column) if options[:name] and ::ActiveRecord::VERSION::MAJOR < 4
157
- args << options if options.any?
158
- return if if_exists and not index_name_exists?(table_name, options[:name] || index_name(table_name, *args), false)
159
- remove_index_without_schema_plus(table_name, *args)
121
+ def remove_foreign_key_sql(from_table, to_table, options)
122
+ if foreign_key_name = get_foreign_key_name(from_table, to_table, options)
123
+ "DROP CONSTRAINT #{options[:if_exists] ? "IF EXISTS" : ""} #{foreign_key_name}"
124
+ end
160
125
  end
161
126
 
127
+
162
128
  # called from individual adpaters, after renaming table from old
163
129
  # name to
164
- def rename_indexes_and_foreign_keys(oldname, newname) #:nodoc:
165
- indexes(newname).select{|index| index.name == index_name(oldname, index.columns)}.each do |index|
166
- rename_index(newname, index.name, index_name(newname, index.columns))
167
- end
130
+ def rename_foreign_keys(oldname, newname) #:nodoc:
168
131
  foreign_keys(newname).each do |fk|
169
- index = indexes(newname).find{|index| index.name == ForeignKeyDefinition.auto_index_name(oldname, fk.column_names)}
132
+ index = indexes(newname).find{|index| index.name == ForeignKeyDefinition.auto_index_name(oldname, fk.column)}
170
133
  begin
171
- remove_foreign_key(newname, fk.name)
134
+ remove_foreign_key(newname, name: fk.name)
172
135
  rescue NotImplementedError
173
136
  # sqlite3 can't remove foreign keys, so just skip it
174
137
  end
@@ -177,69 +140,13 @@ module SchemaPlus
177
140
  # if the index is on a foreign key constraint
178
141
  rename_index(newname, index.name, ForeignKeyDefinition.auto_index_name(newname, index.columns)) if index
179
142
  begin
180
- add_foreign_key(newname, fk.column_names, fk.references_table_name, fk.references_column_names, :name => fk.name.sub(/#{oldname}/, newname), :on_update => fk.on_update, :on_delete => fk.on_delete, :deferrable => fk.deferrable)
143
+ add_foreign_key(newname, fk.to_table, :column => fk.column, :primary_key => fk.primary_key, :name => fk.name.sub(/#{oldname}/, newname), :on_update => fk.on_update, :on_delete => fk.on_delete, :deferrable => fk.deferrable)
181
144
  rescue NotImplementedError
182
145
  # sqlite3 can't add foreign keys, so just skip it
183
146
  end
184
147
  end
185
148
  end
186
149
 
187
- # Returns true if the database supports parital indexes (abstract; only
188
- # Postgresql returns true)
189
- def supports_partial_indexes?
190
- false
191
- end
192
-
193
- module AddColumnOptions
194
- def self.included(base) #:nodoc:
195
- base.alias_method_chain :add_column_options!, :schema_plus
196
- end
197
-
198
- def add_column_options_with_schema_plus!(sql, options)
199
- if options_include_default?(options)
200
- default = options[:default]
201
-
202
- if default.is_a? Hash and [[:expr], [:value]].include?(default.keys)
203
- value = default[:value]
204
- expr = sql_for_function(default[:expr]) || default[:expr] if default[:expr]
205
- else
206
- value = default
207
- expr = sql_for_function(default)
208
- end
209
-
210
- if expr
211
- raise ArgumentError, "Invalid default expression" unless default_expr_valid?(expr)
212
- sql << " DEFAULT #{expr}"
213
- # must explicitly check for :null to allow change_column to work on migrations
214
- if options[:null] == false
215
- sql << " NOT NULL"
216
- end
217
- else
218
- add_column_options_without_schema_plus!(sql, options.merge(default: value))
219
- end
220
- else
221
- add_column_options_without_schema_plus!(sql, options)
222
- end
223
- end
224
-
225
- #####################################################################
226
- #
227
- # The functions below here are abstract; each subclass should
228
- # define them all. Defining them here only for reference.
229
-
230
- # (abstract) Return true if the passed expression can be used as a column
231
- # default value. (For most databases the specific expression
232
- # doesn't matter, and the adapter's function would return a
233
- # constant true if default expressions are supported or false if
234
- # they're not.)
235
- def default_expr_valid?(expr) raise "Internal Error: Connection adapter didn't override abstract function"; end
236
-
237
- # (abstract) Return SQL definition for a given canonical function_name symbol.
238
- # Currently, the only function to support is :now, which should
239
- # return a DATETIME object for the current time.
240
- def sql_for_function(function_name) raise "Internal Error: Connection adapter didn't override abstract function"; end
241
- end
242
-
243
150
  module VisitTableDefinition
244
151
  def self.included(base) #:nodoc:
245
152
  base.alias_method_chain :visit_TableDefinition, :schema_plus
@@ -266,14 +173,6 @@ module SchemaPlus
266
173
  # define them all. Defining them here only for reference.
267
174
  #
268
175
 
269
- # (abstract) Returns the names of all views, as an array of strings
270
- def views(name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end
271
-
272
- # (abstract) Returns the SQL definition of a given view. This is
273
- # the literal SQL would come after 'CREATVE VIEW viewname AS ' in
274
- # the SQL statement to create a view.
275
- def view_definition(view_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; end
276
-
277
176
  # (abstract) Return the ForeignKeyDefinition objects for foreign key
278
177
  # constraints defined on this table
279
178
  def foreign_keys(table_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end
@@ -1,42 +1,4 @@
1
- if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r < "4.2".to_r
2
- class ActiveRecord::ConnectionAdapters::ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
3
- # The name of the foreign key constraint
4
- def name
5
- options[:name]
6
- end
7
-
8
- def column
9
- options[:column]
10
- end
11
-
12
- def primary_key
13
- options[:primary_key] || default_primary_key
14
- end
15
-
16
- # The ON_DELETE behavior for the constraint. See above for the
17
- # possible values.
18
- def on_delete
19
- options[:on_delete]
20
- end
21
-
22
- # The ON_UPDATE behavior for the constraint. See above for the
23
- # possible values.
24
- def on_update
25
- options[:on_update]
26
- end
27
-
28
- def custom_primary_key?
29
- options[:primary_key] != default_primary_key
30
- end
31
-
32
- private
33
- def default_primary_key
34
- "id"
35
- end
36
- end
37
- else
38
- require 'active_record/connection_adapters/abstract/schema_definitions'
39
- end
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
40
2
 
41
3
  module SchemaPlus
42
4
  module ActiveRecord
@@ -46,31 +8,60 @@ module SchemaPlus
46
8
  # The on_update and on_delete attributes can take on the following values:
47
9
  # :cascade
48
10
  # :restrict
49
- # :set_null
11
+ # :nullify
50
12
  # :set_default
51
13
  # :no_action
52
14
  #
53
15
  # The deferrable attribute can take on the following values:
54
16
  # true
55
17
  # :initially_deferred
56
- class ForeignKeyDefinition < ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
18
+ module ForeignKeyDefinition
57
19
 
58
- # The list of column names that are constrained (as strings).
59
- attr_reader :column_names
20
+ def self.included(base)
21
+ base.class_eval do
22
+ alias_method_chain :initialize, :schema_plus
23
+ end
24
+ end
60
25
 
61
- # The list of column names (as strings) of the foreign table that are referenced
62
- # by the constraint
63
- attr_reader :references_column_names
64
- # :enddoc:
26
+ def column_names
27
+ ActiveSupport::Deprecation.warn "ForeignKeyDefinition#column_names is depcreated, use Array.wrap(column)"
28
+ Array.wrap(column)
29
+ end
65
30
 
66
- ACTIONS = { :cascade => "CASCADE", :restrict => "RESTRICT", :set_null => "SET NULL", :set_default => "SET DEFAULT", :no_action => "NO ACTION" }.freeze
31
+ def references_column_names
32
+ ActiveSupport::Deprecation.warn "ForeignKeyDefinition#references_column_names is depcreated, use Array.wrap(primary_key)"
33
+ Array.wrap(primary_key)
34
+ end
67
35
 
68
- def initialize(from_table, to_table, options)
69
- super
70
- @from_table = unquote(from_table)
71
- @to_table = unquote(to_table)
72
- @column_names = unquote(Array.wrap(options.delete(:column_names)))
73
- @references_column_names = unquote(Array.wrap(options.delete(:references_column_names)))
36
+ def references_table_name
37
+ ActiveSupport::Deprecation.warn "ForeignKeyDefinition#references_table_name is depcreated, use #to_table"
38
+ to_table
39
+ end
40
+
41
+ def table_name
42
+ ActiveSupport::Deprecation.warn "ForeignKeyDefinition#table_name is depcreated, use #from_table"
43
+ from_table
44
+ end
45
+
46
+ ACTIONS = { :cascade => "CASCADE", :restrict => "RESTRICT", :nullify => "SET NULL", :set_default => "SET DEFAULT", :no_action => "NO ACTION" }.freeze
47
+ ACTION_LOOKUP = ACTIONS.invert.freeze
48
+
49
+ def initialize_with_schema_plus(from_table, to_table, options={})
50
+ [:on_update, :on_delete].each do |key|
51
+ if options[key] == :set_null
52
+ require 'byebug' ; byebug
53
+ ActiveSupport::Deprecation.warn ":set_null value for #{key} is deprecated. use :nullify instead"
54
+ options[key] = :nullify
55
+ end
56
+ end
57
+
58
+ initialize_without_schema_plus(from_table, to_table, options)
59
+ if column.is_a?(Array) and column.length == 1
60
+ options[:column] = column[0]
61
+ end
62
+ if primary_key.is_a?(Array) and primary_key.length == 1
63
+ options[:primary_key] = primary_key[0]
64
+ end
74
65
 
75
66
  ACTIONS.has_key?(on_update) or raise(ArgumentError, "invalid :on_update action: #{on_update.inspect}") if on_update
76
67
  ACTIONS.has_key?(on_delete) or raise(ArgumentError, "invalid :on_delete action: #{on_delete.inspect}") if on_delete
@@ -80,35 +71,36 @@ module SchemaPlus
80
71
  end
81
72
  end
82
73
 
83
- def table_name
84
- from_table
85
- end
86
-
87
- def references_table_name
88
- to_table
89
- end
90
-
91
- # True if the constraint is deferrable
74
+ # Truthy if the constraint is deferrable
92
75
  def deferrable
93
76
  options[:deferrable]
94
77
  end
95
78
 
96
79
  # Dumps a definition of foreign key.
97
80
  def to_dump(opts={})
98
- dump = (opts[:inline] ? " t.foreign_key" : "add_foreign_key #{table_name.inspect},")
99
- dump << " [#{Array(column_names).collect{ |name| name.inspect }.join(', ')}]"
100
- dump << ", #{references_table_name.inspect}, [#{Array(references_column_names).collect{ |name| name.inspect }.join(', ')}]"
101
- dump << ", :on_update => #{on_update.inspect}" if on_update
102
- dump << ", :on_delete => #{on_delete.inspect}" if on_delete
103
- dump << ", :deferrable => #{deferrable.inspect}" if deferrable
104
- dump << ", :name => #{name.inspect}" if name
105
- dump << "\n"
81
+ opts = opts.keyword_args(:column, :inline)
82
+ dump = case
83
+ when opts.column then "foreign_key: {references:"
84
+ when opts.inline then "t.foreign_key"
85
+ else "add_foreign_key #{from_table.inspect},"
86
+ end
87
+ dump << " #{to_table.inspect}"
88
+
89
+ val_or_array = -> val { val.is_a?(Array) ? "[#{val.map(&:inspect).join(', ')}]" : val.inspect }
90
+
91
+ dump << ", column: #{val_or_array.call column}" unless opts.column
92
+ dump << ", primary_key: #{val_or_array.call column}" if custom_primary_key?
93
+ dump << ", name: #{name.inspect}" if name
94
+ dump << ", on_update: #{on_update.inspect}" if on_update
95
+ dump << ", on_delete: #{on_delete.inspect}" if on_delete
96
+ dump << ", deferrable: #{deferrable.inspect}" if deferrable
97
+ dump << "}" if opts.column
106
98
  dump
107
99
  end
108
100
 
109
101
  def to_sql
110
102
  sql = name ? "CONSTRAINT #{name} " : ""
111
- sql << "FOREIGN KEY (#{quoted_column_names.join(", ")}) REFERENCES #{quoted_references_table_name} (#{quoted_references_column_names.join(", ")})"
103
+ sql << "FOREIGN KEY (#{quoted_column_names.join(", ")}) REFERENCES #{quoted_to_table} (#{quoted_primary_keys.join(", ")})"
112
104
  sql << " ON UPDATE #{ACTIONS[on_update]}" if on_update
113
105
  sql << " ON DELETE #{ACTIONS[on_delete]}" if on_delete
114
106
  sql << " DEFERRABLE" if deferrable
@@ -117,35 +109,23 @@ module SchemaPlus
117
109
  end
118
110
 
119
111
  def quoted_column_names
120
- Array(column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
112
+ Array(column).map { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
121
113
  end
122
114
 
123
- def quoted_references_column_names
124
- Array(references_column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
125
- end
126
-
127
- def quoted_references_table_name
128
- ::ActiveRecord::Base.connection.quote_table_name(references_table_name)
129
- end
130
-
131
- def unquote(names)
132
- if names.is_a?(Array)
133
- names.collect { |name| __unquote(name) }
134
- else
135
- __unquote(names)
136
- end
115
+ def quoted_primary_keys
116
+ Array(primary_key).map { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
137
117
  end
138
118
 
139
- def __unquote(value)
140
- value.to_s.sub(/^["`](.*)["`]$/, '\1')
119
+ def quoted_to_table
120
+ ::ActiveRecord::Base.connection.quote_table_name(to_table)
141
121
  end
142
122
 
143
- def self.default_name(table_name, column_names)
144
- "fk_#{fixup_schema_name(table_name)}_#{Array.wrap(column_names).join('_and_')}"
123
+ def self.default_name(from_table, column)
124
+ "fk_#{fixup_schema_name(from_table)}_#{Array.wrap(column).join('_and_')}"
145
125
  end
146
126
 
147
- def self.auto_index_name(table_name, column_name)
148
- "fk__#{fixup_schema_name(table_name)}_#{Array.wrap(column_name).join('_and_')}"
127
+ def self.auto_index_name(from_table, column_name)
128
+ "fk__#{fixup_schema_name(from_table)}_#{Array.wrap(column_name).join('_and_')}"
149
129
  end
150
130
 
151
131
  def self.fixup_schema_name(table_name)
@@ -153,12 +133,11 @@ module SchemaPlus
153
133
  table_name.to_s.gsub(/[.]/, '_')
154
134
  end
155
135
 
156
- def ==(other) # note equality test ignores :name and options
157
- [:table_name,
158
- :column_names,
159
- :references_table_name,
160
- :references_column_names
161
- ].all? { |attr| self.send(attr) == other.send(attr) }
136
+ def match(test)
137
+ return false unless from_table == test.from_table
138
+ [:to_table, :column].reject{ |attr| test.send(attr).blank? }.all? { |attr|
139
+ test.send(attr).to_s == self.send(attr).to_s
140
+ }
162
141
  end
163
142
  end
164
143
  end