activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,9 +1,10 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require 'active_support/deprecation/reporting'
1
+ require 'active_record/migration/join_table'
3
2
 
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters # :nodoc:
6
5
  module SchemaStatements
6
+ include ActiveRecord::Migration::JoinTable
7
+
7
8
  # Returns a Hash of mappings from the abstract data types to the native
8
9
  # database types. See TableDefinition#column for details on the recognized
9
10
  # abstract data types.
@@ -13,12 +14,11 @@ module ActiveRecord
13
14
 
14
15
  # Truncates a table alias according to the limits of the current adapter.
15
16
  def table_alias_for(table_name)
16
- table_name[0...table_alias_length].gsub(/\./, '_')
17
+ table_name[0...table_alias_length].tr('.', '_')
17
18
  end
18
19
 
19
20
  # Checks to see if the table +table_name+ exists on the database.
20
21
  #
21
- # === Example
22
22
  # table_exists?(:developers)
23
23
  def table_exists?(table_name)
24
24
  tables.include?(table_name.to_s)
@@ -29,7 +29,6 @@ module ActiveRecord
29
29
 
30
30
  # Checks to see if an index exists on a table for a given index definition.
31
31
  #
32
- # === Examples
33
32
  # # Check an index exists
34
33
  # index_exists?(:suppliers, :company_id)
35
34
  #
@@ -37,12 +36,12 @@ module ActiveRecord
37
36
  # index_exists?(:suppliers, [:company_id, :company_type])
38
37
  #
39
38
  # # Check a unique index exists
40
- # index_exists?(:suppliers, :company_id, :unique => true)
39
+ # index_exists?(:suppliers, :company_id, unique: true)
41
40
  #
42
41
  # # Check an index with a custom name exists
43
- # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
42
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id"
44
43
  def index_exists?(table_name, column_name, options = {})
45
- column_names = Array.wrap(column_name)
44
+ column_names = Array(column_name)
46
45
  index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
47
46
  if options[:unique]
48
47
  indexes(table_name).any?{ |i| i.unique && i.name == index_name }
@@ -53,11 +52,10 @@ module ActiveRecord
53
52
 
54
53
  # Returns an array of Column objects for the table specified by +table_name+.
55
54
  # See the concrete implementation for details on the expected parameter values.
56
- def columns(table_name, name = nil) end
55
+ def columns(table_name) end
57
56
 
58
57
  # Checks to see if a column exists in a given table.
59
58
  #
60
- # === Examples
61
59
  # # Check a column exists
62
60
  # column_exists?(:suppliers, :name)
63
61
  #
@@ -65,13 +63,18 @@ module ActiveRecord
65
63
  # column_exists?(:suppliers, :name, :string)
66
64
  #
67
65
  # # Check a column exists with a specific definition
68
- # column_exists?(:suppliers, :name, :string, :limit => 100)
66
+ # column_exists?(:suppliers, :name, :string, limit: 100)
67
+ # column_exists?(:suppliers, :name, :string, default: 'default')
68
+ # column_exists?(:suppliers, :name, :string, null: false)
69
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
69
70
  def column_exists?(table_name, column_name, type = nil, options = {})
70
71
  columns(table_name).any?{ |c| c.name == column_name.to_s &&
71
- (!type || c.type == type) &&
72
- (!options[:limit] || c.limit == options[:limit]) &&
73
- (!options[:precision] || c.precision == options[:precision]) &&
74
- (!options[:scale] || c.scale == options[:scale]) }
72
+ (!type || c.type == type) &&
73
+ (!options.key?(:limit) || c.limit == options[:limit]) &&
74
+ (!options.key?(:precision) || c.precision == options[:precision]) &&
75
+ (!options.key?(:scale) || c.scale == options[:scale]) &&
76
+ (!options.key?(:default) || c.default == options[:default]) &&
77
+ (!options.key?(:null) || c.null == options[:null]) }
75
78
  end
76
79
 
77
80
  # Creates a new table with the name +table_name+. +table_name+ may either
@@ -86,14 +89,14 @@ module ActiveRecord
86
89
  # # table.
87
90
  #
88
91
  # create_table(:suppliers) do |t|
89
- # t.column :name, :string, :limit => 60
92
+ # t.column :name, :string, limit: 60
90
93
  # # Other fields here
91
94
  # end
92
95
  #
93
96
  # === Block form, with shorthand
94
97
  # # You can also use the column types as method calls, rather than calling the column method.
95
98
  # create_table(:suppliers) do |t|
96
- # t.string :name, :limit => 60
99
+ # t.string :name, limit: 60
97
100
  # # Other fields here
98
101
  # end
99
102
  #
@@ -101,7 +104,7 @@ module ActiveRecord
101
104
  # # Creates a table called 'suppliers' with no columns.
102
105
  # create_table(:suppliers)
103
106
  # # Add a column to 'suppliers'.
104
- # add_column(:suppliers, :name, :string, {:limit => 60})
107
+ # add_column(:suppliers, :name, :string, {limit: 60})
105
108
  #
106
109
  # The +options+ hash can include the following keys:
107
110
  # [<tt>:id</tt>]
@@ -123,17 +126,16 @@ module ActiveRecord
123
126
  # Set to true to drop the table before creating it.
124
127
  # Defaults to false.
125
128
  #
126
- # ===== Examples
127
129
  # ====== Add a backend specific option to the generated SQL (MySQL)
128
- # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
130
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
129
131
  # generates:
130
132
  # CREATE TABLE suppliers (
131
133
  # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
132
134
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
133
135
  #
134
136
  # ====== Rename the primary key column
135
- # create_table(:objects, :primary_key => 'guid') do |t|
136
- # t.column :name, :string, :limit => 80
137
+ # create_table(:objects, primary_key: 'guid') do |t|
138
+ # t.column :name, :string, limit: 80
137
139
  # end
138
140
  # generates:
139
141
  # CREATE TABLE objects (
@@ -142,7 +144,7 @@ module ActiveRecord
142
144
  # )
143
145
  #
144
146
  # ====== Do not add a primary key column
145
- # create_table(:categories_suppliers, :id => false) do |t|
147
+ # create_table(:categories_suppliers, id: false) do |t|
146
148
  # t.column :category_id, :integer
147
149
  # t.column :supplier_id, :integer
148
150
  # end
@@ -168,14 +170,74 @@ module ActiveRecord
168
170
  create_sql << td.to_sql
169
171
  create_sql << ") #{options[:options]}"
170
172
  execute create_sql
173
+ td.indexes.each_pair { |c,o| add_index table_name, c, o }
174
+ end
175
+
176
+ # Creates a new join table with the name created using the lexical order of the first two
177
+ # arguments. These arguments can be a String or a Symbol.
178
+ #
179
+ # # Creates a table called 'assemblies_parts' with no id.
180
+ # create_join_table(:assemblies, :parts)
181
+ #
182
+ # You can pass a +options+ hash can include the following keys:
183
+ # [<tt>:table_name</tt>]
184
+ # Sets the table name overriding the default
185
+ # [<tt>:column_options</tt>]
186
+ # Any extra options you want appended to the columns definition.
187
+ # [<tt>:options</tt>]
188
+ # Any extra options you want appended to the table definition.
189
+ # [<tt>:temporary</tt>]
190
+ # Make a temporary table.
191
+ # [<tt>:force</tt>]
192
+ # Set to true to drop the table before creating it.
193
+ # Defaults to false.
194
+ #
195
+ # Note that +create_join_table+ does not create any indices by default; you can use
196
+ # its block form to do so yourself:
197
+ #
198
+ # create_join_table :products, :categories do |t|
199
+ # t.index :products
200
+ # t.index :categories
201
+ # end
202
+ #
203
+ # ====== Add a backend specific option to the generated SQL (MySQL)
204
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
205
+ # generates:
206
+ # CREATE TABLE assemblies_parts (
207
+ # assembly_id int NOT NULL,
208
+ # part_id int NOT NULL,
209
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
210
+ def create_join_table(table_1, table_2, options = {})
211
+ join_table_name = find_join_table_name(table_1, table_2, options)
212
+
213
+ column_options = options.delete(:column_options) || {}
214
+ column_options.reverse_merge!(null: false)
215
+
216
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
217
+
218
+ create_table(join_table_name, options.merge!(id: false)) do |td|
219
+ td.integer t1_column, column_options
220
+ td.integer t2_column, column_options
221
+ yield td if block_given?
222
+ end
223
+ end
224
+
225
+ # Drops the join table specified by the given arguments.
226
+ # See create_join_table for details.
227
+ #
228
+ # Although this command ignores the block if one is given, it can be helpful
229
+ # to provide one in a migration's +change+ method so it can be reverted.
230
+ # In that case, the block will be used by create_join_table.
231
+ def drop_join_table(table_1, table_2, options = {})
232
+ join_table_name = find_join_table_name(table_1, table_2, options)
233
+ drop_table(join_table_name)
171
234
  end
172
235
 
173
236
  # A block for changing columns in +table+.
174
237
  #
175
- # === Example
176
238
  # # change_table() yields a Table instance
177
239
  # change_table(:suppliers) do |t|
178
- # t.column :name, :string, :limit => 60
240
+ # t.column :name, :string, limit: 60
179
241
  # # Other column alterations here
180
242
  # end
181
243
  #
@@ -186,15 +248,14 @@ module ActiveRecord
186
248
  #
187
249
  # Defaults to false.
188
250
  #
189
- # ===== Examples
190
251
  # ====== Add a column
191
252
  # change_table(:suppliers) do |t|
192
- # t.column :name, :string, :limit => 60
253
+ # t.column :name, :string, limit: 60
193
254
  # end
194
255
  #
195
256
  # ====== Add 2 integer columns
196
257
  # change_table(:suppliers) do |t|
197
- # t.integer :width, :height, :null => false, :default => 0
258
+ # t.integer :width, :height, null: false, default: 0
198
259
  # end
199
260
  #
200
261
  # ====== Add created_at/updated_at columns
@@ -211,7 +272,7 @@ module ActiveRecord
211
272
  #
212
273
  # ====== Add a polymorphic foreign key column
213
274
  # change_table(:suppliers) do |t|
214
- # t.belongs_to :company, :polymorphic => true
275
+ # t.belongs_to :company, polymorphic: true
215
276
  # end
216
277
  #
217
278
  # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
@@ -245,13 +306,17 @@ module ActiveRecord
245
306
  end
246
307
 
247
308
  # Renames a table.
248
- # ===== Example
309
+ #
249
310
  # rename_table('octopuses', 'octopi')
250
311
  def rename_table(table_name, new_name)
251
312
  raise NotImplementedError, "rename_table is not implemented"
252
313
  end
253
314
 
254
315
  # Drops a table from the database.
316
+ #
317
+ # Although this command ignores +options+ and the block if one is given, it can be helpful
318
+ # to provide these in a migration's +change+ method so it can be reverted.
319
+ # In that case, +options+ and the block will be used by create_table.
255
320
  def drop_table(table_name, options = {})
256
321
  execute "DROP TABLE #{quote_table_name(table_name)}"
257
322
  end
@@ -264,34 +329,38 @@ module ActiveRecord
264
329
  execute(add_column_sql)
265
330
  end
266
331
 
267
- # Removes the column(s) from the table definition.
268
- # ===== Examples
269
- # remove_column(:suppliers, :qualification)
332
+ # Removes the given columns from the table definition.
333
+ #
270
334
  # remove_columns(:suppliers, :qualification, :experience)
271
- def remove_column(table_name, *column_names)
272
- if column_names.flatten!
273
- message = 'Passing array to remove_columns is deprecated, please use ' +
274
- 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
275
- ActiveSupport::Deprecation.warn message, caller
335
+ def remove_columns(table_name, *column_names)
336
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
337
+ column_names.each do |column_name|
338
+ remove_column(table_name, column_name)
276
339
  end
340
+ end
277
341
 
278
- columns_for_remove(table_name, *column_names).each do |column_name|
279
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}"
280
- end
342
+ # Removes the column from the table definition.
343
+ #
344
+ # remove_column(:suppliers, :qualification)
345
+ #
346
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
347
+ # to provide these in a migration's +change+ method so it can be reverted.
348
+ # In that case, +type+ and +options+ will be used by add_column.
349
+ def remove_column(table_name, column_name, type = nil, options = {})
350
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
281
351
  end
282
- alias :remove_columns :remove_column
283
352
 
284
353
  # Changes the column's definition according to the new options.
285
354
  # See TableDefinition#column for details of the options you can use.
286
- # ===== Examples
287
- # change_column(:suppliers, :name, :string, :limit => 80)
355
+ #
356
+ # change_column(:suppliers, :name, :string, limit: 80)
288
357
  # change_column(:accounts, :description, :text)
289
358
  def change_column(table_name, column_name, type, options = {})
290
359
  raise NotImplementedError, "change_column is not implemented"
291
360
  end
292
361
 
293
362
  # Sets a new default value for a column.
294
- # ===== Examples
363
+ #
295
364
  # change_column_default(:suppliers, :qualification, 'new')
296
365
  # change_column_default(:accounts, :authorized, 1)
297
366
  # change_column_default(:users, :email, nil)
@@ -300,7 +369,7 @@ module ActiveRecord
300
369
  end
301
370
 
302
371
  # Renames a column.
303
- # ===== Example
372
+ #
304
373
  # rename_column(:suppliers, :description, :name)
305
374
  def rename_column(table_name, column_name, new_column_name)
306
375
  raise NotImplementedError, "rename_column is not implemented"
@@ -312,44 +381,49 @@ module ActiveRecord
312
381
  # The index will be named after the table and the column name(s), unless
313
382
  # you pass <tt>:name</tt> as an option.
314
383
  #
315
- # ===== Examples
316
- #
317
384
  # ====== Creating a simple index
318
385
  # add_index(:suppliers, :name)
319
386
  # generates
320
387
  # CREATE INDEX suppliers_name_index ON suppliers(name)
321
388
  #
322
389
  # ====== Creating a unique index
323
- # add_index(:accounts, [:branch_id, :party_id], :unique => true)
390
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
324
391
  # generates
325
392
  # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
326
393
  #
327
394
  # ====== Creating a named index
328
- # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
395
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
329
396
  # generates
330
397
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
331
398
  #
332
399
  # ====== Creating an index with specific key length
333
- # add_index(:accounts, :name, :name => 'by_name', :length => 10)
400
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
334
401
  # generates
335
402
  # CREATE INDEX by_name ON accounts(name(10))
336
403
  #
337
- # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
404
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
338
405
  # generates
339
406
  # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
340
407
  #
341
408
  # Note: SQLite doesn't support index length
342
409
  #
343
410
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
344
- # add_index(:accounts, [:branch_id, :party_id, :surname], :order => {:branch_id => :desc, :part_id => :asc})
411
+ # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
345
412
  # generates
346
413
  # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
347
414
  #
348
415
  # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
349
416
  #
417
+ # ====== Creating a partial index
418
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
419
+ # generates
420
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
421
+ #
422
+ # Note: only supported by PostgreSQL
423
+ #
350
424
  def add_index(table_name, column_name, options = {})
351
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
352
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
425
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
426
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
353
427
  end
354
428
 
355
429
  # Remove the given index from the table.
@@ -357,11 +431,11 @@ module ActiveRecord
357
431
  # Remove the index_accounts_on_column in the accounts table.
358
432
  # remove_index :accounts, :column
359
433
  # Remove the index named index_accounts_on_branch_id in the accounts table.
360
- # remove_index :accounts, :column => :branch_id
434
+ # remove_index :accounts, column: :branch_id
361
435
  # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
362
- # remove_index :accounts, :column => [:branch_id, :party_id]
436
+ # remove_index :accounts, column: [:branch_id, :party_id]
363
437
  # Remove the index named by_branch_party in the accounts table.
364
- # remove_index :accounts, :name => :by_branch_party
438
+ # remove_index :accounts, name: :by_branch_party
365
439
  def remove_index(table_name, options = {})
366
440
  remove_index!(table_name, index_name_for_remove(table_name, options))
367
441
  end
@@ -383,9 +457,9 @@ module ActiveRecord
383
457
  end
384
458
 
385
459
  def index_name(table_name, options) #:nodoc:
386
- if Hash === options # legacy support
460
+ if Hash === options
387
461
  if options[:column]
388
- "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
462
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
389
463
  elsif options[:name]
390
464
  options[:name]
391
465
  else
@@ -406,6 +480,42 @@ module ActiveRecord
406
480
  indexes(table_name).detect { |i| i.name == index_name }
407
481
  end
408
482
 
483
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
484
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
485
+ #
486
+ # ====== Create a user_id column
487
+ # add_reference(:products, :user)
488
+ #
489
+ # ====== Create a supplier_id and supplier_type columns
490
+ # add_belongs_to(:products, :supplier, polymorphic: true)
491
+ #
492
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
493
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
494
+ #
495
+ def add_reference(table_name, ref_name, options = {})
496
+ polymorphic = options.delete(:polymorphic)
497
+ index_options = options.delete(:index)
498
+ add_column(table_name, "#{ref_name}_id", :integer, options)
499
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
500
+ add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
501
+ end
502
+ alias :add_belongs_to :add_reference
503
+
504
+ # Removes the reference(s). Also removes a +type+ column if one exists.
505
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
506
+ #
507
+ # ====== Remove the reference
508
+ # remove_reference(:products, :user, index: true)
509
+ #
510
+ # ====== Remove polymorphic reference
511
+ # remove_reference(:products, :supplier, polymorphic: true)
512
+ #
513
+ def remove_reference(table_name, ref_name, options = {})
514
+ remove_column(table_name, "#{ref_name}_id")
515
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
516
+ end
517
+ alias :remove_belongs_to :remove_reference
518
+
409
519
  # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
410
520
  # entire structure of the database.
411
521
  def structure_dump
@@ -413,38 +523,20 @@ module ActiveRecord
413
523
 
414
524
  def dump_schema_information #:nodoc:
415
525
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
416
- migrated = select_values("SELECT version FROM #{sm_table} ORDER BY version")
417
- migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
526
+
527
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
528
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
529
+ }.join "\n\n"
418
530
  end
419
531
 
420
532
  # Should not be called normally, but this operation is non-destructive.
421
533
  # The migrations module handles this automatically.
422
534
  def initialize_schema_migrations_table
423
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
424
-
425
- unless table_exists?(sm_table)
426
- create_table(sm_table, :id => false) do |schema_migrations_table|
427
- schema_migrations_table.column :version, :string, :null => false
428
- end
429
- add_index sm_table, :version, :unique => true,
430
- :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
431
-
432
- # Backwards-compatibility: if we find schema_info, assume we've
433
- # migrated up to that point:
434
- si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
435
-
436
- if table_exists?(si_table)
437
- ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"
438
-
439
- old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
440
- assume_migrated_upto_version(old_version)
441
- drop_table(si_table)
442
- end
443
- end
535
+ ActiveRecord::SchemaMigration.create_table
444
536
  end
445
537
 
446
538
  def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
447
- migrations_paths = Array.wrap(migrations_paths)
539
+ migrations_paths = Array(migrations_paths)
448
540
  version = version.to_i
449
541
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
450
542
 
@@ -483,7 +575,7 @@ module ActiveRecord
483
575
  column_type_sql << "(#{precision})"
484
576
  end
485
577
  elsif scale
486
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
578
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
487
579
  end
488
580
 
489
581
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
@@ -513,7 +605,7 @@ module ActiveRecord
513
605
  end
514
606
 
515
607
  # Adds timestamps (created_at and updated_at) columns to the named table.
516
- # ===== Examples
608
+ #
517
609
  # add_timestamps(:suppliers)
518
610
  def add_timestamps(table_name)
519
611
  add_column table_name, :created_at, :datetime
@@ -521,7 +613,7 @@ module ActiveRecord
521
613
  end
522
614
 
523
615
  # Removes the timestamp columns (created_at and updated_at) from the table definition.
524
- # ===== Examples
616
+ #
525
617
  # remove_timestamps(:suppliers)
526
618
  def remove_timestamps(table_name)
527
619
  remove_column table_name, :updated_at
@@ -533,7 +625,7 @@ module ActiveRecord
533
625
  if options.is_a?(Hash) && order = options[:order]
534
626
  case order
535
627
  when Hash
536
- column_names.each {|name| option_strings[name] += " #{order[name].to_s.upcase}" if order.has_key?(name)}
628
+ column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
537
629
  when String
538
630
  column_names.each {|name| option_strings[name] += " #{order.upcase}"}
539
631
  end
@@ -559,25 +651,41 @@ module ActiveRecord
559
651
  end
560
652
 
561
653
  def add_index_options(table_name, column_name, options = {})
562
- column_names = Array.wrap(column_name)
563
- index_name = index_name(table_name, :column => column_names)
654
+ column_names = Array(column_name)
655
+ index_name = index_name(table_name, column: column_names)
564
656
 
565
657
  if Hash === options # legacy support, since this param was a string
658
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal)
659
+
566
660
  index_type = options[:unique] ? "UNIQUE" : ""
567
661
  index_name = options[:name].to_s if options.key?(:name)
662
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
663
+
664
+ if supports_partial_index?
665
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
666
+ end
568
667
  else
668
+ if options
669
+ message = "Passing a string as third argument of `add_index` is deprecated and will" +
670
+ " be removed in Rails 4.1." +
671
+ " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
672
+
673
+ ActiveSupport::Deprecation.warn message
674
+ end
675
+
569
676
  index_type = options
677
+ max_index_length = allowed_index_name_length
570
678
  end
571
679
 
572
- if index_name.length > index_name_length
573
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
680
+ if index_name.length > max_index_length
681
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
574
682
  end
575
683
  if index_name_exists?(table_name, index_name, false)
576
684
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
577
685
  end
578
686
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
579
687
 
580
- [index_name, index_type, index_columns]
688
+ [index_name, index_type, index_columns, index_options]
581
689
  end
582
690
 
583
691
  def index_name_for_remove(table_name, options = {})
@@ -591,12 +699,33 @@ module ActiveRecord
591
699
  end
592
700
 
593
701
  def columns_for_remove(table_name, *column_names)
594
- column_names = column_names.flatten
595
-
596
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
702
+ ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future")
703
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank?
597
704
  column_names.map {|column_name| quote_column_name(column_name) }
598
705
  end
599
706
 
707
+ def rename_table_indexes(table_name, new_name)
708
+ indexes(new_name).each do |index|
709
+ generated_index_name = index_name(table_name, column: index.columns)
710
+ if generated_index_name == index.name
711
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
712
+ end
713
+ end
714
+ end
715
+
716
+ def rename_column_indexes(table_name, column_name, new_column_name)
717
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
718
+ indexes(table_name).each do |index|
719
+ next unless index.columns.include?(new_column_name)
720
+ old_columns = index.columns.dup
721
+ old_columns[old_columns.index(new_column_name)] = column_name
722
+ generated_index_name = index_name(table_name, column: old_columns)
723
+ if generated_index_name == index.name
724
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
725
+ end
726
+ end
727
+ end
728
+
600
729
  private
601
730
  def table_definition
602
731
  TableDefinition.new(self)