activerecord 3.2.22.5 → 5.2.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +657 -621
- data/MIT-LICENSE +2 -2
- data/README.rdoc +41 -46
- data/examples/performance.rb +55 -42
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +264 -236
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +127 -75
- data/lib/active_record/associations/association_scope.rb +126 -92
- data/lib/active_record/associations/belongs_to_association.rb +78 -27
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
- data/lib/active_record/associations/builder/association.rb +117 -32
- data/lib/active_record/associations/builder/belongs_to.rb +135 -60
- data/lib/active_record/associations/builder/collection_association.rb +61 -54
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
- data/lib/active_record/associations/builder/has_many.rb +10 -64
- data/lib/active_record/associations/builder/has_one.rb +19 -51
- data/lib/active_record/associations/builder/singular_association.rb +28 -18
- data/lib/active_record/associations/collection_association.rb +226 -293
- data/lib/active_record/associations/collection_proxy.rb +1067 -69
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +83 -47
- data/lib/active_record/associations/has_many_through_association.rb +98 -65
- data/lib/active_record/associations/has_one_association.rb +57 -20
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +212 -164
- data/lib/active_record/associations/preloader/association.rb +95 -89
- data/lib/active_record/associations/preloader/through_association.rb +84 -44
- data/lib/active_record/associations/preloader.rb +123 -111
- data/lib/active_record/associations/singular_association.rb +33 -24
- data/lib/active_record/associations/through_association.rb +60 -26
- data/lib/active_record/associations.rb +1759 -1506
- data/lib/active_record/attribute_assignment.rb +60 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
- data/lib/active_record/attribute_methods/dirty.rb +113 -74
- data/lib/active_record/attribute_methods/primary_key.rb +106 -77
- data/lib/active_record/attribute_methods/query.rb +8 -5
- data/lib/active_record/attribute_methods/read.rb +63 -114
- data/lib/active_record/attribute_methods/serialization.rb +60 -90
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
- data/lib/active_record/attribute_methods/write.rb +43 -45
- data/lib/active_record/attribute_methods.rb +366 -149
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +312 -225
- data/lib/active_record/base.rb +114 -505
- data/lib/active_record/callbacks.rb +145 -67
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
- data/lib/active_record/connection_adapters/column.rb +50 -255
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
- data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +200 -105
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -69
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +245 -60
- data/lib/active_record/explain.rb +35 -71
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -9
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +418 -275
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +209 -100
- data/lib/active_record/integration.rb +116 -21
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +107 -94
- data/lib/active_record/locking/pessimistic.rb +20 -8
- data/lib/active_record/log_subscriber.rb +99 -34
- data/lib/active_record/migration/command_recorder.rb +199 -64
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +893 -296
- data/lib/active_record/model_schema.rb +328 -175
- data/lib/active_record/nested_attributes.rb +338 -242
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +557 -170
- data/lib/active_record/query_cache.rb +14 -43
- data/lib/active_record/querying.rb +36 -24
- data/lib/active_record/railtie.rb +147 -52
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +13 -6
- data/lib/active_record/railties/databases.rake +206 -488
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +734 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +249 -52
- data/lib/active_record/relation/calculations.rb +330 -284
- data/lib/active_record/relation/delegation.rb +135 -37
- data/lib/active_record/relation/finder_methods.rb +450 -287
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +132 -43
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1037 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +48 -151
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +451 -359
- data/lib/active_record/result.rb +129 -20
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +164 -136
- data/lib/active_record/schema.rb +31 -19
- data/lib/active_record/schema_dumper.rb +154 -107
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping/default.rb +108 -98
- data/lib/active_record/scoping/named.rb +125 -112
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +175 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +80 -41
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +240 -119
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +79 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +133 -75
- data/lib/active_record/validations.rb +53 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +89 -57
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/migration.rb +28 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +141 -62
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,139 +1,130 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array"
|
4
|
+
require "active_support/core_ext/hash/except"
|
5
|
+
require "active_support/core_ext/kernel/singleton_class"
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
|
-
# = Active Record Named \Scopes
|
8
|
+
# = Active Record \Named \Scopes
|
9
9
|
module Scoping
|
10
10
|
module Named
|
11
11
|
extend ActiveSupport::Concern
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
# Returns an
|
14
|
+
# Returns an ActiveRecord::Relation scope object.
|
15
15
|
#
|
16
|
-
# posts = Post.
|
16
|
+
# posts = Post.all
|
17
17
|
# posts.size # Fires "select count(*) from posts" and returns the count
|
18
18
|
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
|
19
19
|
#
|
20
|
-
# fruits = Fruit.
|
21
|
-
# fruits = fruits.where(:
|
20
|
+
# fruits = Fruit.all
|
21
|
+
# fruits = fruits.where(color: 'red') if options[:red_only]
|
22
22
|
# fruits = fruits.limit(10) if limited?
|
23
23
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
if options
|
32
|
-
scoped.apply_finder_options(options)
|
33
|
-
else
|
34
|
-
if current_scope
|
24
|
+
# You can define a scope that applies to all finders using
|
25
|
+
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
26
|
+
def all
|
27
|
+
current_scope = self.current_scope
|
28
|
+
|
29
|
+
if current_scope
|
30
|
+
if self == current_scope.klass
|
35
31
|
current_scope.clone
|
36
32
|
else
|
37
|
-
|
38
|
-
scope.default_scoped = true
|
39
|
-
scope
|
33
|
+
relation.merge!(current_scope)
|
40
34
|
end
|
35
|
+
else
|
36
|
+
default_scoped
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
current_scope.scope_for_create
|
40
|
+
def scope_for_association(scope = relation) # :nodoc:
|
41
|
+
current_scope = self.current_scope
|
42
|
+
|
43
|
+
if current_scope && current_scope.empty_scope?
|
44
|
+
scope
|
50
45
|
else
|
51
|
-
scope
|
52
|
-
scope.default_scoped = true
|
53
|
-
scope.scope_for_create
|
46
|
+
default_scoped(scope)
|
54
47
|
end
|
55
48
|
end
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
50
|
+
def default_scoped(scope = relation) # :nodoc:
|
51
|
+
build_default_scope(scope) || scope
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_extensions # :nodoc:
|
55
|
+
if scope = current_scope || build_default_scope
|
56
|
+
scope.extensions
|
57
|
+
else
|
58
|
+
[]
|
59
|
+
end
|
61
60
|
end
|
62
61
|
|
63
|
-
# Adds a class method for retrieving and querying objects.
|
64
|
-
#
|
62
|
+
# Adds a class method for retrieving and querying objects.
|
63
|
+
# The method is intended to return an ActiveRecord::Relation
|
64
|
+
# object, which is composable with other scopes.
|
65
|
+
# If it returns +nil+ or +false+, an
|
66
|
+
# {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
|
67
|
+
#
|
68
|
+
# A \scope represents a narrowing of a database query, such as
|
69
|
+
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
65
70
|
#
|
66
71
|
# class Shirt < ActiveRecord::Base
|
67
|
-
# scope :red, where(:
|
68
|
-
# scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
|
72
|
+
# scope :red, -> { where(color: 'red') }
|
73
|
+
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
69
74
|
# end
|
70
75
|
#
|
71
|
-
# The above calls to
|
72
|
-
#
|
76
|
+
# The above calls to #scope define class methods <tt>Shirt.red</tt> and
|
77
|
+
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
|
78
|
+
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
73
79
|
#
|
74
|
-
#
|
80
|
+
# You should always pass a callable object to the scopes defined
|
81
|
+
# with #scope. This ensures that the scope is re-evaluated each
|
82
|
+
# time it is called.
|
83
|
+
#
|
84
|
+
# Note that this is simply 'syntactic sugar' for defining an actual
|
85
|
+
# class method:
|
75
86
|
#
|
76
87
|
# class Shirt < ActiveRecord::Base
|
77
88
|
# def self.red
|
78
|
-
# where(:
|
89
|
+
# where(color: 'red')
|
79
90
|
# end
|
80
91
|
# end
|
81
92
|
#
|
82
|
-
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
93
|
+
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
94
|
+
# <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
|
95
|
+
# which is composable with other scopes; it resembles the association object
|
96
|
+
# constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
97
|
+
# declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
98
|
+
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
99
|
+
# association objects, named \scopes act like an Array, implementing
|
100
|
+
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
101
|
+
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
102
|
+
# <tt>Shirt.red</tt> really was an array.
|
103
|
+
#
|
104
|
+
# These named \scopes are composable. For instance,
|
105
|
+
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
106
|
+
# both red and dry clean only. Nested finds and calculations also work
|
107
|
+
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
108
|
+
# returns the number of garments for which these criteria obtain.
|
109
|
+
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
110
|
+
#
|
111
|
+
# All scopes are available as class methods on the ActiveRecord::Base
|
112
|
+
# descendant upon which the \scopes were defined. But they are also
|
113
|
+
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
114
|
+
# associations. If,
|
97
115
|
#
|
98
116
|
# class Person < ActiveRecord::Base
|
99
117
|
# has_many :shirts
|
100
118
|
# end
|
101
119
|
#
|
102
|
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
103
|
-
# only shirts.
|
120
|
+
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
121
|
+
# Elton's red, dry clean only shirts.
|
104
122
|
#
|
105
|
-
# Named
|
123
|
+
# \Named scopes can also have extensions, just as with
|
124
|
+
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
106
125
|
#
|
107
126
|
# class Shirt < ActiveRecord::Base
|
108
|
-
# scope :
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
|
112
|
-
#
|
113
|
-
# On Ruby 1.9 you can use the 'stabby lambda' syntax:
|
114
|
-
#
|
115
|
-
# scope :colored, ->(color) { where(:color => color) }
|
116
|
-
#
|
117
|
-
# Note that scopes defined with \scope will be evaluated when they are defined, rather than
|
118
|
-
# when they are used. For example, the following would be incorrect:
|
119
|
-
#
|
120
|
-
# class Post < ActiveRecord::Base
|
121
|
-
# scope :recent, where('published_at >= ?', Time.current - 1.week)
|
122
|
-
# end
|
123
|
-
#
|
124
|
-
# The example above would be 'frozen' to the <tt>Time.current</tt> value when the <tt>Post</tt>
|
125
|
-
# class was defined, and so the resultant SQL query would always be the same. The correct
|
126
|
-
# way to do this would be via a lambda, which will re-evaluate the scope each time
|
127
|
-
# it is called:
|
128
|
-
#
|
129
|
-
# class Post < ActiveRecord::Base
|
130
|
-
# scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) }
|
131
|
-
# end
|
132
|
-
#
|
133
|
-
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
|
134
|
-
#
|
135
|
-
# class Shirt < ActiveRecord::Base
|
136
|
-
# scope :red, where(:color => 'red') do
|
127
|
+
# scope :red, -> { where(color: 'red') } do
|
137
128
|
# def dom_id
|
138
129
|
# 'red_shirts'
|
139
130
|
# end
|
@@ -143,18 +134,18 @@ module ActiveRecord
|
|
143
134
|
# Scopes can also be used while creating/building a record.
|
144
135
|
#
|
145
136
|
# class Article < ActiveRecord::Base
|
146
|
-
# scope :published, where(:
|
137
|
+
# scope :published, -> { where(published: true) }
|
147
138
|
# end
|
148
139
|
#
|
149
140
|
# Article.published.new.published # => true
|
150
141
|
# Article.published.create.published # => true
|
151
142
|
#
|
152
|
-
# Class methods on your model are automatically available
|
143
|
+
# \Class methods on your model are automatically available
|
153
144
|
# on scopes. Assuming the following setup:
|
154
145
|
#
|
155
146
|
# class Article < ActiveRecord::Base
|
156
|
-
# scope :published, where(:
|
157
|
-
# scope :featured, where(:
|
147
|
+
# scope :published, -> { where(published: true) }
|
148
|
+
# scope :featured, -> { where(featured: true) }
|
158
149
|
#
|
159
150
|
# def self.latest_article
|
160
151
|
# order('published_at desc').first
|
@@ -169,31 +160,53 @@ module ActiveRecord
|
|
169
160
|
#
|
170
161
|
# Article.published.featured.latest_article
|
171
162
|
# Article.featured.titles
|
172
|
-
def scope(name,
|
173
|
-
|
174
|
-
|
175
|
-
|
163
|
+
def scope(name, body, &block)
|
164
|
+
unless body.respond_to?(:call)
|
165
|
+
raise ArgumentError, "The scope body needs to be callable."
|
166
|
+
end
|
176
167
|
|
177
|
-
|
178
|
-
|
179
|
-
|
168
|
+
if dangerous_class_method?(name)
|
169
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
170
|
+
"on the model \"#{self.name}\", but Active Record already defined " \
|
171
|
+
"a class method with the same name."
|
172
|
+
end
|
180
173
|
|
181
|
-
|
174
|
+
if method_defined_within?(name, Relation)
|
175
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
176
|
+
"on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
|
177
|
+
"an instance method with the same name."
|
178
|
+
end
|
179
|
+
|
180
|
+
valid_scope_name?(name)
|
181
|
+
extension = Module.new(&block) if block
|
182
182
|
|
183
|
-
|
183
|
+
if body.respond_to?(:to_proc)
|
184
|
+
singleton_class.send(:define_method, name) do |*args|
|
185
|
+
scope = all
|
186
|
+
scope = scope._exec_scope(*args, &body)
|
187
|
+
scope = scope.extending(extension) if extension
|
188
|
+
scope
|
189
|
+
end
|
190
|
+
else
|
191
|
+
singleton_class.send(:define_method, name) do |*args|
|
192
|
+
scope = all
|
193
|
+
scope = scope.scoping { body.call(*args) || scope }
|
194
|
+
scope = scope.extending(extension) if extension
|
195
|
+
scope
|
196
|
+
end
|
184
197
|
end
|
185
198
|
|
186
|
-
|
199
|
+
generate_relation_method(name)
|
187
200
|
end
|
188
201
|
|
189
|
-
|
202
|
+
private
|
190
203
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
204
|
+
def valid_scope_name?(name)
|
205
|
+
if respond_to?(name, true) && logger
|
206
|
+
logger.warn "Creating scope :#{name}. " \
|
207
|
+
"Overwriting existing method #{self.name}.#{name}."
|
208
|
+
end
|
195
209
|
end
|
196
|
-
end
|
197
210
|
end
|
198
211
|
end
|
199
212
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/per_thread_registry"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Scoping
|
@@ -9,144 +11,96 @@ module ActiveRecord
|
|
9
11
|
include Named
|
10
12
|
end
|
11
13
|
|
12
|
-
module ClassMethods
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
# class Article < ActiveRecord::Base
|
18
|
-
# def self.create_with_scope
|
19
|
-
# with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
|
20
|
-
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
|
21
|
-
# a = create(1)
|
22
|
-
# a.blog_id # => 1
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
|
28
|
-
# <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
|
29
|
-
#
|
30
|
-
# <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
|
31
|
-
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
|
32
|
-
# array of strings format for your joins.
|
33
|
-
#
|
34
|
-
# class Article < ActiveRecord::Base
|
35
|
-
# def self.find_with_scope
|
36
|
-
# with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
|
37
|
-
# with_scope(:find => limit(10)) do
|
38
|
-
# all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
|
39
|
-
# end
|
40
|
-
# with_scope(:find => where(:author_id => 3)) do
|
41
|
-
# all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
|
48
|
-
#
|
49
|
-
# class Article < ActiveRecord::Base
|
50
|
-
# def self.find_with_exclusive_scope
|
51
|
-
# with_scope(:find => where(:blog_id => 1).limit(1)) do
|
52
|
-
# with_exclusive_scope(:find => limit(10)) do
|
53
|
-
# all # => SELECT * from articles LIMIT 10
|
54
|
-
# end
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
# *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
|
60
|
-
def with_scope(scope = {}, action = :merge, &block)
|
61
|
-
# If another Active Record class has been passed in, get its current scope
|
62
|
-
scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
|
63
|
-
|
64
|
-
previous_scope = self.current_scope
|
65
|
-
|
66
|
-
if scope.is_a?(Hash)
|
67
|
-
# Dup first and second level of hash (method and params).
|
68
|
-
scope = scope.dup
|
69
|
-
scope.each do |method, params|
|
70
|
-
scope[method] = params.dup unless params == true
|
71
|
-
end
|
72
|
-
|
73
|
-
scope.assert_valid_keys([ :find, :create ])
|
74
|
-
relation = construct_finder_arel(scope[:find] || {})
|
75
|
-
relation.default_scoped = true unless action == :overwrite
|
76
|
-
|
77
|
-
if previous_scope && previous_scope.create_with_value && scope[:create]
|
78
|
-
scope_for_create = if action == :merge
|
79
|
-
previous_scope.create_with_value.merge(scope[:create])
|
80
|
-
else
|
81
|
-
scope[:create]
|
82
|
-
end
|
83
|
-
|
84
|
-
relation = relation.create_with(scope_for_create)
|
85
|
-
else
|
86
|
-
scope_for_create = scope[:create]
|
87
|
-
scope_for_create ||= previous_scope.create_with_value if previous_scope
|
88
|
-
relation = relation.create_with(scope_for_create) if scope_for_create
|
89
|
-
end
|
90
|
-
|
91
|
-
scope = relation
|
92
|
-
end
|
93
|
-
|
94
|
-
scope = previous_scope.merge(scope) if previous_scope && action == :merge
|
14
|
+
module ClassMethods # :nodoc:
|
15
|
+
def current_scope(skip_inherited_scope = false)
|
16
|
+
ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
|
17
|
+
end
|
95
18
|
|
96
|
-
|
97
|
-
|
98
|
-
yield
|
99
|
-
ensure
|
100
|
-
self.current_scope = previous_scope
|
101
|
-
end
|
19
|
+
def current_scope=(scope)
|
20
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
102
21
|
end
|
103
22
|
|
104
|
-
|
23
|
+
# Collects attributes from scopes that should be applied when creating
|
24
|
+
# an AR instance for the particular class this is called on.
|
25
|
+
def scope_attributes
|
26
|
+
all.scope_for_create
|
27
|
+
end
|
105
28
|
|
106
|
-
#
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
|
29
|
+
# Are there attributes associated with this scope?
|
30
|
+
def scope_attributes?
|
31
|
+
current_scope
|
32
|
+
end
|
33
|
+
end
|
111
34
|
|
112
|
-
|
35
|
+
def populate_with_current_scope_attributes # :nodoc:
|
36
|
+
return unless self.class.scope_attributes?
|
113
37
|
|
114
|
-
|
38
|
+
attributes = self.class.scope_attributes
|
39
|
+
_assign_attributes(attributes) if attributes.any?
|
40
|
+
end
|
115
41
|
|
116
|
-
|
117
|
-
|
118
|
-
|
42
|
+
def initialize_internals_callback # :nodoc:
|
43
|
+
super
|
44
|
+
populate_with_current_scope_attributes
|
45
|
+
end
|
119
46
|
|
120
|
-
|
121
|
-
|
122
|
-
|
47
|
+
# This class stores the +:current_scope+ and +:ignore_default_scope+ values
|
48
|
+
# for different classes. The registry is stored as a thread local, which is
|
49
|
+
# accessed through +ScopeRegistry.current+.
|
50
|
+
#
|
51
|
+
# This class allows you to store and get the scope values on different
|
52
|
+
# classes and different types of scopes. For example, if you are attempting
|
53
|
+
# to get the current_scope for the +Board+ model, then you would use the
|
54
|
+
# following code:
|
55
|
+
#
|
56
|
+
# registry = ActiveRecord::Scoping::ScopeRegistry
|
57
|
+
# registry.set_value_for(:current_scope, Board, some_new_scope)
|
58
|
+
#
|
59
|
+
# Now when you run:
|
60
|
+
#
|
61
|
+
# registry.value_for(:current_scope, Board)
|
62
|
+
#
|
63
|
+
# You will obtain whatever was defined in +some_new_scope+. The #value_for
|
64
|
+
# and #set_value_for methods are delegated to the current ScopeRegistry
|
65
|
+
# object, so the above example code can also be called as:
|
66
|
+
#
|
67
|
+
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
68
|
+
# Board, some_new_scope)
|
69
|
+
class ScopeRegistry # :nodoc:
|
70
|
+
extend ActiveSupport::PerThreadRegistry
|
71
|
+
|
72
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
123
76
|
end
|
124
77
|
|
125
|
-
|
126
|
-
|
78
|
+
# Obtains the value for a given +scope_type+ and +model+.
|
79
|
+
def value_for(scope_type, model, skip_inherited_scope = false)
|
80
|
+
raise_invalid_scope_type!(scope_type)
|
81
|
+
return @registry[scope_type][model.name] if skip_inherited_scope
|
82
|
+
klass = model
|
83
|
+
base = model.base_class
|
84
|
+
while klass <= base
|
85
|
+
value = @registry[scope_type][klass.name]
|
86
|
+
return value if value
|
87
|
+
klass = klass.superclass
|
88
|
+
end
|
127
89
|
end
|
128
90
|
|
129
|
-
|
130
|
-
|
91
|
+
# Sets the +value+ for a given +scope_type+ and +model+.
|
92
|
+
def set_value_for(scope_type, model, value)
|
93
|
+
raise_invalid_scope_type!(scope_type)
|
94
|
+
@registry[scope_type][model.name] = value
|
131
95
|
end
|
132
96
|
|
133
97
|
private
|
134
98
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
142
|
-
|
143
|
-
def populate_with_current_scope_attributes
|
144
|
-
return unless self.class.scope_attributes?
|
145
|
-
|
146
|
-
self.class.scope_attributes.each do |att,value|
|
147
|
-
send("#{att}=", value) if respond_to?("#{att}=")
|
148
|
-
end
|
99
|
+
def raise_invalid_scope_type!(scope_type)
|
100
|
+
if !VALID_SCOPE_TYPES.include?(scope_type)
|
101
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
102
|
+
end
|
103
|
+
end
|
149
104
|
end
|
150
|
-
|
151
105
|
end
|
152
106
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module SecureToken
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Example using #has_secure_token
|
9
|
+
#
|
10
|
+
# # Schema: User(token:string, auth_token:string)
|
11
|
+
# class User < ActiveRecord::Base
|
12
|
+
# has_secure_token
|
13
|
+
# has_secure_token :auth_token
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# user = User.new
|
17
|
+
# user.save
|
18
|
+
# user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
|
19
|
+
# user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
|
20
|
+
# user.regenerate_token # => true
|
21
|
+
# user.regenerate_auth_token # => true
|
22
|
+
#
|
23
|
+
# <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
|
24
|
+
#
|
25
|
+
# Note that it's still possible to generate a race condition in the database in the same way that
|
26
|
+
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
|
27
|
+
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
|
28
|
+
def has_secure_token(attribute = :token)
|
29
|
+
# Load securerandom only when has_secure_token is used.
|
30
|
+
require "active_support/core_ext/securerandom"
|
31
|
+
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
|
32
|
+
before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_unique_secure_token
|
36
|
+
SecureRandom.base58(24)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,18 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord #:nodoc:
|
2
|
-
# = Active Record Serialization
|
4
|
+
# = Active Record \Serialization
|
3
5
|
module Serialization
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
include ActiveModel::Serializers::JSON
|
6
8
|
|
9
|
+
included do
|
10
|
+
self.include_root_in_json = false
|
11
|
+
end
|
12
|
+
|
7
13
|
def serializable_hash(options = nil)
|
8
|
-
options = options.try(:
|
14
|
+
options = options.try(:dup) || {}
|
9
15
|
|
10
|
-
options[:except] = Array
|
11
|
-
options[:except] |= Array
|
16
|
+
options[:except] = Array(options[:except]).map(&:to_s)
|
17
|
+
options[:except] |= Array(self.class.inheritance_column)
|
12
18
|
|
13
19
|
super(options)
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
17
|
-
|
18
|
-
require 'active_record/serializers/xml_serializer'
|