activerecord 4.2.0 → 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 +4 -4
- data/CHANGELOG.md +1537 -789
- 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/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- 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 +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- 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 +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- 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/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- 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 +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- 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 +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- 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 +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- 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/type_map_initializer.rb +17 -5
- 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/oid.rb +1 -6
- 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 +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- 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 +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- 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 +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -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/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- 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/relation.rb +176 -115
- 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 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- 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 +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- 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 +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- 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 +21 -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.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- 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 +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- 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 +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- 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 -101
@@ -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
|
@@ -11,15 +11,26 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
module ClassMethods
|
13
13
|
def current_scope #:nodoc:
|
14
|
-
ScopeRegistry.value_for(:current_scope,
|
14
|
+
ScopeRegistry.value_for(:current_scope, self)
|
15
15
|
end
|
16
16
|
|
17
17
|
def current_scope=(scope) #:nodoc:
|
18
|
-
ScopeRegistry.set_value_for(:current_scope,
|
18
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Collects attributes from scopes that should be applied when creating
|
22
|
+
# an AR instance for the particular class this is called on.
|
23
|
+
def scope_attributes # :nodoc:
|
24
|
+
all.scope_for_create
|
25
|
+
end
|
26
|
+
|
27
|
+
# Are there attributes associated with this scope?
|
28
|
+
def scope_attributes? # :nodoc:
|
29
|
+
current_scope
|
19
30
|
end
|
20
31
|
end
|
21
32
|
|
22
|
-
def populate_with_current_scope_attributes
|
33
|
+
def populate_with_current_scope_attributes # :nodoc:
|
23
34
|
return unless self.class.scope_attributes?
|
24
35
|
|
25
36
|
self.class.scope_attributes.each do |att,value|
|
@@ -27,7 +38,7 @@ module ActiveRecord
|
|
27
38
|
end
|
28
39
|
end
|
29
40
|
|
30
|
-
def initialize_internals_callback
|
41
|
+
def initialize_internals_callback # :nodoc:
|
31
42
|
super
|
32
43
|
populate_with_current_scope_attributes
|
33
44
|
end
|
@@ -42,18 +53,18 @@ module ActiveRecord
|
|
42
53
|
# following code:
|
43
54
|
#
|
44
55
|
# registry = ActiveRecord::Scoping::ScopeRegistry
|
45
|
-
# registry.set_value_for(:current_scope,
|
56
|
+
# registry.set_value_for(:current_scope, Board, some_new_scope)
|
46
57
|
#
|
47
58
|
# Now when you run:
|
48
59
|
#
|
49
|
-
# registry.value_for(:current_scope,
|
60
|
+
# registry.value_for(:current_scope, Board)
|
50
61
|
#
|
51
|
-
# You will obtain whatever was defined in +some_new_scope+. The
|
52
|
-
# and
|
62
|
+
# You will obtain whatever was defined in +some_new_scope+. The #value_for
|
63
|
+
# and #set_value_for methods are delegated to the current ScopeRegistry
|
53
64
|
# object, so the above example code can also be called as:
|
54
65
|
#
|
55
66
|
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
56
|
-
#
|
67
|
+
# Board, some_new_scope)
|
57
68
|
class ScopeRegistry # :nodoc:
|
58
69
|
extend ActiveSupport::PerThreadRegistry
|
59
70
|
|
@@ -63,16 +74,22 @@ module ActiveRecord
|
|
63
74
|
@registry = Hash.new { |hash, key| hash[key] = {} }
|
64
75
|
end
|
65
76
|
|
66
|
-
# Obtains the value for a given +
|
67
|
-
def value_for(scope_type,
|
77
|
+
# Obtains the value for a given +scope_type+ and +model+.
|
78
|
+
def value_for(scope_type, model)
|
68
79
|
raise_invalid_scope_type!(scope_type)
|
69
|
-
|
80
|
+
klass = model
|
81
|
+
base = model.base_class
|
82
|
+
while klass <= base
|
83
|
+
value = @registry[scope_type][klass.name]
|
84
|
+
return value if value
|
85
|
+
klass = klass.superclass
|
86
|
+
end
|
70
87
|
end
|
71
88
|
|
72
|
-
# Sets the +value+ for a given +scope_type+ and +
|
73
|
-
def set_value_for(scope_type,
|
89
|
+
# Sets the +value+ for a given +scope_type+ and +model+.
|
90
|
+
def set_value_for(scope_type, model, value)
|
74
91
|
raise_invalid_scope_type!(scope_type)
|
75
|
-
@registry[scope_type][
|
92
|
+
@registry[scope_type][model.name] = value
|
76
93
|
end
|
77
94
|
|
78
95
|
private
|
@@ -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
|