activerecord 4.1.16 → 4.2.11.3

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.

Potentially problematic release.


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

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1801
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +19 -11
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +19 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +55 -69
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
 
40
40
  class PendingMigrationError < MigrationError#:nodoc:
41
41
  def initialize
42
- if defined?(Rails)
42
+ if defined?(Rails.env)
43
43
  super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
44
44
  else
45
45
  super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
@@ -161,21 +161,14 @@ module ActiveRecord
161
161
  # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
162
162
  # UTC formatted date and time that the migration was generated.
163
163
  #
164
- # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
165
- # MyNewMigration.
166
- #
167
164
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
168
165
  #
169
166
  # rails generate migration add_fieldname_to_tablename fieldname:string
170
167
  #
171
168
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
172
169
  # class AddFieldnameToTablename < ActiveRecord::Migration
173
- # def up
174
- # add_column :tablenames, :fieldname, :string
175
- # end
176
- #
177
- # def down
178
- # remove_column :tablenames, :fieldname
170
+ # def change
171
+ # add_column :tablenames, :field, :string
179
172
  # end
180
173
  # end
181
174
  #
@@ -188,14 +181,17 @@ module ActiveRecord
188
181
  #
189
182
  # To roll the database back to a previous migration version, use
190
183
  # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
191
- # you wish to downgrade. If any of the migrations throw an
192
- # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
193
- # have some manual work to do.
184
+ # you wish to downgrade. Alternatively, you can also use the STEP option if you
185
+ # wish to rollback last few migrations. <tt>rake db:migrate STEP=2</tt> will rollback
186
+ # the latest two migrations.
187
+ #
188
+ # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
189
+ # that step will fail and you'll have some manual work to do.
194
190
  #
195
191
  # == Database support
196
192
  #
197
193
  # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
198
- # SQL Server, Sybase, and Oracle (all supported databases except DB2).
194
+ # SQL Server, and Oracle (all supported databases except DB2).
199
195
  #
200
196
  # == More examples
201
197
  #
@@ -311,9 +307,8 @@ module ActiveRecord
311
307
  #
312
308
  # == Reversible Migrations
313
309
  #
314
- # Starting with Rails 3.1, you will be able to define reversible migrations.
315
310
  # Reversible migrations are migrations that know how to go +down+ for you.
316
- # You simply supply the +up+ logic, and the Migration system will figure out
311
+ # You simply supply the +up+ logic, and the Migration system figures out
317
312
  # how to execute the down commands for you.
318
313
  #
319
314
  # To define a reversible migration, define the +change+ method in your
@@ -372,13 +367,21 @@ module ActiveRecord
372
367
  end
373
368
 
374
369
  def call(env)
375
- mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
376
- if @last_check < mtime
377
- ActiveRecord::Migration.check_pending!
378
- @last_check = mtime
370
+ if connection.supports_migrations?
371
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
372
+ if @last_check < mtime
373
+ ActiveRecord::Migration.check_pending!(connection)
374
+ @last_check = mtime
375
+ end
379
376
  end
380
377
  @app.call(env)
381
378
  end
379
+
380
+ private
381
+
382
+ def connection
383
+ ActiveRecord::Base.connection
384
+ end
382
385
  end
383
386
 
384
387
  class << self
@@ -390,7 +393,7 @@ module ActiveRecord
390
393
  end
391
394
 
392
395
  def load_schema_if_pending!
393
- if ActiveRecord::Migrator.needs_migration?
396
+ if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
394
397
  # Roundrip to Rake to allow plugins to hook into database initialization.
395
398
  FileUtils.cd Rails.root do
396
399
  current_config = Base.connection_config
@@ -417,7 +420,10 @@ module ActiveRecord
417
420
  new.migrate direction
418
421
  end
419
422
 
420
- # Disable DDL transactions for this migration.
423
+ # Disable the transaction wrapping this migration.
424
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
425
+ #
426
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
421
427
  def disable_ddl_transaction!
422
428
  @disable_ddl_transaction = true
423
429
  end
@@ -649,7 +655,10 @@ module ActiveRecord
649
655
  unless @connection.respond_to? :revert
650
656
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
651
657
  arguments[0] = proper_table_name(arguments.first, table_name_options)
652
- arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
658
+ if [:rename_table, :add_foreign_key].include?(method) ||
659
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
660
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
661
+ end
653
662
  end
654
663
  end
655
664
  return super unless connection.respond_to?(method)
@@ -811,22 +820,22 @@ module ActiveRecord
811
820
  migrations = migrations(migrations_paths)
812
821
  migrations.select! { |m| yield m } if block_given?
813
822
 
814
- self.new(:up, migrations, target_version).migrate
823
+ new(:up, migrations, target_version).migrate
815
824
  end
816
825
 
817
826
  def down(migrations_paths, target_version = nil, &block)
818
827
  migrations = migrations(migrations_paths)
819
828
  migrations.select! { |m| yield m } if block_given?
820
829
 
821
- self.new(:down, migrations, target_version).migrate
830
+ new(:down, migrations, target_version).migrate
822
831
  end
823
832
 
824
833
  def run(direction, migrations_paths, target_version)
825
- self.new(direction, migrations(migrations_paths), target_version).run
834
+ new(direction, migrations(migrations_paths), target_version).run
826
835
  end
827
836
 
828
837
  def open(migrations_paths)
829
- self.new(:up, migrations(migrations_paths), nil)
838
+ new(:up, migrations(migrations_paths), nil)
830
839
  end
831
840
 
832
841
  def schema_migrations_table_name
@@ -849,6 +858,10 @@ module ActiveRecord
849
858
  (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
850
859
  end
851
860
 
861
+ def any_migrations?
862
+ migrations(migrations_paths).any?
863
+ end
864
+
852
865
  def last_version
853
866
  last_migration.version
854
867
  end
@@ -857,19 +870,6 @@ module ActiveRecord
857
870
  migrations(migrations_paths).last || NullMigration.new
858
871
  end
859
872
 
860
- def proper_table_name(name, options = {})
861
- ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
862
- options = {
863
- table_name_prefix: ActiveRecord::Base.table_name_prefix,
864
- table_name_suffix: ActiveRecord::Base.table_name_suffix
865
- }.merge(options)
866
- if name.respond_to? :table_name
867
- name.table_name
868
- else
869
- "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
870
- end
871
- end
872
-
873
873
  def migrations_paths
874
874
  @migrations_paths ||= ['db/migrate']
875
875
  # just to not break things if someone uses: migration_path = some_string
@@ -880,14 +880,15 @@ module ActiveRecord
880
880
  migrations_paths.first
881
881
  end
882
882
 
883
+ def parse_migration_filename(filename) # :nodoc:
884
+ File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
885
+ end
886
+
883
887
  def migrations(paths)
884
888
  paths = Array(paths)
885
889
 
886
- files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
887
-
888
- migrations = files.map do |file|
889
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
890
-
890
+ migrations = migration_files(paths).map do |file|
891
+ version, name, scope = parse_migration_filename(file)
891
892
  raise IllegalMigrationNameError.new(file) unless version
892
893
  version = version.to_i
893
894
  name = name.camelize
@@ -898,10 +899,34 @@ module ActiveRecord
898
899
  migrations.sort_by(&:version)
899
900
  end
900
901
 
902
+ def migrations_status(paths)
903
+ paths = Array(paths)
904
+
905
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
906
+
907
+ file_list = migration_files(paths).map do |file|
908
+ version, name, scope = parse_migration_filename(file)
909
+ raise IllegalMigrationNameError.new(file) unless version
910
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
+ status = db_list.delete(version) ? "up" : "down"
912
+ [status, version, (name + scope).humanize]
913
+ end.compact
914
+
915
+ db_list.map! do |version|
916
+ ["up", version, "********** NO FILE **********"]
917
+ end
918
+
919
+ (db_list + file_list).sort_by { |_, version, _| version }
920
+ end
921
+
922
+ def migration_files(paths)
923
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
+ end
925
+
901
926
  private
902
927
 
903
928
  def move(direction, migrations_paths, steps)
904
- migrator = self.new(direction, migrations(migrations_paths))
929
+ migrator = new(direction, migrations(migrations_paths))
905
930
  start_index = migrator.migrations.index(migrator.current_migration)
906
931
 
907
932
  if start_index
@@ -29,6 +29,10 @@ module ActiveRecord
29
29
  # :singleton-method:
30
30
  # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
31
31
  # "people_basecamp"). By default, the suffix is the empty string.
32
+ #
33
+ # If you are organising your models within modules, you can add a suffix to the models within
34
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
35
+ # returns your chosen suffix.
32
36
  class_attribute :table_name_suffix, instance_writer: false
33
37
  self.table_name_suffix = ""
34
38
 
@@ -47,6 +51,19 @@ module ActiveRecord
47
51
  self.pluralize_table_names = true
48
52
 
49
53
  self.inheritance_column = 'type'
54
+
55
+ delegate :type_for_attribute, to: :class
56
+ end
57
+
58
+ # Derives the join table name for +first_table+ and +second_table+. The
59
+ # table names appear in alphabetical order. A common prefix is removed
60
+ # (useful for namespaced models like Music::Artist and Music::Record):
61
+ #
62
+ # artists, records => artists_records
63
+ # records, artists => artists_records
64
+ # music_artists, music_records => music_artists_records
65
+ def self.derive_join_table_name(first_table, second_table) # :nodoc:
66
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
50
67
  end
51
68
 
52
69
  module ClassMethods
@@ -153,6 +170,10 @@ module ActiveRecord
153
170
  (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
154
171
  end
155
172
 
173
+ def full_table_name_suffix #:nodoc:
174
+ (parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
175
+ end
176
+
156
177
  # Defines the name of the table column which will store the class name on single-table
157
178
  # inheritance situations.
158
179
  #
@@ -190,7 +211,7 @@ module ActiveRecord
190
211
  # given block. This is required for Oracle and is useful for any
191
212
  # database which relies on sequences for primary key generation.
192
213
  #
193
- # If a sequence name is not explicitly set when using Oracle or Firebird,
214
+ # If a sequence name is not explicitly set when using Oracle,
194
215
  # it will default to the commonly used pattern of: #{table_name}_seq
195
216
  #
196
217
  # If a sequence name is not explicitly set when using PostgreSQL, it
@@ -209,50 +230,29 @@ module ActiveRecord
209
230
  connection.schema_cache.table_exists?(table_name)
210
231
  end
211
232
 
212
- # Returns an array of column objects for the table associated with this class.
213
- def columns
214
- @columns ||= connection.schema_cache.columns(table_name).map do |col|
215
- col = col.dup
216
- col.primary = (col.name == primary_key)
217
- col
218
- end
219
- end
220
-
221
- # Returns a hash of column objects for the table associated with this class.
222
- def columns_hash
223
- @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
233
+ def attributes_builder # :nodoc:
234
+ @attributes_builder ||= AttributeSet::Builder.new(column_types, primary_key)
224
235
  end
225
236
 
226
237
  def column_types # :nodoc:
227
- @column_types ||= decorate_columns(columns_hash.dup)
228
- end
229
-
230
- def decorate_columns(columns_hash) # :nodoc:
231
- return if columns_hash.empty?
232
-
233
- @serialized_column_names ||= self.columns_hash.keys.find_all do |name|
234
- serialized_attributes.key?(name)
235
- end
236
-
237
- @serialized_column_names.each do |name|
238
- columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name])
239
- end
240
-
241
- @time_zone_column_names ||= self.columns_hash.find_all do |name, col|
242
- create_time_zone_conversion_attribute?(name, col)
243
- end.map!(&:first)
244
-
245
- @time_zone_column_names.each do |name|
246
- columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
238
+ @column_types ||= columns_hash.transform_values(&:cast_type).tap do |h|
239
+ h.default = Type::Value.new
247
240
  end
241
+ end
248
242
 
249
- columns_hash
243
+ def type_for_attribute(attr_name) # :nodoc:
244
+ column_types[attr_name]
250
245
  end
251
246
 
252
247
  # Returns a hash where the keys are column names and the values are
253
248
  # default values when instantiating the AR object for this table.
254
249
  def column_defaults
255
- @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
250
+ _default_attributes.dup.to_hash
251
+ end
252
+
253
+ def _default_attributes # :nodoc:
254
+ @default_attributes ||= attributes_builder.build_from_database(
255
+ raw_default_values)
256
256
  end
257
257
 
258
258
  # Returns an array of column names as strings.
@@ -263,7 +263,7 @@ module ActiveRecord
263
263
  # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
264
264
  # and columns used for single table inheritance have been removed.
265
265
  def content_columns
266
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
266
+ @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
267
267
  end
268
268
 
269
269
  # Resets all the cached information about columns, which will cause them
@@ -295,28 +295,17 @@ module ActiveRecord
295
295
  def reset_column_information
296
296
  connection.clear_cache!
297
297
  undefine_attribute_methods
298
- connection.schema_cache.clear_table_cache!(table_name) if table_exists?
299
-
300
- @arel_engine = nil
301
- @column_defaults = nil
302
- @column_names = nil
303
- @columns = nil
304
- @columns_hash = nil
305
- @column_types = nil
306
- @content_columns = nil
307
- @dynamic_methods_hash = nil
308
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
309
- @relation = nil
310
- @serialized_column_names = nil
311
- @time_zone_column_names = nil
312
- @cached_time_zone = nil
313
- end
298
+ connection.schema_cache.clear_table_cache!(table_name)
299
+
300
+ @arel_engine = nil
301
+ @column_names = nil
302
+ @column_types = nil
303
+ @content_columns = nil
304
+ @default_attributes = nil
305
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
+ @relation = nil
314
307
 
315
- # This is a hook for use by modules that need to do extra stuff to
316
- # attributes when they are initialized. (e.g. attribute
317
- # serialization)
318
- def initialize_attributes(attributes, options = {}) #:nodoc:
319
- attributes
308
+ initialize_find_by_cache
320
309
  end
321
310
 
322
311
  private
@@ -337,12 +326,17 @@ module ActiveRecord
337
326
  contained = contained.singularize if parent.pluralize_table_names
338
327
  contained += '_'
339
328
  end
340
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
329
+
330
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
341
331
  else
342
332
  # STI subclasses always use their superclass' table.
343
333
  base.table_name
344
334
  end
345
335
  end
336
+
337
+ def raw_default_values
338
+ columns_hash.transform_values(&:default)
339
+ end
346
340
  end
347
341
  end
348
342
  end
@@ -485,10 +485,10 @@ module ActiveRecord
485
485
  end
486
486
 
487
487
  # Takes in a limit and checks if the attributes_collection has too many
488
- # records. The method will take limits in the form of symbols, procs, and
489
- # number-like objects (anything that can be compared with an integer).
488
+ # records. It accepts limit in the form of symbol, proc, or
489
+ # number-like object (anything that can be compared with an integer).
490
490
  #
491
- # Will raise an TooManyRecords error if the attributes_collection is
491
+ # Raises TooManyRecords error if the attributes_collection is
492
492
  # larger than the limit.
493
493
  def check_record_limit!(limit, attributes_collection)
494
494
  if limit
@@ -516,10 +516,10 @@ module ActiveRecord
516
516
 
517
517
  # Determines if a hash contains a truthy _destroy key.
518
518
  def has_destroy_flag?(hash)
519
- ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
519
+ Type::Boolean.new.type_cast_from_user(hash['_destroy'])
520
520
  end
521
521
 
522
- # Determines if a new record should be build by checking for
522
+ # Determines if a new record should be rejected by checking
523
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
524
  # association and evaluates to +true+.
525
525
  def reject_new_record?(association_name, attributes)
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  NoTouching.applied_to?(self.class)
46
46
  end
47
47
 
48
- def touch(*)
48
+ def touch(*) # :nodoc:
49
49
  super unless no_touching?
50
50
  end
51
51
  end
@@ -36,6 +36,23 @@ module ActiveRecord
36
36
  end
37
37
  end
38
38
 
39
+ # Creates an object (or multiple objects) and saves it to the database,
40
+ # if validations pass. Raises a RecordInvalid error if validations fail,
41
+ # unlike Base#create.
42
+ #
43
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes.
44
+ # These describe which attributes to be created on the object, or
45
+ # multiple objects when given an Array of Hashes.
46
+ def create!(attributes = nil, &block)
47
+ if attributes.is_a?(Array)
48
+ attributes.collect { |attr| create!(attr, &block) }
49
+ else
50
+ object = new(attributes, &block)
51
+ object.save!
52
+ object
53
+ end
54
+ end
55
+
39
56
  # Given an attributes hash, +instantiate+ returns a new instance of
40
57
  # the appropriate class. Accepts only keys as strings.
41
58
  #
@@ -48,8 +65,8 @@ module ActiveRecord
48
65
  # how this "single-table" inheritance mapping is implemented.
49
66
  def instantiate(attributes, column_types = {})
50
67
  klass = discriminate_class_for_record(attributes)
51
- column_types = klass.decorate_columns(column_types.dup)
52
- klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
68
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
69
+ klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
53
70
  end
54
71
 
55
72
  private
@@ -122,7 +139,7 @@ module ActiveRecord
122
139
  # Attributes marked as readonly are silently ignored if the record is
123
140
  # being updated.
124
141
  def save!(*)
125
- create_or_update || raise(RecordNotSaved)
142
+ create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
126
143
  end
127
144
 
128
145
  # Deletes the record in the database and freezes this instance to
@@ -149,8 +166,9 @@ module ActiveRecord
149
166
  # and <tt>destroy</tt> returns +false+. See
150
167
  # ActiveRecord::Callbacks for further details.
151
168
  def destroy
152
- raise ReadOnlyRecord if readonly?
169
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
153
170
  destroy_associations
171
+ self.class.connection.add_transaction_record(self)
154
172
  destroy_row if persisted?
155
173
  @destroyed = true
156
174
  freeze
@@ -164,7 +182,7 @@ module ActiveRecord
164
182
  # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
165
183
  # ActiveRecord::Callbacks for further details.
166
184
  def destroy!
167
- destroy || raise(ActiveRecord::RecordNotDestroyed)
185
+ destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
168
186
  end
169
187
 
170
188
  # Returns an instance of the specified +klass+ with the attributes of the
@@ -180,7 +198,6 @@ module ActiveRecord
180
198
  def becomes(klass)
181
199
  became = klass.new
182
200
  became.instance_variable_set("@attributes", @attributes)
183
- became.instance_variable_set("@attributes_cache", @attributes_cache)
184
201
  changed_attributes = @changed_attributes if defined?(@changed_attributes)
185
202
  became.instance_variable_set("@changed_attributes", changed_attributes || {})
186
203
  became.instance_variable_set("@new_record", new_record?)
@@ -215,6 +232,8 @@ module ActiveRecord
215
232
  #
216
233
  # This method raises an +ActiveRecord::ActiveRecordError+ if the
217
234
  # attribute is marked as readonly.
235
+ #
236
+ # See also +update_column+.
218
237
  def update_attribute(name, value)
219
238
  name = name.to_s
220
239
  verify_readonly_attribute(name)
@@ -270,7 +289,8 @@ module ActiveRecord
270
289
  # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
271
290
  # objects, or when at least one of the attributes is marked as readonly.
272
291
  def update_columns(attributes)
273
- raise ActiveRecordError, "cannot update on a new record object" unless persisted?
292
+ raise ActiveRecordError, "cannot update a new record" if new_record?
293
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
274
294
 
275
295
  attributes.each_key do |key|
276
296
  verify_readonly_attribute(key.to_s)
@@ -349,7 +369,7 @@ module ActiveRecord
349
369
  # # => #<Account id: 1, email: 'account@example.com'>
350
370
  #
351
371
  # Attributes are reloaded from the database, and caches busted, in
352
- # particular the associations cache.
372
+ # particular the associations cache and the QueryCache.
353
373
  #
354
374
  # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
355
375
  # is raised. Otherwise, in addition to the in-place modification the method
@@ -387,6 +407,7 @@ module ActiveRecord
387
407
  def reload(options = nil)
388
408
  clear_aggregation_cache
389
409
  clear_association_cache
410
+ self.class.connection.clear_query_cache
390
411
 
391
412
  fresh_object =
392
413
  if options && options[:lock]
@@ -395,25 +416,24 @@ module ActiveRecord
395
416
  self.class.unscoped { self.class.find(id) }
396
417
  end
397
418
 
398
- @attributes.update(fresh_object.instance_variable_get('@attributes'))
399
-
400
- @column_types = self.class.column_types
401
- @column_types_override = fresh_object.instance_variable_get('@column_types_override')
402
- @attributes_cache = {}
403
- @new_record = false
419
+ @attributes = fresh_object.instance_variable_get('@attributes')
420
+ @new_record = false
404
421
  self
405
422
  end
406
423
 
407
424
  # Saves the record with the updated_at/on attributes set to the current time.
408
425
  # Please note that no validation is performed and only the +after_touch+,
409
426
  # +after_commit+ and +after_rollback+ callbacks are executed.
410
- # If an attribute name is passed, that attribute is updated along with
411
- # updated_at/on attributes.
412
427
  #
413
- # product.touch # updates updated_at/on
414
- # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
428
+ # If attribute names are passed, they are updated along with updated_at/on
429
+ # attributes.
430
+ #
431
+ # product.touch # updates updated_at/on
432
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
433
+ # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
415
434
  #
416
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
435
+ # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
436
+ # associated object.
417
437
  #
418
438
  # class Brake < ActiveRecord::Base
419
439
  # belongs_to :car, touch: true
@@ -432,11 +452,11 @@ module ActiveRecord
432
452
  # ball = Ball.new
433
453
  # ball.touch(:updated_at) # => raises ActiveRecordError
434
454
  #
435
- def touch(name = nil)
455
+ def touch(*names)
436
456
  raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
437
457
 
438
458
  attributes = timestamp_attributes_for_update_in_model
439
- attributes << name if name
459
+ attributes.concat(names)
440
460
 
441
461
  unless attributes.empty?
442
462
  current_time = current_time_from_proper_timezone
@@ -449,7 +469,7 @@ module ActiveRecord
449
469
 
450
470
  changes[self.class.locking_column] = increment_lock if locking_enabled?
451
471
 
452
- changed_attributes.except!(*changes.keys)
472
+ clear_attribute_changes(changes.keys)
453
473
  primary_key = self.class.primary_key
454
474
  self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
455
475
  else
@@ -470,7 +490,7 @@ module ActiveRecord
470
490
  def relation_for_destroy
471
491
  pk = self.class.primary_key
472
492
  column = self.class.columns_hash[pk]
473
- substitute = self.class.connection.substitute_at(column, 0)
493
+ substitute = self.class.connection.substitute_at(column)
474
494
 
475
495
  relation = self.class.unscoped.where(
476
496
  self.class.arel_table[pk].eq(substitute))
@@ -480,14 +500,14 @@ module ActiveRecord
480
500
  end
481
501
 
482
502
  def create_or_update
483
- raise ReadOnlyRecord if readonly?
503
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
484
504
  result = new_record? ? _create_record : _update_record
485
505
  result != false
486
506
  end
487
507
 
488
508
  # Updates the associated record with values matching those of the instance attributes.
489
509
  # Returns the number of affected rows.
490
- def _update_record(attribute_names = @attributes.keys)
510
+ def _update_record(attribute_names = self.attribute_names)
491
511
  attributes_values = arel_attributes_with_values_for_update(attribute_names)
492
512
  if attributes_values.empty?
493
513
  0
@@ -498,7 +518,7 @@ module ActiveRecord
498
518
 
499
519
  # Creates a record with values matching those of the instance attributes
500
520
  # and returns its id.
501
- def _create_record(attribute_names = @attributes.keys)
521
+ def _create_record(attribute_names = self.attribute_names)
502
522
  attributes_values = arel_attributes_with_values_for_create(attribute_names)
503
523
 
504
524
  new_id = self.class.unscoped.insert attributes_values
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  # = Active Record Query Cache
4
3
  class QueryCache
@@ -29,9 +28,10 @@ module ActiveRecord
29
28
  end
30
29
 
31
30
  def call(env)
32
- enabled = ActiveRecord::Base.connection.query_cache_enabled
31
+ connection = ActiveRecord::Base.connection
32
+ enabled = connection.query_cache_enabled
33
33
  connection_id = ActiveRecord::Base.connection_id
34
- ActiveRecord::Base.connection.enable_query_cache!
34
+ connection.enable_query_cache!
35
35
 
36
36
  response = @app.call(env)
37
37
  response[2] = Rack::BodyProxy.new(response[2]) do