activerecord 3.2.22.5 → 5.2.8
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 +5 -5
- data/CHANGELOG.md +657 -621
- data/MIT-LICENSE +2 -2
- data/README.rdoc +41 -46
- data/examples/performance.rb +55 -42
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +264 -236
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +127 -75
- data/lib/active_record/associations/association_scope.rb +126 -92
- data/lib/active_record/associations/belongs_to_association.rb +78 -27
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
- data/lib/active_record/associations/builder/association.rb +117 -32
- data/lib/active_record/associations/builder/belongs_to.rb +135 -60
- data/lib/active_record/associations/builder/collection_association.rb +61 -54
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
- data/lib/active_record/associations/builder/has_many.rb +10 -64
- data/lib/active_record/associations/builder/has_one.rb +19 -51
- data/lib/active_record/associations/builder/singular_association.rb +28 -18
- data/lib/active_record/associations/collection_association.rb +226 -293
- data/lib/active_record/associations/collection_proxy.rb +1067 -69
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +83 -47
- data/lib/active_record/associations/has_many_through_association.rb +98 -65
- data/lib/active_record/associations/has_one_association.rb +57 -20
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +212 -164
- data/lib/active_record/associations/preloader/association.rb +95 -89
- data/lib/active_record/associations/preloader/through_association.rb +84 -44
- data/lib/active_record/associations/preloader.rb +123 -111
- data/lib/active_record/associations/singular_association.rb +33 -24
- data/lib/active_record/associations/through_association.rb +60 -26
- data/lib/active_record/associations.rb +1759 -1506
- data/lib/active_record/attribute_assignment.rb +60 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
- data/lib/active_record/attribute_methods/dirty.rb +113 -74
- data/lib/active_record/attribute_methods/primary_key.rb +106 -77
- data/lib/active_record/attribute_methods/query.rb +8 -5
- data/lib/active_record/attribute_methods/read.rb +63 -114
- data/lib/active_record/attribute_methods/serialization.rb +60 -90
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
- data/lib/active_record/attribute_methods/write.rb +43 -45
- data/lib/active_record/attribute_methods.rb +366 -149
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +312 -225
- data/lib/active_record/base.rb +114 -505
- data/lib/active_record/callbacks.rb +145 -67
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
- data/lib/active_record/connection_adapters/column.rb +50 -255
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -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 +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
- data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +200 -105
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -69
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +245 -60
- data/lib/active_record/explain.rb +35 -71
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -9
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +418 -275
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +209 -100
- data/lib/active_record/integration.rb +116 -21
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +107 -94
- data/lib/active_record/locking/pessimistic.rb +20 -8
- data/lib/active_record/log_subscriber.rb +99 -34
- data/lib/active_record/migration/command_recorder.rb +199 -64
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +893 -296
- data/lib/active_record/model_schema.rb +328 -175
- data/lib/active_record/nested_attributes.rb +338 -242
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +557 -170
- data/lib/active_record/query_cache.rb +14 -43
- data/lib/active_record/querying.rb +36 -24
- data/lib/active_record/railtie.rb +147 -52
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +13 -6
- data/lib/active_record/railties/databases.rake +206 -488
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +734 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +249 -52
- data/lib/active_record/relation/calculations.rb +330 -284
- data/lib/active_record/relation/delegation.rb +135 -37
- data/lib/active_record/relation/finder_methods.rb +450 -287
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +132 -43
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1037 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +48 -151
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +451 -359
- data/lib/active_record/result.rb +129 -20
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +164 -136
- data/lib/active_record/schema.rb +31 -19
- data/lib/active_record/schema_dumper.rb +154 -107
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping/default.rb +108 -98
- data/lib/active_record/scoping/named.rb +125 -112
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +175 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +80 -41
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +240 -119
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +79 -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 +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +133 -75
- data/lib/active_record/validations.rb +53 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +89 -57
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/migration.rb +28 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +141 -62
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,36 +1,27 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require 'active_support/core_ext/array/wrap'
|
12
|
-
require 'active_support/core_ext/object/blank'
|
13
|
-
require 'active_support/core_ext/logger'
|
14
|
-
require 'active_support/ordered_hash'
|
15
|
-
require 'active_record/fixtures/file'
|
3
|
+
require "erb"
|
4
|
+
require "yaml"
|
5
|
+
require "zlib"
|
6
|
+
require "set"
|
7
|
+
require "active_support/dependencies"
|
8
|
+
require "active_support/core_ext/digest/uuid"
|
9
|
+
require "active_record/fixture_set/file"
|
10
|
+
require "active_record/errors"
|
16
11
|
|
17
|
-
|
12
|
+
module ActiveRecord
|
18
13
|
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
|
19
14
|
end
|
20
|
-
else
|
21
|
-
class FixtureClassNotFound < StandardError #:nodoc:
|
22
|
-
end
|
23
|
-
end
|
24
15
|
|
25
|
-
module ActiveRecord
|
26
16
|
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
|
27
17
|
#
|
28
18
|
# They are stored in YAML files, one file per model, which are placed in the directory
|
29
19
|
# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
|
30
20
|
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
|
31
|
-
# The fixture file ends with the
|
32
|
-
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
|
33
|
-
#
|
21
|
+
# The fixture file ends with the +.yml+ file extension, for example:
|
22
|
+
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
|
23
|
+
#
|
24
|
+
# The format of a fixture file looks like this:
|
34
25
|
#
|
35
26
|
# rubyonrails:
|
36
27
|
# id: 1
|
@@ -46,7 +37,7 @@ module ActiveRecord
|
|
46
37
|
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
|
47
38
|
# separated by a blank line for your viewing pleasure.
|
48
39
|
#
|
49
|
-
# Note
|
40
|
+
# Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
|
50
41
|
# See http://yaml.org/type/omap.html
|
51
42
|
# for the specification. You will need ordered fixtures when you have foreign key constraints
|
52
43
|
# on keys in the same table. This is commonly needed for tree structures. Example:
|
@@ -74,20 +65,39 @@ module ActiveRecord
|
|
74
65
|
# end
|
75
66
|
# end
|
76
67
|
#
|
77
|
-
# By default,
|
78
|
-
# so this test will succeed.
|
68
|
+
# By default, +test_helper.rb+ will load all of your fixtures into your test
|
69
|
+
# database, so this test will succeed.
|
79
70
|
#
|
80
|
-
# The testing environment will automatically load the
|
71
|
+
# The testing environment will automatically load all the fixtures into the database before each
|
81
72
|
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
|
82
73
|
#
|
83
74
|
# In addition to being available in the database, the fixture's data may also be accessed by
|
84
|
-
# using a special dynamic method, which has the same name as the model
|
85
|
-
# name of the fixture to instantiate:
|
75
|
+
# using a special dynamic method, which has the same name as the model.
|
86
76
|
#
|
87
|
-
#
|
77
|
+
# Passing in a fixture name to this dynamic method returns the fixture matching this name:
|
78
|
+
#
|
79
|
+
# test "find one" do
|
88
80
|
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
|
89
81
|
# end
|
90
82
|
#
|
83
|
+
# Passing in multiple fixture names returns all fixtures matching these names:
|
84
|
+
#
|
85
|
+
# test "find all by name" do
|
86
|
+
# assert_equal 2, web_sites(:rubyonrails, :google).length
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# Passing in no arguments returns all fixtures:
|
90
|
+
#
|
91
|
+
# test "find all" do
|
92
|
+
# assert_equal 2, web_sites.length
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# Passing in any fixture name that does not exist will raise <tt>StandardError</tt>:
|
96
|
+
#
|
97
|
+
# test "find by name that does not exist" do
|
98
|
+
# assert_raise(StandardError) { web_sites(:reddit) }
|
99
|
+
# end
|
100
|
+
#
|
91
101
|
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
|
92
102
|
# following tests:
|
93
103
|
#
|
@@ -96,11 +106,11 @@ module ActiveRecord
|
|
96
106
|
# end
|
97
107
|
#
|
98
108
|
# test "find_alt_method_2" do
|
99
|
-
# assert_equal "Ruby on Rails", @rubyonrails.
|
109
|
+
# assert_equal "Ruby on Rails", @rubyonrails.name
|
100
110
|
# end
|
101
111
|
#
|
102
|
-
# In order to use these methods to access fixtured data within your
|
103
|
-
# following in your
|
112
|
+
# In order to use these methods to access fixtured data within your test cases, you must specify one of the
|
113
|
+
# following in your ActiveSupport::TestCase-derived class:
|
104
114
|
#
|
105
115
|
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
|
106
116
|
# self.use_instantiated_fixtures = true
|
@@ -114,14 +124,14 @@ module ActiveRecord
|
|
114
124
|
#
|
115
125
|
# = Dynamic fixtures with ERB
|
116
126
|
#
|
117
|
-
#
|
127
|
+
# Sometimes you don't care about the content of the fixtures as much as you care about the volume.
|
118
128
|
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
|
119
129
|
# testing, like:
|
120
130
|
#
|
121
131
|
# <% 1.upto(1000) do |i| %>
|
122
132
|
# fix_<%= i %>:
|
123
133
|
# id: <%= i %>
|
124
|
-
# name: guy_<%=
|
134
|
+
# name: guy_<%= i %>
|
125
135
|
# <% end %>
|
126
136
|
#
|
127
137
|
# This will create 1000 very simple fixtures.
|
@@ -133,34 +143,51 @@ module ActiveRecord
|
|
133
143
|
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
|
134
144
|
# in fixtures are to be considered a code smell.
|
135
145
|
#
|
136
|
-
#
|
146
|
+
# Helper methods defined in a fixture will not be available in other fixtures, to prevent against
|
147
|
+
# unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
|
148
|
+
# that is included in ActiveRecord::FixtureSet.context_class.
|
149
|
+
#
|
150
|
+
# - define a helper method in <tt>test_helper.rb</tt>
|
151
|
+
# module FixtureFileHelpers
|
152
|
+
# def file_sha(path)
|
153
|
+
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
154
|
+
# end
|
155
|
+
# end
|
156
|
+
# ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
|
157
|
+
#
|
158
|
+
# - use the helper method in a fixture
|
159
|
+
# photo:
|
160
|
+
# name: kitten.png
|
161
|
+
# sha: <%= file_sha 'files/kitten.png' %>
|
162
|
+
#
|
163
|
+
# = Transactional Tests
|
137
164
|
#
|
138
165
|
# Test cases can use begin+rollback to isolate their changes to the database instead of having to
|
139
166
|
# delete+insert for every test case.
|
140
167
|
#
|
141
168
|
# class FooTest < ActiveSupport::TestCase
|
142
|
-
# self.
|
169
|
+
# self.use_transactional_tests = true
|
143
170
|
#
|
144
171
|
# test "godzilla" do
|
145
|
-
#
|
172
|
+
# assert_not_empty Foo.all
|
146
173
|
# Foo.destroy_all
|
147
|
-
#
|
174
|
+
# assert_empty Foo.all
|
148
175
|
# end
|
149
176
|
#
|
150
177
|
# test "godzilla aftermath" do
|
151
|
-
#
|
178
|
+
# assert_not_empty Foo.all
|
152
179
|
# end
|
153
180
|
# end
|
154
181
|
#
|
155
182
|
# If you preload your test database with all fixture data (probably in the rake task) and use
|
156
|
-
# transactional
|
183
|
+
# transactional tests, then you may omit all fixtures declarations in your test cases since
|
157
184
|
# all the data's already there and every case rolls back its changes.
|
158
185
|
#
|
159
186
|
# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
|
160
187
|
# true. This will provide access to fixture data for every table that has been loaded through
|
161
188
|
# fixtures (depending on the value of +use_instantiated_fixtures+).
|
162
189
|
#
|
163
|
-
# When *not* to use transactional
|
190
|
+
# When *not* to use transactional tests:
|
164
191
|
#
|
165
192
|
# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
|
166
193
|
# all parent transactions commit, particularly, the fixtures transaction which is begun in setup
|
@@ -176,6 +203,9 @@ module ActiveRecord
|
|
176
203
|
# * Stable, autogenerated IDs
|
177
204
|
# * Label references for associations (belongs_to, has_one, has_many)
|
178
205
|
# * HABTM associations as inline lists
|
206
|
+
#
|
207
|
+
# There are some more advanced features available even if the id is specified:
|
208
|
+
#
|
179
209
|
# * Autofilled timestamp columns
|
180
210
|
# * Fixture label interpolation
|
181
211
|
# * Support for YAML defaults
|
@@ -262,7 +292,7 @@ module ActiveRecord
|
|
262
292
|
#
|
263
293
|
# ### in fruit.rb
|
264
294
|
#
|
265
|
-
# belongs_to :eater, :
|
295
|
+
# belongs_to :eater, polymorphic: true
|
266
296
|
#
|
267
297
|
# ### in fruits.yml
|
268
298
|
#
|
@@ -358,41 +388,68 @@ module ActiveRecord
|
|
358
388
|
# geeksomnia:
|
359
389
|
# name: Geeksomnia's Account
|
360
390
|
# subdomain: $LABEL
|
391
|
+
# email: $LABEL@email.com
|
361
392
|
#
|
362
393
|
# Also, sometimes (like when porting older join table fixtures) you'll need
|
363
394
|
# to be able to get a hold of the identifier for a given label. ERB
|
364
395
|
# to the rescue:
|
365
396
|
#
|
366
397
|
# george_reginald:
|
367
|
-
# monkey_id: <%= ActiveRecord::
|
368
|
-
# pirate_id: <%= ActiveRecord::
|
398
|
+
# monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
|
399
|
+
# pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
|
369
400
|
#
|
370
401
|
# == Support for YAML defaults
|
371
402
|
#
|
372
|
-
# You
|
373
|
-
#
|
403
|
+
# You can set and reuse defaults in your fixtures YAML file.
|
404
|
+
# This is the same technique used in the +database.yml+ file to specify
|
405
|
+
# defaults:
|
374
406
|
#
|
375
407
|
# DEFAULTS: &DEFAULTS
|
376
408
|
# created_on: <%= 3.weeks.ago.to_s(:db) %>
|
377
409
|
#
|
378
410
|
# first:
|
379
411
|
# name: Smurf
|
380
|
-
# *DEFAULTS
|
412
|
+
# <<: *DEFAULTS
|
381
413
|
#
|
382
414
|
# second:
|
383
415
|
# name: Fraggle
|
384
|
-
# *DEFAULTS
|
416
|
+
# <<: *DEFAULTS
|
385
417
|
#
|
386
418
|
# Any fixture labeled "DEFAULTS" is safely ignored.
|
387
|
-
|
388
|
-
|
419
|
+
#
|
420
|
+
# == Configure the fixture model class
|
421
|
+
#
|
422
|
+
# It's possible to set the fixture's model class directly in the YAML file.
|
423
|
+
# This is helpful when fixtures are loaded outside tests and
|
424
|
+
# +set_fixture_class+ is not available (e.g.
|
425
|
+
# when running <tt>rails db:fixtures:load</tt>).
|
426
|
+
#
|
427
|
+
# _fixture:
|
428
|
+
# model_class: User
|
429
|
+
# david:
|
430
|
+
# name: David
|
431
|
+
#
|
432
|
+
# Any fixtures labeled "_fixture" are safely ignored.
|
433
|
+
class FixtureSet
|
434
|
+
#--
|
435
|
+
# An instance of FixtureSet is normally stored in a single YAML file and
|
436
|
+
# possibly in a folder with the same name.
|
437
|
+
#++
|
438
|
+
|
439
|
+
MAX_ID = 2**30 - 1
|
440
|
+
|
441
|
+
@@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
|
389
442
|
|
390
|
-
|
443
|
+
def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
|
444
|
+
config.pluralize_table_names ?
|
445
|
+
fixture_set_name.singularize.camelize :
|
446
|
+
fixture_set_name.camelize
|
447
|
+
end
|
391
448
|
|
392
|
-
def self.
|
393
|
-
|
394
|
-
|
395
|
-
|
449
|
+
def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
|
450
|
+
"#{ config.table_name_prefix }"\
|
451
|
+
"#{ fixture_set_name.tr('/', '_') }"\
|
452
|
+
"#{ config.table_name_suffix }".to_sym
|
396
453
|
end
|
397
454
|
|
398
455
|
def self.reset_cache
|
@@ -419,11 +476,7 @@ module ActiveRecord
|
|
419
476
|
cache_for_connection(connection).update(fixtures_map)
|
420
477
|
end
|
421
478
|
|
422
|
-
|
423
|
-
# TODO:NOTE: in the next version, the __with_new_arity suffix and
|
424
|
-
# the method with the old arity will be removed.
|
425
|
-
#++
|
426
|
-
def self.instantiate_fixtures__with_new_arity(object, fixture_set, load_instances = true) # :nodoc:
|
479
|
+
def self.instantiate_fixtures(object, fixture_set, load_instances = true)
|
427
480
|
if load_instances
|
428
481
|
fixture_set.each do |fixture_name, fixture|
|
429
482
|
begin
|
@@ -435,124 +488,136 @@ module ActiveRecord
|
|
435
488
|
end
|
436
489
|
end
|
437
490
|
|
438
|
-
# The use with parameters <tt>(object, fixture_set_name, fixture_set, load_instances = true)</tt> is deprecated, +fixture_set_name+ parameter is not used.
|
439
|
-
# Use as:
|
440
|
-
#
|
441
|
-
# instantiate_fixtures(object, fixture_set, load_instances = true)
|
442
|
-
def self.instantiate_fixtures(object, fixture_set, load_instances = true, rails_3_2_compatibility_argument = true)
|
443
|
-
unless load_instances == true || load_instances == false
|
444
|
-
ActiveSupport::Deprecation.warn(
|
445
|
-
"ActiveRecord::Fixtures.instantiate_fixtures with parameters (object, fixture_set_name, fixture_set, load_instances = true) is deprecated and shall be removed from future releases. Use it with parameters (object, fixture_set, load_instances = true) instead (skip fixture_set_name).",
|
446
|
-
caller)
|
447
|
-
fixture_set = load_instances
|
448
|
-
load_instances = rails_3_2_compatibility_argument
|
449
|
-
end
|
450
|
-
instantiate_fixtures__with_new_arity(object, fixture_set, load_instances)
|
451
|
-
end
|
452
|
-
|
453
491
|
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
|
454
492
|
all_loaded_fixtures.each_value do |fixture_set|
|
455
|
-
|
493
|
+
instantiate_fixtures(object, fixture_set, load_instances)
|
456
494
|
end
|
457
495
|
end
|
458
496
|
|
459
|
-
cattr_accessor :all_loaded_fixtures
|
460
|
-
self.all_loaded_fixtures = {}
|
497
|
+
cattr_accessor :all_loaded_fixtures, default: {}
|
461
498
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
}
|
499
|
+
class ClassCache
|
500
|
+
def initialize(class_names, config)
|
501
|
+
@class_names = class_names.stringify_keys
|
502
|
+
@config = config
|
467
503
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
files_to_read = table_names.reject { |table_name|
|
472
|
-
fixture_is_cached?(connection, table_name)
|
473
|
-
}
|
504
|
+
# Remove string values that aren't constants or subclasses of AR
|
505
|
+
@class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
|
506
|
+
end
|
474
507
|
|
475
|
-
|
476
|
-
|
477
|
-
|
508
|
+
def [](fs_name)
|
509
|
+
@class_names.fetch(fs_name) {
|
510
|
+
klass = default_fixture_model(fs_name, @config).safe_constantize
|
511
|
+
insert_class(@class_names, fs_name, klass)
|
512
|
+
}
|
513
|
+
end
|
478
514
|
|
479
|
-
|
480
|
-
table_name = path.tr '/', '_'
|
515
|
+
private
|
481
516
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
517
|
+
def insert_class(class_names, name, klass)
|
518
|
+
# We only want to deal with AR objects.
|
519
|
+
if klass && klass < ActiveRecord::Base
|
520
|
+
class_names[name] = klass
|
521
|
+
else
|
522
|
+
class_names[name] = nil
|
487
523
|
end
|
524
|
+
end
|
488
525
|
|
489
|
-
|
526
|
+
def default_fixture_model(fs_name, config)
|
527
|
+
ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
|
528
|
+
end
|
529
|
+
end
|
490
530
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
table_rows = ff.table_rows
|
531
|
+
def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
|
532
|
+
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
533
|
+
class_names = ClassCache.new class_names, config
|
495
534
|
|
496
|
-
|
497
|
-
|
498
|
-
end
|
535
|
+
# FIXME: Apparently JK uses this.
|
536
|
+
connection = block_given? ? yield : ActiveRecord::Base.connection
|
499
537
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
end
|
504
|
-
end
|
505
|
-
end
|
538
|
+
files_to_read = fixture_set_names.reject { |fs_name|
|
539
|
+
fixture_is_cached?(connection, fs_name)
|
540
|
+
}
|
506
541
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
542
|
+
unless files_to_read.empty?
|
543
|
+
fixtures_map = {}
|
544
|
+
|
545
|
+
fixture_sets = files_to_read.map do |fs_name|
|
546
|
+
klass = class_names[fs_name]
|
547
|
+
conn = klass ? klass.connection : connection
|
548
|
+
fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
|
549
|
+
conn,
|
550
|
+
fs_name,
|
551
|
+
klass,
|
552
|
+
::File.join(fixtures_directory, fs_name))
|
553
|
+
end
|
554
|
+
|
555
|
+
update_all_loaded_fixtures fixtures_map
|
556
|
+
fixture_sets_by_connection = fixture_sets.group_by { |fs| fs.model_class ? fs.model_class.connection : connection }
|
557
|
+
|
558
|
+
fixture_sets_by_connection.each do |conn, set|
|
559
|
+
table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
|
560
|
+
|
561
|
+
set.each do |fs|
|
562
|
+
fs.table_rows.each do |table, rows|
|
563
|
+
table_rows_for_connection[table].unshift(*rows)
|
512
564
|
end
|
513
565
|
end
|
566
|
+
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
514
567
|
|
515
|
-
|
568
|
+
# Cap primary key sequences to max(pk).
|
569
|
+
if conn.respond_to?(:reset_pk_sequence!)
|
570
|
+
set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
|
571
|
+
end
|
516
572
|
end
|
573
|
+
|
574
|
+
cache_fixtures(connection, fixtures_map)
|
517
575
|
end
|
518
|
-
cached_fixtures(connection,
|
576
|
+
cached_fixtures(connection, fixture_set_names)
|
519
577
|
end
|
520
578
|
|
521
579
|
# Returns a consistent, platform-independent identifier for +label+.
|
522
|
-
#
|
523
|
-
def self.identify(label)
|
524
|
-
|
580
|
+
# Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
|
581
|
+
def self.identify(label, column_type = :integer)
|
582
|
+
if column_type == :uuid
|
583
|
+
Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
|
584
|
+
else
|
585
|
+
Zlib.crc32(label.to_s) % MAX_ID
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# Superclass for the evaluation contexts used by ERB fixtures.
|
590
|
+
def self.context_class
|
591
|
+
@context_class ||= Class.new
|
525
592
|
end
|
526
593
|
|
527
|
-
|
594
|
+
def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
|
595
|
+
all_loaded_fixtures.update(fixtures_map)
|
596
|
+
end
|
528
597
|
|
529
|
-
|
530
|
-
@connection = connection
|
531
|
-
@table_name = table_name
|
532
|
-
@fixture_path = fixture_path
|
533
|
-
@name = table_name # preserve fixture base name
|
534
|
-
@class_name = class_name
|
598
|
+
attr_reader :table_name, :name, :fixtures, :model_class, :config
|
535
599
|
|
536
|
-
|
537
|
-
@
|
600
|
+
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
|
601
|
+
@name = name
|
602
|
+
@path = path
|
603
|
+
@config = config
|
538
604
|
|
539
|
-
|
540
|
-
if class_name.is_a?(Class)
|
541
|
-
@table_name = class_name.table_name
|
542
|
-
@connection = class_name.connection
|
543
|
-
@model_class = class_name
|
544
|
-
else
|
545
|
-
@model_class = class_name.constantize rescue nil
|
546
|
-
end
|
605
|
+
self.model_class = class_name
|
547
606
|
|
548
|
-
read_fixture_files
|
607
|
+
@fixtures = read_fixture_files(path)
|
608
|
+
|
609
|
+
@connection = connection
|
610
|
+
|
611
|
+
@table_name = (model_class.respond_to?(:table_name) ?
|
612
|
+
model_class.table_name :
|
613
|
+
self.class.default_fixture_table_name(name, config))
|
549
614
|
end
|
550
615
|
|
551
616
|
def [](x)
|
552
617
|
fixtures[x]
|
553
618
|
end
|
554
619
|
|
555
|
-
def []=(k,v)
|
620
|
+
def []=(k, v)
|
556
621
|
fixtures[k] = v
|
557
622
|
end
|
558
623
|
|
@@ -564,37 +629,43 @@ module ActiveRecord
|
|
564
629
|
fixtures.size
|
565
630
|
end
|
566
631
|
|
567
|
-
#
|
632
|
+
# Returns a hash of rows to be inserted. The key is the table, the value is
|
568
633
|
# a list of rows to insert to that table.
|
569
634
|
def table_rows
|
570
|
-
now =
|
571
|
-
now = now.to_s(:db)
|
635
|
+
now = config.default_timezone == :utc ? Time.now.utc : Time.now
|
572
636
|
|
573
637
|
# allow a standard key to be used for doing defaults in YAML
|
574
|
-
fixtures.delete(
|
638
|
+
fixtures.delete("DEFAULTS")
|
575
639
|
|
576
640
|
# track any join tables we need to insert later
|
577
|
-
rows = Hash.new { |h,table| h[table] = [] }
|
641
|
+
rows = Hash.new { |h, table| h[table] = [] }
|
578
642
|
|
579
643
|
rows[table_name] = fixtures.map do |label, fixture|
|
580
644
|
row = fixture.to_hash
|
581
645
|
|
582
|
-
if model_class
|
646
|
+
if model_class
|
583
647
|
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
|
584
648
|
if model_class.record_timestamps
|
585
|
-
timestamp_column_names.each do |
|
586
|
-
row[
|
649
|
+
timestamp_column_names.each do |c_name|
|
650
|
+
row[c_name] = now unless row.key?(c_name)
|
587
651
|
end
|
588
652
|
end
|
589
653
|
|
590
654
|
# interpolate the fixture label
|
591
655
|
row.each do |key, value|
|
592
|
-
row[key] = label if value.is_a?(String)
|
656
|
+
row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
|
593
657
|
end
|
594
658
|
|
595
659
|
# generate a primary key if necessary
|
596
660
|
if has_primary_key_column? && !row.include?(primary_key_name)
|
597
|
-
row[primary_key_name] = ActiveRecord::
|
661
|
+
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Resolve enums
|
665
|
+
model_class.defined_enums.each do |name, values|
|
666
|
+
if row.include?(name)
|
667
|
+
row[name] = values.fetch(row[name], row[name])
|
668
|
+
end
|
598
669
|
end
|
599
670
|
|
600
671
|
# If STI is used, find the correct subclass for association reflection
|
@@ -605,28 +676,24 @@ module ActiveRecord
|
|
605
676
|
model_class
|
606
677
|
end
|
607
678
|
|
608
|
-
reflection_class.
|
679
|
+
reflection_class._reflections.each_value do |association|
|
609
680
|
case association.macro
|
610
681
|
when :belongs_to
|
611
682
|
# Do not replace association name with association foreign key if they are named the same
|
612
683
|
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
613
684
|
|
614
685
|
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
|
615
|
-
if association.
|
686
|
+
if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
|
616
687
|
# support polymorphic belongs_to as "label (Type)"
|
617
688
|
row[association.foreign_type] = $1
|
618
689
|
end
|
619
690
|
|
620
|
-
|
691
|
+
fk_type = reflection_class.type_for_attribute(fk_name).type
|
692
|
+
row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
|
621
693
|
end
|
622
|
-
when :
|
623
|
-
if
|
624
|
-
|
625
|
-
table_name = association.options[:join_table]
|
626
|
-
rows[table_name].concat targets.map { |target|
|
627
|
-
{ association.foreign_key => row[primary_key_name],
|
628
|
-
association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
|
629
|
-
}
|
694
|
+
when :has_many
|
695
|
+
if association.options[:through]
|
696
|
+
add_join_records(rows, row, HasManyThroughProxy.new(association))
|
630
697
|
end
|
631
698
|
end
|
632
699
|
end
|
@@ -637,11 +704,63 @@ module ActiveRecord
|
|
637
704
|
rows
|
638
705
|
end
|
639
706
|
|
707
|
+
class ReflectionProxy # :nodoc:
|
708
|
+
def initialize(association)
|
709
|
+
@association = association
|
710
|
+
end
|
711
|
+
|
712
|
+
def join_table
|
713
|
+
@association.join_table
|
714
|
+
end
|
715
|
+
|
716
|
+
def name
|
717
|
+
@association.name
|
718
|
+
end
|
719
|
+
|
720
|
+
def primary_key_type
|
721
|
+
@association.klass.type_for_attribute(@association.klass.primary_key).type
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
class HasManyThroughProxy < ReflectionProxy # :nodoc:
|
726
|
+
def rhs_key
|
727
|
+
@association.foreign_key
|
728
|
+
end
|
729
|
+
|
730
|
+
def lhs_key
|
731
|
+
@association.through_reflection.foreign_key
|
732
|
+
end
|
733
|
+
|
734
|
+
def join_table
|
735
|
+
@association.through_reflection.table_name
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
640
739
|
private
|
641
740
|
def primary_key_name
|
642
741
|
@primary_key_name ||= model_class && model_class.primary_key
|
643
742
|
end
|
644
743
|
|
744
|
+
def primary_key_type
|
745
|
+
@primary_key_type ||= model_class && model_class.type_for_attribute(model_class.primary_key).type
|
746
|
+
end
|
747
|
+
|
748
|
+
def add_join_records(rows, row, association)
|
749
|
+
# This is the case when the join table has no fixtures file
|
750
|
+
if (targets = row.delete(association.name.to_s))
|
751
|
+
table_name = association.join_table
|
752
|
+
column_type = association.primary_key_type
|
753
|
+
lhs_key = association.lhs_key
|
754
|
+
rhs_key = association.rhs_key
|
755
|
+
|
756
|
+
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
757
|
+
rows[table_name].concat targets.map { |target|
|
758
|
+
{ lhs_key => row[primary_key_name],
|
759
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
760
|
+
}
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
645
764
|
def has_primary_key_column?
|
646
765
|
@has_primary_key_column ||= primary_key_name &&
|
647
766
|
model_class.columns.any? { |c| c.name == primary_key_name }
|
@@ -657,27 +776,38 @@ module ActiveRecord
|
|
657
776
|
end
|
658
777
|
|
659
778
|
def column_names
|
660
|
-
@column_names ||= @connection.columns(@table_name).collect
|
779
|
+
@column_names ||= @connection.columns(@table_name).collect(&:name)
|
661
780
|
end
|
662
781
|
|
663
|
-
def
|
664
|
-
|
782
|
+
def model_class=(class_name)
|
783
|
+
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
|
784
|
+
@model_class = class_name
|
785
|
+
else
|
786
|
+
@model_class = class_name.safe_constantize if class_name
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
# Loads the fixtures from the YAML file at +path+.
|
791
|
+
# If the file sets the +model_class+ and current instance value is not set,
|
792
|
+
# it uses the file value.
|
793
|
+
def read_fixture_files(path)
|
794
|
+
yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
|
665
795
|
::File.file?(f)
|
666
|
-
} + [yaml_file_path]
|
796
|
+
} + [yaml_file_path(path)]
|
667
797
|
|
668
|
-
yaml_files.
|
669
|
-
|
670
|
-
fh.
|
671
|
-
|
798
|
+
yaml_files.each_with_object({}) do |file, fixtures|
|
799
|
+
FixtureSet::File.open(file) do |fh|
|
800
|
+
self.model_class ||= fh.model_class if fh.model_class
|
801
|
+
fh.each do |fixture_name, row|
|
802
|
+
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
|
672
803
|
end
|
673
804
|
end
|
674
805
|
end
|
675
806
|
end
|
676
807
|
|
677
|
-
def yaml_file_path
|
678
|
-
"#{
|
808
|
+
def yaml_file_path(path)
|
809
|
+
"#{path}.yml"
|
679
810
|
end
|
680
|
-
|
681
811
|
end
|
682
812
|
|
683
813
|
class Fixture #:nodoc:
|
@@ -712,7 +842,9 @@ module ActiveRecord
|
|
712
842
|
|
713
843
|
def find
|
714
844
|
if model_class
|
715
|
-
model_class.
|
845
|
+
model_class.unscoped do
|
846
|
+
model_class.find(fixture[model_class.primary_key])
|
847
|
+
end
|
716
848
|
else
|
717
849
|
raise FixtureClassNotFound, "No class attached to find."
|
718
850
|
end
|
@@ -724,91 +856,79 @@ module ActiveRecord
|
|
724
856
|
module TestFixtures
|
725
857
|
extend ActiveSupport::Concern
|
726
858
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
class_attribute :fixture_path
|
732
|
-
class_attribute :fixture_table_names
|
733
|
-
class_attribute :fixture_class_names
|
734
|
-
class_attribute :use_transactional_fixtures
|
735
|
-
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
|
736
|
-
class_attribute :pre_loaded_fixtures
|
859
|
+
def before_setup # :nodoc:
|
860
|
+
setup_fixtures
|
861
|
+
super
|
862
|
+
end
|
737
863
|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
864
|
+
def after_teardown # :nodoc:
|
865
|
+
super
|
866
|
+
teardown_fixtures
|
867
|
+
end
|
742
868
|
|
743
|
-
|
744
|
-
|
745
|
-
|
869
|
+
included do
|
870
|
+
class_attribute :fixture_path, instance_writer: false
|
871
|
+
class_attribute :fixture_table_names, default: []
|
872
|
+
class_attribute :fixture_class_names, default: {}
|
873
|
+
class_attribute :use_transactional_tests, default: true
|
874
|
+
class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
|
875
|
+
class_attribute :pre_loaded_fixtures, default: false
|
876
|
+
class_attribute :config, default: ActiveRecord::Base
|
746
877
|
end
|
747
878
|
|
748
879
|
module ClassMethods
|
880
|
+
# Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
|
881
|
+
#
|
882
|
+
# Examples:
|
883
|
+
#
|
884
|
+
# set_fixture_class some_fixture: SomeModel,
|
885
|
+
# 'namespaced/fixture' => Another::Model
|
886
|
+
#
|
887
|
+
# The keys must be the fixture names, that coincide with the short paths to the fixture files.
|
749
888
|
def set_fixture_class(class_names = {})
|
750
|
-
self.fixture_class_names =
|
889
|
+
self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
|
751
890
|
end
|
752
891
|
|
753
|
-
def fixtures(*
|
754
|
-
if
|
755
|
-
|
756
|
-
|
892
|
+
def fixtures(*fixture_set_names)
|
893
|
+
if fixture_set_names.first == :all
|
894
|
+
fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
|
895
|
+
fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
|
757
896
|
else
|
758
|
-
|
897
|
+
fixture_set_names = fixture_set_names.flatten.map(&:to_s)
|
759
898
|
end
|
760
899
|
|
761
|
-
self.fixture_table_names |=
|
762
|
-
|
763
|
-
setup_fixture_accessors(fixture_names)
|
764
|
-
end
|
765
|
-
|
766
|
-
def try_to_load_dependency(file_name)
|
767
|
-
require_dependency file_name
|
768
|
-
rescue LoadError => e
|
769
|
-
# Let's hope the developer has included it himself
|
770
|
-
|
771
|
-
# Let's warn in case this is a subdependency, otherwise
|
772
|
-
# subdependency error messages are totally cryptic
|
773
|
-
if ActiveRecord::Base.logger
|
774
|
-
ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
|
-
def require_fixture_classes(fixture_names = nil)
|
779
|
-
(fixture_names || fixture_table_names).each do |fixture_name|
|
780
|
-
file_name = fixture_name.to_s
|
781
|
-
file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
|
782
|
-
try_to_load_dependency(file_name)
|
783
|
-
end
|
900
|
+
self.fixture_table_names |= fixture_set_names
|
901
|
+
setup_fixture_accessors(fixture_set_names)
|
784
902
|
end
|
785
903
|
|
786
|
-
def setup_fixture_accessors(
|
787
|
-
|
904
|
+
def setup_fixture_accessors(fixture_set_names = nil)
|
905
|
+
fixture_set_names = Array(fixture_set_names || fixture_table_names)
|
788
906
|
methods = Module.new do
|
789
|
-
|
790
|
-
|
907
|
+
fixture_set_names.each do |fs_name|
|
908
|
+
fs_name = fs_name.to_s
|
909
|
+
accessor_name = fs_name.tr("/", "_").to_sym
|
791
910
|
|
792
|
-
define_method(
|
793
|
-
force_reload =
|
911
|
+
define_method(accessor_name) do |*fixture_names|
|
912
|
+
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
|
913
|
+
return_single_record = fixture_names.size == 1
|
914
|
+
fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
|
794
915
|
|
795
|
-
@fixture_cache[
|
916
|
+
@fixture_cache[fs_name] ||= {}
|
796
917
|
|
797
|
-
instances =
|
798
|
-
|
918
|
+
instances = fixture_names.map do |f_name|
|
919
|
+
f_name = f_name.to_s if f_name.is_a?(Symbol)
|
920
|
+
@fixture_cache[fs_name].delete(f_name) if force_reload
|
799
921
|
|
800
|
-
if @loaded_fixtures[
|
801
|
-
|
802
|
-
@fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
|
803
|
-
end
|
922
|
+
if @loaded_fixtures[fs_name][f_name]
|
923
|
+
@fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
|
804
924
|
else
|
805
|
-
raise StandardError, "No fixture
|
925
|
+
raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
|
806
926
|
end
|
807
927
|
end
|
808
928
|
|
809
|
-
|
929
|
+
return_single_record ? instances.first : instances
|
810
930
|
end
|
811
|
-
private
|
931
|
+
private accessor_name
|
812
932
|
end
|
813
933
|
end
|
814
934
|
include methods
|
@@ -816,7 +936,7 @@ module ActiveRecord
|
|
816
936
|
|
817
937
|
def uses_transaction(*methods)
|
818
938
|
@uses_transaction = [] unless defined?(@uses_transaction)
|
819
|
-
@uses_transaction.concat methods.map
|
939
|
+
@uses_transaction.concat methods.map(&:to_s)
|
820
940
|
end
|
821
941
|
|
822
942
|
def uses_transaction?(method)
|
@@ -826,40 +946,60 @@ module ActiveRecord
|
|
826
946
|
end
|
827
947
|
|
828
948
|
def run_in_transaction?
|
829
|
-
|
949
|
+
use_transactional_tests &&
|
830
950
|
!self.class.uses_transaction?(method_name)
|
831
951
|
end
|
832
952
|
|
833
|
-
def setup_fixtures
|
834
|
-
|
835
|
-
|
836
|
-
if pre_loaded_fixtures && !use_transactional_fixtures
|
837
|
-
raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
|
953
|
+
def setup_fixtures(config = ActiveRecord::Base)
|
954
|
+
if pre_loaded_fixtures && !use_transactional_tests
|
955
|
+
raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
|
838
956
|
end
|
839
957
|
|
840
958
|
@fixture_cache = {}
|
841
959
|
@fixture_connections = []
|
842
960
|
@@already_loaded_fixtures ||= {}
|
961
|
+
@connection_subscriber = nil
|
843
962
|
|
844
963
|
# Load fixtures once and begin transaction.
|
845
964
|
if run_in_transaction?
|
846
965
|
if @@already_loaded_fixtures[self.class]
|
847
966
|
@loaded_fixtures = @@already_loaded_fixtures[self.class]
|
848
967
|
else
|
849
|
-
@loaded_fixtures = load_fixtures
|
968
|
+
@loaded_fixtures = load_fixtures(config)
|
850
969
|
@@already_loaded_fixtures[self.class] = @loaded_fixtures
|
851
970
|
end
|
971
|
+
|
972
|
+
# Begin transactions for connections already established
|
852
973
|
@fixture_connections = enlist_fixture_connections
|
853
974
|
@fixture_connections.each do |connection|
|
854
|
-
connection.
|
855
|
-
connection.
|
856
|
-
connection.begin_db_transaction
|
975
|
+
connection.begin_transaction joinable: false
|
976
|
+
connection.pool.lock_thread = true
|
857
977
|
end
|
978
|
+
|
979
|
+
# When connections are established in the future, begin a transaction too
|
980
|
+
@connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
981
|
+
spec_name = payload[:spec_name] if payload.key?(:spec_name)
|
982
|
+
|
983
|
+
if spec_name
|
984
|
+
begin
|
985
|
+
connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
|
986
|
+
rescue ConnectionNotEstablished
|
987
|
+
connection = nil
|
988
|
+
end
|
989
|
+
|
990
|
+
if connection && !@fixture_connections.include?(connection)
|
991
|
+
connection.begin_transaction joinable: false
|
992
|
+
connection.pool.lock_thread = true
|
993
|
+
@fixture_connections << connection
|
994
|
+
end
|
995
|
+
end
|
996
|
+
end
|
997
|
+
|
858
998
|
# Load fixtures for every test.
|
859
999
|
else
|
860
|
-
ActiveRecord::
|
1000
|
+
ActiveRecord::FixtureSet.reset_cache
|
861
1001
|
@@already_loaded_fixtures[self.class] = nil
|
862
|
-
@loaded_fixtures = load_fixtures
|
1002
|
+
@loaded_fixtures = load_fixtures(config)
|
863
1003
|
end
|
864
1004
|
|
865
1005
|
# Instantiate fixtures for every test if requested.
|
@@ -867,50 +1007,39 @@ module ActiveRecord
|
|
867
1007
|
end
|
868
1008
|
|
869
1009
|
def teardown_fixtures
|
870
|
-
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
|
871
|
-
|
872
|
-
unless run_in_transaction?
|
873
|
-
ActiveRecord::Fixtures.reset_cache
|
874
|
-
end
|
875
|
-
|
876
1010
|
# Rollback changes if a transaction is active.
|
877
1011
|
if run_in_transaction?
|
1012
|
+
ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
|
878
1013
|
@fixture_connections.each do |connection|
|
879
|
-
if connection.
|
880
|
-
|
881
|
-
connection.decrement_open_transactions
|
882
|
-
end
|
1014
|
+
connection.rollback_transaction if connection.transaction_open?
|
1015
|
+
connection.pool.lock_thread = false
|
883
1016
|
end
|
884
1017
|
@fixture_connections.clear
|
1018
|
+
else
|
1019
|
+
ActiveRecord::FixtureSet.reset_cache
|
885
1020
|
end
|
1021
|
+
|
886
1022
|
ActiveRecord::Base.clear_active_connections!
|
887
1023
|
end
|
888
1024
|
|
889
1025
|
def enlist_fixture_connections
|
890
|
-
ActiveRecord::Base.connection_handler.
|
1026
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
|
891
1027
|
end
|
892
1028
|
|
893
1029
|
private
|
894
|
-
def load_fixtures
|
895
|
-
fixtures = ActiveRecord::
|
1030
|
+
def load_fixtures(config)
|
1031
|
+
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
|
896
1032
|
Hash[fixtures.map { |f| [f.name, f] }]
|
897
1033
|
end
|
898
1034
|
|
899
|
-
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
|
900
|
-
@@required_fixture_classes = false
|
901
|
-
|
902
1035
|
def instantiate_fixtures
|
903
1036
|
if pre_loaded_fixtures
|
904
|
-
raise RuntimeError,
|
905
|
-
|
906
|
-
self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
|
907
|
-
@@required_fixture_classes = true
|
908
|
-
end
|
909
|
-
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
|
1037
|
+
raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
|
1038
|
+
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
|
910
1039
|
else
|
911
|
-
raise RuntimeError,
|
1040
|
+
raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
|
912
1041
|
@loaded_fixtures.each_value do |fixture_set|
|
913
|
-
ActiveRecord::
|
1042
|
+
ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
|
914
1043
|
end
|
915
1044
|
end
|
916
1045
|
end
|
@@ -920,3 +1049,17 @@ module ActiveRecord
|
|
920
1049
|
end
|
921
1050
|
end
|
922
1051
|
end
|
1052
|
+
|
1053
|
+
class ActiveRecord::FixtureSet::RenderContext # :nodoc:
|
1054
|
+
def self.create_subclass
|
1055
|
+
Class.new ActiveRecord::FixtureSet.context_class do
|
1056
|
+
def get_binding
|
1057
|
+
binding()
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def binary(path)
|
1061
|
+
%(!!binary "#{Base64.strict_encode64(File.read(path))}")
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
end
|