activerecord 5.2.8 → 6.0.0.beta1
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 +299 -788
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +35 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +15 -16
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- 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 +74 -0
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/active_record.rb +2 -1
- 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/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 +63 -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 +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +67 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +44 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
data/lib/active_record/enum.rb
CHANGED
@@ -141,10 +141,7 @@ module ActiveRecord
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
|
145
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
146
|
-
protected
|
147
|
-
|
144
|
+
private
|
148
145
|
attr_reader :name, :mapping, :subtype
|
149
146
|
end
|
150
147
|
|
@@ -152,14 +149,16 @@ module ActiveRecord
|
|
152
149
|
klass = self
|
153
150
|
enum_prefix = definitions.delete(:_prefix)
|
154
151
|
enum_suffix = definitions.delete(:_suffix)
|
152
|
+
enum_scopes = definitions.delete(:_scopes)
|
155
153
|
definitions.each do |name, values|
|
154
|
+
assert_valid_enum_definition_values(values)
|
156
155
|
# statuses = { }
|
157
156
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
158
157
|
name = name.to_s
|
159
158
|
|
160
159
|
# def self.statuses() statuses end
|
161
160
|
detect_enum_conflict!(name, name.pluralize, true)
|
162
|
-
singleton_class.
|
161
|
+
singleton_class.define_method(name.pluralize) { enum_values }
|
163
162
|
defined_enums[name] = enum_values
|
164
163
|
|
165
164
|
detect_enum_conflict!(name, name)
|
@@ -197,8 +196,10 @@ module ActiveRecord
|
|
197
196
|
define_method("#{value_method_name}!") { update!(attr => value) }
|
198
197
|
|
199
198
|
# scope :active, -> { where(status: 0) }
|
200
|
-
|
201
|
-
|
199
|
+
if enum_scopes != false
|
200
|
+
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
201
|
+
klass.scope value_method_name, -> { where(attr => value) }
|
202
|
+
end
|
202
203
|
end
|
203
204
|
end
|
204
205
|
enum_values.freeze
|
@@ -214,10 +215,24 @@ module ActiveRecord
|
|
214
215
|
end
|
215
216
|
end
|
216
217
|
|
218
|
+
def assert_valid_enum_definition_values(values)
|
219
|
+
unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
|
220
|
+
error_message = <<~MSG
|
221
|
+
Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
|
222
|
+
MSG
|
223
|
+
raise ArgumentError, error_message
|
224
|
+
end
|
225
|
+
|
226
|
+
if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
|
227
|
+
raise ArgumentError, "Enum label name must not be blank."
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
217
231
|
ENUM_CONFLICT_MESSAGE = \
|
218
232
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
219
233
|
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
220
234
|
"by %{source}."
|
235
|
+
private_constant :ENUM_CONFLICT_MESSAGE
|
221
236
|
|
222
237
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
223
238
|
if klass_method && dangerous_class_method?(method_name)
|
data/lib/active_record/errors.rb
CHANGED
@@ -49,6 +49,10 @@ module ActiveRecord
|
|
49
49
|
class ConnectionNotEstablished < ActiveRecordError
|
50
50
|
end
|
51
51
|
|
52
|
+
# Raised when a write to the database is attempted on a read only connection.
|
53
|
+
class ReadOnlyError < ActiveRecordError
|
54
|
+
end
|
55
|
+
|
52
56
|
# Raised when Active Record cannot find a record by given id or set of ids.
|
53
57
|
class RecordNotFound < ActiveRecordError
|
54
58
|
attr_reader :model, :primary_key, :id
|
@@ -97,9 +101,13 @@ module ActiveRecord
|
|
97
101
|
#
|
98
102
|
# Wraps the underlying database error as +cause+.
|
99
103
|
class StatementInvalid < ActiveRecordError
|
100
|
-
def initialize(message = nil)
|
104
|
+
def initialize(message = nil, sql: nil, binds: nil)
|
101
105
|
super(message || $!.try(:message))
|
106
|
+
@sql = sql
|
107
|
+
@binds = binds
|
102
108
|
end
|
109
|
+
|
110
|
+
attr_reader :sql, :binds
|
103
111
|
end
|
104
112
|
|
105
113
|
# Defunct wrapper class kept for compatibility.
|
@@ -111,33 +119,23 @@ module ActiveRecord
|
|
111
119
|
class RecordNotUnique < WrappedDatabaseException
|
112
120
|
end
|
113
121
|
|
114
|
-
# Raised when a record cannot be inserted or updated because it references a non-existent record
|
122
|
+
# Raised when a record cannot be inserted or updated because it references a non-existent record,
|
123
|
+
# or when a record cannot be deleted because a parent record references it.
|
115
124
|
class InvalidForeignKey < WrappedDatabaseException
|
116
125
|
end
|
117
126
|
|
118
127
|
# Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
|
119
128
|
class MismatchedForeignKey < StatementInvalid
|
120
|
-
def initialize(
|
121
|
-
adapter =
|
122
|
-
message: nil,
|
123
|
-
sql: nil,
|
124
|
-
binds: nil,
|
125
|
-
table: nil,
|
126
|
-
foreign_key: nil,
|
127
|
-
target_table: nil,
|
128
|
-
primary_key: nil,
|
129
|
-
primary_key_column: nil
|
130
|
-
)
|
129
|
+
def initialize(adapter = nil, message: nil, sql: nil, binds: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
|
130
|
+
@adapter = adapter
|
131
131
|
if table
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
|
137
|
-
(For example `t.#{type} :#{foreign_key}`).
|
132
|
+
msg = +<<~EOM
|
133
|
+
Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
|
134
|
+
This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
|
135
|
+
To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
|
138
136
|
EOM
|
139
137
|
else
|
140
|
-
msg =
|
138
|
+
msg = +<<~EOM
|
141
139
|
There is a mismatch between the foreign key and primary key column types.
|
142
140
|
Verify that the foreign key column type and the primary key of the associated table match types.
|
143
141
|
EOM
|
@@ -145,8 +143,13 @@ module ActiveRecord
|
|
145
143
|
if message
|
146
144
|
msg << "\nOriginal message: #{message}"
|
147
145
|
end
|
148
|
-
super(msg)
|
146
|
+
super(msg, sql: sql, binds: binds)
|
149
147
|
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def column_type(table, column)
|
151
|
+
@adapter.columns(table).detect { |c| c.name == column }.sql_type
|
152
|
+
end
|
150
153
|
end
|
151
154
|
|
152
155
|
# Raised when a record cannot be inserted or updated because it would violate a not null constraint.
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
# Returns a formatted string ready to be logged.
|
19
19
|
def exec_explain(queries) # :nodoc:
|
20
20
|
str = queries.map do |sql, binds|
|
21
|
-
msg = "EXPLAIN for: #{sql}"
|
21
|
+
msg = +"EXPLAIN for: #{sql}"
|
22
22
|
unless binds.empty?
|
23
23
|
msg << " "
|
24
24
|
msg << binds.map { |attr| render_bind(attr) }.inspect
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class FixtureSet
|
5
|
+
class ModelMetadata # :nodoc:
|
6
|
+
def initialize(model_class)
|
7
|
+
@model_class = model_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def primary_key_name
|
11
|
+
@primary_key_name ||= @model_class && @model_class.primary_key
|
12
|
+
end
|
13
|
+
|
14
|
+
def primary_key_type
|
15
|
+
@primary_key_type ||= @model_class && @model_class.type_for_attribute(@model_class.primary_key).type
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_primary_key_column?
|
19
|
+
@has_primary_key_column ||= primary_key_name &&
|
20
|
+
@model_class.columns.any? { |col| col.name == primary_key_name }
|
21
|
+
end
|
22
|
+
|
23
|
+
def timestamp_column_names
|
24
|
+
@timestamp_column_names ||=
|
25
|
+
%w(created_at created_on updated_at updated_on) & @model_class.column_names
|
26
|
+
end
|
27
|
+
|
28
|
+
def inheritance_column_name
|
29
|
+
@inheritance_column_name ||= @model_class && @model_class.inheritance_column
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: This class has to be defined in compact style in
|
4
|
+
# order for rendering context subclassing to work correctly.
|
5
|
+
class ActiveRecord::FixtureSet::RenderContext # :nodoc:
|
6
|
+
def self.create_subclass
|
7
|
+
Class.new(ActiveRecord::FixtureSet.context_class) do
|
8
|
+
def get_binding
|
9
|
+
binding()
|
10
|
+
end
|
11
|
+
|
12
|
+
def binary(path)
|
13
|
+
%(!!binary "#{Base64.strict_encode64(File.read(path))}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class FixtureSet
|
5
|
+
class TableRow # :nodoc:
|
6
|
+
class ReflectionProxy # :nodoc:
|
7
|
+
def initialize(association)
|
8
|
+
@association = association
|
9
|
+
end
|
10
|
+
|
11
|
+
def join_table
|
12
|
+
@association.join_table
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
@association.name
|
17
|
+
end
|
18
|
+
|
19
|
+
def primary_key_type
|
20
|
+
@association.klass.type_for_attribute(@association.klass.primary_key).type
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class HasManyThroughProxy < ReflectionProxy # :nodoc:
|
25
|
+
def rhs_key
|
26
|
+
@association.foreign_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def lhs_key
|
30
|
+
@association.through_reflection.foreign_key
|
31
|
+
end
|
32
|
+
|
33
|
+
def join_table
|
34
|
+
@association.through_reflection.table_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(fixture, table_rows:, label:, now:)
|
39
|
+
@table_rows = table_rows
|
40
|
+
@label = label
|
41
|
+
@now = now
|
42
|
+
@row = fixture.to_hash
|
43
|
+
fill_row_model_attributes
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_hash
|
47
|
+
@row
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def model_metadata
|
53
|
+
@table_rows.model_metadata
|
54
|
+
end
|
55
|
+
|
56
|
+
def model_class
|
57
|
+
@table_rows.model_class
|
58
|
+
end
|
59
|
+
|
60
|
+
def fill_row_model_attributes
|
61
|
+
return unless model_class
|
62
|
+
fill_timestamps
|
63
|
+
interpolate_label
|
64
|
+
generate_primary_key
|
65
|
+
resolve_enums
|
66
|
+
resolve_sti_reflections
|
67
|
+
end
|
68
|
+
|
69
|
+
def reflection_class
|
70
|
+
@reflection_class ||= if @row.include?(model_metadata.inheritance_column_name)
|
71
|
+
@row[model_metadata.inheritance_column_name].constantize rescue model_class
|
72
|
+
else
|
73
|
+
model_class
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def fill_timestamps
|
78
|
+
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
|
79
|
+
if model_class.record_timestamps
|
80
|
+
model_metadata.timestamp_column_names.each do |c_name|
|
81
|
+
@row[c_name] = @now unless @row.key?(c_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def interpolate_label
|
87
|
+
# interpolate the fixture label
|
88
|
+
@row.each do |key, value|
|
89
|
+
@row[key] = value.gsub("$LABEL", @label.to_s) if value.is_a?(String)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_primary_key
|
94
|
+
# generate a primary key if necessary
|
95
|
+
if model_metadata.has_primary_key_column? && !@row.include?(model_metadata.primary_key_name)
|
96
|
+
@row[model_metadata.primary_key_name] = ActiveRecord::FixtureSet.identify(
|
97
|
+
@label, model_metadata.primary_key_type
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolve_enums
|
103
|
+
model_class.defined_enums.each do |name, values|
|
104
|
+
if @row.include?(name)
|
105
|
+
@row[name] = values.fetch(@row[name], @row[name])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def resolve_sti_reflections
|
111
|
+
# If STI is used, find the correct subclass for association reflection
|
112
|
+
reflection_class._reflections.each_value do |association|
|
113
|
+
case association.macro
|
114
|
+
when :belongs_to
|
115
|
+
# Do not replace association name with association foreign key if they are named the same
|
116
|
+
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
117
|
+
|
118
|
+
if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
|
119
|
+
if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
|
120
|
+
# support polymorphic belongs_to as "label (Type)"
|
121
|
+
@row[association.foreign_type] = $1
|
122
|
+
end
|
123
|
+
|
124
|
+
fk_type = reflection_class.type_for_attribute(fk_name).type
|
125
|
+
@row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
|
126
|
+
end
|
127
|
+
when :has_many
|
128
|
+
if association.options[:through]
|
129
|
+
add_join_records(HasManyThroughProxy.new(association))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_join_records(association)
|
136
|
+
# This is the case when the join table has no fixtures file
|
137
|
+
if (targets = @row.delete(association.name.to_s))
|
138
|
+
table_name = association.join_table
|
139
|
+
column_type = association.primary_key_type
|
140
|
+
lhs_key = association.lhs_key
|
141
|
+
rhs_key = association.rhs_key
|
142
|
+
|
143
|
+
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
144
|
+
joins = targets.map do |target|
|
145
|
+
{ lhs_key => @row[model_metadata.primary_key_name],
|
146
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
147
|
+
end
|
148
|
+
@table_rows.tables[table_name].concat(joins)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/fixture_set/table_row"
|
4
|
+
require "active_record/fixture_set/model_metadata"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
class FixtureSet
|
8
|
+
class TableRows # :nodoc:
|
9
|
+
def initialize(table_name, model_class:, fixtures:, config:)
|
10
|
+
@model_class = model_class
|
11
|
+
|
12
|
+
# track any join tables we need to insert later
|
13
|
+
@tables = Hash.new { |h, table| h[table] = [] }
|
14
|
+
|
15
|
+
# ensure this table is loaded before any HABTM associations
|
16
|
+
@tables[table_name] = nil
|
17
|
+
|
18
|
+
build_table_rows_from(table_name, fixtures, config)
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :tables, :model_class
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
@tables.transform_values { |rows| rows.map(&:to_hash) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def model_metadata
|
28
|
+
@model_metadata ||= ModelMetadata.new(model_class)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def build_table_rows_from(table_name, fixtures, config)
|
34
|
+
now = config.default_timezone == :utc ? Time.now.utc : Time.now
|
35
|
+
|
36
|
+
@tables[table_name] = fixtures.map do |label, fixture|
|
37
|
+
TableRow.new(
|
38
|
+
fixture,
|
39
|
+
table_rows: self,
|
40
|
+
label: label,
|
41
|
+
now: now,
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|