activerecord 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1086 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record.rb +195 -0
- data/lib/active_record/aggregations.rb +285 -0
- data/lib/active_record/association_relation.rb +49 -0
- data/lib/active_record/associations.rb +1865 -0
- data/lib/active_record/associations/alias_tracker.rb +81 -0
- data/lib/active_record/associations/association.rb +340 -0
- data/lib/active_record/associations/association_scope.rb +166 -0
- data/lib/active_record/associations/belongs_to_association.rb +124 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
- data/lib/active_record/associations/builder/association.rb +136 -0
- data/lib/active_record/associations/builder/belongs_to.rb +130 -0
- data/lib/active_record/associations/builder/collection_association.rb +72 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
- data/lib/active_record/associations/builder/has_many.rb +19 -0
- data/lib/active_record/associations/builder/has_one.rb +64 -0
- data/lib/active_record/associations/builder/singular_association.rb +44 -0
- data/lib/active_record/associations/collection_association.rb +498 -0
- data/lib/active_record/associations/collection_proxy.rb +1128 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +136 -0
- data/lib/active_record/associations/has_many_through_association.rb +220 -0
- data/lib/active_record/associations/has_one_association.rb +118 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency.rb +262 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +201 -0
- data/lib/active_record/associations/preloader/association.rb +133 -0
- data/lib/active_record/associations/preloader/through_association.rb +116 -0
- data/lib/active_record/associations/singular_association.rb +59 -0
- data/lib/active_record/associations/through_association.rb +121 -0
- data/lib/active_record/attribute_assignment.rb +85 -0
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods.rb +420 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
- data/lib/active_record/attribute_methods/dirty.rb +221 -0
- data/lib/active_record/attribute_methods/primary_key.rb +136 -0
- data/lib/active_record/attribute_methods/query.rb +41 -0
- data/lib/active_record/attribute_methods/read.rb +47 -0
- data/lib/active_record/attribute_methods/serialization.rb +90 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
- data/lib/active_record/attribute_methods/write.rb +61 -0
- data/lib/active_record/attributes.rb +279 -0
- data/lib/active_record/autosave_association.rb +512 -0
- data/lib/active_record/base.rb +328 -0
- data/lib/active_record/callbacks.rb +339 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1175 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +516 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +155 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +772 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +830 -0
- data/lib/active_record/connection_adapters/column.rb +95 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +184 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +953 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +561 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
- data/lib/active_record/connection_handling.rb +274 -0
- data/lib/active_record/core.rb +603 -0
- data/lib/active_record/counter_cache.rb +193 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +122 -0
- data/lib/active_record/enum.rb +274 -0
- data/lib/active_record/errors.rb +388 -0
- data/lib/active_record/explain.rb +50 -0
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +738 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +293 -0
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +207 -0
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +197 -0
- data/lib/active_record/locking/pessimistic.rb +89 -0
- data/lib/active_record/log_subscriber.rb +118 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +1397 -0
- data/lib/active_record/migration/command_recorder.rb +284 -0
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/model_schema.rb +545 -0
- data/lib/active_record/nested_attributes.rb +600 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +967 -0
- data/lib/active_record/query_cache.rb +52 -0
- data/lib/active_record/querying.rb +82 -0
- data/lib/active_record/railtie.rb +263 -0
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +7 -0
- data/lib/active_record/railties/controller_runtime.rb +51 -0
- data/lib/active_record/railties/databases.rake +527 -0
- data/lib/active_record/readonly_attributes.rb +24 -0
- data/lib/active_record/reflection.rb +1042 -0
- data/lib/active_record/relation.rb +860 -0
- data/lib/active_record/relation/batches.rb +290 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/calculations.rb +424 -0
- data/lib/active_record/relation/delegation.rb +130 -0
- data/lib/active_record/relation/finder_methods.rb +561 -0
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +184 -0
- data/lib/active_record/relation/predicate_builder.rb +150 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +1371 -0
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +77 -0
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/result.rb +168 -0
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +214 -0
- data/lib/active_record/schema.rb +61 -0
- data/lib/active_record/schema_dumper.rb +270 -0
- data/lib/active_record/schema_migration.rb +60 -0
- data/lib/active_record/scoping.rb +106 -0
- data/lib/active_record/scoping/default.rb +151 -0
- data/lib/active_record/scoping/named.rb +217 -0
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/statement_cache.rb +148 -0
- data/lib/active_record/store.rb +290 -0
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +506 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +167 -0
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +493 -0
- data/lib/active_record/translation.rb +24 -0
- data/lib/active_record/type.rb +78 -0
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -0
- data/lib/active_record/validations.rb +94 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +60 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +226 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +19 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration.rb +48 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- metadata +418 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Integration
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
##
|
11
|
+
# :singleton-method:
|
12
|
+
# Indicates the format used to generate the timestamp in the cache key, if
|
13
|
+
# versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
|
14
|
+
#
|
15
|
+
# This is +:usec+, by default.
|
16
|
+
class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
|
17
|
+
|
18
|
+
##
|
19
|
+
# :singleton-method:
|
20
|
+
# Indicates whether to use a stable #cache_key method that is accompanied
|
21
|
+
# by a changing version in the #cache_version method.
|
22
|
+
#
|
23
|
+
# This is +true+, by default on Rails 5.2 and above.
|
24
|
+
class_attribute :cache_versioning, instance_writer: false, default: false
|
25
|
+
|
26
|
+
##
|
27
|
+
# :singleton-method:
|
28
|
+
# Indicates whether to use a stable #cache_key method that is accompanied
|
29
|
+
# by a changing version in the #cache_version method on collections.
|
30
|
+
#
|
31
|
+
# This is +false+, by default until Rails 6.1.
|
32
|
+
class_attribute :collection_cache_versioning, instance_writer: false, default: false
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a +String+, which Action Pack uses for constructing a URL to this
|
36
|
+
# object. The default implementation returns this record's id as a +String+,
|
37
|
+
# or +nil+ if this record's unsaved.
|
38
|
+
#
|
39
|
+
# For example, suppose that you have a User model, and that you have a
|
40
|
+
# <tt>resources :users</tt> route. Normally, +user_path+ will
|
41
|
+
# construct a path with the user object's 'id' in it:
|
42
|
+
#
|
43
|
+
# user = User.find_by(name: 'Phusion')
|
44
|
+
# user_path(user) # => "/users/1"
|
45
|
+
#
|
46
|
+
# You can override +to_param+ in your model to make +user_path+ construct
|
47
|
+
# a path using the user's name instead of the user's id:
|
48
|
+
#
|
49
|
+
# class User < ActiveRecord::Base
|
50
|
+
# def to_param # overridden
|
51
|
+
# name
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# user = User.find_by(name: 'Phusion')
|
56
|
+
# user_path(user) # => "/users/Phusion"
|
57
|
+
def to_param
|
58
|
+
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
|
59
|
+
id && id.to_s # Be sure to stringify the id for routes
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a stable cache key that can be used to identify this record.
|
63
|
+
#
|
64
|
+
# Product.new.cache_key # => "products/new"
|
65
|
+
# Product.find(5).cache_key # => "products/5"
|
66
|
+
#
|
67
|
+
# If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
|
68
|
+
# the cache key will also include a version.
|
69
|
+
#
|
70
|
+
# Product.cache_versioning = false
|
71
|
+
# Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
|
72
|
+
def cache_key
|
73
|
+
if new_record?
|
74
|
+
"#{model_name.cache_key}/new"
|
75
|
+
else
|
76
|
+
if cache_version
|
77
|
+
"#{model_name.cache_key}/#{id}"
|
78
|
+
else
|
79
|
+
timestamp = max_updated_column_timestamp
|
80
|
+
|
81
|
+
if timestamp
|
82
|
+
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
83
|
+
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
84
|
+
else
|
85
|
+
"#{model_name.cache_key}/#{id}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a cache version that can be used together with the cache key to form
|
92
|
+
# a recyclable caching scheme. By default, the #updated_at column is used for the
|
93
|
+
# cache_version, but this method can be overwritten to return something else.
|
94
|
+
#
|
95
|
+
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
|
96
|
+
# +false+ (which it is by default until Rails 6.0).
|
97
|
+
def cache_version
|
98
|
+
return unless cache_versioning
|
99
|
+
|
100
|
+
if has_attribute?("updated_at")
|
101
|
+
timestamp = updated_at_before_type_cast
|
102
|
+
if can_use_fast_cache_version?(timestamp)
|
103
|
+
raw_timestamp_to_cache_version(timestamp)
|
104
|
+
elsif timestamp = updated_at
|
105
|
+
timestamp.utc.to_s(cache_timestamp_format)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
if self.class.has_attribute?("updated_at")
|
109
|
+
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns a cache key along with the version.
|
115
|
+
def cache_key_with_version
|
116
|
+
if version = cache_version
|
117
|
+
"#{cache_key}-#{version}"
|
118
|
+
else
|
119
|
+
cache_key
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
module ClassMethods
|
124
|
+
# Defines your model's +to_param+ method to generate "pretty" URLs
|
125
|
+
# using +method_name+, which can be any attribute or method that
|
126
|
+
# responds to +to_s+.
|
127
|
+
#
|
128
|
+
# class User < ActiveRecord::Base
|
129
|
+
# to_param :name
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# user = User.find_by(name: 'Fancy Pants')
|
133
|
+
# user.id # => 123
|
134
|
+
# user_path(user) # => "/users/123-fancy-pants"
|
135
|
+
#
|
136
|
+
# Values longer than 20 characters will be truncated. The value
|
137
|
+
# is truncated word by word.
|
138
|
+
#
|
139
|
+
# user = User.find_by(name: 'David Heinemeier Hansson')
|
140
|
+
# user.id # => 125
|
141
|
+
# user_path(user) # => "/users/125-david-heinemeier"
|
142
|
+
#
|
143
|
+
# Because the generated param begins with the record's +id+, it is
|
144
|
+
# suitable for passing to +find+. In a controller, for example:
|
145
|
+
#
|
146
|
+
# params[:id] # => "123-fancy-pants"
|
147
|
+
# User.find(params[:id]).id # => 123
|
148
|
+
def to_param(method_name = nil)
|
149
|
+
if method_name.nil?
|
150
|
+
super()
|
151
|
+
else
|
152
|
+
define_method :to_param do
|
153
|
+
if (default = super()) &&
|
154
|
+
(result = send(method_name).to_s).present? &&
|
155
|
+
(param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
|
156
|
+
"#{default}-#{param}"
|
157
|
+
else
|
158
|
+
default
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
|
165
|
+
collection.send(:compute_cache_key, timestamp_column)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
# Detects if the value before type cast
|
171
|
+
# can be used to generate a cache_version.
|
172
|
+
#
|
173
|
+
# The fast cache version only works with a
|
174
|
+
# string value directly from the database.
|
175
|
+
#
|
176
|
+
# We also must check if the timestamp format has been changed
|
177
|
+
# or if the timezone is not set to UTC then
|
178
|
+
# we cannot apply our transformations correctly.
|
179
|
+
def can_use_fast_cache_version?(timestamp)
|
180
|
+
timestamp.is_a?(String) &&
|
181
|
+
cache_timestamp_format == :usec &&
|
182
|
+
default_timezone == :utc &&
|
183
|
+
!updated_at_came_from_user?
|
184
|
+
end
|
185
|
+
|
186
|
+
# Converts a raw database string to `:usec`
|
187
|
+
# format.
|
188
|
+
#
|
189
|
+
# Example:
|
190
|
+
#
|
191
|
+
# timestamp = "2018-10-15 20:02:15.266505"
|
192
|
+
# raw_timestamp_to_cache_version(timestamp)
|
193
|
+
# # => "20181015200215266505"
|
194
|
+
#
|
195
|
+
# PostgreSQL truncates trailing zeros,
|
196
|
+
# https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
|
197
|
+
# to account for this we pad the output with zeros
|
198
|
+
def raw_timestamp_to_cache_version(timestamp)
|
199
|
+
key = timestamp.delete("- :.")
|
200
|
+
if key.length < 20
|
201
|
+
key.ljust(20, "0")
|
202
|
+
else
|
203
|
+
key
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/scoping/default"
|
4
|
+
require "active_record/scoping/named"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
# This class is used to create a table that keeps track of values and keys such
|
8
|
+
# as which environment migrations were run in.
|
9
|
+
class InternalMetadata < ActiveRecord::Base # :nodoc:
|
10
|
+
class << self
|
11
|
+
def _internal?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def primary_key
|
16
|
+
"key"
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_name
|
20
|
+
"#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
find_or_initialize_by(key: key).update!(value: value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
where(key: key).pluck(:value).first
|
29
|
+
end
|
30
|
+
|
31
|
+
def table_exists?
|
32
|
+
connection.table_exists?(table_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates an internal metadata table with columns +key+ and +value+
|
36
|
+
def create_table
|
37
|
+
unless table_exists?
|
38
|
+
key_options = connection.internal_string_options_for_primary_key
|
39
|
+
|
40
|
+
connection.create_table(table_name, id: false) do |t|
|
41
|
+
t.string :key, key_options
|
42
|
+
t.string :value
|
43
|
+
t.timestamps
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def drop_table
|
49
|
+
connection.drop_table table_name, if_exists: true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module LegacyYamlAdapter
|
5
|
+
def self.convert(klass, coder)
|
6
|
+
return coder unless coder.is_a?(Psych::Coder)
|
7
|
+
|
8
|
+
case coder["active_record_yaml_version"]
|
9
|
+
when 1, 2 then coder
|
10
|
+
else
|
11
|
+
if coder["attributes"].is_a?(ActiveModel::AttributeSet)
|
12
|
+
Rails420.convert(klass, coder)
|
13
|
+
else
|
14
|
+
Rails41.convert(klass, coder)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Rails420
|
20
|
+
def self.convert(klass, coder)
|
21
|
+
attribute_set = coder["attributes"]
|
22
|
+
|
23
|
+
klass.attribute_names.each do |attr_name|
|
24
|
+
attribute = attribute_set[attr_name]
|
25
|
+
if attribute.type.is_a?(Delegator)
|
26
|
+
type_from_klass = klass.type_for_attribute(attr_name)
|
27
|
+
attribute_set[attr_name] = attribute.with_type(type_from_klass)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
coder
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Rails41
|
36
|
+
def self.convert(klass, coder)
|
37
|
+
attributes = klass.attributes_builder
|
38
|
+
.build_from_database(coder["attributes"])
|
39
|
+
new_record = coder["attributes"][klass.primary_key].blank?
|
40
|
+
|
41
|
+
{
|
42
|
+
"attributes" => attributes,
|
43
|
+
"new_record" => new_record,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
en:
|
2
|
+
# Attributes names common to most models
|
3
|
+
#attributes:
|
4
|
+
#created_at: "Created at"
|
5
|
+
#updated_at: "Updated at"
|
6
|
+
|
7
|
+
# Default error messages
|
8
|
+
errors:
|
9
|
+
messages:
|
10
|
+
required: "must exist"
|
11
|
+
taken: "has already been taken"
|
12
|
+
|
13
|
+
# Active Record models configuration
|
14
|
+
activerecord:
|
15
|
+
errors:
|
16
|
+
messages:
|
17
|
+
record_invalid: "Validation failed: %{errors}"
|
18
|
+
restrict_dependent_destroy:
|
19
|
+
has_one: "Cannot delete record because a dependent %{record} exists"
|
20
|
+
has_many: "Cannot delete record because dependent %{record} exist"
|
21
|
+
# Append your own errors here or at the model/attributes scope.
|
22
|
+
|
23
|
+
# You can define own errors for models or model attributes.
|
24
|
+
# The values :model, :attribute and :value are always available for interpolation.
|
25
|
+
#
|
26
|
+
# For example,
|
27
|
+
# models:
|
28
|
+
# user:
|
29
|
+
# blank: "This is a custom blank message for %{model}: %{attribute}"
|
30
|
+
# attributes:
|
31
|
+
# login:
|
32
|
+
# blank: "This is a custom blank message for User login"
|
33
|
+
# Will define custom blank validation message for User model and
|
34
|
+
# custom blank validation message for login attribute of User model.
|
35
|
+
#models:
|
36
|
+
|
37
|
+
# Translate model names. Used in Model.human_name().
|
38
|
+
#models:
|
39
|
+
# For example,
|
40
|
+
# user: "Dude"
|
41
|
+
# will translate User model name to "Dude"
|
42
|
+
|
43
|
+
# Translate model attribute names. Used in Model.human_attribute_name(attribute).
|
44
|
+
#attributes:
|
45
|
+
# For example,
|
46
|
+
# user:
|
47
|
+
# login: "Handle"
|
48
|
+
# will translate User attribute "login" as "Handle"
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Locking
|
5
|
+
# == What is Optimistic Locking
|
6
|
+
#
|
7
|
+
# Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
|
8
|
+
# conflicts with the data. It does this by checking whether another process has made changes to a record since
|
9
|
+
# it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
|
10
|
+
# and the update is ignored.
|
11
|
+
#
|
12
|
+
# Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
|
13
|
+
#
|
14
|
+
# == Usage
|
15
|
+
#
|
16
|
+
# Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
|
17
|
+
# record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
|
18
|
+
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
|
19
|
+
#
|
20
|
+
# p1 = Person.find(1)
|
21
|
+
# p2 = Person.find(1)
|
22
|
+
#
|
23
|
+
# p1.first_name = "Michael"
|
24
|
+
# p1.save
|
25
|
+
#
|
26
|
+
# p2.first_name = "should fail"
|
27
|
+
# p2.save # Raises an ActiveRecord::StaleObjectError
|
28
|
+
#
|
29
|
+
# Optimistic locking will also check for stale data when objects are destroyed. Example:
|
30
|
+
#
|
31
|
+
# p1 = Person.find(1)
|
32
|
+
# p2 = Person.find(1)
|
33
|
+
#
|
34
|
+
# p1.first_name = "Michael"
|
35
|
+
# p1.save
|
36
|
+
#
|
37
|
+
# p2.destroy # Raises an ActiveRecord::StaleObjectError
|
38
|
+
#
|
39
|
+
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
|
40
|
+
# or otherwise apply the business logic needed to resolve the conflict.
|
41
|
+
#
|
42
|
+
# This locking mechanism will function inside a single Ruby process. To make it work across all
|
43
|
+
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
|
44
|
+
#
|
45
|
+
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
|
46
|
+
# To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
|
47
|
+
#
|
48
|
+
# class Person < ActiveRecord::Base
|
49
|
+
# self.locking_column = :lock_person
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
module Optimistic
|
53
|
+
extend ActiveSupport::Concern
|
54
|
+
|
55
|
+
included do
|
56
|
+
class_attribute :lock_optimistically, instance_writer: false, default: true
|
57
|
+
end
|
58
|
+
|
59
|
+
def locking_enabled? #:nodoc:
|
60
|
+
self.class.locking_enabled?
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def _create_record(attribute_names = self.attribute_names)
|
65
|
+
if locking_enabled?
|
66
|
+
# We always want to persist the locking version, even if we don't detect
|
67
|
+
# a change from the default, since the database might have no default
|
68
|
+
attribute_names |= [self.class.locking_column]
|
69
|
+
end
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
def _touch_row(attribute_names, time)
|
74
|
+
@_touch_attr_names << self.class.locking_column if locking_enabled?
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def _update_row(attribute_names, attempted_action = "update")
|
79
|
+
return super unless locking_enabled?
|
80
|
+
|
81
|
+
begin
|
82
|
+
locking_column = self.class.locking_column
|
83
|
+
previous_lock_value = read_attribute_before_type_cast(locking_column)
|
84
|
+
attribute_names << locking_column
|
85
|
+
|
86
|
+
self[locking_column] += 1
|
87
|
+
|
88
|
+
affected_rows = self.class._update_record(
|
89
|
+
attributes_with_values(attribute_names),
|
90
|
+
@primary_key => id_in_database,
|
91
|
+
locking_column => previous_lock_value
|
92
|
+
)
|
93
|
+
|
94
|
+
if affected_rows != 1
|
95
|
+
raise ActiveRecord::StaleObjectError.new(self, attempted_action)
|
96
|
+
end
|
97
|
+
|
98
|
+
affected_rows
|
99
|
+
|
100
|
+
# If something went wrong, revert the locking_column value.
|
101
|
+
rescue Exception
|
102
|
+
self[locking_column] = previous_lock_value.to_i
|
103
|
+
raise
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def destroy_row
|
108
|
+
return super unless locking_enabled?
|
109
|
+
|
110
|
+
locking_column = self.class.locking_column
|
111
|
+
|
112
|
+
affected_rows = self.class._delete_record(
|
113
|
+
@primary_key => id_in_database,
|
114
|
+
locking_column => read_attribute_before_type_cast(locking_column)
|
115
|
+
)
|
116
|
+
|
117
|
+
if affected_rows != 1
|
118
|
+
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
119
|
+
end
|
120
|
+
|
121
|
+
affected_rows
|
122
|
+
end
|
123
|
+
|
124
|
+
module ClassMethods
|
125
|
+
DEFAULT_LOCKING_COLUMN = "lock_version"
|
126
|
+
|
127
|
+
# Returns true if the +lock_optimistically+ flag is set to true
|
128
|
+
# (which it is, by default) and the table includes the
|
129
|
+
# +locking_column+ column (defaults to +lock_version+).
|
130
|
+
def locking_enabled?
|
131
|
+
lock_optimistically && columns_hash[locking_column]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Set the column to use for optimistic locking. Defaults to +lock_version+.
|
135
|
+
def locking_column=(value)
|
136
|
+
reload_schema_from_cache
|
137
|
+
@locking_column = value.to_s
|
138
|
+
end
|
139
|
+
|
140
|
+
# The version column used for optimistic locking. Defaults to +lock_version+.
|
141
|
+
def locking_column
|
142
|
+
@locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
|
143
|
+
@locking_column
|
144
|
+
end
|
145
|
+
|
146
|
+
# Reset the column used for optimistic locking back to the +lock_version+ default.
|
147
|
+
def reset_locking_column
|
148
|
+
self.locking_column = DEFAULT_LOCKING_COLUMN
|
149
|
+
end
|
150
|
+
|
151
|
+
# Make sure the lock version column gets updated when counters are
|
152
|
+
# updated.
|
153
|
+
def update_counters(id, counters)
|
154
|
+
counters = counters.merge(locking_column => 1) if locking_enabled?
|
155
|
+
super
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
161
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
162
|
+
# sub class being decorated. As such, changes to `lock_optimistically`, or
|
163
|
+
# `locking_column` would not be picked up.
|
164
|
+
def inherited(subclass)
|
165
|
+
subclass.class_eval do
|
166
|
+
is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
|
167
|
+
decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
|
168
|
+
LockingType.new(type)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
super
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# In de/serialize we change `nil` to 0, so that we can allow passing
|
177
|
+
# `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
|
178
|
+
# during update record.
|
179
|
+
class LockingType < DelegateClass(Type::Value) # :nodoc:
|
180
|
+
def deserialize(value)
|
181
|
+
super.to_i
|
182
|
+
end
|
183
|
+
|
184
|
+
def serialize(value)
|
185
|
+
super.to_i
|
186
|
+
end
|
187
|
+
|
188
|
+
def init_with(coder)
|
189
|
+
__setobj__(coder["subtype"])
|
190
|
+
end
|
191
|
+
|
192
|
+
def encode_with(coder)
|
193
|
+
coder["subtype"] = __getobj__
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|