activerecord 7.0.0 → 7.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +109 -27
- data/lib/active_record/translation.rb +1 -3
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +12 -6
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -13,8 +13,7 @@ module ActiveRecord
|
|
|
13
13
|
##
|
|
14
14
|
# :singleton-method:
|
|
15
15
|
# A list of tables which should not be dumped to the schema.
|
|
16
|
-
# Acceptable values are strings
|
|
17
|
-
# Only strings are accepted if ActiveRecord.schema_format == :sql.
|
|
16
|
+
# Acceptable values are strings and regexps.
|
|
18
17
|
cattr_accessor :ignore_tables, default: []
|
|
19
18
|
|
|
20
19
|
##
|
|
@@ -29,9 +28,23 @@ module ActiveRecord
|
|
|
29
28
|
# should not be dumped to db/schema.rb.
|
|
30
29
|
cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
|
|
31
30
|
|
|
31
|
+
##
|
|
32
|
+
# :singleton-method:
|
|
33
|
+
# Specify a custom regular expression matching exclusion constraints which name
|
|
34
|
+
# should not be dumped to db/schema.rb.
|
|
35
|
+
cattr_accessor :excl_ignore_pattern, default: /^excl_rails_[0-9a-f]{10}$/
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# :singleton-method:
|
|
39
|
+
# Specify a custom regular expression matching unique constraints which name
|
|
40
|
+
# should not be dumped to db/schema.rb.
|
|
41
|
+
cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/
|
|
42
|
+
|
|
32
43
|
class << self
|
|
33
|
-
def dump(
|
|
34
|
-
connection
|
|
44
|
+
def dump(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)
|
|
45
|
+
pool.with_connection do |connection|
|
|
46
|
+
connection.create_schema_dumper(generate_options(config)).dump(stream)
|
|
47
|
+
end
|
|
35
48
|
stream
|
|
36
49
|
end
|
|
37
50
|
|
|
@@ -46,6 +59,7 @@ module ActiveRecord
|
|
|
46
59
|
|
|
47
60
|
def dump(stream)
|
|
48
61
|
header(stream)
|
|
62
|
+
schemas(stream)
|
|
49
63
|
extensions(stream)
|
|
50
64
|
types(stream)
|
|
51
65
|
tables(stream)
|
|
@@ -58,8 +72,13 @@ module ActiveRecord
|
|
|
58
72
|
|
|
59
73
|
def initialize(connection, options = {})
|
|
60
74
|
@connection = connection
|
|
61
|
-
@version = connection.migration_context.current_version rescue nil
|
|
75
|
+
@version = connection.pool.migration_context.current_version rescue nil
|
|
62
76
|
@options = options
|
|
77
|
+
@ignore_tables = [
|
|
78
|
+
ActiveRecord::Base.schema_migrations_table_name,
|
|
79
|
+
ActiveRecord::Base.internal_metadata_table_name,
|
|
80
|
+
self.class.ignore_tables
|
|
81
|
+
].flatten
|
|
63
82
|
end
|
|
64
83
|
|
|
65
84
|
# turns 20170404131909 into "2017_04_04_131909"
|
|
@@ -74,22 +93,21 @@ module ActiveRecord
|
|
|
74
93
|
end
|
|
75
94
|
|
|
76
95
|
def header(stream)
|
|
77
|
-
stream.puts
|
|
78
|
-
# This file is auto-generated from the current state of the database. Instead
|
|
79
|
-
# of editing this file, please use the migrations feature of Active Record to
|
|
80
|
-
# incrementally modify your database, and then regenerate this schema definition.
|
|
81
|
-
#
|
|
82
|
-
# This file is the source Rails uses to define your schema when running `bin/rails
|
|
83
|
-
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
|
84
|
-
# be faster and is potentially less error prone than running all of your
|
|
85
|
-
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
86
|
-
# migrations use external dependencies or application code.
|
|
87
|
-
#
|
|
88
|
-
# It's strongly recommended that you check this file into your version control system.
|
|
89
|
-
|
|
90
|
-
ActiveRecord::Schema.define(#{define_params}) do
|
|
91
|
-
|
|
92
|
-
HEADER
|
|
96
|
+
stream.puts <<~HEADER
|
|
97
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
98
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
99
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
100
|
+
#
|
|
101
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
|
102
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
|
103
|
+
# be faster and is potentially less error prone than running all of your
|
|
104
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
105
|
+
# migrations use external dependencies or application code.
|
|
106
|
+
#
|
|
107
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
108
|
+
|
|
109
|
+
ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
|
|
110
|
+
HEADER
|
|
93
111
|
end
|
|
94
112
|
|
|
95
113
|
def trailer(stream)
|
|
@@ -104,18 +122,31 @@ HEADER
|
|
|
104
122
|
def types(stream)
|
|
105
123
|
end
|
|
106
124
|
|
|
125
|
+
# schemas are only supported by PostgreSQL
|
|
126
|
+
def schemas(stream)
|
|
127
|
+
end
|
|
128
|
+
|
|
107
129
|
def tables(stream)
|
|
108
130
|
sorted_tables = @connection.tables.sort
|
|
109
131
|
|
|
110
|
-
sorted_tables.
|
|
111
|
-
|
|
132
|
+
not_ignored_tables = sorted_tables.reject { |table_name| ignored?(table_name) }
|
|
133
|
+
|
|
134
|
+
not_ignored_tables.each_with_index do |table_name, index|
|
|
135
|
+
table(table_name, stream)
|
|
136
|
+
stream.puts if index < not_ignored_tables.count - 1
|
|
112
137
|
end
|
|
113
138
|
|
|
114
139
|
# dump foreign keys at the end to make sure all dependent tables exist.
|
|
115
140
|
if @connection.supports_foreign_keys?
|
|
116
|
-
|
|
117
|
-
|
|
141
|
+
foreign_keys_stream = StringIO.new
|
|
142
|
+
not_ignored_tables.each do |tbl|
|
|
143
|
+
foreign_keys(tbl, foreign_keys_stream)
|
|
118
144
|
end
|
|
145
|
+
|
|
146
|
+
foreign_keys_string = foreign_keys_stream.string
|
|
147
|
+
stream.puts if foreign_keys_string.length > 0
|
|
148
|
+
|
|
149
|
+
stream.print foreign_keys_string
|
|
119
150
|
end
|
|
120
151
|
end
|
|
121
152
|
|
|
@@ -172,12 +203,12 @@ HEADER
|
|
|
172
203
|
|
|
173
204
|
indexes_in_create(table, tbl)
|
|
174
205
|
check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
|
206
|
+
exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
|
|
207
|
+
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
|
175
208
|
|
|
176
209
|
tbl.puts " end"
|
|
177
|
-
tbl.puts
|
|
178
210
|
|
|
179
|
-
tbl.
|
|
180
|
-
stream.print tbl.read
|
|
211
|
+
stream.print tbl.string
|
|
181
212
|
rescue => e
|
|
182
213
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
|
183
214
|
stream.puts "# #{e.message}"
|
|
@@ -202,6 +233,18 @@ HEADER
|
|
|
202
233
|
|
|
203
234
|
def indexes_in_create(table, stream)
|
|
204
235
|
if (indexes = @connection.indexes(table)).any?
|
|
236
|
+
if @connection.supports_exclusion_constraints? && (exclusion_constraints = @connection.exclusion_constraints(table)).any?
|
|
237
|
+
exclusion_constraint_names = exclusion_constraints.collect(&:name)
|
|
238
|
+
|
|
239
|
+
indexes = indexes.reject { |index| exclusion_constraint_names.include?(index.name) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
if @connection.supports_unique_constraints? && (unique_constraints = @connection.unique_constraints(table)).any?
|
|
243
|
+
unique_constraint_names = unique_constraints.collect(&:name)
|
|
244
|
+
|
|
245
|
+
indexes = indexes.reject { |index| unique_constraint_names.include?(index.name) }
|
|
246
|
+
end
|
|
247
|
+
|
|
205
248
|
index_statements = indexes.map do |index|
|
|
206
249
|
" t.index #{index_parts(index).join(', ')}"
|
|
207
250
|
end
|
|
@@ -220,6 +263,8 @@ HEADER
|
|
|
220
263
|
index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
|
|
221
264
|
index_parts << "where: #{index.where.inspect}" if index.where
|
|
222
265
|
index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
|
|
266
|
+
index_parts << "include: #{index.include.inspect}" if index.include
|
|
267
|
+
index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
|
|
223
268
|
index_parts << "type: #{index.type.inspect}" if index.type
|
|
224
269
|
index_parts << "comment: #{index.comment.inspect}" if index.comment
|
|
225
270
|
index_parts
|
|
@@ -236,6 +281,8 @@ HEADER
|
|
|
236
281
|
parts << "name: #{check_constraint.name.inspect}"
|
|
237
282
|
end
|
|
238
283
|
|
|
284
|
+
parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
|
|
285
|
+
|
|
239
286
|
" #{parts.join(', ')}"
|
|
240
287
|
end
|
|
241
288
|
|
|
@@ -251,7 +298,7 @@ HEADER
|
|
|
251
298
|
remove_prefix_and_suffix(foreign_key.to_table).inspect,
|
|
252
299
|
]
|
|
253
300
|
|
|
254
|
-
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
|
|
301
|
+
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
|
|
255
302
|
parts << "column: #{foreign_key.column.inspect}"
|
|
256
303
|
end
|
|
257
304
|
|
|
@@ -266,6 +313,7 @@ HEADER
|
|
|
266
313
|
parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
|
|
267
314
|
parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
|
|
268
315
|
parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
|
|
316
|
+
parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?
|
|
269
317
|
|
|
270
318
|
" #{parts.join(', ')}"
|
|
271
319
|
end
|
|
@@ -293,13 +341,17 @@ HEADER
|
|
|
293
341
|
end
|
|
294
342
|
|
|
295
343
|
def remove_prefix_and_suffix(table)
|
|
344
|
+
# This method appears at the top when profiling active_record test cases run.
|
|
345
|
+
# Avoid costly calculation when there are no prefix and suffix.
|
|
346
|
+
return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?
|
|
347
|
+
|
|
296
348
|
prefix = Regexp.escape(@options[:table_name_prefix].to_s)
|
|
297
349
|
suffix = Regexp.escape(@options[:table_name_suffix].to_s)
|
|
298
350
|
table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
|
|
299
351
|
end
|
|
300
352
|
|
|
301
353
|
def ignored?(table_name)
|
|
302
|
-
|
|
354
|
+
@ignore_tables.any? do |ignored|
|
|
303
355
|
ignored === remove_prefix_and_suffix(table_name)
|
|
304
356
|
end
|
|
305
357
|
end
|
|
@@ -1,54 +1,105 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_record/scoping/default"
|
|
4
|
-
require "active_record/scoping/named"
|
|
5
|
-
|
|
6
3
|
module ActiveRecord
|
|
7
4
|
# This class is used to create a table that keeps track of which migrations
|
|
8
5
|
# have been applied to a given database. When a migration is run, its schema
|
|
9
|
-
# number is inserted in to the
|
|
6
|
+
# number is inserted in to the schema migrations table so it doesn't need
|
|
10
7
|
# to be executed the next time.
|
|
11
|
-
class SchemaMigration
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
class SchemaMigration # :nodoc:
|
|
9
|
+
class NullSchemaMigration # :nodoc:
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :arel_table
|
|
13
|
+
|
|
14
|
+
def initialize(pool)
|
|
15
|
+
@pool = pool
|
|
16
|
+
@arel_table = Arel::Table.new(table_name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_version(version)
|
|
20
|
+
im = Arel::InsertManager.new(arel_table)
|
|
21
|
+
im.insert(arel_table[primary_key] => version)
|
|
22
|
+
@pool.with_connection do |connection|
|
|
23
|
+
connection.insert(im, "#{self.class} Create", primary_key, version)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def delete_version(version)
|
|
28
|
+
dm = Arel::DeleteManager.new(arel_table)
|
|
29
|
+
dm.wheres = [arel_table[primary_key].eq(version)]
|
|
30
|
+
|
|
31
|
+
@pool.with_connection do |connection|
|
|
32
|
+
connection.delete(dm, "#{self.class} Destroy")
|
|
15
33
|
end
|
|
34
|
+
end
|
|
16
35
|
|
|
17
|
-
|
|
18
|
-
|
|
36
|
+
def delete_all_versions
|
|
37
|
+
@pool.with_connection do |connection|
|
|
38
|
+
versions.each do |version|
|
|
39
|
+
delete_version(version)
|
|
40
|
+
end
|
|
19
41
|
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def primary_key
|
|
45
|
+
"version"
|
|
46
|
+
end
|
|
20
47
|
|
|
21
|
-
|
|
48
|
+
def table_name
|
|
49
|
+
"#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{ActiveRecord::Base.table_name_suffix}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def create_table
|
|
53
|
+
@pool.with_connection do |connection|
|
|
22
54
|
unless connection.table_exists?(table_name)
|
|
23
55
|
connection.create_table(table_name, id: false) do |t|
|
|
24
56
|
t.string :version, **connection.internal_string_options_for_primary_key
|
|
25
57
|
end
|
|
26
58
|
end
|
|
27
59
|
end
|
|
60
|
+
end
|
|
28
61
|
|
|
29
|
-
|
|
62
|
+
def drop_table
|
|
63
|
+
@pool.with_connection do |connection|
|
|
30
64
|
connection.drop_table table_name, if_exists: true
|
|
31
65
|
end
|
|
66
|
+
end
|
|
32
67
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
def normalize_migration_number(number)
|
|
69
|
+
"%.3d" % number.to_i
|
|
70
|
+
end
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
def normalized_versions
|
|
73
|
+
versions.map { |v| normalize_migration_number v }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def versions
|
|
77
|
+
sm = Arel::SelectManager.new(arel_table)
|
|
78
|
+
sm.project(arel_table[primary_key])
|
|
79
|
+
sm.order(arel_table[primary_key].asc)
|
|
40
80
|
|
|
41
|
-
|
|
42
|
-
|
|
81
|
+
@pool.with_connection do |connection|
|
|
82
|
+
connection.select_values(sm, "#{self.class} Load")
|
|
43
83
|
end
|
|
84
|
+
end
|
|
44
85
|
|
|
45
|
-
|
|
46
|
-
|
|
86
|
+
def integer_versions
|
|
87
|
+
versions.map(&:to_i)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def count
|
|
91
|
+
sm = Arel::SelectManager.new(arel_table)
|
|
92
|
+
sm.project(*Arel::Nodes::Count.new([Arel.star]))
|
|
93
|
+
|
|
94
|
+
@pool.with_connection do |connection|
|
|
95
|
+
connection.select_values(sm, "#{self.class} Count").first
|
|
47
96
|
end
|
|
48
97
|
end
|
|
49
98
|
|
|
50
|
-
def
|
|
51
|
-
|
|
99
|
+
def table_exists?
|
|
100
|
+
@pool.with_connection do |connection|
|
|
101
|
+
connection.data_source_exists?(table_name)
|
|
102
|
+
end
|
|
52
103
|
end
|
|
53
104
|
end
|
|
54
105
|
end
|
|
@@ -24,14 +24,22 @@ module ActiveRecord
|
|
|
24
24
|
# Returns a scope for the model without the previously set scopes.
|
|
25
25
|
#
|
|
26
26
|
# class Post < ActiveRecord::Base
|
|
27
|
+
# belongs_to :user
|
|
28
|
+
#
|
|
27
29
|
# def self.default_scope
|
|
28
30
|
# where(published: true)
|
|
29
31
|
# end
|
|
30
32
|
# end
|
|
31
33
|
#
|
|
34
|
+
# class User < ActiveRecord::Base
|
|
35
|
+
# has_many :posts
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
32
38
|
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
|
33
39
|
# Post.unscoped.all # Fires "SELECT * FROM posts"
|
|
34
40
|
# Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
|
|
41
|
+
# User.find(1).posts # Fires "SELECT * FROM posts WHERE published = true AND posts.user_id = 1"
|
|
42
|
+
# User.find(1).posts.unscoped # Fires "SELECT * FROM posts"
|
|
35
43
|
#
|
|
36
44
|
# This method also accepts a block. All queries inside the block will
|
|
37
45
|
# not use the previously set scopes.
|
|
@@ -48,10 +56,6 @@ module ActiveRecord
|
|
|
48
56
|
super || default_scopes.any? || respond_to?(:default_scope)
|
|
49
57
|
end
|
|
50
58
|
|
|
51
|
-
def before_remove_const # :nodoc:
|
|
52
|
-
self.current_scope = nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
59
|
# Checks if the model has any default scopes. If all_queries
|
|
56
60
|
# is set to true, the method will check if there are any
|
|
57
61
|
# default_scopes for the model where +all_queries+ is true.
|
|
@@ -71,7 +75,8 @@ module ActiveRecord
|
|
|
71
75
|
# default_scope { where(published: true) }
|
|
72
76
|
# end
|
|
73
77
|
#
|
|
74
|
-
# Article.all
|
|
78
|
+
# Article.all
|
|
79
|
+
# # SELECT * FROM articles WHERE published = true
|
|
75
80
|
#
|
|
76
81
|
# The #default_scope is also applied while creating/building a record.
|
|
77
82
|
# It is not applied while updating or deleting a record.
|
|
@@ -83,7 +88,7 @@ module ActiveRecord
|
|
|
83
88
|
# <tt>all_queries: true</tt>:
|
|
84
89
|
#
|
|
85
90
|
# class Article < ActiveRecord::Base
|
|
86
|
-
# default_scope { where(blog_id: 1) }, all_queries: true
|
|
91
|
+
# default_scope -> { where(blog_id: 1) }, all_queries: true
|
|
87
92
|
# end
|
|
88
93
|
#
|
|
89
94
|
# Applying a default scope to all queries will ensure that records
|
|
@@ -92,7 +97,7 @@ module ActiveRecord
|
|
|
92
97
|
# queries that return a single object by primary key.
|
|
93
98
|
#
|
|
94
99
|
# Article.find(1).destroy
|
|
95
|
-
#
|
|
100
|
+
# # DELETE ... FROM `articles` where ID = 1 AND blog_id = 1;
|
|
96
101
|
#
|
|
97
102
|
# (You can also pass any object which responds to +call+ to the
|
|
98
103
|
# +default_scope+ macro, and it will be called when building the
|
|
@@ -106,7 +111,8 @@ module ActiveRecord
|
|
|
106
111
|
# default_scope { where(rating: 'G') }
|
|
107
112
|
# end
|
|
108
113
|
#
|
|
109
|
-
# Article.all
|
|
114
|
+
# Article.all
|
|
115
|
+
# # SELECT * FROM articles WHERE published = true AND rating = 'G'
|
|
110
116
|
#
|
|
111
117
|
# This is also the case with inheritance and module includes where the
|
|
112
118
|
# parent or module defines a #default_scope and the child or including
|
|
@@ -150,11 +156,13 @@ module ActiveRecord
|
|
|
150
156
|
end
|
|
151
157
|
elsif default_scopes.any?
|
|
152
158
|
evaluate_default_scope do
|
|
153
|
-
default_scopes.inject(relation) do |
|
|
159
|
+
default_scopes.inject(relation) do |combined_scope, scope_obj|
|
|
154
160
|
if execute_scope?(all_queries, scope_obj)
|
|
155
161
|
scope = scope_obj.scope.respond_to?(:to_proc) ? scope_obj.scope : scope_obj.scope.method(:call)
|
|
156
162
|
|
|
157
|
-
|
|
163
|
+
combined_scope.instance_exec(&scope) || combined_scope
|
|
164
|
+
else
|
|
165
|
+
combined_scope
|
|
158
166
|
end
|
|
159
167
|
end
|
|
160
168
|
end
|
|
@@ -164,8 +172,8 @@ module ActiveRecord
|
|
|
164
172
|
# If all_queries is nil, only execute on select and insert queries.
|
|
165
173
|
#
|
|
166
174
|
# If all_queries is true, check if the default_scope object has
|
|
167
|
-
# all_queries set, then execute on all queries; select, insert, update
|
|
168
|
-
# and
|
|
175
|
+
# all_queries set, then execute on all queries; select, insert, update,
|
|
176
|
+
# delete, and reload.
|
|
169
177
|
def execute_scope?(all_queries, default_scope_obj)
|
|
170
178
|
all_queries.nil? || all_queries && default_scope_obj.all_queries
|
|
171
179
|
end
|
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
|
19
19
|
#
|
|
20
20
|
# You can define a scope that applies to all finders using
|
|
21
21
|
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
|
22
|
-
def all
|
|
22
|
+
def all(all_queries: nil)
|
|
23
23
|
scope = current_scope
|
|
24
24
|
|
|
25
25
|
if scope
|
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
|
29
29
|
relation.merge!(scope)
|
|
30
30
|
end
|
|
31
31
|
else
|
|
32
|
-
default_scoped
|
|
32
|
+
default_scoped(all_queries: all_queries)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -190,6 +190,7 @@ module ActiveRecord
|
|
|
190
190
|
|
|
191
191
|
private
|
|
192
192
|
def singleton_method_added(name)
|
|
193
|
+
super
|
|
193
194
|
generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
|
|
194
195
|
end
|
|
195
196
|
end
|
|
@@ -119,11 +119,12 @@ module ActiveRecord
|
|
|
119
119
|
return scope_type[model.name] if skip_inherited_scope
|
|
120
120
|
klass = model
|
|
121
121
|
base = model.base_class
|
|
122
|
-
while klass
|
|
122
|
+
while klass != base
|
|
123
123
|
value = scope_type[klass.name]
|
|
124
124
|
return value if value
|
|
125
125
|
klass = klass.superclass
|
|
126
126
|
end
|
|
127
|
+
scope_type[klass.name]
|
|
127
128
|
end
|
|
128
129
|
|
|
129
130
|
# Sets the +value+ for a given +scope_type+ and +model+.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module SecurePassword
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
include ActiveModel::SecurePassword
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
# Given a set of attributes, finds a record using the non-password
|
|
11
|
+
# attributes, and then authenticates that record using the password
|
|
12
|
+
# attributes. Returns the record if authentication succeeds; otherwise,
|
|
13
|
+
# returns +nil+.
|
|
14
|
+
#
|
|
15
|
+
# Regardless of whether a record is found, +authenticate_by+ will
|
|
16
|
+
# cryptographically digest the given password attributes. This behavior
|
|
17
|
+
# helps mitigate timing-based enumeration attacks, wherein an attacker can
|
|
18
|
+
# determine if a passworded record exists even without knowing the
|
|
19
|
+
# password.
|
|
20
|
+
#
|
|
21
|
+
# Raises an ArgumentError if the set of attributes doesn't contain at
|
|
22
|
+
# least one password and one non-password attribute.
|
|
23
|
+
#
|
|
24
|
+
# ==== Examples
|
|
25
|
+
#
|
|
26
|
+
# class User < ActiveRecord::Base
|
|
27
|
+
# has_secure_password
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# User.create(name: "John Doe", email: "jdoe@example.com", password: "abc123")
|
|
31
|
+
#
|
|
32
|
+
# User.authenticate_by(email: "jdoe@example.com", password: "abc123").name # => "John Doe" (in 373.4ms)
|
|
33
|
+
# User.authenticate_by(email: "jdoe@example.com", password: "wrong") # => nil (in 373.9ms)
|
|
34
|
+
# User.authenticate_by(email: "wrong@example.com", password: "abc123") # => nil (in 373.6ms)
|
|
35
|
+
#
|
|
36
|
+
# User.authenticate_by(email: "jdoe@example.com", password: nil) # => nil (no queries executed)
|
|
37
|
+
# User.authenticate_by(email: "jdoe@example.com", password: "") # => nil (no queries executed)
|
|
38
|
+
#
|
|
39
|
+
# User.authenticate_by(email: "jdoe@example.com") # => ArgumentError
|
|
40
|
+
# User.authenticate_by(password: "abc123") # => ArgumentError
|
|
41
|
+
def authenticate_by(attributes)
|
|
42
|
+
passwords, identifiers = attributes.to_h.partition do |name, value|
|
|
43
|
+
!has_attribute?(name) && has_attribute?("#{name}_digest")
|
|
44
|
+
end.map(&:to_h)
|
|
45
|
+
|
|
46
|
+
raise ArgumentError, "One or more password arguments are required" if passwords.empty?
|
|
47
|
+
raise ArgumentError, "One or more finder arguments are required" if identifiers.empty?
|
|
48
|
+
|
|
49
|
+
return if passwords.any? { |name, value| value.nil? || value.empty? }
|
|
50
|
+
|
|
51
|
+
if record = find_by(identifiers)
|
|
52
|
+
record if passwords.count { |name, value| record.public_send(:"authenticate_#{name}", value) } == passwords.size
|
|
53
|
+
else
|
|
54
|
+
new(passwords)
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -24,12 +24,26 @@ module ActiveRecord
|
|
|
24
24
|
# user.regenerate_token # => true
|
|
25
25
|
# user.regenerate_auth_token # => true
|
|
26
26
|
#
|
|
27
|
-
#
|
|
27
|
+
# +SecureRandom::base58+ is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
|
|
28
28
|
#
|
|
29
29
|
# Note that it's still possible to generate a race condition in the database in the same way that
|
|
30
30
|
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
|
|
31
31
|
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
|
|
32
|
-
|
|
32
|
+
#
|
|
33
|
+
# === Options
|
|
34
|
+
#
|
|
35
|
+
# [:length]
|
|
36
|
+
# Length of the Secure Random, with a minimum of 24 characters. It will
|
|
37
|
+
# default to 24.
|
|
38
|
+
#
|
|
39
|
+
# [:on]
|
|
40
|
+
# The callback when the value is generated. When called with <tt>on:
|
|
41
|
+
# :initialize</tt>, the value is generated in an
|
|
42
|
+
# <tt>after_initialize</tt> callback, otherwise the value will be used
|
|
43
|
+
# in a <tt>before_</tt> callback. When not specified, +:on+ will use the value of
|
|
44
|
+
# <tt>config.active_record.generate_secure_token_on</tt>, which defaults to +:initialize+
|
|
45
|
+
# starting in \Rails 7.1.
|
|
46
|
+
def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH, on: ActiveRecord.generate_secure_token_on)
|
|
33
47
|
if length < MINIMUM_TOKEN_LENGTH
|
|
34
48
|
raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters."
|
|
35
49
|
end
|
|
@@ -37,7 +51,11 @@ module ActiveRecord
|
|
|
37
51
|
# Load securerandom only when has_secure_token is used.
|
|
38
52
|
require "active_support/core_ext/securerandom"
|
|
39
53
|
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
|
|
40
|
-
|
|
54
|
+
set_callback on, on == :initialize ? :after : :before do
|
|
55
|
+
if new_record? && !query_attribute(attribute)
|
|
56
|
+
send("#{attribute}=", self.class.generate_unique_secure_token(length: length))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
41
59
|
end
|
|
42
60
|
|
|
43
61
|
def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
|