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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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
|
174
|
-
# add_column :tablenames, :
|
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.
|
192
|
-
# <tt>
|
193
|
-
#
|
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,
|
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
|
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
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
830
|
+
new(:down, migrations, target_version).migrate
|
822
831
|
end
|
823
832
|
|
824
833
|
def run(direction, migrations_paths, target_version)
|
825
|
-
|
834
|
+
new(direction, migrations(migrations_paths), target_version).run
|
826
835
|
end
|
827
836
|
|
828
837
|
def open(migrations_paths)
|
829
|
-
|
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
|
-
|
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 =
|
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
|
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
|
-
|
213
|
-
|
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 ||=
|
228
|
-
|
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
|
-
|
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
|
-
|
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.
|
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)
|
299
|
-
|
300
|
-
@arel_engine
|
301
|
-
@
|
302
|
-
@
|
303
|
-
@
|
304
|
-
@
|
305
|
-
@
|
306
|
-
@
|
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
|
-
|
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
|
-
|
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.
|
489
|
-
# number-like
|
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
|
-
#
|
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
|
-
|
519
|
+
Type::Boolean.new.type_cast_from_user(hash['_destroy'])
|
520
520
|
end
|
521
521
|
|
522
|
-
# Determines if a new record should be
|
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)
|
@@ -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
|
-
|
52
|
-
klass.allocate.init_with('attributes' => attributes, '
|
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(
|
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
|
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
|
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
|
-
#
|
414
|
-
#
|
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
|
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(
|
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
|
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
|
-
|
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
|
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 =
|
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 =
|
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
|
-
|
31
|
+
connection = ActiveRecord::Base.connection
|
32
|
+
enabled = connection.query_cache_enabled
|
33
33
|
connection_id = ActiveRecord::Base.connection_id
|
34
|
-
|
34
|
+
connection.enable_query_cache!
|
35
35
|
|
36
36
|
response = @app.call(env)
|
37
37
|
response[2] = Rack::BodyProxy.new(response[2]) do
|