activerecord 4.2.11.1 → 5.0.0
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 +1282 -1195
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +8 -4
- data/lib/active_record/aggregations.rb +35 -24
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +49 -41
- data/lib/active_record/associations/collection_proxy.rb +67 -27
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +46 -52
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +76 -47
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +49 -16
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration.rb +363 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +129 -41
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +69 -46
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +79 -108
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +57 -43
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +30 -29
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +59 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
# Returns an
|
12
|
+
# Returns an ActiveRecord::Relation scope object.
|
13
13
|
#
|
14
14
|
# posts = Post.all
|
15
15
|
# posts.size # Fires "select count(*) from posts" and returns the count
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
# fruits = fruits.limit(10) if limited?
|
21
21
|
#
|
22
22
|
# You can define a scope that applies to all finders using
|
23
|
-
#
|
23
|
+
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
24
24
|
def all
|
25
25
|
if current_scope
|
26
26
|
current_scope.clone
|
@@ -30,22 +30,22 @@ module ActiveRecord
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def default_scoped # :nodoc:
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
# Collects attributes from scopes that should be applied when creating
|
37
|
-
# an AR instance for the particular class this is called on.
|
38
|
-
def scope_attributes # :nodoc:
|
39
|
-
all.scope_for_create
|
40
|
-
end
|
33
|
+
scope = build_default_scope
|
41
34
|
|
42
|
-
|
43
|
-
|
44
|
-
|
35
|
+
if scope
|
36
|
+
relation.spawn.merge!(scope)
|
37
|
+
else
|
38
|
+
relation
|
39
|
+
end
|
45
40
|
end
|
46
41
|
|
47
|
-
# Adds a class method for retrieving and querying objects.
|
48
|
-
#
|
42
|
+
# Adds a class method for retrieving and querying objects.
|
43
|
+
# The method is intended to return an ActiveRecord::Relation
|
44
|
+
# object, which is composable with other scopes.
|
45
|
+
# If it returns nil or false, an
|
46
|
+
# {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
|
47
|
+
#
|
48
|
+
# A \scope represents a narrowing of a database query, such as
|
49
49
|
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
50
50
|
#
|
51
51
|
# class Shirt < ActiveRecord::Base
|
@@ -53,12 +53,12 @@ module ActiveRecord
|
|
53
53
|
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
54
54
|
# end
|
55
55
|
#
|
56
|
-
# The above calls to
|
56
|
+
# The above calls to #scope define class methods <tt>Shirt.red</tt> and
|
57
57
|
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
|
58
58
|
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
59
59
|
#
|
60
60
|
# You should always pass a callable object to the scopes defined
|
61
|
-
# with
|
61
|
+
# with #scope. This ensures that the scope is re-evaluated each
|
62
62
|
# time it is called.
|
63
63
|
#
|
64
64
|
# Note that this is simply 'syntactic sugar' for defining an actual
|
@@ -71,14 +71,15 @@ module ActiveRecord
|
|
71
71
|
# end
|
72
72
|
#
|
73
73
|
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
74
|
-
# <tt>Shirt.red</tt> is not an Array
|
75
|
-
#
|
76
|
-
#
|
74
|
+
# <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
|
75
|
+
# which is composable with other scopes; it resembles the association object
|
76
|
+
# constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
77
|
+
# declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
77
78
|
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
78
79
|
# association objects, named \scopes act like an Array, implementing
|
79
80
|
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
80
81
|
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
81
|
-
# <tt>Shirt.red</tt> really was an
|
82
|
+
# <tt>Shirt.red</tt> really was an array.
|
82
83
|
#
|
83
84
|
# These named \scopes are composable. For instance,
|
84
85
|
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
@@ -89,7 +90,8 @@ module ActiveRecord
|
|
89
90
|
#
|
90
91
|
# All scopes are available as class methods on the ActiveRecord::Base
|
91
92
|
# descendant upon which the \scopes were defined. But they are also
|
92
|
-
# available to
|
93
|
+
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
94
|
+
# associations. If,
|
93
95
|
#
|
94
96
|
# class Person < ActiveRecord::Base
|
95
97
|
# has_many :shirts
|
@@ -98,8 +100,8 @@ module ActiveRecord
|
|
98
100
|
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
99
101
|
# Elton's red, dry clean only shirts.
|
100
102
|
#
|
101
|
-
# \Named scopes can also have extensions, just as with
|
102
|
-
# declarations:
|
103
|
+
# \Named scopes can also have extensions, just as with
|
104
|
+
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
103
105
|
#
|
104
106
|
# class Shirt < ActiveRecord::Base
|
105
107
|
# scope :red, -> { where(color: 'red') } do
|
@@ -149,13 +151,32 @@ module ActiveRecord
|
|
149
151
|
"a class method with the same name."
|
150
152
|
end
|
151
153
|
|
154
|
+
valid_scope_name?(name)
|
152
155
|
extension = Module.new(&block) if block
|
153
156
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
+
if body.respond_to?(:to_proc)
|
158
|
+
singleton_class.send(:define_method, name) do |*args|
|
159
|
+
scope = all.scoping { instance_exec(*args, &body) }
|
160
|
+
scope = scope.extending(extension) if extension
|
161
|
+
|
162
|
+
scope || all
|
163
|
+
end
|
164
|
+
else
|
165
|
+
singleton_class.send(:define_method, name) do |*args|
|
166
|
+
scope = all.scoping { body.call(*args) }
|
167
|
+
scope = scope.extending(extension) if extension
|
168
|
+
|
169
|
+
scope || all
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
157
175
|
|
158
|
-
|
176
|
+
def valid_scope_name?(name)
|
177
|
+
if respond_to?(name, true)
|
178
|
+
logger.warn "Creating scope :#{name}. " \
|
179
|
+
"Overwriting existing method #{self.name}.#{name}."
|
159
180
|
end
|
160
181
|
end
|
161
182
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module SecureToken
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# Example using #has_secure_token
|
7
|
+
#
|
8
|
+
# # Schema: User(token:string, auth_token:string)
|
9
|
+
# class User < ActiveRecord::Base
|
10
|
+
# has_secure_token
|
11
|
+
# has_secure_token :auth_token
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# user = User.new
|
15
|
+
# user.save
|
16
|
+
# user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
|
17
|
+
# user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
|
18
|
+
# user.regenerate_token # => true
|
19
|
+
# user.regenerate_auth_token # => true
|
20
|
+
#
|
21
|
+
# <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
|
22
|
+
#
|
23
|
+
# Note that it's still possible to generate a race condition in the database in the same way that
|
24
|
+
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
|
25
|
+
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
|
26
|
+
def has_secure_token(attribute = :token)
|
27
|
+
# Load securerandom only when has_secure_token is used.
|
28
|
+
require 'active_support/core_ext/securerandom'
|
29
|
+
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
|
30
|
+
before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?")}
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_unique_secure_token
|
34
|
+
SecureRandom.base58(24)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActiveRecord #:nodoc:
|
2
|
-
# = Active Record Serialization
|
2
|
+
# = Active Record \Serialization
|
3
3
|
module Serialization
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
include ActiveModel::Serializers::JSON
|
@@ -11,12 +11,10 @@ module ActiveRecord #:nodoc:
|
|
11
11
|
def serializable_hash(options = nil)
|
12
12
|
options = options.try(:clone) || {}
|
13
13
|
|
14
|
-
options[:except] = Array(options[:except]).map
|
14
|
+
options[:except] = Array(options[:except]).map(&:to_s)
|
15
15
|
options[:except] |= Array(self.class.inheritance_column)
|
16
16
|
|
17
17
|
super(options)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
22
|
-
require 'active_record/serializers/xml_serializer'
|
@@ -7,12 +7,14 @@ module ActiveRecord
|
|
7
7
|
# Book.where(name: "my book").where("author_id > 3")
|
8
8
|
# end
|
9
9
|
#
|
10
|
-
# The cached statement is executed by using the
|
10
|
+
# The cached statement is executed by using the
|
11
|
+
# [connection.execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute} method:
|
11
12
|
#
|
12
13
|
# cache.execute([], Book, Book.connection)
|
13
14
|
#
|
14
|
-
# The relation returned by the block is cached, and for each
|
15
|
-
#
|
15
|
+
# The relation returned by the block is cached, and for each
|
16
|
+
# [execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute}
|
17
|
+
# call the cached relation gets duped. Database is queried when +to_a+ is called on the relation.
|
16
18
|
#
|
17
19
|
# If you want to cache the statement without the values you can use the +bind+ method of the
|
18
20
|
# block parameter.
|
@@ -47,8 +49,8 @@ module ActiveRecord
|
|
47
49
|
|
48
50
|
def sql_for(binds, connection)
|
49
51
|
val = @values.dup
|
50
|
-
binds = binds
|
51
|
-
@indexes.each { |i| val[i] = connection.quote(
|
52
|
+
binds = connection.prepare_binds_for_database(binds)
|
53
|
+
@indexes.each { |i| val[i] = connection.quote(binds.shift) }
|
52
54
|
val.join
|
53
55
|
end
|
54
56
|
end
|
@@ -67,21 +69,21 @@ module ActiveRecord
|
|
67
69
|
end
|
68
70
|
|
69
71
|
class BindMap # :nodoc:
|
70
|
-
def initialize(
|
72
|
+
def initialize(bound_attributes)
|
71
73
|
@indexes = []
|
72
|
-
@
|
74
|
+
@bound_attributes = bound_attributes
|
73
75
|
|
74
|
-
|
75
|
-
if Substitute === value
|
76
|
+
bound_attributes.each_with_index do |attr, i|
|
77
|
+
if Substitute === attr.value
|
76
78
|
@indexes << i
|
77
79
|
end
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
81
83
|
def bind(values)
|
82
|
-
|
83
|
-
@indexes.each_with_index { |offset,i|
|
84
|
-
|
84
|
+
bas = @bound_attributes.dup
|
85
|
+
@indexes.each_with_index { |offset,i| bas[offset] = bas[offset].with_cast_value(values[i]) }
|
86
|
+
bas
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
@@ -89,7 +91,7 @@ module ActiveRecord
|
|
89
91
|
|
90
92
|
def self.create(connection, block = Proc.new)
|
91
93
|
relation = block.call Params.new
|
92
|
-
bind_map = BindMap.new relation.
|
94
|
+
bind_map = BindMap.new relation.bound_attributes
|
93
95
|
query_builder = connection.cacheable_query relation.arel
|
94
96
|
new query_builder, bind_map
|
95
97
|
end
|
@@ -104,7 +106,7 @@ module ActiveRecord
|
|
104
106
|
|
105
107
|
sql = query_builder.sql_for bind_values, connection
|
106
108
|
|
107
|
-
klass.find_by_sql
|
109
|
+
klass.find_by_sql(sql, bind_values, preparable: true)
|
108
110
|
end
|
109
111
|
alias :call :execute
|
110
112
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -15,11 +15,16 @@ module ActiveRecord
|
|
15
15
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
16
16
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
17
17
|
#
|
18
|
-
# NOTE
|
19
|
-
# the serialization provided by
|
18
|
+
# NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
|
19
|
+
# the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
20
|
+
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
|
20
21
|
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
21
22
|
# using a symbol.
|
22
23
|
#
|
24
|
+
# NOTE: The default validations with the exception of +uniqueness+ will work.
|
25
|
+
# For example, if you want to check for +uniqueness+ with +hstore+ you will
|
26
|
+
# need to use a custom validation to handle it.
|
27
|
+
#
|
23
28
|
# Examples:
|
24
29
|
#
|
25
30
|
# class User < ActiveRecord::Base
|
@@ -39,7 +44,7 @@ module ActiveRecord
|
|
39
44
|
# store_accessor :settings, :privileges, :servants
|
40
45
|
# end
|
41
46
|
#
|
42
|
-
# The stored attribute names can be retrieved using
|
47
|
+
# The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
|
43
48
|
#
|
44
49
|
# User.stored_attributes[:settings] # [:color, :homepage]
|
45
50
|
#
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# ActiveRecord::Suppressor prevents the receiver from being saved during
|
3
|
+
# a given block.
|
4
|
+
#
|
5
|
+
# For example, here's a pattern of creating notifications when new comments
|
6
|
+
# are posted. (The notification may in turn trigger an email, a push
|
7
|
+
# notification, or just appear in the UI somewhere):
|
8
|
+
#
|
9
|
+
# class Comment < ActiveRecord::Base
|
10
|
+
# belongs_to :commentable, polymorphic: true
|
11
|
+
# after_create -> { Notification.create! comment: self,
|
12
|
+
# recipients: commentable.recipients }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# That's what you want the bulk of the time. New comment creates a new
|
16
|
+
# Notification. But there may well be off cases, like copying a commentable
|
17
|
+
# and its comments, where you don't want that. So you'd have a concern
|
18
|
+
# something like this:
|
19
|
+
#
|
20
|
+
# module Copyable
|
21
|
+
# def copy_to(destination)
|
22
|
+
# Notification.suppress do
|
23
|
+
# # Copy logic that creates new comments that we do not want
|
24
|
+
# # triggering notifications.
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
module Suppressor
|
29
|
+
extend ActiveSupport::Concern
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
def suppress(&block)
|
33
|
+
SuppressorRegistry.suppressed[name] = true
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
SuppressorRegistry.suppressed[name] = false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def save(*) # :nodoc:
|
41
|
+
SuppressorRegistry.suppressed[self.class.name] ? true : super
|
42
|
+
end
|
43
|
+
|
44
|
+
def save!(*) # :nodoc:
|
45
|
+
SuppressorRegistry.suppressed[self.class.name] ? true : super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class SuppressorRegistry # :nodoc:
|
50
|
+
extend ActiveSupport::PerThreadRegistry
|
51
|
+
|
52
|
+
attr_reader :suppressed
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@suppressed = {}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class TableMetadata # :nodoc:
|
3
|
+
delegate :foreign_type, :foreign_key, to: :association, prefix: true
|
4
|
+
delegate :association_primary_key, to: :association
|
5
|
+
|
6
|
+
def initialize(klass, arel_table, association = nil)
|
7
|
+
@klass = klass
|
8
|
+
@arel_table = arel_table
|
9
|
+
@association = association
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve_column_aliases(hash)
|
13
|
+
# This method is a hot spot, so for now, use Hash[] to dup the hash.
|
14
|
+
# https://bugs.ruby-lang.org/issues/7166
|
15
|
+
new_hash = Hash[hash]
|
16
|
+
hash.each do |key, _|
|
17
|
+
if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
|
18
|
+
new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
new_hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def arel_attribute(column_name)
|
25
|
+
if klass
|
26
|
+
klass.arel_attribute(column_name, arel_table)
|
27
|
+
else
|
28
|
+
arel_table[column_name]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def type(column_name)
|
33
|
+
if klass
|
34
|
+
klass.type_for_attribute(column_name.to_s)
|
35
|
+
else
|
36
|
+
Type::Value.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def associated_with?(association_name)
|
41
|
+
klass && klass._reflect_on_association(association_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def associated_table(table_name)
|
45
|
+
return self if table_name == arel_table.name
|
46
|
+
|
47
|
+
association = klass._reflect_on_association(table_name)
|
48
|
+
if association && !association.polymorphic?
|
49
|
+
association_klass = association.klass
|
50
|
+
arel_table = association_klass.arel_table.alias(table_name)
|
51
|
+
else
|
52
|
+
type_caster = TypeCaster::Connection.new(klass, table_name)
|
53
|
+
association_klass = nil
|
54
|
+
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
|
55
|
+
end
|
56
|
+
|
57
|
+
TableMetadata.new(association_klass, arel_table, association)
|
58
|
+
end
|
59
|
+
|
60
|
+
def polymorphic_association?
|
61
|
+
association && association.polymorphic?
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
attr_reader :klass, :arel_table, :association
|
67
|
+
end
|
68
|
+
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class DatabaseAlreadyExists < StandardError; end # :nodoc:
|
6
6
|
class DatabaseNotSupported < StandardError; end # :nodoc:
|
7
7
|
|
8
|
-
#
|
8
|
+
# ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
|
9
9
|
# logic behind common tasks used to manage database and migrations.
|
10
10
|
#
|
11
11
|
# The tasks defined here are used with Rake tasks provided by Active Record.
|
@@ -18,15 +18,15 @@ module ActiveRecord
|
|
18
18
|
#
|
19
19
|
# The possible config values are:
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
21
|
+
# * +env+: current environment (like Rails.env).
|
22
|
+
# * +database_configuration+: configuration of your databases (as in +config/database.yml+).
|
23
|
+
# * +db_dir+: your +db+ directory.
|
24
|
+
# * +fixtures_path+: a path to fixtures directory.
|
25
|
+
# * +migrations_paths+: a list of paths to directories with migrations.
|
26
|
+
# * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
|
27
|
+
# * +root+: a path to the root of the application.
|
28
28
|
#
|
29
|
-
# Example usage of
|
29
|
+
# Example usage of DatabaseTasks outside Rails could look as such:
|
30
30
|
#
|
31
31
|
# include ActiveRecord::Tasks
|
32
32
|
# DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
|
@@ -42,6 +42,22 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
|
44
44
|
|
45
|
+
def check_protected_environments!
|
46
|
+
unless ENV['DISABLE_DATABASE_ENVIRONMENT_CHECK']
|
47
|
+
current = ActiveRecord::Migrator.current_environment
|
48
|
+
stored = ActiveRecord::Migrator.last_stored_environment
|
49
|
+
|
50
|
+
if ActiveRecord::Migrator.protected_environment?
|
51
|
+
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
52
|
+
end
|
53
|
+
|
54
|
+
if stored && stored != current
|
55
|
+
raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue ActiveRecord::NoDatabaseError
|
59
|
+
end
|
60
|
+
|
45
61
|
def register_task(pattern, task)
|
46
62
|
@tasks ||= {}
|
47
63
|
@tasks[pattern] = task
|
@@ -91,15 +107,21 @@ module ActiveRecord
|
|
91
107
|
def create(*arguments)
|
92
108
|
configuration = arguments.first
|
93
109
|
class_for_adapter(configuration['adapter']).new(*arguments).create
|
110
|
+
$stdout.puts "Created database '#{configuration['database']}'"
|
94
111
|
rescue DatabaseAlreadyExists
|
95
|
-
$stderr.puts "#{configuration['database']} already exists"
|
112
|
+
$stderr.puts "Database '#{configuration['database']}' already exists"
|
96
113
|
rescue Exception => error
|
97
|
-
$stderr.puts error
|
114
|
+
$stderr.puts error
|
98
115
|
$stderr.puts "Couldn't create database for #{configuration.inspect}"
|
116
|
+
raise
|
99
117
|
end
|
100
118
|
|
101
119
|
def create_all
|
120
|
+
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
|
102
121
|
each_local_configuration { |configuration| create configuration }
|
122
|
+
if old_pool
|
123
|
+
ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec)
|
124
|
+
end
|
103
125
|
end
|
104
126
|
|
105
127
|
def create_current(environment = env)
|
@@ -112,11 +134,13 @@ module ActiveRecord
|
|
112
134
|
def drop(*arguments)
|
113
135
|
configuration = arguments.first
|
114
136
|
class_for_adapter(configuration['adapter']).new(*arguments).drop
|
137
|
+
$stdout.puts "Dropped database '#{configuration['database']}'"
|
115
138
|
rescue ActiveRecord::NoDatabaseError
|
116
139
|
$stderr.puts "Database '#{configuration['database']}' does not exist"
|
117
140
|
rescue Exception => error
|
118
|
-
$stderr.puts error
|
119
|
-
$stderr.puts "Couldn't drop #{configuration['database']}"
|
141
|
+
$stderr.puts error
|
142
|
+
$stderr.puts "Couldn't drop database '#{configuration['database']}'"
|
143
|
+
raise
|
120
144
|
end
|
121
145
|
|
122
146
|
def drop_all
|
@@ -130,8 +154,6 @@ module ActiveRecord
|
|
130
154
|
end
|
131
155
|
|
132
156
|
def migrate
|
133
|
-
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
|
134
|
-
|
135
157
|
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
136
158
|
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
137
159
|
scope = ENV['SCOPE']
|
@@ -191,27 +213,7 @@ module ActiveRecord
|
|
191
213
|
class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
|
192
214
|
end
|
193
215
|
|
194
|
-
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
195
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
196
|
-
This method will act on a specific connection in the future.
|
197
|
-
To act on the current connection, use `load_schema_current` instead.
|
198
|
-
MSG
|
199
|
-
|
200
|
-
load_schema_current(format, file)
|
201
|
-
end
|
202
|
-
|
203
|
-
def schema_file(format = ActiveRecord::Base.schema_format)
|
204
|
-
case format
|
205
|
-
when :ruby
|
206
|
-
File.join(db_dir, "schema.rb")
|
207
|
-
when :sql
|
208
|
-
File.join(db_dir, "structure.sql")
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# This method is the successor of +load_schema+. We should rename it
|
213
|
-
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
214
|
-
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
216
|
+
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
215
217
|
file ||= schema_file(format)
|
216
218
|
|
217
219
|
case format
|
@@ -225,24 +227,37 @@ module ActiveRecord
|
|
225
227
|
else
|
226
228
|
raise ArgumentError, "unknown format #{format.inspect}"
|
227
229
|
end
|
230
|
+
ActiveRecord::InternalMetadata.create_table
|
231
|
+
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
|
228
232
|
end
|
229
233
|
|
230
|
-
def
|
231
|
-
|
232
|
-
|
234
|
+
def load_schema_for(*args)
|
235
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
236
|
+
This method was renamed to `#load_schema` and will be removed in the future.
|
237
|
+
Use `#load_schema` instead.
|
238
|
+
MSG
|
239
|
+
load_schema(*args)
|
240
|
+
end
|
241
|
+
|
242
|
+
def schema_file(format = ActiveRecord::Base.schema_format)
|
243
|
+
case format
|
244
|
+
when :ruby
|
245
|
+
File.join(db_dir, "schema.rb")
|
246
|
+
when :sql
|
247
|
+
File.join(db_dir, "structure.sql")
|
233
248
|
end
|
234
249
|
end
|
235
250
|
|
236
251
|
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
237
252
|
each_current_configuration(environment) { |configuration|
|
238
|
-
|
253
|
+
load_schema configuration, format, file
|
239
254
|
}
|
240
255
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
241
256
|
end
|
242
257
|
|
243
258
|
def check_schema_file(filename)
|
244
259
|
unless File.exist?(filename)
|
245
|
-
message = %{#{filename} doesn't exist yet. Run `
|
260
|
+
message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
|
246
261
|
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
|
247
262
|
Kernel.abort message
|
248
263
|
end
|
@@ -270,8 +285,7 @@ module ActiveRecord
|
|
270
285
|
|
271
286
|
def each_current_configuration(environment)
|
272
287
|
environments = [environment]
|
273
|
-
|
274
|
-
environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
|
288
|
+
environments << 'test' if environment == 'development'
|
275
289
|
|
276
290
|
configurations = ActiveRecord::Base.configurations.values_at(*environments)
|
277
291
|
configurations.compact.each do |configuration|
|