activerecord 3.2.22.5 → 4.2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1632 -609
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -41
- data/examples/performance.rb +31 -19
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +56 -42
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -36
- data/lib/active_record/associations/association.rb +73 -55
- data/lib/active_record/associations/association_scope.rb +143 -82
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +125 -31
- data/lib/active_record/associations/builder/belongs_to.rb +89 -61
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -51
- data/lib/active_record/associations/builder/singular_association.rb +23 -17
- data/lib/active_record/associations/collection_association.rb +251 -177
- data/lib/active_record/associations/collection_proxy.rb +963 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +113 -22
- data/lib/active_record/associations/has_many_through_association.rb +99 -39
- data/lib/active_record/associations/has_one_association.rb +43 -20
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
- 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 +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +62 -33
- data/lib/active_record/associations/preloader.rb +101 -79
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +30 -16
- data/lib/active_record/associations.rb +463 -345
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +142 -151
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +137 -57
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +73 -106
- data/lib/active_record/attribute_methods/serialization.rb +44 -94
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
- data/lib/active_record/attribute_methods/write.rb +57 -44
- data/lib/active_record/attribute_methods.rb +301 -141
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +246 -217
- data/lib/active_record/base.rb +70 -474
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
- data/lib/active_record/connection_adapters/column.rb +31 -245
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
- data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -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/float.rb +21 -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/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
- data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +157 -105
- data/lib/active_record/dynamic_matchers.rb +119 -63
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +94 -36
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +9 -5
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +302 -215
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +143 -70
- data/lib/active_record/integration.rb +65 -12
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +73 -52
- data/lib/active_record/locking/pessimistic.rb +5 -5
- data/lib/active_record/log_subscriber.rb +24 -21
- data/lib/active_record/migration/command_recorder.rb +124 -32
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +511 -213
- data/lib/active_record/model_schema.rb +91 -117
- data/lib/active_record/nested_attributes.rb +184 -130
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +276 -117
- data/lib/active_record/query_cache.rb +19 -37
- data/lib/active_record/querying.rb +28 -18
- data/lib/active_record/railtie.rb +73 -40
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +141 -416
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +1 -4
- data/lib/active_record/reflection.rb +513 -154
- data/lib/active_record/relation/batches.rb +91 -43
- data/lib/active_record/relation/calculations.rb +199 -161
- data/lib/active_record/relation/delegation.rb +116 -25
- data/lib/active_record/relation/finder_methods.rb +362 -248
- 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/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -43
- data/lib/active_record/relation/query_methods.rb +928 -167
- data/lib/active_record/relation/spawn_methods.rb +48 -149
- data/lib/active_record/relation.rb +352 -207
- data/lib/active_record/result.rb +101 -10
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +56 -59
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +106 -63
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +50 -57
- data/lib/active_record/scoping/named.rb +73 -109
- data/lib/active_record/scoping.rb +58 -123
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +12 -22
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +168 -15
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +23 -16
- data/lib/active_record/transactions.rb +125 -79
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +24 -16
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +123 -64
- data/lib/active_record/validations.rb +36 -29
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +66 -46
- data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +101 -45
- 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/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/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- 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,108 @@
|
|
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 <tt>ActiveRecord::Relation</tt> 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
|
+
# <tt>ActiveRecord::Base.default_scope</tt>.
|
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
|
-
|
32
|
+
def default_scoped # :nodoc:
|
33
|
+
relation.merge(build_default_scope)
|
34
|
+
end
|
35
|
+
|
45
36
|
# Collects attributes from scopes that should be applied when creating
|
46
37
|
# an AR instance for the particular class this is called on.
|
47
38
|
def scope_attributes # :nodoc:
|
48
|
-
|
49
|
-
current_scope.scope_for_create
|
50
|
-
else
|
51
|
-
scope = relation
|
52
|
-
scope.default_scoped = true
|
53
|
-
scope.scope_for_create
|
54
|
-
end
|
39
|
+
all.scope_for_create
|
55
40
|
end
|
56
41
|
|
57
|
-
##
|
58
42
|
# Are there default attributes associated with this scope?
|
59
43
|
def scope_attributes? # :nodoc:
|
60
44
|
current_scope || default_scopes.any?
|
61
45
|
end
|
62
46
|
|
63
|
-
# Adds a class method for retrieving and querying objects. A \scope
|
64
|
-
#
|
47
|
+
# Adds a class method for retrieving and querying objects. A \scope
|
48
|
+
# 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
|
-
# <tt>Shirt.red.
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
# <tt>Shirt.red.dry_clean_only
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# the
|
73
|
+
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
74
|
+
# <tt>Shirt.red</tt> is not an Array; it resembles the association object
|
75
|
+
# constructed by a +has_many+ declaration. For instance, you can invoke
|
76
|
+
# <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
77
|
+
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
78
|
+
# association objects, named \scopes act like an Array, implementing
|
79
|
+
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
80
|
+
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
81
|
+
# <tt>Shirt.red</tt> really was an Array.
|
82
|
+
#
|
83
|
+
# These named \scopes are composable. For instance,
|
84
|
+
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
85
|
+
# both red and dry clean only. Nested finds and calculations also work
|
86
|
+
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
87
|
+
# returns the number of garments for which these criteria obtain.
|
88
|
+
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
89
|
+
#
|
90
|
+
# All scopes are available as class methods on the ActiveRecord::Base
|
91
|
+
# descendant upon which the \scopes were defined. But they are also
|
92
|
+
# available to +has_many+ associations. If,
|
97
93
|
#
|
98
94
|
# class Person < ActiveRecord::Base
|
99
95
|
# has_many :shirts
|
100
96
|
# end
|
101
97
|
#
|
102
|
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
103
|
-
# only shirts.
|
98
|
+
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
99
|
+
# Elton's red, dry clean only shirts.
|
104
100
|
#
|
105
|
-
# Named
|
101
|
+
# \Named scopes can also have extensions, just as with +has_many+
|
102
|
+
# declarations:
|
106
103
|
#
|
107
104
|
# 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
|
105
|
+
# scope :red, -> { where(color: 'red') } do
|
137
106
|
# def dom_id
|
138
107
|
# 'red_shirts'
|
139
108
|
# end
|
@@ -143,18 +112,18 @@ module ActiveRecord
|
|
143
112
|
# Scopes can also be used while creating/building a record.
|
144
113
|
#
|
145
114
|
# class Article < ActiveRecord::Base
|
146
|
-
# scope :published, where(:
|
115
|
+
# scope :published, -> { where(published: true) }
|
147
116
|
# end
|
148
117
|
#
|
149
118
|
# Article.published.new.published # => true
|
150
119
|
# Article.published.create.published # => true
|
151
120
|
#
|
152
|
-
# Class methods on your model are automatically available
|
121
|
+
# \Class methods on your model are automatically available
|
153
122
|
# on scopes. Assuming the following setup:
|
154
123
|
#
|
155
124
|
# class Article < ActiveRecord::Base
|
156
|
-
# scope :published, where(:
|
157
|
-
# scope :featured, where(:
|
125
|
+
# scope :published, -> { where(published: true) }
|
126
|
+
# scope :featured, -> { where(featured: true) }
|
158
127
|
#
|
159
128
|
# def self.latest_article
|
160
129
|
# order('published_at desc').first
|
@@ -169,29 +138,24 @@ module ActiveRecord
|
|
169
138
|
#
|
170
139
|
# Article.published.featured.latest_article
|
171
140
|
# Article.featured.titles
|
172
|
-
def scope(name,
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
scope_proc = lambda do |*args|
|
178
|
-
options = scope_options.respond_to?(:call) ? unscoped { scope_options.call(*args) } : scope_options
|
179
|
-
options = scoped.apply_finder_options(options) if options.is_a?(Hash)
|
180
|
-
|
181
|
-
relation = scoped.merge(options)
|
141
|
+
def scope(name, body, &block)
|
142
|
+
unless body.respond_to?(:call)
|
143
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
144
|
+
end
|
182
145
|
|
183
|
-
|
146
|
+
if dangerous_class_method?(name)
|
147
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
148
|
+
"on the model \"#{self.name}\", but Active Record already defined " \
|
149
|
+
"a class method with the same name."
|
184
150
|
end
|
185
151
|
|
186
|
-
|
187
|
-
end
|
152
|
+
extension = Module.new(&block) if block
|
188
153
|
|
189
|
-
|
154
|
+
singleton_class.send(:define_method, name) do |*args|
|
155
|
+
scope = all.scoping { body.call(*args) }
|
156
|
+
scope = scope.extending(extension) if extension
|
190
157
|
|
191
|
-
|
192
|
-
if logger && respond_to?(name, true)
|
193
|
-
logger.warn "Creating scope :#{name}. " \
|
194
|
-
"Overwriting existing method #{self.name}.#{name}."
|
158
|
+
scope || all
|
195
159
|
end
|
196
160
|
end
|
197
161
|
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,78 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
|
14
|
-
|
15
|
-
# <tt>:create</tt> parameters are an attributes hash.
|
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
|
95
|
-
|
96
|
-
self.current_scope = scope
|
97
|
-
begin
|
98
|
-
yield
|
99
|
-
ensure
|
100
|
-
self.current_scope = previous_scope
|
101
|
-
end
|
13
|
+
def current_scope #:nodoc:
|
14
|
+
ScopeRegistry.value_for(:current_scope, base_class.to_s)
|
102
15
|
end
|
103
16
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
|
109
|
-
raise ArgumentError, <<-MSG
|
110
|
-
New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
|
17
|
+
def current_scope=(scope) #:nodoc:
|
18
|
+
ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
|
19
|
+
end
|
20
|
+
end
|
111
21
|
|
112
|
-
|
22
|
+
def populate_with_current_scope_attributes
|
23
|
+
return unless self.class.scope_attributes?
|
113
24
|
|
114
|
-
|
25
|
+
self.class.scope_attributes.each do |att,value|
|
26
|
+
send("#{att}=", value) if respond_to?("#{att}=")
|
27
|
+
end
|
28
|
+
end
|
115
29
|
|
116
|
-
|
117
|
-
|
118
|
-
|
30
|
+
def initialize_internals_callback
|
31
|
+
super
|
32
|
+
populate_with_current_scope_attributes
|
33
|
+
end
|
119
34
|
|
120
|
-
|
121
|
-
|
122
|
-
|
35
|
+
# This class stores the +:current_scope+ and +:ignore_default_scope+ values
|
36
|
+
# for different classes. The registry is stored as a thread local, which is
|
37
|
+
# accessed through +ScopeRegistry.current+.
|
38
|
+
#
|
39
|
+
# This class allows you to store and get the scope values on different
|
40
|
+
# classes and different types of scopes. For example, if you are attempting
|
41
|
+
# to get the current_scope for the +Board+ model, then you would use the
|
42
|
+
# following code:
|
43
|
+
#
|
44
|
+
# registry = ActiveRecord::Scoping::ScopeRegistry
|
45
|
+
# registry.set_value_for(:current_scope, "Board", some_new_scope)
|
46
|
+
#
|
47
|
+
# Now when you run:
|
48
|
+
#
|
49
|
+
# registry.value_for(:current_scope, "Board")
|
50
|
+
#
|
51
|
+
# You will obtain whatever was defined in +some_new_scope+. The +value_for+
|
52
|
+
# and +set_value_for+ methods are delegated to the current +ScopeRegistry+
|
53
|
+
# object, so the above example code can also be called as:
|
54
|
+
#
|
55
|
+
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
56
|
+
# "Board", some_new_scope)
|
57
|
+
class ScopeRegistry # :nodoc:
|
58
|
+
extend ActiveSupport::PerThreadRegistry
|
59
|
+
|
60
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
123
64
|
end
|
124
65
|
|
125
|
-
|
126
|
-
|
66
|
+
# Obtains the value for a given +scope_name+ and +variable_name+.
|
67
|
+
def value_for(scope_type, variable_name)
|
68
|
+
raise_invalid_scope_type!(scope_type)
|
69
|
+
@registry[scope_type][variable_name]
|
127
70
|
end
|
128
71
|
|
129
|
-
|
130
|
-
|
72
|
+
# Sets the +value+ for a given +scope_type+ and +variable_name+.
|
73
|
+
def set_value_for(scope_type, variable_name, value)
|
74
|
+
raise_invalid_scope_type!(scope_type)
|
75
|
+
@registry[scope_type][variable_name] = value
|
131
76
|
end
|
132
77
|
|
133
78
|
private
|
134
79
|
|
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}=")
|
80
|
+
def raise_invalid_scope_type!(scope_type)
|
81
|
+
if !VALID_SCOPE_TYPES.include?(scope_type)
|
82
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
83
|
+
end
|
148
84
|
end
|
149
85
|
end
|
150
|
-
|
151
86
|
end
|
152
87
|
end
|
@@ -4,11 +4,15 @@ module ActiveRecord #:nodoc:
|
|
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 { |n| n.to_s }
|
15
|
+
options[:except] |= Array(self.class.inheritance_column)
|
12
16
|
|
13
17
|
super(options)
|
14
18
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/array/wrap'
|
2
1
|
require 'active_support/core_ext/hash/conversions'
|
3
2
|
|
4
3
|
module ActiveRecord #:nodoc:
|
@@ -19,8 +18,8 @@ module ActiveRecord #:nodoc:
|
|
19
18
|
# <id type="integer">1</id>
|
20
19
|
# <approved type="boolean">false</approved>
|
21
20
|
# <replies-count type="integer">0</replies-count>
|
22
|
-
# <bonus-time type="
|
23
|
-
# <written-on type="
|
21
|
+
# <bonus-time type="dateTime">2000-01-01T08:28:00+12:00</bonus-time>
|
22
|
+
# <written-on type="dateTime">2003-07-16T09:28:00+1200</written-on>
|
24
23
|
# <content>Have a nice day</content>
|
25
24
|
# <author-email-address>david@loudthinking.com</author-email-address>
|
26
25
|
# <parent-id></parent-id>
|
@@ -37,7 +36,7 @@ module ActiveRecord #:nodoc:
|
|
37
36
|
#
|
38
37
|
# For instance:
|
39
38
|
#
|
40
|
-
# topic.to_xml(:
|
39
|
+
# topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])
|
41
40
|
#
|
42
41
|
# <topic>
|
43
42
|
# <title>The First Topic</title>
|
@@ -51,7 +50,7 @@ module ActiveRecord #:nodoc:
|
|
51
50
|
#
|
52
51
|
# To include first level associations use <tt>:include</tt>:
|
53
52
|
#
|
54
|
-
# firm.to_xml :
|
53
|
+
# firm.to_xml include: [ :account, :clients ]
|
55
54
|
#
|
56
55
|
# <?xml version="1.0" encoding="UTF-8"?>
|
57
56
|
# <firm>
|
@@ -82,7 +81,7 @@ module ActiveRecord #:nodoc:
|
|
82
81
|
# associated with models.
|
83
82
|
#
|
84
83
|
# proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
|
85
|
-
# firm.to_xml :
|
84
|
+
# firm.to_xml procs: [ proc ]
|
86
85
|
#
|
87
86
|
# <firm>
|
88
87
|
# # ... normal attributes as shown above ...
|
@@ -91,7 +90,7 @@ module ActiveRecord #:nodoc:
|
|
91
90
|
#
|
92
91
|
# To include deeper levels of associations pass a hash like this:
|
93
92
|
#
|
94
|
-
# firm.to_xml :
|
93
|
+
# firm.to_xml include: {account: {}, clients: {include: :address}}
|
95
94
|
# <?xml version="1.0" encoding="UTF-8"?>
|
96
95
|
# <firm>
|
97
96
|
# <id type="integer">1</id>
|
@@ -121,7 +120,7 @@ module ActiveRecord #:nodoc:
|
|
121
120
|
#
|
122
121
|
# To include any methods on the model being called use <tt>:methods</tt>:
|
123
122
|
#
|
124
|
-
# firm.to_xml :
|
123
|
+
# firm.to_xml methods: [ :calculated_earnings, :real_earnings ]
|
125
124
|
#
|
126
125
|
# <firm>
|
127
126
|
# # ... normal attributes as shown above ...
|
@@ -133,7 +132,7 @@ module ActiveRecord #:nodoc:
|
|
133
132
|
# modified version of the options hash that was given to +to_xml+:
|
134
133
|
#
|
135
134
|
# proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
|
136
|
-
# firm.to_xml :
|
135
|
+
# firm.to_xml procs: [ proc ]
|
137
136
|
#
|
138
137
|
# <firm>
|
139
138
|
# # ... normal attributes as shown above ...
|
@@ -165,7 +164,7 @@ module ActiveRecord #:nodoc:
|
|
165
164
|
# def to_xml(options = {})
|
166
165
|
# require 'builder'
|
167
166
|
# options[:indent] ||= 2
|
168
|
-
# xml = options[:builder] ||= ::Builder::XmlMarkup.new(:
|
167
|
+
# xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
|
169
168
|
# xml.instruct! unless options[:skip_instruct]
|
170
169
|
# xml.level_one do
|
171
170
|
# xml.tag!(:second_level, 'content')
|
@@ -178,21 +177,12 @@ module ActiveRecord #:nodoc:
|
|
178
177
|
end
|
179
178
|
|
180
179
|
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
|
181
|
-
def initialize(*args)
|
182
|
-
super
|
183
|
-
options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column)
|
184
|
-
end
|
185
|
-
|
186
180
|
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
|
187
181
|
def compute_type
|
188
182
|
klass = @serializable.class
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
klass.columns_hash[name].type
|
193
|
-
else
|
194
|
-
NilClass
|
195
|
-
end
|
183
|
+
column = klass.columns_hash[name] || Type::Value.new
|
184
|
+
|
185
|
+
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] || column.type
|
196
186
|
|
197
187
|
{ :text => :string,
|
198
188
|
:time => :datetime }[type] || type
|