activerecord 3.2.19 → 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 +7 -0
- data/CHANGELOG.md +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- 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 +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- 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 +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- 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 +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- 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 +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- 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 +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- 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 +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- 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 +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- 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 +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -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/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- 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/has_and_belongs_to_many.rb +0 -60
- 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/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/model/templates/migration.rb +0 -15
- 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,110 @@
|
|
1
1
|
require 'active_support/core_ext/array'
|
2
2
|
require 'active_support/core_ext/hash/except'
|
3
3
|
require 'active_support/core_ext/kernel/singleton_class'
|
4
|
-
require 'active_support/core_ext/object/blank'
|
5
|
-
require 'active_support/core_ext/class/attribute'
|
6
4
|
|
7
5
|
module ActiveRecord
|
8
|
-
# = Active Record Named \Scopes
|
6
|
+
# = Active Record \Named \Scopes
|
9
7
|
module Scoping
|
10
8
|
module Named
|
11
9
|
extend ActiveSupport::Concern
|
12
10
|
|
13
11
|
module ClassMethods
|
14
|
-
# Returns an
|
12
|
+
# Returns an ActiveRecord::Relation scope object.
|
15
13
|
#
|
16
|
-
# posts = Post.
|
14
|
+
# posts = Post.all
|
17
15
|
# posts.size # Fires "select count(*) from posts" and returns the count
|
18
16
|
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
|
19
17
|
#
|
20
|
-
# fruits = Fruit.
|
21
|
-
# fruits = fruits.where(:
|
18
|
+
# fruits = Fruit.all
|
19
|
+
# fruits = fruits.where(color: 'red') if options[:red_only]
|
22
20
|
# fruits = fruits.limit(10) if limited?
|
23
21
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# ActiveRecord::Base.default_scope.
|
30
|
-
def scoped(options = nil)
|
31
|
-
if options
|
32
|
-
scoped.apply_finder_options(options)
|
22
|
+
# You can define a scope that applies to all finders using
|
23
|
+
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
24
|
+
def all
|
25
|
+
if current_scope
|
26
|
+
current_scope.clone
|
33
27
|
else
|
34
|
-
|
35
|
-
current_scope.clone
|
36
|
-
else
|
37
|
-
scope = relation
|
38
|
-
scope.default_scoped = true
|
39
|
-
scope
|
40
|
-
end
|
28
|
+
default_scoped
|
41
29
|
end
|
42
30
|
end
|
43
31
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
current_scope.scope_for_create
|
32
|
+
def default_scoped # :nodoc:
|
33
|
+
scope = build_default_scope
|
34
|
+
|
35
|
+
if scope
|
36
|
+
relation.spawn.merge!(scope)
|
50
37
|
else
|
51
|
-
|
52
|
-
scope.default_scoped = true
|
53
|
-
scope.scope_for_create
|
38
|
+
relation
|
54
39
|
end
|
55
40
|
end
|
56
41
|
|
57
|
-
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
64
|
-
#
|
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
|
+
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
65
50
|
#
|
66
51
|
# class Shirt < ActiveRecord::Base
|
67
|
-
# scope :red, where(:
|
68
|
-
# scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
|
52
|
+
# scope :red, -> { where(color: 'red') }
|
53
|
+
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
69
54
|
# end
|
70
55
|
#
|
71
|
-
# The above calls to
|
72
|
-
#
|
56
|
+
# The above calls to #scope define class methods <tt>Shirt.red</tt> and
|
57
|
+
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
|
58
|
+
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
73
59
|
#
|
74
|
-
#
|
60
|
+
# You should always pass a callable object to the scopes defined
|
61
|
+
# with #scope. This ensures that the scope is re-evaluated each
|
62
|
+
# time it is called.
|
63
|
+
#
|
64
|
+
# Note that this is simply 'syntactic sugar' for defining an actual
|
65
|
+
# class method:
|
75
66
|
#
|
76
67
|
# class Shirt < ActiveRecord::Base
|
77
68
|
# def self.red
|
78
|
-
# where(:
|
69
|
+
# where(color: 'red')
|
79
70
|
# end
|
80
71
|
# end
|
81
72
|
#
|
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
|
-
#
|
73
|
+
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
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>,
|
78
|
+
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
79
|
+
# association objects, named \scopes act like an Array, implementing
|
80
|
+
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
81
|
+
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
82
|
+
# <tt>Shirt.red</tt> really was an array.
|
83
|
+
#
|
84
|
+
# These named \scopes are composable. For instance,
|
85
|
+
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
86
|
+
# both red and dry clean only. Nested finds and calculations also work
|
87
|
+
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
88
|
+
# returns the number of garments for which these criteria obtain.
|
89
|
+
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
90
|
+
#
|
91
|
+
# All scopes are available as class methods on the ActiveRecord::Base
|
92
|
+
# descendant upon which the \scopes were defined. But they are also
|
93
|
+
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
94
|
+
# associations. If,
|
97
95
|
#
|
98
96
|
# class Person < ActiveRecord::Base
|
99
97
|
# has_many :shirts
|
100
98
|
# end
|
101
99
|
#
|
102
|
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
103
|
-
# only shirts.
|
104
|
-
#
|
105
|
-
# Named \scopes can also be procedural:
|
106
|
-
#
|
107
|
-
# class Shirt < ActiveRecord::Base
|
108
|
-
# scope :colored, lambda { |color| where(:color => color) }
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
|
100
|
+
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
101
|
+
# Elton's red, dry clean only shirts.
|
112
102
|
#
|
113
|
-
#
|
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:
|
103
|
+
# \Named scopes can also have extensions, just as with
|
104
|
+
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
134
105
|
#
|
135
106
|
# class Shirt < ActiveRecord::Base
|
136
|
-
# scope :red, where(:
|
107
|
+
# scope :red, -> { where(color: 'red') } do
|
137
108
|
# def dom_id
|
138
109
|
# 'red_shirts'
|
139
110
|
# end
|
@@ -143,18 +114,18 @@ module ActiveRecord
|
|
143
114
|
# Scopes can also be used while creating/building a record.
|
144
115
|
#
|
145
116
|
# class Article < ActiveRecord::Base
|
146
|
-
# scope :published, where(:
|
117
|
+
# scope :published, -> { where(published: true) }
|
147
118
|
# end
|
148
119
|
#
|
149
120
|
# Article.published.new.published # => true
|
150
121
|
# Article.published.create.published # => true
|
151
122
|
#
|
152
|
-
# Class methods on your model are automatically available
|
123
|
+
# \Class methods on your model are automatically available
|
153
124
|
# on scopes. Assuming the following setup:
|
154
125
|
#
|
155
126
|
# class Article < ActiveRecord::Base
|
156
|
-
# scope :published, where(:
|
157
|
-
# scope :featured, where(:
|
127
|
+
# scope :published, -> { where(published: true) }
|
128
|
+
# scope :featured, -> { where(featured: true) }
|
158
129
|
#
|
159
130
|
# def self.latest_article
|
160
131
|
# order('published_at desc').first
|
@@ -169,27 +140,41 @@ module ActiveRecord
|
|
169
140
|
#
|
170
141
|
# Article.published.featured.latest_article
|
171
142
|
# Article.featured.titles
|
172
|
-
def scope(name,
|
173
|
-
|
143
|
+
def scope(name, body, &block)
|
144
|
+
unless body.respond_to?(:call)
|
145
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
146
|
+
end
|
147
|
+
|
148
|
+
if dangerous_class_method?(name)
|
149
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
150
|
+
"on the model \"#{self.name}\", but Active Record already defined " \
|
151
|
+
"a class method with the same name."
|
152
|
+
end
|
153
|
+
|
174
154
|
valid_scope_name?(name)
|
175
|
-
extension = Module.new(&
|
155
|
+
extension = Module.new(&block) if block
|
176
156
|
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
180
161
|
|
181
|
-
|
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
|
182
168
|
|
183
|
-
|
169
|
+
scope || all
|
170
|
+
end
|
184
171
|
end
|
185
|
-
|
186
|
-
singleton_class.send(:redefine_method, name, &scope_proc)
|
187
172
|
end
|
188
173
|
|
189
174
|
protected
|
190
175
|
|
191
176
|
def valid_scope_name?(name)
|
192
|
-
if
|
177
|
+
if respond_to?(name, true)
|
193
178
|
logger.warn "Creating scope :#{name}. " \
|
194
179
|
"Overwriting existing method #{self.name}.#{name}."
|
195
180
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'active_support/
|
1
|
+
require 'active_support/per_thread_registry'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Scoping
|
@@ -10,143 +10,95 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
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
|
13
|
+
def current_scope #:nodoc:
|
14
|
+
ScopeRegistry.value_for(:current_scope, self)
|
15
|
+
end
|
95
16
|
|
96
|
-
|
97
|
-
|
98
|
-
yield
|
99
|
-
ensure
|
100
|
-
self.current_scope = previous_scope
|
101
|
-
end
|
17
|
+
def current_scope=(scope) #:nodoc:
|
18
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
102
19
|
end
|
103
20
|
|
104
|
-
|
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
|
105
26
|
|
106
|
-
#
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
|
27
|
+
# Are there attributes associated with this scope?
|
28
|
+
def scope_attributes? # :nodoc:
|
29
|
+
current_scope
|
30
|
+
end
|
31
|
+
end
|
111
32
|
|
112
|
-
|
33
|
+
def populate_with_current_scope_attributes # :nodoc:
|
34
|
+
return unless self.class.scope_attributes?
|
113
35
|
|
114
|
-
|
36
|
+
self.class.scope_attributes.each do |att,value|
|
37
|
+
send("#{att}=", value) if respond_to?("#{att}=")
|
38
|
+
end
|
39
|
+
end
|
115
40
|
|
116
|
-
|
117
|
-
|
118
|
-
|
41
|
+
def initialize_internals_callback # :nodoc:
|
42
|
+
super
|
43
|
+
populate_with_current_scope_attributes
|
44
|
+
end
|
119
45
|
|
120
|
-
|
121
|
-
|
122
|
-
|
46
|
+
# This class stores the +:current_scope+ and +:ignore_default_scope+ values
|
47
|
+
# for different classes. The registry is stored as a thread local, which is
|
48
|
+
# accessed through +ScopeRegistry.current+.
|
49
|
+
#
|
50
|
+
# This class allows you to store and get the scope values on different
|
51
|
+
# classes and different types of scopes. For example, if you are attempting
|
52
|
+
# to get the current_scope for the +Board+ model, then you would use the
|
53
|
+
# following code:
|
54
|
+
#
|
55
|
+
# registry = ActiveRecord::Scoping::ScopeRegistry
|
56
|
+
# registry.set_value_for(:current_scope, Board, some_new_scope)
|
57
|
+
#
|
58
|
+
# Now when you run:
|
59
|
+
#
|
60
|
+
# registry.value_for(:current_scope, Board)
|
61
|
+
#
|
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
|
64
|
+
# object, so the above example code can also be called as:
|
65
|
+
#
|
66
|
+
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
67
|
+
# Board, some_new_scope)
|
68
|
+
class ScopeRegistry # :nodoc:
|
69
|
+
extend ActiveSupport::PerThreadRegistry
|
70
|
+
|
71
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
123
75
|
end
|
124
76
|
|
125
|
-
|
126
|
-
|
77
|
+
# Obtains the value for a given +scope_type+ and +model+.
|
78
|
+
def value_for(scope_type, model)
|
79
|
+
raise_invalid_scope_type!(scope_type)
|
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
|
127
87
|
end
|
128
88
|
|
129
|
-
|
130
|
-
|
89
|
+
# Sets the +value+ for a given +scope_type+ and +model+.
|
90
|
+
def set_value_for(scope_type, model, value)
|
91
|
+
raise_invalid_scope_type!(scope_type)
|
92
|
+
@registry[scope_type][model.name] = value
|
131
93
|
end
|
132
94
|
|
133
95
|
private
|
134
96
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
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}=")
|
97
|
+
def raise_invalid_scope_type!(scope_type)
|
98
|
+
if !VALID_SCOPE_TYPES.include?(scope_type)
|
99
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
100
|
+
end
|
148
101
|
end
|
149
102
|
end
|
150
|
-
|
151
103
|
end
|
152
104
|
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,18 +1,20 @@
|
|
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
|
6
6
|
|
7
|
+
included do
|
8
|
+
self.include_root_in_json = false
|
9
|
+
end
|
10
|
+
|
7
11
|
def serializable_hash(options = nil)
|
8
12
|
options = options.try(:clone) || {}
|
9
13
|
|
10
|
-
options[:except] = Array
|
11
|
-
options[:except] |= Array
|
14
|
+
options[:except] = Array(options[:except]).map(&:to_s)
|
15
|
+
options[:except] |= Array(self.class.inheritance_column)
|
12
16
|
|
13
17
|
super(options)
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
17
|
-
|
18
|
-
require 'active_record/serializers/xml_serializer'
|