activerecord 5.0.7.2 → 6.0.6.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 +4 -4
- data/CHANGELOG.md +844 -1944
- data/MIT-LICENSE +3 -1
- data/README.rdoc +9 -7
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +18 -14
- data/lib/active_record/associations/alias_tracker.rb +24 -34
- data/lib/active_record/associations/association.rb +113 -55
- data/lib/active_record/associations/association_scope.rb +102 -96
- data/lib/active_record/associations/belongs_to_association.rb +58 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +18 -25
- data/lib/active_record/associations/builder/belongs_to.rb +43 -54
- data/lib/active_record/associations/builder/collection_association.rb +7 -18
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
- data/lib/active_record/associations/builder/has_many.rb +4 -0
- data/lib/active_record/associations/builder/has_one.rb +37 -1
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +93 -254
- data/lib/active_record/associations/collection_proxy.rb +159 -122
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -30
- data/lib/active_record/associations/has_many_through_association.rb +58 -44
- data/lib/active_record/associations/has_one_association.rb +59 -54
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -85
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +152 -177
- data/lib/active_record/associations/preloader/association.rb +101 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/preloader.rb +94 -103
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/attribute_assignment.rb +54 -61
- data/lib/active_record/attribute_decorators.rb +38 -17
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +85 -92
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +20 -49
- data/lib/active_record/attribute_methods/serialization.rb +29 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
- data/lib/active_record/attribute_methods/write.rb +34 -33
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attributes.rb +38 -25
- data/lib/active_record/autosave_association.rb +58 -39
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +64 -35
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +558 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +128 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +233 -147
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
- data/lib/active_record/connection_adapters/abstract_adapter.rb +373 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -562
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +12 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
- data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
- 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 +261 -267
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +143 -40
- data/lib/active_record/core.rb +207 -160
- data/lib/active_record/counter_cache.rb +60 -28
- 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 +78 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +67 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -4
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +13 -8
- 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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +194 -504
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +150 -99
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +16 -19
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +85 -86
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +48 -29
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/migration/command_recorder.rb +134 -100
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +369 -302
- data/lib/active_record/model_schema.rb +160 -127
- data/lib/active_record/nested_attributes.rb +213 -202
- data/lib/active_record/no_touching.rb +12 -3
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +446 -77
- data/lib/active_record/query_cache.rb +13 -12
- data/lib/active_record/querying.rb +37 -24
- data/lib/active_record/railtie.rb +128 -36
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +312 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +214 -254
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/batches.rb +98 -52
- data/lib/active_record/relation/calculations.rb +212 -173
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +207 -247
- data/lib/active_record/relation/from_clause.rb +6 -8
- data/lib/active_record/relation/merger.rb +82 -61
- data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +83 -105
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +488 -332
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -96
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/relation.rb +443 -318
- data/lib/active_record/result.rb +69 -40
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +83 -99
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +71 -69
- data/lib/active_record/schema_migration.rb +16 -6
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +51 -26
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/secure_token.rb +4 -2
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +63 -28
- data/lib/active_record/store.rb +121 -41
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +39 -18
- data/lib/active_record/tasks/database_tasks.rb +271 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
- data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +243 -0
- data/lib/active_record/timestamp.rb +70 -36
- data/lib/active_record/touch_later.rb +8 -6
- data/lib/active_record/transactions.rb +141 -157
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +16 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +23 -18
- data/lib/active_record/type_caster/connection.rb +17 -12
- data/lib/active_record/type_caster/map.rb +5 -4
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +3 -2
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +29 -42
- data/lib/active_record/validations.rb +7 -5
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +37 -22
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -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/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/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +256 -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/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +62 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
- data/lib/rails/generators/active_record/migration.rb +17 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +138 -52
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
- /data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# See ActiveRecord::Transactions::ClassMethods for documentation.
|
3
5
|
module Transactions
|
@@ -11,7 +13,6 @@ module ActiveRecord
|
|
11
13
|
:before_commit_without_transaction_enrollment,
|
12
14
|
:commit_without_transaction_enrollment,
|
13
15
|
:rollback_without_transaction_enrollment,
|
14
|
-
terminator: deprecated_false_terminator,
|
15
16
|
scope: [:kind, :name]
|
16
17
|
end
|
17
18
|
|
@@ -169,7 +170,7 @@ module ActiveRecord
|
|
169
170
|
# writing, the only database that we're aware of that supports true nested
|
170
171
|
# transactions, is MS-SQL. Because of this, Active Record emulates nested
|
171
172
|
# transactions by using savepoints on MySQL and PostgreSQL. See
|
172
|
-
#
|
173
|
+
# https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
|
173
174
|
# for more information about savepoints.
|
174
175
|
#
|
175
176
|
# === \Callbacks
|
@@ -189,8 +190,8 @@ module ActiveRecord
|
|
189
190
|
#
|
190
191
|
# === Caveats
|
191
192
|
#
|
192
|
-
# If you're on MySQL, then do not use DDL operations in nested
|
193
|
-
# blocks that are emulated with savepoints. That is, do not execute statements
|
193
|
+
# If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
|
194
|
+
# transactions blocks that are emulated with savepoints. That is, do not execute statements
|
194
195
|
# like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
|
195
196
|
# releases all savepoints upon executing a DDL operation. When +transaction+
|
196
197
|
# is finished and tries to release the savepoint it created earlier, a
|
@@ -207,8 +208,8 @@ module ActiveRecord
|
|
207
208
|
# Note that "TRUNCATE" is also a MySQL DDL statement!
|
208
209
|
module ClassMethods
|
209
210
|
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
|
210
|
-
def transaction(options
|
211
|
-
connection.transaction(options, &block)
|
211
|
+
def transaction(**options, &block)
|
212
|
+
connection.transaction(**options, &block)
|
212
213
|
end
|
213
214
|
|
214
215
|
def before_commit(*args, &block) # :nodoc:
|
@@ -233,6 +234,12 @@ module ActiveRecord
|
|
233
234
|
set_callback(:commit, :after, *args, &block)
|
234
235
|
end
|
235
236
|
|
237
|
+
# Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
|
238
|
+
def after_save_commit(*args, &block)
|
239
|
+
set_options_for_callbacks!(args, on: [ :create, :update ])
|
240
|
+
set_callback(:commit, :after, *args, &block)
|
241
|
+
end
|
242
|
+
|
236
243
|
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
237
244
|
def after_create_commit(*args, &block)
|
238
245
|
set_options_for_callbacks!(args, on: :create)
|
@@ -274,69 +281,45 @@ module ActiveRecord
|
|
274
281
|
set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
|
275
282
|
end
|
276
283
|
|
277
|
-
def raise_in_transactional_callbacks
|
278
|
-
ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.')
|
279
|
-
true
|
280
|
-
end
|
281
|
-
|
282
|
-
def raise_in_transactional_callbacks=(value)
|
283
|
-
ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks= is deprecated, has no effect and will be removed without replacement.')
|
284
|
-
value
|
285
|
-
end
|
286
|
-
|
287
284
|
private
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
285
|
+
def set_options_for_callbacks!(args, enforced_options = {})
|
286
|
+
options = args.extract_options!.merge!(enforced_options)
|
287
|
+
args << options
|
288
|
+
|
289
|
+
if options[:on]
|
290
|
+
fire_on = Array(options[:on])
|
291
|
+
assert_valid_transaction_action(fire_on)
|
292
|
+
options[:if] = Array(options[:if])
|
293
|
+
options[:if].unshift(-> { transaction_include_any_action?(fire_on) })
|
294
|
+
end
|
298
295
|
end
|
299
|
-
end
|
300
296
|
|
301
|
-
|
302
|
-
|
303
|
-
|
297
|
+
def assert_valid_transaction_action(actions)
|
298
|
+
if (actions - ACTIONS).any?
|
299
|
+
raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
|
300
|
+
end
|
304
301
|
end
|
305
|
-
end
|
306
302
|
end
|
307
303
|
|
308
304
|
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
|
309
305
|
def transaction(options = {}, &block)
|
310
|
-
self.class.transaction(options, &block)
|
306
|
+
self.class.transaction(**options, &block)
|
311
307
|
end
|
312
308
|
|
313
309
|
def destroy #:nodoc:
|
314
310
|
with_transaction_returning_status { super }
|
315
311
|
end
|
316
312
|
|
317
|
-
def save(
|
318
|
-
rollback_active_record_state! do
|
319
|
-
with_transaction_returning_status { super }
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
def save!(*) #:nodoc:
|
313
|
+
def save(*, **) #:nodoc:
|
324
314
|
with_transaction_returning_status { super }
|
325
315
|
end
|
326
316
|
|
327
|
-
def
|
317
|
+
def save!(*, **) #:nodoc:
|
328
318
|
with_transaction_returning_status { super }
|
329
319
|
end
|
330
320
|
|
331
|
-
|
332
|
-
|
333
|
-
remember_transaction_record_state
|
334
|
-
yield
|
335
|
-
rescue Exception
|
336
|
-
restore_transaction_record_state
|
337
|
-
raise
|
338
|
-
ensure
|
339
|
-
clear_transaction_record_state
|
321
|
+
def touch(*, **) #:nodoc:
|
322
|
+
with_transaction_returning_status { super }
|
340
323
|
end
|
341
324
|
|
342
325
|
def before_committed! # :nodoc:
|
@@ -349,12 +332,14 @@ module ActiveRecord
|
|
349
332
|
# Ensure that it is not called if the object was never persisted (failed create),
|
350
333
|
# but call it after the commit of a destroyed object.
|
351
334
|
def committed!(should_run_callbacks: true) #:nodoc:
|
352
|
-
|
335
|
+
force_clear_transaction_record_state
|
336
|
+
if should_run_callbacks
|
337
|
+
@_committed_already_called = true
|
353
338
|
_run_commit_without_transaction_enrollment_callbacks
|
354
339
|
_run_commit_callbacks
|
355
340
|
end
|
356
341
|
ensure
|
357
|
-
|
342
|
+
@_committed_already_called = @_trigger_update_callback = @_trigger_destroy_callback = false
|
358
343
|
end
|
359
344
|
|
360
345
|
# Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
|
@@ -367,18 +352,7 @@ module ActiveRecord
|
|
367
352
|
ensure
|
368
353
|
restore_transaction_record_state(force_restore_state)
|
369
354
|
clear_transaction_record_state
|
370
|
-
|
371
|
-
|
372
|
-
# Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
|
373
|
-
# can be called.
|
374
|
-
def add_to_transaction
|
375
|
-
if has_transactional_callbacks?
|
376
|
-
self.class.connection.add_transaction_record(self)
|
377
|
-
else
|
378
|
-
sync_with_transaction_state
|
379
|
-
set_transaction_state(self.class.connection.transaction_state)
|
380
|
-
end
|
381
|
-
remember_transaction_record_state
|
355
|
+
@_trigger_update_callback = @_trigger_destroy_callback = false if force_restore_state
|
382
356
|
end
|
383
357
|
|
384
358
|
# Executes +method+ within a transaction and captures its return value as a
|
@@ -390,120 +364,130 @@ module ActiveRecord
|
|
390
364
|
def with_transaction_returning_status
|
391
365
|
status = nil
|
392
366
|
self.class.transaction do
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
status = nil
|
367
|
+
if has_transactional_callbacks?
|
368
|
+
add_to_transaction
|
369
|
+
else
|
370
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
371
|
+
@transaction_state = self.class.connection.transaction_state
|
399
372
|
end
|
373
|
+
remember_transaction_record_state
|
400
374
|
|
375
|
+
status = yield
|
401
376
|
raise ActiveRecord::Rollback unless status
|
402
377
|
end
|
403
378
|
status
|
404
|
-
ensure
|
405
|
-
if @transaction_state && @transaction_state.committed?
|
406
|
-
clear_transaction_record_state
|
407
|
-
end
|
408
379
|
end
|
409
380
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
def remember_transaction_record_state #:nodoc:
|
414
|
-
@_start_transaction_state[:id] = id
|
415
|
-
@_start_transaction_state.reverse_merge!(
|
416
|
-
new_record: @new_record,
|
417
|
-
destroyed: @destroyed,
|
418
|
-
frozen?: frozen?,
|
419
|
-
)
|
420
|
-
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
|
381
|
+
def trigger_transactional_callbacks? # :nodoc:
|
382
|
+
(@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
|
383
|
+
_trigger_destroy_callback && destroyed?
|
421
384
|
end
|
422
385
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
386
|
+
private
|
387
|
+
attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
|
388
|
+
|
389
|
+
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
390
|
+
def remember_transaction_record_state
|
391
|
+
@_start_transaction_state ||= {
|
392
|
+
id: id,
|
393
|
+
new_record: @new_record,
|
394
|
+
destroyed: @destroyed,
|
395
|
+
attributes: @attributes,
|
396
|
+
frozen?: frozen?,
|
397
|
+
level: 0
|
398
|
+
}
|
399
|
+
@_start_transaction_state[:level] += 1
|
400
|
+
|
401
|
+
if _committed_already_called
|
402
|
+
@_new_record_before_last_commit = false
|
403
|
+
else
|
404
|
+
@_new_record_before_last_commit = @_start_transaction_state[:new_record]
|
405
|
+
end
|
406
|
+
end
|
428
407
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
408
|
+
# Clear the new record state and id of a record.
|
409
|
+
def clear_transaction_record_state
|
410
|
+
return unless @_start_transaction_state
|
411
|
+
@_start_transaction_state[:level] -= 1
|
412
|
+
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
|
413
|
+
end
|
433
414
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
415
|
+
# Force to clear the transaction record state.
|
416
|
+
def force_clear_transaction_record_state
|
417
|
+
@_start_transaction_state = nil
|
418
|
+
@transaction_state = nil
|
419
|
+
end
|
420
|
+
|
421
|
+
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
422
|
+
def restore_transaction_record_state(force_restore_state = false)
|
423
|
+
if restore_state = @_start_transaction_state
|
424
|
+
if force_restore_state || restore_state[:level] <= 1
|
425
|
+
@new_record = restore_state[:new_record]
|
426
|
+
@destroyed = restore_state[:destroyed]
|
427
|
+
@attributes = restore_state[:attributes].map do |attr|
|
428
|
+
value = @attributes.fetch_value(attr.name)
|
429
|
+
attr = attr.with_value_from_user(value) if attr.value != value
|
430
|
+
attr
|
431
|
+
end
|
432
|
+
@mutations_from_database = nil
|
433
|
+
@mutations_before_last_save = nil
|
434
|
+
if @attributes.fetch_value(@primary_key) != restore_state[:id]
|
435
|
+
@attributes.write_from_user(@primary_key, restore_state[:id])
|
436
|
+
end
|
437
|
+
freeze if restore_state[:frozen?]
|
446
438
|
end
|
447
|
-
freeze if restore_state[:frozen?]
|
448
439
|
end
|
449
440
|
end
|
450
|
-
end
|
451
441
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
when :destroy
|
464
|
-
destroyed?
|
465
|
-
when :update
|
466
|
-
!(transaction_record_state(:new_record) || destroyed?)
|
442
|
+
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
|
443
|
+
def transaction_include_any_action?(actions)
|
444
|
+
actions.any? do |action|
|
445
|
+
case action
|
446
|
+
when :create
|
447
|
+
persisted? && @_new_record_before_last_commit
|
448
|
+
when :update
|
449
|
+
!(@_new_record_before_last_commit || destroyed?) && _trigger_update_callback
|
450
|
+
when :destroy
|
451
|
+
_trigger_destroy_callback
|
452
|
+
end
|
467
453
|
end
|
468
454
|
end
|
469
|
-
end
|
470
455
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
def has_transactional_callbacks? # :nodoc:
|
478
|
-
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
|
479
|
-
end
|
456
|
+
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
457
|
+
# callbacks can be called.
|
458
|
+
def add_to_transaction
|
459
|
+
self.class.connection.add_transaction_record(self)
|
460
|
+
end
|
480
461
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
#
|
485
|
-
# The +@transaction_state+ variable stores the states of the associated
|
486
|
-
# transaction. This relies on the fact that a transaction can only be in
|
487
|
-
# one rollback or commit (otherwise a list of states would be required)
|
488
|
-
# Each Active Record object inside of a transaction carries that transaction's
|
489
|
-
# TransactionState.
|
490
|
-
#
|
491
|
-
# This method checks to see if the ActiveRecord object's state reflects
|
492
|
-
# the TransactionState, and rolls back or commits the Active Record object
|
493
|
-
# as appropriate.
|
494
|
-
#
|
495
|
-
# Since Active Record objects can be inside multiple transactions, this
|
496
|
-
# method recursively goes through the parent of the TransactionState and
|
497
|
-
# checks if the Active Record object reflects the state of the object.
|
498
|
-
def sync_with_transaction_state
|
499
|
-
update_attributes_from_transaction_state(@transaction_state)
|
500
|
-
end
|
462
|
+
def has_transactional_callbacks?
|
463
|
+
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
|
464
|
+
end
|
501
465
|
|
502
|
-
|
503
|
-
if
|
504
|
-
|
505
|
-
|
466
|
+
# Updates the attributes on this particular Active Record object so that
|
467
|
+
# if it's associated with a transaction, then the state of the Active Record
|
468
|
+
# object will be updated to reflect the current state of the transaction.
|
469
|
+
#
|
470
|
+
# The <tt>@transaction_state</tt> variable stores the states of the associated
|
471
|
+
# transaction. This relies on the fact that a transaction can only be in
|
472
|
+
# one rollback or commit (otherwise a list of states would be required).
|
473
|
+
# Each Active Record object inside of a transaction carries that transaction's
|
474
|
+
# TransactionState.
|
475
|
+
#
|
476
|
+
# This method checks to see if the ActiveRecord object's state reflects
|
477
|
+
# the TransactionState, and rolls back or commits the Active Record object
|
478
|
+
# as appropriate.
|
479
|
+
def sync_with_transaction_state
|
480
|
+
if transaction_state = @transaction_state
|
481
|
+
if transaction_state.fully_committed?
|
482
|
+
force_clear_transaction_record_state
|
483
|
+
elsif transaction_state.committed?
|
484
|
+
clear_transaction_record_state
|
485
|
+
elsif transaction_state.rolledback?
|
486
|
+
force_restore_state = transaction_state.fully_rolledback?
|
487
|
+
restore_transaction_record_state(force_restore_state)
|
488
|
+
clear_transaction_record_state
|
489
|
+
end
|
490
|
+
end
|
506
491
|
end
|
507
|
-
end
|
508
492
|
end
|
509
493
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Translation
|
3
5
|
include ActiveModel::Translation
|
@@ -8,7 +10,7 @@ module ActiveRecord
|
|
8
10
|
classes = [klass]
|
9
11
|
return classes if klass == ActiveRecord::Base
|
10
12
|
|
11
|
-
while klass
|
13
|
+
while !klass.base_class?
|
12
14
|
classes << klass = klass.superclass
|
13
15
|
end
|
14
16
|
classes
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/type/registry"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
# :stopdoc:
|
@@ -9,16 +11,15 @@ module ActiveRecord
|
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
14
|
+
def registration_klass
|
15
|
+
Registration
|
16
|
+
end
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
registrations
|
19
|
-
.select { |registration| registration.matches?(symbol, *args) }
|
20
|
-
.max
|
21
|
-
end
|
18
|
+
def find_registration(symbol, *args, **kwargs)
|
19
|
+
registrations
|
20
|
+
.select { |registration| registration.matches?(symbol, *args, **kwargs) }
|
21
|
+
.max
|
22
|
+
end
|
22
23
|
end
|
23
24
|
|
24
25
|
class Registration
|
@@ -51,43 +52,41 @@ module ActiveRecord
|
|
51
52
|
end
|
52
53
|
|
53
54
|
protected
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
attr_reader :name, :block, :adapter, :override
|
56
|
+
|
57
|
+
def priority
|
58
|
+
result = 0
|
59
|
+
if adapter
|
60
|
+
result |= 1
|
61
|
+
end
|
62
|
+
if override
|
63
|
+
result |= 2
|
64
|
+
end
|
65
|
+
result
|
64
66
|
end
|
65
|
-
result
|
66
|
-
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
def priority_except_adapter
|
69
|
+
priority & 0b111111100
|
70
|
+
end
|
71
71
|
|
72
72
|
private
|
73
|
+
def matches_adapter?(adapter: nil, **)
|
74
|
+
(self.adapter.nil? || adapter == self.adapter)
|
75
|
+
end
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def conflicts_with?(other)
|
79
|
-
same_priority_except_adapter?(other) &&
|
80
|
-
has_adapter_conflict?(other)
|
81
|
-
end
|
77
|
+
def conflicts_with?(other)
|
78
|
+
same_priority_except_adapter?(other) &&
|
79
|
+
has_adapter_conflict?(other)
|
80
|
+
end
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
def same_priority_except_adapter?(other)
|
83
|
+
priority_except_adapter == other.priority_except_adapter
|
84
|
+
end
|
86
85
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
def has_adapter_conflict?(other)
|
87
|
+
(override.nil? && other.adapter) ||
|
88
|
+
(adapter && other.override.nil?)
|
89
|
+
end
|
91
90
|
end
|
92
91
|
|
93
92
|
class DecorationRegistration < Registration
|
@@ -110,17 +109,14 @@ module ActiveRecord
|
|
110
109
|
super | 4
|
111
110
|
end
|
112
111
|
|
113
|
-
protected
|
114
|
-
|
115
|
-
attr_reader :options, :klass
|
116
|
-
|
117
112
|
private
|
113
|
+
attr_reader :options, :klass
|
118
114
|
|
119
|
-
|
120
|
-
|
121
|
-
|
115
|
+
def matches_options?(**kwargs)
|
116
|
+
options.all? do |key, value|
|
117
|
+
kwargs[key] == value
|
118
|
+
end
|
122
119
|
end
|
123
|
-
end
|
124
120
|
end
|
125
121
|
end
|
126
122
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Type
|
5
|
+
class DecimalWithoutScale < ActiveModel::Type::BigInteger # :nodoc:
|
6
|
+
def type
|
7
|
+
:decimal
|
8
|
+
end
|
9
|
+
|
10
|
+
def type_cast_for_schema(value)
|
11
|
+
value.to_s.inspect
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Type
|
3
5
|
class HashLookupTypeMap < TypeMap # :nodoc:
|
@@ -14,10 +16,9 @@ module ActiveRecord
|
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
19
|
+
def perform_fetch(type, *args, &block)
|
20
|
+
@mapping.fetch(type, block).call(type, *args)
|
21
|
+
end
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Type
|
5
|
+
class Json < ActiveModel::Type::Value
|
6
|
+
include ActiveModel::Type::Helpers::Mutable
|
7
|
+
|
8
|
+
def type
|
9
|
+
:json
|
10
|
+
end
|
11
|
+
|
12
|
+
def deserialize(value)
|
13
|
+
return value unless value.is_a?(::String)
|
14
|
+
ActiveSupport::JSON.decode(value) rescue nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
ActiveSupport::JSON.encode(value) unless value.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def changed_in_place?(raw_old_value, new_value)
|
22
|
+
deserialize(raw_old_value) != new_value
|
23
|
+
end
|
24
|
+
|
25
|
+
def accessor
|
26
|
+
ActiveRecord::Store::StringKeyedHashAccessor
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|