omg-activerecord 8.0.0.alpha1
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 +7 -0
- data/CHANGELOG.md +355 -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/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -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/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -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 +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -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/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +121 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +70 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +177 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- 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/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- 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/join_table.rb +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -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 +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -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 +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -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/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -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 +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -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 +46 -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/nodes.rb +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Assertions
|
|
5
|
+
module QueryAssertions
|
|
6
|
+
# Asserts that the number of SQL queries executed in the given block matches the expected count.
|
|
7
|
+
#
|
|
8
|
+
# # Check for exact number of queries
|
|
9
|
+
# assert_queries_count(1) { Post.first }
|
|
10
|
+
#
|
|
11
|
+
# # Check for any number of queries
|
|
12
|
+
# assert_queries_count { Post.first }
|
|
13
|
+
#
|
|
14
|
+
# If the +:include_schema+ option is provided, any queries (including schema related) are counted.
|
|
15
|
+
#
|
|
16
|
+
# assert_queries_count(1, include_schema: true) { Post.columns }
|
|
17
|
+
#
|
|
18
|
+
def assert_queries_count(count = nil, include_schema: false, &block)
|
|
19
|
+
ActiveRecord::Base.lease_connection.materialize_transactions
|
|
20
|
+
|
|
21
|
+
counter = SQLCounter.new
|
|
22
|
+
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
|
23
|
+
result = _assert_nothing_raised_or_warn("assert_queries_count", &block)
|
|
24
|
+
queries = include_schema ? counter.log_all : counter.log
|
|
25
|
+
if count
|
|
26
|
+
assert_equal count, queries.size, "#{queries.size} instead of #{count} queries were executed. Queries: #{queries.join("\n\n")}"
|
|
27
|
+
else
|
|
28
|
+
assert_operator queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
|
29
|
+
end
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Asserts that no SQL queries are executed in the given block.
|
|
35
|
+
#
|
|
36
|
+
# assert_no_queries { post.comments }
|
|
37
|
+
#
|
|
38
|
+
# If the +:include_schema+ option is provided, any queries (including schema related) are counted.
|
|
39
|
+
#
|
|
40
|
+
# assert_no_queries(include_schema: true) { Post.columns }
|
|
41
|
+
#
|
|
42
|
+
def assert_no_queries(include_schema: false, &block)
|
|
43
|
+
assert_queries_count(0, include_schema: include_schema, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Asserts that the SQL queries executed in the given block match expected pattern.
|
|
47
|
+
#
|
|
48
|
+
# # Check for exact number of queries
|
|
49
|
+
# assert_queries_match(/LIMIT \?/, count: 1) { Post.first }
|
|
50
|
+
#
|
|
51
|
+
# # Check for any number of queries
|
|
52
|
+
# assert_queries_match(/LIMIT \?/) { Post.first }
|
|
53
|
+
#
|
|
54
|
+
# If the +:include_schema+ option is provided, any queries (including schema related)
|
|
55
|
+
# that match the matcher are considered.
|
|
56
|
+
#
|
|
57
|
+
# assert_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
|
|
58
|
+
#
|
|
59
|
+
def assert_queries_match(match, count: nil, include_schema: false, &block)
|
|
60
|
+
ActiveRecord::Base.lease_connection.materialize_transactions
|
|
61
|
+
|
|
62
|
+
counter = SQLCounter.new
|
|
63
|
+
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
|
64
|
+
result = _assert_nothing_raised_or_warn("assert_queries_match", &block)
|
|
65
|
+
queries = include_schema ? counter.log_all : counter.log
|
|
66
|
+
matched_queries = queries.select { |query| match === query }
|
|
67
|
+
|
|
68
|
+
if count
|
|
69
|
+
assert_equal count, matched_queries.size, "#{matched_queries.size} instead of #{count} queries were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
|
70
|
+
else
|
|
71
|
+
assert_operator matched_queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
result
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Asserts that no SQL queries matching the pattern are executed in the given block.
|
|
79
|
+
#
|
|
80
|
+
# assert_no_queries_match(/SELECT/i) { post.comments }
|
|
81
|
+
#
|
|
82
|
+
# If the +:include_schema+ option is provided, any queries (including schema related)
|
|
83
|
+
# that match the matcher are counted.
|
|
84
|
+
#
|
|
85
|
+
# assert_no_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
|
|
86
|
+
#
|
|
87
|
+
def assert_no_queries_match(match, include_schema: false, &block)
|
|
88
|
+
assert_queries_match(match, count: 0, include_schema: include_schema, &block)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class SQLCounter # :nodoc:
|
|
92
|
+
attr_reader :log_full, :log_all
|
|
93
|
+
|
|
94
|
+
def initialize
|
|
95
|
+
@log_full = []
|
|
96
|
+
@log_all = []
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def log
|
|
100
|
+
@log_full.map(&:first)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def call(*, payload)
|
|
104
|
+
return if payload[:cached]
|
|
105
|
+
|
|
106
|
+
sql = payload[:sql]
|
|
107
|
+
@log_all << sql
|
|
108
|
+
|
|
109
|
+
unless payload[:name] == "SCHEMA"
|
|
110
|
+
bound_values = (payload[:binds] || []).map do |value|
|
|
111
|
+
value = value.value_for_database if value.respond_to?(:value_for_database)
|
|
112
|
+
value
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
@log_full << [sql, bound_values]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# = Active Record \Timestamp
|
|
5
|
+
#
|
|
6
|
+
# Active Record automatically timestamps create and update operations if the
|
|
7
|
+
# table has fields named <tt>created_at/created_on</tt> or
|
|
8
|
+
# <tt>updated_at/updated_on</tt>.
|
|
9
|
+
#
|
|
10
|
+
# Timestamping can be turned off by setting:
|
|
11
|
+
#
|
|
12
|
+
# config.active_record.record_timestamps = false
|
|
13
|
+
#
|
|
14
|
+
# Timestamps are in UTC by default but you can use the local timezone by setting:
|
|
15
|
+
#
|
|
16
|
+
# config.active_record.default_timezone = :local
|
|
17
|
+
#
|
|
18
|
+
# == Time Zone aware attributes
|
|
19
|
+
#
|
|
20
|
+
# Active Record keeps all the <tt>datetime</tt> and <tt>time</tt> columns
|
|
21
|
+
# timezone aware. By default, these values are stored in the database as UTC
|
|
22
|
+
# and converted back to the current <tt>Time.zone</tt> when pulled from the database.
|
|
23
|
+
#
|
|
24
|
+
# This feature can be turned off completely by setting:
|
|
25
|
+
#
|
|
26
|
+
# config.active_record.time_zone_aware_attributes = false
|
|
27
|
+
#
|
|
28
|
+
# You can also specify that only <tt>datetime</tt> columns should be time-zone
|
|
29
|
+
# aware (while <tt>time</tt> should not) by setting:
|
|
30
|
+
#
|
|
31
|
+
# ActiveRecord::Base.time_zone_aware_types = [:datetime]
|
|
32
|
+
#
|
|
33
|
+
# You can also add database-specific timezone aware types. For example, for PostgreSQL:
|
|
34
|
+
#
|
|
35
|
+
# ActiveRecord::Base.time_zone_aware_types += [:tsrange, :tstzrange]
|
|
36
|
+
#
|
|
37
|
+
# Finally, you can indicate specific attributes of a model for which time zone
|
|
38
|
+
# conversion should not applied, for instance by setting:
|
|
39
|
+
#
|
|
40
|
+
# class Topic < ActiveRecord::Base
|
|
41
|
+
# self.skip_time_zone_conversion_for_attributes = [:written_on]
|
|
42
|
+
# end
|
|
43
|
+
module Timestamp
|
|
44
|
+
extend ActiveSupport::Concern
|
|
45
|
+
|
|
46
|
+
included do
|
|
47
|
+
class_attribute :record_timestamps, default: true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def initialize_dup(other) # :nodoc:
|
|
51
|
+
super
|
|
52
|
+
clear_timestamp_attributes
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module ClassMethods # :nodoc:
|
|
56
|
+
def touch_attributes_with_time(*names, time: nil)
|
|
57
|
+
names = names.map(&:to_s)
|
|
58
|
+
names = names.map { |name| attribute_aliases[name] || name }
|
|
59
|
+
attribute_names = timestamp_attributes_for_update_in_model
|
|
60
|
+
attribute_names |= names
|
|
61
|
+
attribute_names.index_with(time || current_time_from_proper_timezone)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def timestamp_attributes_for_create_in_model
|
|
65
|
+
@timestamp_attributes_for_create_in_model ||=
|
|
66
|
+
(timestamp_attributes_for_create & column_names).freeze
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def timestamp_attributes_for_update_in_model
|
|
70
|
+
@timestamp_attributes_for_update_in_model ||=
|
|
71
|
+
(timestamp_attributes_for_update & column_names).freeze
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def all_timestamp_attributes_in_model
|
|
75
|
+
@all_timestamp_attributes_in_model ||=
|
|
76
|
+
(timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def current_time_from_proper_timezone
|
|
80
|
+
with_connection { |c| c.default_timezone == :utc ? Time.now.utc : Time.now }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
protected
|
|
84
|
+
def reload_schema_from_cache(recursive = true)
|
|
85
|
+
@timestamp_attributes_for_create_in_model = nil
|
|
86
|
+
@timestamp_attributes_for_update_in_model = nil
|
|
87
|
+
@all_timestamp_attributes_in_model = nil
|
|
88
|
+
super
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
def timestamp_attributes_for_create
|
|
93
|
+
["created_at", "created_on"].map! { |name| attribute_aliases[name] || name }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def timestamp_attributes_for_update
|
|
97
|
+
["updated_at", "updated_on"].map! { |name| attribute_aliases[name] || name }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
def init_internals
|
|
103
|
+
super
|
|
104
|
+
@_touch_record = nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def _create_record
|
|
108
|
+
if record_timestamps
|
|
109
|
+
current_time = current_time_from_proper_timezone
|
|
110
|
+
|
|
111
|
+
all_timestamp_attributes_in_model.each do |column|
|
|
112
|
+
_write_attribute(column, current_time) unless _read_attribute(column)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
super
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def _update_record
|
|
120
|
+
record_update_timestamps
|
|
121
|
+
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def create_or_update(touch: true, **)
|
|
126
|
+
@_touch_record = touch
|
|
127
|
+
super
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def record_update_timestamps
|
|
131
|
+
if @_touch_record && should_record_timestamps?
|
|
132
|
+
current_time = current_time_from_proper_timezone
|
|
133
|
+
|
|
134
|
+
timestamp_attributes_for_update_in_model.each do |column|
|
|
135
|
+
next if will_save_change_to_attribute?(column)
|
|
136
|
+
_write_attribute(column, current_time)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
yield if block_given?
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def should_record_timestamps?
|
|
144
|
+
record_timestamps && (!partial_updates? || has_changes_to_save?)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def timestamp_attributes_for_create_in_model
|
|
148
|
+
self.class.timestamp_attributes_for_create_in_model
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def timestamp_attributes_for_update_in_model
|
|
152
|
+
self.class.timestamp_attributes_for_update_in_model
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def all_timestamp_attributes_in_model
|
|
156
|
+
self.class.all_timestamp_attributes_in_model
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def current_time_from_proper_timezone
|
|
160
|
+
self.class.current_time_from_proper_timezone
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def max_updated_column_timestamp
|
|
164
|
+
timestamp_attributes_for_update_in_model
|
|
165
|
+
.filter_map { |attr| (v = self[attr]) && (v.is_a?(::Time) ? v : v.to_time) }
|
|
166
|
+
.max
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Clear attributes and changed_attributes
|
|
170
|
+
def clear_timestamp_attributes
|
|
171
|
+
all_timestamp_attributes_in_model.each do |attribute_name|
|
|
172
|
+
self[attribute_name] = nil
|
|
173
|
+
clear_attribute_change(attribute_name)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/object/json"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module TokenFor
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
class_attribute :token_definitions, instance_accessor: false, instance_predicate: false, default: {}
|
|
11
|
+
class_attribute :generated_token_verifier, instance_accessor: false, instance_predicate: false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
TokenDefinition = Struct.new(:defining_class, :purpose, :expires_in, :block) do # :nodoc:
|
|
15
|
+
def full_purpose
|
|
16
|
+
@full_purpose ||= [defining_class.name, purpose, expires_in].join("\n")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def message_verifier
|
|
20
|
+
defining_class.generated_token_verifier
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def payload_for(model)
|
|
24
|
+
block ? [model.id, model.instance_eval(&block).as_json] : [model.id]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generate_token(model)
|
|
28
|
+
message_verifier.generate(payload_for(model), expires_in: expires_in, purpose: full_purpose)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resolve_token(token)
|
|
32
|
+
payload = message_verifier.verified(token, purpose: full_purpose)
|
|
33
|
+
model = yield(payload[0]) if payload
|
|
34
|
+
model if model && payload_for(model) == payload
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module RelationMethods
|
|
39
|
+
# Finds a record using a given +token+ for a predefined +purpose+. Returns
|
|
40
|
+
# +nil+ if the token is invalid or the record was not found.
|
|
41
|
+
def find_by_token_for(purpose, token)
|
|
42
|
+
raise UnknownPrimaryKey.new(self) unless model.primary_key
|
|
43
|
+
model.token_definitions.fetch(purpose).resolve_token(token) { |id| find_by(model.primary_key => [id]) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Finds a record using a given +token+ for a predefined +purpose+. Raises
|
|
47
|
+
# ActiveSupport::MessageVerifier::InvalidSignature if the token is invalid
|
|
48
|
+
# (e.g. expired, bad format, etc). Raises ActiveRecord::RecordNotFound if
|
|
49
|
+
# the token is valid but the record was not found.
|
|
50
|
+
def find_by_token_for!(purpose, token)
|
|
51
|
+
model.token_definitions.fetch(purpose).resolve_token(token) { |id| find(id) } ||
|
|
52
|
+
(raise ActiveSupport::MessageVerifier::InvalidSignature)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
module ClassMethods
|
|
57
|
+
# Defines the behavior of tokens generated for a specific +purpose+.
|
|
58
|
+
# A token can be generated by calling TokenFor#generate_token_for on a
|
|
59
|
+
# record. Later, that record can be fetched by calling #find_by_token_for
|
|
60
|
+
# (or #find_by_token_for!) with the same purpose and token.
|
|
61
|
+
#
|
|
62
|
+
# Tokens are signed so that they are tamper-proof. Thus they can be
|
|
63
|
+
# exposed to outside world as, for example, password reset tokens.
|
|
64
|
+
#
|
|
65
|
+
# By default, tokens do not expire. They can be configured to expire by
|
|
66
|
+
# specifying a duration via the +expires_in+ option. The duration becomes
|
|
67
|
+
# part of the token's signature, so changing the value of +expires_in+
|
|
68
|
+
# will automatically invalidate previously generated tokens.
|
|
69
|
+
#
|
|
70
|
+
# A block may also be specified. When generating a token with
|
|
71
|
+
# TokenFor#generate_token_for, the block will be evaluated in the context
|
|
72
|
+
# of the record, and its return value will be embedded in the token as
|
|
73
|
+
# JSON. Later, when fetching the record with #find_by_token_for, the block
|
|
74
|
+
# will be evaluated again in the context of the fetched record. If the two
|
|
75
|
+
# JSON values do not match, the token will be treated as invalid. Note
|
|
76
|
+
# that the value returned by the block <b>should not contain sensitive
|
|
77
|
+
# information</b> because it will be embedded in the token as
|
|
78
|
+
# <b>human-readable plaintext JSON</b>.
|
|
79
|
+
#
|
|
80
|
+
# ==== Examples
|
|
81
|
+
#
|
|
82
|
+
# class User < ActiveRecord::Base
|
|
83
|
+
# has_secure_password
|
|
84
|
+
#
|
|
85
|
+
# generates_token_for :password_reset, expires_in: 15.minutes do
|
|
86
|
+
# # Last 10 characters of password salt, which changes when password is updated:
|
|
87
|
+
# password_salt&.last(10)
|
|
88
|
+
# end
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# user = User.first
|
|
92
|
+
#
|
|
93
|
+
# token = user.generate_token_for(:password_reset)
|
|
94
|
+
# User.find_by_token_for(:password_reset, token) # => user
|
|
95
|
+
# # 16 minutes later...
|
|
96
|
+
# User.find_by_token_for(:password_reset, token) # => nil
|
|
97
|
+
#
|
|
98
|
+
# token = user.generate_token_for(:password_reset)
|
|
99
|
+
# User.find_by_token_for(:password_reset, token) # => user
|
|
100
|
+
# user.update!(password: "new password")
|
|
101
|
+
# User.find_by_token_for(:password_reset, token) # => nil
|
|
102
|
+
def generates_token_for(purpose, expires_in: nil, &block)
|
|
103
|
+
self.token_definitions = token_definitions.merge(purpose => TokenDefinition.new(self, purpose, expires_in, block))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def find_by_token_for(purpose, token) # :nodoc:
|
|
107
|
+
all.find_by_token_for(purpose, token)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def find_by_token_for!(purpose, token) # :nodoc:
|
|
111
|
+
all.find_by_token_for!(purpose, token)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Generates a token for a predefined +purpose+.
|
|
116
|
+
#
|
|
117
|
+
# Use ClassMethods#generates_token_for to define a token purpose and
|
|
118
|
+
# behavior.
|
|
119
|
+
def generate_token_for(purpose)
|
|
120
|
+
self.class.token_definitions.fetch(purpose).generate_token(self)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# = Active Record Touch Later
|
|
5
|
+
module TouchLater # :nodoc:
|
|
6
|
+
def before_committed!
|
|
7
|
+
touch_deferred_attributes if has_defer_touch_attrs? && persisted?
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def touch_later(*names) # :nodoc:
|
|
12
|
+
_raise_record_not_touched_error unless persisted?
|
|
13
|
+
|
|
14
|
+
@_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
|
|
15
|
+
@_defer_touch_attrs |= names.map! do |name|
|
|
16
|
+
name = name.to_s
|
|
17
|
+
self.class.attribute_aliases[name] || name
|
|
18
|
+
end unless names.empty?
|
|
19
|
+
|
|
20
|
+
@_touch_time = current_time_from_proper_timezone
|
|
21
|
+
|
|
22
|
+
surreptitiously_touch @_defer_touch_attrs
|
|
23
|
+
add_to_transaction
|
|
24
|
+
@_new_record_before_last_commit ||= false
|
|
25
|
+
|
|
26
|
+
# touch the parents as we are not calling the after_save callbacks
|
|
27
|
+
self.class.reflect_on_all_associations.each do |r|
|
|
28
|
+
if touch = r.options[:touch]
|
|
29
|
+
if r.macro == :belongs_to
|
|
30
|
+
ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch)
|
|
31
|
+
elsif r.macro == :has_one
|
|
32
|
+
ActiveRecord::Associations::Builder::HasOne.touch_record(self, r.name, touch)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def touch(*names, time: nil) # :nodoc:
|
|
39
|
+
if has_defer_touch_attrs?
|
|
40
|
+
names |= @_defer_touch_attrs
|
|
41
|
+
super(*names, time: time)
|
|
42
|
+
@_defer_touch_attrs, @_touch_time = nil, nil
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def init_internals
|
|
50
|
+
super
|
|
51
|
+
@_defer_touch_attrs = nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def surreptitiously_touch(attr_names)
|
|
55
|
+
attr_names.each do |attr_name|
|
|
56
|
+
_write_attribute(attr_name, @_touch_time)
|
|
57
|
+
clear_attribute_change(attr_name)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def touch_deferred_attributes
|
|
62
|
+
@_skip_dirty_tracking = true
|
|
63
|
+
touch(time: @_touch_time)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def has_defer_touch_attrs?
|
|
67
|
+
@_defer_touch_attrs.present?
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/digest"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
# Class specifies the interface to interact with the current transaction state.
|
|
7
|
+
#
|
|
8
|
+
# It can either map to an actual transaction/savepoint, or represent the
|
|
9
|
+
# absence of a transaction.
|
|
10
|
+
#
|
|
11
|
+
# == State
|
|
12
|
+
#
|
|
13
|
+
# We say that a transaction is _finalized_ when it wraps a real transaction
|
|
14
|
+
# that has been either committed or rolled back.
|
|
15
|
+
#
|
|
16
|
+
# A transaction is _open_ if it wraps a real transaction that is not finalized.
|
|
17
|
+
#
|
|
18
|
+
# On the other hand, a transaction is _closed_ when it is not open. That is,
|
|
19
|
+
# when it represents absence of transaction, or it wraps a real but finalized
|
|
20
|
+
# one.
|
|
21
|
+
#
|
|
22
|
+
# You can check whether a transaction is open or closed with the +open?+ and
|
|
23
|
+
# +closed?+ predicates:
|
|
24
|
+
#
|
|
25
|
+
# if Article.current_transaction.open?
|
|
26
|
+
# # We are inside a real and not finalized transaction.
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Closed transactions are `blank?` too.
|
|
30
|
+
#
|
|
31
|
+
# == Callbacks
|
|
32
|
+
#
|
|
33
|
+
# After updating the database state, you may sometimes need to perform some extra work, or reflect these
|
|
34
|
+
# changes in a remote system like clearing or updating a cache:
|
|
35
|
+
#
|
|
36
|
+
# def publish_article(article)
|
|
37
|
+
# article.update!(published: true)
|
|
38
|
+
# NotificationService.article_published(article)
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# The above code works but has one important flaw, which is that it no longer works properly if called inside
|
|
42
|
+
# a transaction, as it will interact with the remote system before the changes are persisted:
|
|
43
|
+
#
|
|
44
|
+
# Article.transaction do
|
|
45
|
+
# article = create_article(article)
|
|
46
|
+
# publish_article(article)
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
# The callbacks offered by ActiveRecord::Transaction allow to rewriting this method in a way that is compatible
|
|
50
|
+
# with transactions:
|
|
51
|
+
#
|
|
52
|
+
# def publish_article(article)
|
|
53
|
+
# article.update!(published: true)
|
|
54
|
+
# Article.current_transaction.after_commit do
|
|
55
|
+
# NotificationService.article_published(article)
|
|
56
|
+
# end
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# In the above example, if +publish_article+ is called inside a transaction, the callback will be invoked
|
|
60
|
+
# after the transaction is successfully committed, and if called outside a transaction, the callback will be invoked
|
|
61
|
+
# immediately.
|
|
62
|
+
#
|
|
63
|
+
# == Caveats
|
|
64
|
+
#
|
|
65
|
+
# When using after_commit callbacks, it is important to note that if the callback raises an error, the transaction
|
|
66
|
+
# won't be rolled back as it was already committed. Relying solely on these to synchronize state between multiple
|
|
67
|
+
# systems may lead to consistency issues.
|
|
68
|
+
class Transaction
|
|
69
|
+
def initialize(internal_transaction) # :nodoc:
|
|
70
|
+
@internal_transaction = internal_transaction
|
|
71
|
+
@uuid = nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Registers a block to be called after the transaction is fully committed.
|
|
75
|
+
#
|
|
76
|
+
# If there is no currently open transactions, the block is called
|
|
77
|
+
# immediately, unless the transaction is finalized, in which case attempting
|
|
78
|
+
# to register the callback raises ActiveRecord::ActiveRecordError.
|
|
79
|
+
#
|
|
80
|
+
# If the transaction has a parent transaction, the callback is transferred to
|
|
81
|
+
# the parent when the current transaction commits, or dropped when the current transaction
|
|
82
|
+
# is rolled back. This operation is repeated until the outermost transaction is reached.
|
|
83
|
+
#
|
|
84
|
+
# If the callback raises an error, the transaction remains committed.
|
|
85
|
+
def after_commit(&block)
|
|
86
|
+
if @internal_transaction.nil?
|
|
87
|
+
yield
|
|
88
|
+
else
|
|
89
|
+
@internal_transaction.after_commit(&block)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Registers a block to be called after the transaction is rolled back.
|
|
94
|
+
#
|
|
95
|
+
# If there is no currently open transactions, the block is not called. But
|
|
96
|
+
# if the transaction is finalized, attempting to register the callback
|
|
97
|
+
# raises ActiveRecord::ActiveRecordError.
|
|
98
|
+
#
|
|
99
|
+
# If the transaction is successfully committed but has a parent
|
|
100
|
+
# transaction, the callback is automatically added to the parent transaction.
|
|
101
|
+
#
|
|
102
|
+
# If the entire chain of nested transactions are all successfully committed,
|
|
103
|
+
# the block is never called.
|
|
104
|
+
#
|
|
105
|
+
# If the transaction is already finalized, attempting to register a callback
|
|
106
|
+
# will raise ActiveRecord::ActiveRecordError.
|
|
107
|
+
def after_rollback(&block)
|
|
108
|
+
@internal_transaction&.after_rollback(&block)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns true if the transaction exists and isn't finalized yet.
|
|
112
|
+
def open?
|
|
113
|
+
!closed?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Returns true if the transaction doesn't exist or is finalized.
|
|
117
|
+
def closed?
|
|
118
|
+
@internal_transaction.nil? || @internal_transaction.state.finalized?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
alias_method :blank?, :closed?
|
|
122
|
+
|
|
123
|
+
# Returns a UUID for this transaction or +nil+ if no transaction is open.
|
|
124
|
+
def uuid
|
|
125
|
+
if @internal_transaction
|
|
126
|
+
@uuid ||= Digest::UUID.uuid_v4
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
NULL_TRANSACTION = new(nil).freeze
|
|
131
|
+
end
|
|
132
|
+
end
|