activerecord 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  extend ActiveSupport::Concern
10
10
 
11
11
  module ClassMethods
12
- # Returns an <tt>ActiveRecord::Relation</tt> scope object.
12
+ # Returns an ActiveRecord::Relation scope object.
13
13
  #
14
14
  # posts = Post.all
15
15
  # posts.size # Fires "select count(*) from posts" and returns the count
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  # fruits = fruits.limit(10) if limited?
21
21
  #
22
22
  # You can define a scope that applies to all finders using
23
- # <tt>ActiveRecord::Base.default_scope</tt>.
23
+ # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
24
24
  def all
25
25
  if current_scope
26
26
  current_scope.clone
@@ -30,22 +30,22 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def default_scoped # :nodoc:
33
- relation.merge(build_default_scope)
34
- end
35
-
36
- # Collects attributes from scopes that should be applied when creating
37
- # an AR instance for the particular class this is called on.
38
- def scope_attributes # :nodoc:
39
- all.scope_for_create
40
- end
33
+ scope = build_default_scope
41
34
 
42
- # Are there default attributes associated with this scope?
43
- def scope_attributes? # :nodoc:
44
- current_scope || default_scopes.any?
35
+ if scope
36
+ relation.spawn.merge!(scope)
37
+ else
38
+ relation
39
+ end
45
40
  end
46
41
 
47
- # Adds a class method for retrieving and querying objects. A \scope
48
- # represents a narrowing of a database query, such as
42
+ # Adds a class method for retrieving and querying objects.
43
+ # The method is intended to return an ActiveRecord::Relation
44
+ # object, which is composable with other scopes.
45
+ # If it returns nil or false, an
46
+ # {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
47
+ #
48
+ # A \scope represents a narrowing of a database query, such as
49
49
  # <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
50
50
  #
51
51
  # class Shirt < ActiveRecord::Base
@@ -53,12 +53,12 @@ module ActiveRecord
53
53
  # scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
54
54
  # end
55
55
  #
56
- # The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
56
+ # The above calls to #scope define class methods <tt>Shirt.red</tt> and
57
57
  # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
58
58
  # represents the query <tt>Shirt.where(color: 'red')</tt>.
59
59
  #
60
60
  # You should always pass a callable object to the scopes defined
61
- # with +scope+. This ensures that the scope is re-evaluated each
61
+ # with #scope. This ensures that the scope is re-evaluated each
62
62
  # time it is called.
63
63
  #
64
64
  # Note that this is simply 'syntactic sugar' for defining an actual
@@ -71,14 +71,15 @@ module ActiveRecord
71
71
  # end
72
72
  #
73
73
  # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
74
- # <tt>Shirt.red</tt> is not an Array; 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>,
74
+ # <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
75
+ # which is composable with other scopes; it resembles the association object
76
+ # constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
77
+ # declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
77
78
  # <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
78
79
  # association objects, named \scopes act like an Array, implementing
79
80
  # Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
80
81
  # and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
81
- # <tt>Shirt.red</tt> really was an Array.
82
+ # <tt>Shirt.red</tt> really was an array.
82
83
  #
83
84
  # These named \scopes are composable. For instance,
84
85
  # <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
@@ -89,7 +90,8 @@ module ActiveRecord
89
90
  #
90
91
  # All scopes are available as class methods on the ActiveRecord::Base
91
92
  # descendant upon which the \scopes were defined. But they are also
92
- # available to +has_many+ associations. If,
93
+ # available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
94
+ # associations. If,
93
95
  #
94
96
  # class Person < ActiveRecord::Base
95
97
  # has_many :shirts
@@ -98,8 +100,8 @@ module ActiveRecord
98
100
  # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
99
101
  # Elton's red, dry clean only shirts.
100
102
  #
101
- # \Named scopes can also have extensions, just as with +has_many+
102
- # declarations:
103
+ # \Named scopes can also have extensions, just as with
104
+ # {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
103
105
  #
104
106
  # class Shirt < ActiveRecord::Base
105
107
  # scope :red, -> { where(color: 'red') } do
@@ -149,13 +151,32 @@ module ActiveRecord
149
151
  "a class method with the same name."
150
152
  end
151
153
 
154
+ valid_scope_name?(name)
152
155
  extension = Module.new(&block) if block
153
156
 
154
- singleton_class.send(:define_method, name) do |*args|
155
- scope = all.scoping { body.call(*args) }
156
- scope = scope.extending(extension) if extension
157
+ if body.respond_to?(:to_proc)
158
+ singleton_class.send(:define_method, name) do |*args|
159
+ scope = all.scoping { instance_exec(*args, &body) }
160
+ scope = scope.extending(extension) if extension
161
+
162
+ scope || all
163
+ end
164
+ else
165
+ singleton_class.send(:define_method, name) do |*args|
166
+ scope = all.scoping { body.call(*args) }
167
+ scope = scope.extending(extension) if extension
168
+
169
+ scope || all
170
+ end
171
+ end
172
+ end
173
+
174
+ protected
157
175
 
158
- scope || all
176
+ def valid_scope_name?(name)
177
+ if respond_to?(name, true)
178
+ logger.warn "Creating scope :#{name}. " \
179
+ "Overwriting existing method #{self.name}.#{name}."
159
180
  end
160
181
  end
161
182
  end
@@ -11,15 +11,26 @@ module ActiveRecord
11
11
 
12
12
  module ClassMethods
13
13
  def current_scope #:nodoc:
14
- ScopeRegistry.value_for(:current_scope, base_class.to_s)
14
+ ScopeRegistry.value_for(:current_scope, self)
15
15
  end
16
16
 
17
17
  def current_scope=(scope) #:nodoc:
18
- ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
18
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
19
+ end
20
+
21
+ # Collects attributes from scopes that should be applied when creating
22
+ # an AR instance for the particular class this is called on.
23
+ def scope_attributes # :nodoc:
24
+ all.scope_for_create
25
+ end
26
+
27
+ # Are there attributes associated with this scope?
28
+ def scope_attributes? # :nodoc:
29
+ current_scope
19
30
  end
20
31
  end
21
32
 
22
- def populate_with_current_scope_attributes
33
+ def populate_with_current_scope_attributes # :nodoc:
23
34
  return unless self.class.scope_attributes?
24
35
 
25
36
  self.class.scope_attributes.each do |att,value|
@@ -27,7 +38,7 @@ module ActiveRecord
27
38
  end
28
39
  end
29
40
 
30
- def initialize_internals_callback
41
+ def initialize_internals_callback # :nodoc:
31
42
  super
32
43
  populate_with_current_scope_attributes
33
44
  end
@@ -42,18 +53,18 @@ module ActiveRecord
42
53
  # following code:
43
54
  #
44
55
  # registry = ActiveRecord::Scoping::ScopeRegistry
45
- # registry.set_value_for(:current_scope, "Board", some_new_scope)
56
+ # registry.set_value_for(:current_scope, Board, some_new_scope)
46
57
  #
47
58
  # Now when you run:
48
59
  #
49
- # registry.value_for(:current_scope, "Board")
60
+ # registry.value_for(:current_scope, Board)
50
61
  #
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+
62
+ # You will obtain whatever was defined in +some_new_scope+. The #value_for
63
+ # and #set_value_for methods are delegated to the current ScopeRegistry
53
64
  # object, so the above example code can also be called as:
54
65
  #
55
66
  # ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
56
- # "Board", some_new_scope)
67
+ # Board, some_new_scope)
57
68
  class ScopeRegistry # :nodoc:
58
69
  extend ActiveSupport::PerThreadRegistry
59
70
 
@@ -63,16 +74,22 @@ module ActiveRecord
63
74
  @registry = Hash.new { |hash, key| hash[key] = {} }
64
75
  end
65
76
 
66
- # Obtains the value for a given +scope_name+ and +variable_name+.
67
- def value_for(scope_type, variable_name)
77
+ # Obtains the value for a given +scope_type+ and +model+.
78
+ def value_for(scope_type, model)
68
79
  raise_invalid_scope_type!(scope_type)
69
- @registry[scope_type][variable_name]
80
+ klass = model
81
+ base = model.base_class
82
+ while klass <= base
83
+ value = @registry[scope_type][klass.name]
84
+ return value if value
85
+ klass = klass.superclass
86
+ end
70
87
  end
71
88
 
72
- # Sets the +value+ for a given +scope_type+ and +variable_name+.
73
- def set_value_for(scope_type, variable_name, value)
89
+ # Sets the +value+ for a given +scope_type+ and +model+.
90
+ def set_value_for(scope_type, model, value)
74
91
  raise_invalid_scope_type!(scope_type)
75
- @registry[scope_type][variable_name] = value
92
+ @registry[scope_type][model.name] = value
76
93
  end
77
94
 
78
95
  private
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ module SecureToken
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ # Example using #has_secure_token
7
+ #
8
+ # # Schema: User(token:string, auth_token:string)
9
+ # class User < ActiveRecord::Base
10
+ # has_secure_token
11
+ # has_secure_token :auth_token
12
+ # end
13
+ #
14
+ # user = User.new
15
+ # user.save
16
+ # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
17
+ # user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
18
+ # user.regenerate_token # => true
19
+ # user.regenerate_auth_token # => true
20
+ #
21
+ # <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
22
+ #
23
+ # Note that it's still possible to generate a race condition in the database in the same way that
24
+ # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
25
+ # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
26
+ def has_secure_token(attribute = :token)
27
+ # Load securerandom only when has_secure_token is used.
28
+ require 'active_support/core_ext/securerandom'
29
+ define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
30
+ before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?")}
31
+ end
32
+
33
+ def generate_unique_secure_token
34
+ SecureRandom.base58(24)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord #:nodoc:
2
- # = Active Record Serialization
2
+ # = Active Record \Serialization
3
3
  module Serialization
4
4
  extend ActiveSupport::Concern
5
5
  include ActiveModel::Serializers::JSON
@@ -11,12 +11,10 @@ module ActiveRecord #:nodoc:
11
11
  def serializable_hash(options = nil)
12
12
  options = options.try(:clone) || {}
13
13
 
14
- options[:except] = Array(options[:except]).map { |n| n.to_s }
14
+ options[:except] = Array(options[:except]).map(&:to_s)
15
15
  options[:except] |= Array(self.class.inheritance_column)
16
16
 
17
17
  super(options)
18
18
  end
19
19
  end
20
20
  end
21
-
22
- require 'active_record/serializers/xml_serializer'
@@ -7,12 +7,14 @@ module ActiveRecord
7
7
  # Book.where(name: "my book").where("author_id > 3")
8
8
  # end
9
9
  #
10
- # The cached statement is executed by using the +execute+ method:
10
+ # The cached statement is executed by using the
11
+ # [connection.execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute} method:
11
12
  #
12
13
  # cache.execute([], Book, Book.connection)
13
14
  #
14
- # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
15
- # Database is queried when +to_a+ is called on the relation.
15
+ # The relation returned by the block is cached, and for each
16
+ # [execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute}
17
+ # call the cached relation gets duped. Database is queried when +to_a+ is called on the relation.
16
18
  #
17
19
  # If you want to cache the statement without the values you can use the +bind+ method of the
18
20
  # block parameter.
@@ -47,8 +49,8 @@ module ActiveRecord
47
49
 
48
50
  def sql_for(binds, connection)
49
51
  val = @values.dup
50
- binds = binds.dup
51
- @indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
52
+ binds = connection.prepare_binds_for_database(binds)
53
+ @indexes.each { |i| val[i] = connection.quote(binds.shift) }
52
54
  val.join
53
55
  end
54
56
  end
@@ -67,21 +69,21 @@ module ActiveRecord
67
69
  end
68
70
 
69
71
  class BindMap # :nodoc:
70
- def initialize(bind_values)
72
+ def initialize(bound_attributes)
71
73
  @indexes = []
72
- @bind_values = bind_values
74
+ @bound_attributes = bound_attributes
73
75
 
74
- bind_values.each_with_index do |(_, value), i|
75
- if Substitute === value
76
+ bound_attributes.each_with_index do |attr, i|
77
+ if Substitute === attr.value
76
78
  @indexes << i
77
79
  end
78
80
  end
79
81
  end
80
82
 
81
83
  def bind(values)
82
- bvs = @bind_values.map { |pair| pair.dup }
83
- @indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] }
84
- bvs
84
+ bas = @bound_attributes.dup
85
+ @indexes.each_with_index { |offset,i| bas[offset] = bas[offset].with_cast_value(values[i]) }
86
+ bas
85
87
  end
86
88
  end
87
89
 
@@ -89,7 +91,7 @@ module ActiveRecord
89
91
 
90
92
  def self.create(connection, block = Proc.new)
91
93
  relation = block.call Params.new
92
- bind_map = BindMap.new relation.bind_values
94
+ bind_map = BindMap.new relation.bound_attributes
93
95
  query_builder = connection.cacheable_query relation.arel
94
96
  new query_builder, bind_map
95
97
  end
@@ -104,7 +106,7 @@ module ActiveRecord
104
106
 
105
107
  sql = query_builder.sql_for bind_values, connection
106
108
 
107
- klass.find_by_sql sql, bind_values
109
+ klass.find_by_sql(sql, bind_values, preparable: true)
108
110
  end
109
111
  alias :call :execute
110
112
  end
@@ -15,11 +15,16 @@ module ActiveRecord
15
15
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
16
16
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
17
17
  #
18
- # NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
19
- # the serialization provided by +store+. Simply use +store_accessor+ instead to generate
18
+ # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
19
+ # the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
20
+ # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
20
21
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
21
22
  # using a symbol.
22
23
  #
24
+ # NOTE: The default validations with the exception of +uniqueness+ will work.
25
+ # For example, if you want to check for +uniqueness+ with +hstore+ you will
26
+ # need to use a custom validation to handle it.
27
+ #
23
28
  # Examples:
24
29
  #
25
30
  # class User < ActiveRecord::Base
@@ -39,7 +44,7 @@ module ActiveRecord
39
44
  # store_accessor :settings, :privileges, :servants
40
45
  # end
41
46
  #
42
- # The stored attribute names can be retrieved using +stored_attributes+.
47
+ # The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
43
48
  #
44
49
  # User.stored_attributes[:settings] # [:color, :homepage]
45
50
  #
@@ -0,0 +1,58 @@
1
+ module ActiveRecord
2
+ # ActiveRecord::Suppressor prevents the receiver from being saved during
3
+ # a given block.
4
+ #
5
+ # For example, here's a pattern of creating notifications when new comments
6
+ # are posted. (The notification may in turn trigger an email, a push
7
+ # notification, or just appear in the UI somewhere):
8
+ #
9
+ # class Comment < ActiveRecord::Base
10
+ # belongs_to :commentable, polymorphic: true
11
+ # after_create -> { Notification.create! comment: self,
12
+ # recipients: commentable.recipients }
13
+ # end
14
+ #
15
+ # That's what you want the bulk of the time. New comment creates a new
16
+ # Notification. But there may well be off cases, like copying a commentable
17
+ # and its comments, where you don't want that. So you'd have a concern
18
+ # something like this:
19
+ #
20
+ # module Copyable
21
+ # def copy_to(destination)
22
+ # Notification.suppress do
23
+ # # Copy logic that creates new comments that we do not want
24
+ # # triggering notifications.
25
+ # end
26
+ # end
27
+ # end
28
+ module Suppressor
29
+ extend ActiveSupport::Concern
30
+
31
+ module ClassMethods
32
+ def suppress(&block)
33
+ SuppressorRegistry.suppressed[name] = true
34
+ yield
35
+ ensure
36
+ SuppressorRegistry.suppressed[name] = false
37
+ end
38
+ end
39
+
40
+ def save(*) # :nodoc:
41
+ SuppressorRegistry.suppressed[self.class.name] ? true : super
42
+ end
43
+
44
+ def save!(*) # :nodoc:
45
+ SuppressorRegistry.suppressed[self.class.name] ? true : super
46
+ end
47
+ end
48
+
49
+ class SuppressorRegistry # :nodoc:
50
+ extend ActiveSupport::PerThreadRegistry
51
+
52
+ attr_reader :suppressed
53
+
54
+ def initialize
55
+ @suppressed = {}
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,68 @@
1
+ module ActiveRecord
2
+ class TableMetadata # :nodoc:
3
+ delegate :foreign_type, :foreign_key, to: :association, prefix: true
4
+ delegate :association_primary_key, to: :association
5
+
6
+ def initialize(klass, arel_table, association = nil)
7
+ @klass = klass
8
+ @arel_table = arel_table
9
+ @association = association
10
+ end
11
+
12
+ def resolve_column_aliases(hash)
13
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
14
+ # https://bugs.ruby-lang.org/issues/7166
15
+ new_hash = Hash[hash]
16
+ hash.each do |key, _|
17
+ if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
18
+ new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
19
+ end
20
+ end
21
+ new_hash
22
+ end
23
+
24
+ def arel_attribute(column_name)
25
+ if klass
26
+ klass.arel_attribute(column_name, arel_table)
27
+ else
28
+ arel_table[column_name]
29
+ end
30
+ end
31
+
32
+ def type(column_name)
33
+ if klass
34
+ klass.type_for_attribute(column_name.to_s)
35
+ else
36
+ Type::Value.new
37
+ end
38
+ end
39
+
40
+ def associated_with?(association_name)
41
+ klass && klass._reflect_on_association(association_name)
42
+ end
43
+
44
+ def associated_table(table_name)
45
+ return self if table_name == arel_table.name
46
+
47
+ association = klass._reflect_on_association(table_name)
48
+ if association && !association.polymorphic?
49
+ association_klass = association.klass
50
+ arel_table = association_klass.arel_table.alias(table_name)
51
+ else
52
+ type_caster = TypeCaster::Connection.new(klass, table_name)
53
+ association_klass = nil
54
+ arel_table = Arel::Table.new(table_name, type_caster: type_caster)
55
+ end
56
+
57
+ TableMetadata.new(association_klass, arel_table, association)
58
+ end
59
+
60
+ def polymorphic_association?
61
+ association && association.polymorphic?
62
+ end
63
+
64
+ protected
65
+
66
+ attr_reader :klass, :arel_table, :association
67
+ end
68
+ end