activerecord 5.1.7 → 5.2.8.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 +629 -661
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +41 -37
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +27 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +12 -4
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +59 -47
- data/lib/active_record/associations/collection_proxy.rb +20 -49
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +12 -1
- data/lib/active_record/associations/has_many_through_association.rb +36 -30
- data/lib/active_record/associations/has_one_association.rb +12 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/join_dependency.rb +48 -93
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +18 -38
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +26 -11
- data/lib/active_record/associations.rb +40 -63
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +30 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +35 -19
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +15 -1
- data/lib/active_record/collection_cache_key.rb +12 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
- data/lib/active_record/connection_adapters/column.rb +3 -1
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- 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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +233 -111
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +81 -94
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +51 -61
- data/lib/active_record/counter_cache.rb +10 -3
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +42 -3
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +47 -9
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +167 -16
- data/lib/active_record/query_cache.rb +6 -8
- data/lib/active_record/querying.rb +4 -2
- data/lib/active_record/railtie.rb +80 -6
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +108 -194
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +45 -19
- data/lib/active_record/relation/delegation.rb +45 -27
- data/lib/active_record/relation/finder_methods.rb +75 -76
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +128 -99
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +120 -214
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping/default.rb +8 -9
- data/lib/active_record/scoping/named.rb +23 -7
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +23 -13
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +25 -14
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +33 -28
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- 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 +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- 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 +2 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- 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/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +26 -39
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- 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 -18
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- 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/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -37
data/lib/active_record/enum.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/object/deep_dup"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -95,8 +97,7 @@ module ActiveRecord
|
|
95
97
|
|
96
98
|
module Enum
|
97
99
|
def self.extended(base) # :nodoc:
|
98
|
-
base.class_attribute(:defined_enums, instance_writer: false)
|
99
|
-
base.defined_enums = {}
|
100
|
+
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
100
101
|
end
|
101
102
|
|
102
103
|
def inherited(base) # :nodoc:
|
@@ -154,11 +155,12 @@ module ActiveRecord
|
|
154
155
|
definitions.each do |name, values|
|
155
156
|
# statuses = { }
|
156
157
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
157
|
-
name
|
158
|
+
name = name.to_s
|
158
159
|
|
159
160
|
# def self.statuses() statuses end
|
160
|
-
detect_enum_conflict!(name, name.
|
161
|
-
|
161
|
+
detect_enum_conflict!(name, name.pluralize, true)
|
162
|
+
singleton_class.send(:define_method, name.pluralize) { enum_values }
|
163
|
+
defined_enums[name] = enum_values
|
162
164
|
|
163
165
|
detect_enum_conflict!(name, name)
|
164
166
|
detect_enum_conflict!(name, "#{name}=")
|
@@ -170,7 +172,7 @@ module ActiveRecord
|
|
170
172
|
|
171
173
|
_enum_methods_module.module_eval do
|
172
174
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
173
|
-
pairs.each do |
|
175
|
+
pairs.each do |label, value|
|
174
176
|
if enum_prefix == true
|
175
177
|
prefix = "#{name}_"
|
176
178
|
elsif enum_prefix
|
@@ -182,23 +184,24 @@ module ActiveRecord
|
|
182
184
|
suffix = "_#{enum_suffix}"
|
183
185
|
end
|
184
186
|
|
185
|
-
value_method_name = "#{prefix}#{
|
186
|
-
enum_values[
|
187
|
+
value_method_name = "#{prefix}#{label}#{suffix}"
|
188
|
+
enum_values[label] = value
|
189
|
+
label = label.to_s
|
187
190
|
|
188
|
-
# def active?() status ==
|
191
|
+
# def active?() status == "active" end
|
189
192
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
190
|
-
define_method("#{value_method_name}?") { self[attr] ==
|
193
|
+
define_method("#{value_method_name}?") { self[attr] == label }
|
191
194
|
|
192
|
-
# def active!() update!
|
195
|
+
# def active!() update!(status: 0) end
|
193
196
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
194
197
|
define_method("#{value_method_name}!") { update!(attr => value) }
|
195
198
|
|
196
|
-
# scope :active, -> { where
|
199
|
+
# scope :active, -> { where(status: 0) }
|
197
200
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
198
201
|
klass.scope value_method_name, -> { where(attr => value) }
|
199
202
|
end
|
200
203
|
end
|
201
|
-
|
204
|
+
enum_values.freeze
|
202
205
|
end
|
203
206
|
end
|
204
207
|
|
@@ -219,6 +222,8 @@ module ActiveRecord
|
|
219
222
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
220
223
|
if klass_method && dangerous_class_method?(method_name)
|
221
224
|
raise_conflict_error(enum_name, method_name, type: "class")
|
225
|
+
elsif klass_method && method_defined_within?(method_name, Relation)
|
226
|
+
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
222
227
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
223
228
|
raise_conflict_error(enum_name, method_name)
|
224
229
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
data/lib/active_record/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record Errors
|
3
5
|
#
|
@@ -105,7 +107,7 @@ module ActiveRecord
|
|
105
107
|
class WrappedDatabaseException < StatementInvalid
|
106
108
|
end
|
107
109
|
|
108
|
-
# Raised when a record cannot be inserted because it would violate a uniqueness constraint.
|
110
|
+
# Raised when a record cannot be inserted or updated because it would violate a uniqueness constraint.
|
109
111
|
class RecordNotUnique < WrappedDatabaseException
|
110
112
|
end
|
111
113
|
|
@@ -173,7 +175,7 @@ module ActiveRecord
|
|
173
175
|
class NoDatabaseError < StatementInvalid
|
174
176
|
end
|
175
177
|
|
176
|
-
# Raised when
|
178
|
+
# Raised when PostgreSQL returns 'cached plan must not change result type' and
|
177
179
|
# we cannot retry gracefully (e.g. inside a transaction)
|
178
180
|
class PreparedStatementCacheExpired < StatementInvalid
|
179
181
|
end
|
@@ -319,7 +321,7 @@ module ActiveRecord
|
|
319
321
|
#
|
320
322
|
# See the following:
|
321
323
|
#
|
322
|
-
# *
|
324
|
+
# * https://www.postgresql.org/docs/current/static/transaction-iso.html
|
323
325
|
# * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
|
324
326
|
class TransactionRollbackError < StatementInvalid
|
325
327
|
end
|
@@ -338,4 +340,41 @@ module ActiveRecord
|
|
338
340
|
# +reverse_order+ to automatically reverse.
|
339
341
|
class IrreversibleOrderError < ActiveRecordError
|
340
342
|
end
|
343
|
+
|
344
|
+
# LockWaitTimeout will be raised when lock wait timeout exceeded.
|
345
|
+
class LockWaitTimeout < StatementInvalid
|
346
|
+
end
|
347
|
+
|
348
|
+
# StatementTimeout will be raised when statement timeout exceeded.
|
349
|
+
class StatementTimeout < StatementInvalid
|
350
|
+
end
|
351
|
+
|
352
|
+
# QueryCanceled will be raised when canceling statement due to user request.
|
353
|
+
class QueryCanceled < StatementInvalid
|
354
|
+
end
|
355
|
+
|
356
|
+
# UnknownAttributeReference is raised when an unknown and potentially unsafe
|
357
|
+
# value is passed to a query method when allow_unsafe_raw_sql is set to
|
358
|
+
# :disabled. For example, passing a non column name value to a relation's
|
359
|
+
# #order method might cause this exception.
|
360
|
+
#
|
361
|
+
# When working around this exception, caution should be taken to avoid SQL
|
362
|
+
# injection vulnerabilities when passing user-provided values to query
|
363
|
+
# methods. Known-safe values can be passed to query methods by wrapping them
|
364
|
+
# in Arel.sql.
|
365
|
+
#
|
366
|
+
# For example, with allow_unsafe_raw_sql set to :disabled, the following
|
367
|
+
# code would raise this exception:
|
368
|
+
#
|
369
|
+
# Post.order("length(title)").first
|
370
|
+
#
|
371
|
+
# The desired result can be accomplished by wrapping the known-safe string
|
372
|
+
# in Arel.sql:
|
373
|
+
#
|
374
|
+
# Post.order(Arel.sql("length(title)")).first
|
375
|
+
#
|
376
|
+
# Again, such a workaround should *not* be used when passing user-provided
|
377
|
+
# values, such as request parameters or model attributes to query methods.
|
378
|
+
class UnknownAttributeReference < ActiveRecordError
|
379
|
+
end
|
341
380
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/explain_registry"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -16,7 +18,7 @@ module ActiveRecord
|
|
16
18
|
# Returns a formatted string ready to be logged.
|
17
19
|
def exec_explain(queries) # :nodoc:
|
18
20
|
str = queries.map do |sql, binds|
|
19
|
-
msg = "EXPLAIN for: #{sql}"
|
21
|
+
msg = "EXPLAIN for: #{sql}".dup
|
20
22
|
unless binds.empty?
|
21
23
|
msg << " "
|
22
24
|
msg << binds.map { |attr| render_bind(attr) }.inspect
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "erb"
|
2
4
|
require "yaml"
|
3
5
|
require "zlib"
|
@@ -70,13 +72,32 @@ module ActiveRecord
|
|
70
72
|
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
|
71
73
|
#
|
72
74
|
# In addition to being available in the database, the fixture's data may also be accessed by
|
73
|
-
# using a special dynamic method, which has the same name as the model
|
74
|
-
#
|
75
|
+
# using a special dynamic method, which has the same name as the model.
|
76
|
+
#
|
77
|
+
# Passing in a fixture name to this dynamic method returns the fixture matching this name:
|
75
78
|
#
|
76
|
-
# test "find" do
|
79
|
+
# test "find one" do
|
77
80
|
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
|
78
81
|
# end
|
79
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
|
+
#
|
80
101
|
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
|
81
102
|
# following tests:
|
82
103
|
#
|
@@ -126,7 +147,7 @@ module ActiveRecord
|
|
126
147
|
# unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
|
127
148
|
# that is included in ActiveRecord::FixtureSet.context_class.
|
128
149
|
#
|
129
|
-
# - define a helper method in
|
150
|
+
# - define a helper method in <tt>test_helper.rb</tt>
|
130
151
|
# module FixtureFileHelpers
|
131
152
|
# def file_sha(path)
|
132
153
|
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
@@ -148,13 +169,13 @@ module ActiveRecord
|
|
148
169
|
# self.use_transactional_tests = true
|
149
170
|
#
|
150
171
|
# test "godzilla" do
|
151
|
-
#
|
172
|
+
# assert_not_empty Foo.all
|
152
173
|
# Foo.destroy_all
|
153
|
-
#
|
174
|
+
# assert_empty Foo.all
|
154
175
|
# end
|
155
176
|
#
|
156
177
|
# test "godzilla aftermath" do
|
157
|
-
#
|
178
|
+
# assert_not_empty Foo.all
|
158
179
|
# end
|
159
180
|
# end
|
160
181
|
#
|
@@ -473,8 +494,7 @@ module ActiveRecord
|
|
473
494
|
end
|
474
495
|
end
|
475
496
|
|
476
|
-
cattr_accessor :all_loaded_fixtures
|
477
|
-
self.all_loaded_fixtures = {}
|
497
|
+
cattr_accessor :all_loaded_fixtures, default: {}
|
478
498
|
|
479
499
|
class ClassCache
|
480
500
|
def initialize(class_names, config)
|
@@ -520,49 +540,38 @@ module ActiveRecord
|
|
520
540
|
}
|
521
541
|
|
522
542
|
unless files_to_read.empty?
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
end
|
535
|
-
|
536
|
-
update_all_loaded_fixtures fixtures_map
|
537
|
-
|
538
|
-
connection.transaction(requires_new: true) do
|
539
|
-
deleted_tables = Hash.new { |h, k| h[k] = Set.new }
|
540
|
-
fixture_sets.each do |fs|
|
541
|
-
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
|
542
|
-
table_rows = fs.table_rows
|
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
|
543
554
|
|
544
|
-
|
545
|
-
|
546
|
-
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete"
|
547
|
-
end
|
548
|
-
deleted_tables[conn] << table
|
549
|
-
end
|
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 }
|
550
557
|
|
551
|
-
|
552
|
-
|
553
|
-
conn.insert_fixture(row, fixture_set_name)
|
554
|
-
end
|
555
|
-
end
|
558
|
+
fixture_sets_by_connection.each do |conn, set|
|
559
|
+
table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
|
556
560
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
end
|
561
|
+
set.each do |fs|
|
562
|
+
fs.table_rows.each do |table, rows|
|
563
|
+
table_rows_for_connection[table].unshift(*rows)
|
561
564
|
end
|
562
565
|
end
|
566
|
+
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
563
567
|
|
564
|
-
|
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
|
565
572
|
end
|
573
|
+
|
574
|
+
cache_fixtures(connection, fixtures_map)
|
566
575
|
end
|
567
576
|
cached_fixtures(connection, fixture_set_names)
|
568
577
|
end
|
@@ -859,20 +868,12 @@ module ActiveRecord
|
|
859
868
|
|
860
869
|
included do
|
861
870
|
class_attribute :fixture_path, instance_writer: false
|
862
|
-
class_attribute :fixture_table_names
|
863
|
-
class_attribute :fixture_class_names
|
864
|
-
class_attribute :use_transactional_tests
|
865
|
-
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
|
866
|
-
class_attribute :pre_loaded_fixtures
|
867
|
-
class_attribute :config
|
868
|
-
|
869
|
-
self.fixture_table_names = []
|
870
|
-
self.use_instantiated_fixtures = false
|
871
|
-
self.pre_loaded_fixtures = false
|
872
|
-
self.config = ActiveRecord::Base
|
873
|
-
|
874
|
-
self.fixture_class_names = {}
|
875
|
-
self.use_transactional_tests = true
|
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
|
876
877
|
end
|
877
878
|
|
878
879
|
module ClassMethods
|
@@ -909,6 +910,8 @@ module ActiveRecord
|
|
909
910
|
|
910
911
|
define_method(accessor_name) do |*fixture_names|
|
911
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?
|
912
915
|
|
913
916
|
@fixture_cache[fs_name] ||= {}
|
914
917
|
|
@@ -923,7 +926,7 @@ module ActiveRecord
|
|
923
926
|
end
|
924
927
|
end
|
925
928
|
|
926
|
-
|
929
|
+
return_single_record ? instances.first : instances
|
927
930
|
end
|
928
931
|
private accessor_name
|
929
932
|
end
|
@@ -1053,6 +1056,10 @@ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
|
|
1053
1056
|
def get_binding
|
1054
1057
|
binding()
|
1055
1058
|
end
|
1059
|
+
|
1060
|
+
def binary(path)
|
1061
|
+
%(!!binary "#{Base64.strict_encode64(File.read(path))}")
|
1062
|
+
end
|
1056
1063
|
end
|
1057
1064
|
end
|
1058
1065
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
|
3
5
|
def self.gem_version
|
@@ -6,9 +8,9 @@ module ActiveRecord
|
|
6
8
|
|
7
9
|
module VERSION
|
8
10
|
MAJOR = 5
|
9
|
-
MINOR =
|
10
|
-
TINY =
|
11
|
-
PRE =
|
11
|
+
MINOR = 2
|
12
|
+
TINY = 8
|
13
|
+
PRE = "1"
|
12
14
|
|
13
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
14
16
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/indifferent_access"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -30,7 +32,7 @@ module ActiveRecord
|
|
30
32
|
# for differentiating between them or reloading the right type with find.
|
31
33
|
#
|
32
34
|
# Note, all the attributes for all the cases are kept in the same table. Read more:
|
33
|
-
#
|
35
|
+
# https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
|
34
36
|
#
|
35
37
|
module Inheritance
|
36
38
|
extend ActiveSupport::Concern
|
@@ -38,22 +40,20 @@ module ActiveRecord
|
|
38
40
|
included do
|
39
41
|
# Determines whether to store the full constant name including namespace when using STI.
|
40
42
|
# This is true, by default.
|
41
|
-
class_attribute :store_full_sti_class, instance_writer: false
|
42
|
-
self.store_full_sti_class = true
|
43
|
+
class_attribute :store_full_sti_class, instance_writer: false, default: true
|
43
44
|
end
|
44
45
|
|
45
46
|
module ClassMethods
|
46
47
|
# Determines if one of the attributes passed in is the inheritance column,
|
47
48
|
# and if the inheritance column is attr accessible, it initializes an
|
48
49
|
# instance of the given subclass instead of the base class.
|
49
|
-
def new(
|
50
|
+
def new(attributes = nil, &block)
|
50
51
|
if abstract_class? || self == Base
|
51
52
|
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
|
52
53
|
end
|
53
54
|
|
54
|
-
attrs = args.first
|
55
55
|
if has_attribute?(inheritance_column)
|
56
|
-
subclass = subclass_from_attributes(
|
56
|
+
subclass = subclass_from_attributes(attributes)
|
57
57
|
|
58
58
|
if subclass.nil? && base_class == self
|
59
59
|
subclass = subclass_from_attributes(column_defaults)
|
@@ -61,7 +61,7 @@ module ActiveRecord
|
|
61
61
|
end
|
62
62
|
|
63
63
|
if subclass && subclass != self
|
64
|
-
subclass.new(
|
64
|
+
subclass.new(attributes, &block)
|
65
65
|
else
|
66
66
|
super
|
67
67
|
end
|
@@ -104,21 +104,47 @@ module ActiveRecord
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
-
# Set this to true if this is an abstract class (see
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
107
|
+
# Set this to +true+ if this is an abstract class (see
|
108
|
+
# <tt>abstract_class?</tt>).
|
109
|
+
# If you are using inheritance with Active Record and don't want a class
|
110
|
+
# to be considered as part of the STI hierarchy, you must set this to
|
111
|
+
# true.
|
112
|
+
# +ApplicationRecord+, for example, is generated as an abstract class.
|
113
|
+
#
|
114
|
+
# Consider the following default behaviour:
|
115
|
+
#
|
116
|
+
# Shape = Class.new(ActiveRecord::Base)
|
117
|
+
# Polygon = Class.new(Shape)
|
118
|
+
# Square = Class.new(Polygon)
|
119
|
+
#
|
120
|
+
# Shape.table_name # => "shapes"
|
121
|
+
# Polygon.table_name # => "shapes"
|
122
|
+
# Square.table_name # => "shapes"
|
123
|
+
# Shape.create! # => #<Shape id: 1, type: nil>
|
124
|
+
# Polygon.create! # => #<Polygon id: 2, type: "Polygon">
|
125
|
+
# Square.create! # => #<Square id: 3, type: "Square">
|
126
|
+
#
|
127
|
+
# However, when using <tt>abstract_class</tt>, +Shape+ is omitted from
|
128
|
+
# the hierarchy:
|
111
129
|
#
|
112
|
-
# class
|
130
|
+
# class Shape < ActiveRecord::Base
|
113
131
|
# self.abstract_class = true
|
114
132
|
# end
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# end
|
118
|
-
#
|
133
|
+
# Polygon = Class.new(Shape)
|
134
|
+
# Square = Class.new(Polygon)
|
119
135
|
#
|
120
|
-
#
|
136
|
+
# Shape.table_name # => nil
|
137
|
+
# Polygon.table_name # => "polygons"
|
138
|
+
# Square.table_name # => "polygons"
|
139
|
+
# Shape.create! # => NotImplementedError: Shape is an abstract class and cannot be instantiated.
|
140
|
+
# Polygon.create! # => #<Polygon id: 1, type: nil>
|
141
|
+
# Square.create! # => #<Square id: 2, type: "Square">
|
121
142
|
#
|
143
|
+
# Note that in the above example, to disallow the creation of a plain
|
144
|
+
# +Polygon+, you should use <tt>validates :type, presence: true</tt>,
|
145
|
+
# instead of setting it as an abstract class. This way, +Polygon+ will
|
146
|
+
# stay in the hierarchy, and Active Record will continue to correctly
|
147
|
+
# derive the table name.
|
122
148
|
attr_accessor :abstract_class
|
123
149
|
|
124
150
|
# Returns whether this class is an abstract class or not.
|
@@ -130,6 +156,10 @@ module ActiveRecord
|
|
130
156
|
store_full_sti_class ? name : name.demodulize
|
131
157
|
end
|
132
158
|
|
159
|
+
def polymorphic_name
|
160
|
+
base_class.name
|
161
|
+
end
|
162
|
+
|
133
163
|
def inherited(subclass)
|
134
164
|
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
135
165
|
super
|
@@ -217,7 +247,7 @@ module ActiveRecord
|
|
217
247
|
def subclass_from_attributes(attrs)
|
218
248
|
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
|
219
249
|
if attrs.is_a?(Hash)
|
220
|
-
subclass_name = attrs
|
250
|
+
subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym]
|
221
251
|
|
222
252
|
if subclass_name.present?
|
223
253
|
find_sti_class(subclass_name)
|
@@ -246,7 +276,7 @@ module ActiveRecord
|
|
246
276
|
def ensure_proper_type
|
247
277
|
klass = self.class
|
248
278
|
if klass.finder_needs_type_condition?
|
249
|
-
|
279
|
+
_write_attribute(klass.inheritance_column, klass.sti_name)
|
250
280
|
end
|
251
281
|
end
|
252
282
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/string/filters"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -7,12 +9,19 @@ module ActiveRecord
|
|
7
9
|
included do
|
8
10
|
##
|
9
11
|
# :singleton-method:
|
10
|
-
# Indicates the format used to generate the timestamp in the cache key
|
11
|
-
# Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
|
12
|
+
# Indicates the format used to generate the timestamp in the cache key, if
|
13
|
+
# versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
|
12
14
|
#
|
13
15
|
# This is +:usec+, by default.
|
14
|
-
class_attribute :cache_timestamp_format, instance_writer: false
|
15
|
-
|
16
|
+
class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
|
17
|
+
|
18
|
+
##
|
19
|
+
# :singleton-method:
|
20
|
+
# Indicates whether to use a stable #cache_key method that is accompanied
|
21
|
+
# by a changing version in the #cache_version method.
|
22
|
+
#
|
23
|
+
# This is +false+, by default until Rails 6.0.
|
24
|
+
class_attribute :cache_versioning, instance_writer: false, default: false
|
16
25
|
end
|
17
26
|
|
18
27
|
# Returns a +String+, which Action Pack uses for constructing a URL to this
|
@@ -42,35 +51,65 @@ module ActiveRecord
|
|
42
51
|
id && id.to_s # Be sure to stringify the id for routes
|
43
52
|
end
|
44
53
|
|
45
|
-
# Returns a cache key that can be used to identify this record.
|
54
|
+
# Returns a stable cache key that can be used to identify this record.
|
46
55
|
#
|
47
56
|
# Product.new.cache_key # => "products/new"
|
48
|
-
# Product.find(5).cache_key # => "products/5"
|
49
|
-
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
|
57
|
+
# Product.find(5).cache_key # => "products/5"
|
50
58
|
#
|
51
|
-
#
|
52
|
-
#
|
59
|
+
# If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
|
60
|
+
# the cache key will also include a version.
|
53
61
|
#
|
54
|
-
#
|
62
|
+
# Product.cache_versioning = false
|
63
|
+
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
|
55
64
|
def cache_key(*timestamp_names)
|
56
65
|
if new_record?
|
57
66
|
"#{model_name.cache_key}/new"
|
58
67
|
else
|
59
|
-
|
60
|
-
|
68
|
+
if cache_version && timestamp_names.none?
|
69
|
+
"#{model_name.cache_key}/#{id}"
|
61
70
|
else
|
62
|
-
|
63
|
-
|
71
|
+
timestamp = if timestamp_names.any?
|
72
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
73
|
+
Specifying a timestamp name for #cache_key has been deprecated in favor of
|
74
|
+
the explicit #cache_version method that can be overwritten.
|
75
|
+
MSG
|
64
76
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
77
|
+
max_updated_column_timestamp(timestamp_names)
|
78
|
+
else
|
79
|
+
max_updated_column_timestamp
|
80
|
+
end
|
81
|
+
|
82
|
+
if timestamp
|
83
|
+
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
84
|
+
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
85
|
+
else
|
86
|
+
"#{model_name.cache_key}/#{id}"
|
87
|
+
end
|
70
88
|
end
|
71
89
|
end
|
72
90
|
end
|
73
91
|
|
92
|
+
# Returns a cache version that can be used together with the cache key to form
|
93
|
+
# a recyclable caching scheme. By default, the #updated_at column is used for the
|
94
|
+
# cache_version, but this method can be overwritten to return something else.
|
95
|
+
#
|
96
|
+
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
|
97
|
+
# +false+ (which it is by default until Rails 6.0).
|
98
|
+
def cache_version
|
99
|
+
if cache_versioning && timestamp = try(:updated_at)
|
100
|
+
timestamp.utc.to_s(:usec)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a cache key along with the version.
|
105
|
+
def cache_key_with_version
|
106
|
+
if version = cache_version
|
107
|
+
"#{cache_key}-#{version}"
|
108
|
+
else
|
109
|
+
cache_key
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
74
113
|
module ClassMethods
|
75
114
|
# Defines your model's +to_param+ method to generate "pretty" URLs
|
76
115
|
# using +method_name+, which can be any attribute or method that
|