activerecord 4.2.11 → 5.2.4.1

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +580 -1626
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,6 +1,8 @@
1
- require 'active_support/core_ext/array'
2
- require 'active_support/core_ext/hash/except'
3
- require 'active_support/core_ext/kernel/singleton_class'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array"
4
+ require "active_support/core_ext/hash/except"
5
+ require "active_support/core_ext/kernel/singleton_class"
4
6
 
5
7
  module ActiveRecord
6
8
  # = Active Record \Named \Scopes
@@ -9,7 +11,7 @@ module ActiveRecord
9
11
  extend ActiveSupport::Concern
10
12
 
11
13
  module ClassMethods
12
- # Returns an <tt>ActiveRecord::Relation</tt> scope object.
14
+ # Returns an ActiveRecord::Relation scope object.
13
15
  #
14
16
  # posts = Post.all
15
17
  # posts.size # Fires "select count(*) from posts" and returns the count
@@ -20,32 +22,50 @@ module ActiveRecord
20
22
  # fruits = fruits.limit(10) if limited?
21
23
  #
22
24
  # You can define a scope that applies to all finders using
23
- # <tt>ActiveRecord::Base.default_scope</tt>.
25
+ # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
24
26
  def all
27
+ current_scope = self.current_scope
28
+
25
29
  if current_scope
26
- current_scope.clone
30
+ if self == current_scope.klass
31
+ current_scope.clone
32
+ else
33
+ relation.merge!(current_scope)
34
+ end
27
35
  else
28
36
  default_scoped
29
37
  end
30
38
  end
31
39
 
32
- def default_scoped # :nodoc:
33
- relation.merge(build_default_scope)
40
+ def scope_for_association(scope = relation) # :nodoc:
41
+ current_scope = self.current_scope
42
+
43
+ if current_scope && current_scope.empty_scope?
44
+ scope
45
+ else
46
+ default_scoped(scope)
47
+ end
34
48
  end
35
49
 
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
50
+ def default_scoped(scope = relation) # :nodoc:
51
+ build_default_scope(scope) || scope
40
52
  end
41
53
 
42
- # Are there default attributes associated with this scope?
43
- def scope_attributes? # :nodoc:
44
- current_scope || default_scopes.any?
54
+ def default_extensions # :nodoc:
55
+ if scope = current_scope || build_default_scope
56
+ scope.extensions
57
+ else
58
+ []
59
+ end
45
60
  end
46
61
 
47
- # Adds a class method for retrieving and querying objects. A \scope
48
- # represents a narrowing of a database query, such as
62
+ # Adds a class method for retrieving and querying objects.
63
+ # The method is intended to return an ActiveRecord::Relation
64
+ # object, which is composable with other scopes.
65
+ # If it returns +nil+ or +false+, an
66
+ # {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
67
+ #
68
+ # A \scope represents a narrowing of a database query, such as
49
69
  # <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
50
70
  #
51
71
  # class Shirt < ActiveRecord::Base
@@ -53,12 +73,12 @@ module ActiveRecord
53
73
  # scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
54
74
  # end
55
75
  #
56
- # The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
76
+ # The above calls to #scope define class methods <tt>Shirt.red</tt> and
57
77
  # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
58
78
  # represents the query <tt>Shirt.where(color: 'red')</tt>.
59
79
  #
60
80
  # You should always pass a callable object to the scopes defined
61
- # with +scope+. This ensures that the scope is re-evaluated each
81
+ # with #scope. This ensures that the scope is re-evaluated each
62
82
  # time it is called.
63
83
  #
64
84
  # Note that this is simply 'syntactic sugar' for defining an actual
@@ -71,14 +91,15 @@ module ActiveRecord
71
91
  # end
72
92
  #
73
93
  # 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>,
94
+ # <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
95
+ # which is composable with other scopes; it resembles the association object
96
+ # constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
97
+ # declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
77
98
  # <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
78
99
  # association objects, named \scopes act like an Array, implementing
79
100
  # Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
80
101
  # and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
81
- # <tt>Shirt.red</tt> really was an Array.
102
+ # <tt>Shirt.red</tt> really was an array.
82
103
  #
83
104
  # These named \scopes are composable. For instance,
84
105
  # <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
@@ -89,7 +110,8 @@ module ActiveRecord
89
110
  #
90
111
  # All scopes are available as class methods on the ActiveRecord::Base
91
112
  # descendant upon which the \scopes were defined. But they are also
92
- # available to +has_many+ associations. If,
113
+ # available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
114
+ # associations. If,
93
115
  #
94
116
  # class Person < ActiveRecord::Base
95
117
  # has_many :shirts
@@ -98,8 +120,8 @@ module ActiveRecord
98
120
  # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
99
121
  # Elton's red, dry clean only shirts.
100
122
  #
101
- # \Named scopes can also have extensions, just as with +has_many+
102
- # declarations:
123
+ # \Named scopes can also have extensions, just as with
124
+ # {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
103
125
  #
104
126
  # class Shirt < ActiveRecord::Base
105
127
  # scope :red, -> { where(color: 'red') } do
@@ -140,7 +162,7 @@ module ActiveRecord
140
162
  # Article.featured.titles
141
163
  def scope(name, body, &block)
142
164
  unless body.respond_to?(:call)
143
- raise ArgumentError, 'The scope body needs to be callable.'
165
+ raise ArgumentError, "The scope body needs to be callable."
144
166
  end
145
167
 
146
168
  if dangerous_class_method?(name)
@@ -149,15 +171,42 @@ module ActiveRecord
149
171
  "a class method with the same name."
150
172
  end
151
173
 
152
- extension = Module.new(&block) if block
174
+ if method_defined_within?(name, Relation)
175
+ raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
176
+ "on the model \"#{self.name}\", but ActiveRecord::Relation already defined " \
177
+ "an instance method with the same name."
178
+ end
153
179
 
154
- singleton_class.send(:define_method, name) do |*args|
155
- scope = all.scoping { body.call(*args) }
156
- scope = scope.extending(extension) if extension
180
+ valid_scope_name?(name)
181
+ extension = Module.new(&block) if block
157
182
 
158
- scope || all
183
+ if body.respond_to?(:to_proc)
184
+ singleton_class.send(:define_method, name) do |*args|
185
+ scope = all
186
+ scope = scope._exec_scope(*args, &body)
187
+ scope = scope.extending(extension) if extension
188
+ scope
189
+ end
190
+ else
191
+ singleton_class.send(:define_method, name) do |*args|
192
+ scope = all
193
+ scope = scope.scoping { body.call(*args) || scope }
194
+ scope = scope.extending(extension) if extension
195
+ scope
196
+ end
159
197
  end
198
+
199
+ generate_relation_method(name)
160
200
  end
201
+
202
+ private
203
+
204
+ def valid_scope_name?(name)
205
+ if respond_to?(name, true) && logger
206
+ logger.warn "Creating scope :#{name}. " \
207
+ "Overwriting existing method #{self.name}.#{name}."
208
+ end
209
+ end
161
210
  end
162
211
  end
163
212
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/per_thread_registry'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/per_thread_registry"
2
4
 
3
5
  module ActiveRecord
4
6
  module Scoping
@@ -9,25 +11,35 @@ module ActiveRecord
9
11
  include Named
10
12
  end
11
13
 
12
- module ClassMethods
13
- def current_scope #:nodoc:
14
- ScopeRegistry.value_for(:current_scope, base_class.to_s)
14
+ module ClassMethods # :nodoc:
15
+ def current_scope(skip_inherited_scope = false)
16
+ ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
17
+ end
18
+
19
+ def current_scope=(scope)
20
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
21
+ end
22
+
23
+ # Collects attributes from scopes that should be applied when creating
24
+ # an AR instance for the particular class this is called on.
25
+ def scope_attributes
26
+ all.scope_for_create
15
27
  end
16
28
 
17
- def current_scope=(scope) #:nodoc:
18
- ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
29
+ # Are there attributes associated with this scope?
30
+ def scope_attributes?
31
+ current_scope
19
32
  end
20
33
  end
21
34
 
22
- def populate_with_current_scope_attributes
35
+ def populate_with_current_scope_attributes # :nodoc:
23
36
  return unless self.class.scope_attributes?
24
37
 
25
- self.class.scope_attributes.each do |att,value|
26
- send("#{att}=", value) if respond_to?("#{att}=")
27
- end
38
+ attributes = self.class.scope_attributes
39
+ _assign_attributes(attributes) if attributes.any?
28
40
  end
29
41
 
30
- def initialize_internals_callback
42
+ def initialize_internals_callback # :nodoc:
31
43
  super
32
44
  populate_with_current_scope_attributes
33
45
  end
@@ -42,18 +54,18 @@ module ActiveRecord
42
54
  # following code:
43
55
  #
44
56
  # registry = ActiveRecord::Scoping::ScopeRegistry
45
- # registry.set_value_for(:current_scope, "Board", some_new_scope)
57
+ # registry.set_value_for(:current_scope, Board, some_new_scope)
46
58
  #
47
59
  # Now when you run:
48
60
  #
49
- # registry.value_for(:current_scope, "Board")
61
+ # registry.value_for(:current_scope, Board)
50
62
  #
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+
63
+ # You will obtain whatever was defined in +some_new_scope+. The #value_for
64
+ # and #set_value_for methods are delegated to the current ScopeRegistry
53
65
  # object, so the above example code can also be called as:
54
66
  #
55
67
  # ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
56
- # "Board", some_new_scope)
68
+ # Board, some_new_scope)
57
69
  class ScopeRegistry # :nodoc:
58
70
  extend ActiveSupport::PerThreadRegistry
59
71
 
@@ -63,25 +75,32 @@ module ActiveRecord
63
75
  @registry = Hash.new { |hash, key| hash[key] = {} }
64
76
  end
65
77
 
66
- # Obtains the value for a given +scope_name+ and +variable_name+.
67
- def value_for(scope_type, variable_name)
78
+ # Obtains the value for a given +scope_type+ and +model+.
79
+ def value_for(scope_type, model, skip_inherited_scope = false)
68
80
  raise_invalid_scope_type!(scope_type)
69
- @registry[scope_type][variable_name]
81
+ return @registry[scope_type][model.name] if skip_inherited_scope
82
+ klass = model
83
+ base = model.base_class
84
+ while klass <= base
85
+ value = @registry[scope_type][klass.name]
86
+ return value if value
87
+ klass = klass.superclass
88
+ end
70
89
  end
71
90
 
72
- # Sets the +value+ for a given +scope_type+ and +variable_name+.
73
- def set_value_for(scope_type, variable_name, value)
91
+ # Sets the +value+ for a given +scope_type+ and +model+.
92
+ def set_value_for(scope_type, model, value)
74
93
  raise_invalid_scope_type!(scope_type)
75
- @registry[scope_type][variable_name] = value
94
+ @registry[scope_type][model.name] = value
76
95
  end
77
96
 
78
97
  private
79
98
 
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"
99
+ def raise_invalid_scope_type!(scope_type)
100
+ if !VALID_SCOPE_TYPES.include?(scope_type)
101
+ raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
102
+ end
83
103
  end
84
- end
85
104
  end
86
105
  end
87
106
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module SecureToken
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Example using #has_secure_token
9
+ #
10
+ # # Schema: User(token:string, auth_token:string)
11
+ # class User < ActiveRecord::Base
12
+ # has_secure_token
13
+ # has_secure_token :auth_token
14
+ # end
15
+ #
16
+ # user = User.new
17
+ # user.save
18
+ # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
19
+ # user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
20
+ # user.regenerate_token # => true
21
+ # user.regenerate_auth_token # => true
22
+ #
23
+ # <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
24
+ #
25
+ # Note that it's still possible to generate a race condition in the database in the same way that
26
+ # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
27
+ # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
28
+ def has_secure_token(attribute = :token)
29
+ # Load securerandom only when has_secure_token is used.
30
+ require "active_support/core_ext/securerandom"
31
+ define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
32
+ before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
33
+ end
34
+
35
+ def generate_unique_secure_token
36
+ SecureRandom.base58(24)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord #:nodoc:
2
- # = Active Record Serialization
4
+ # = Active Record \Serialization
3
5
  module Serialization
4
6
  extend ActiveSupport::Concern
5
7
  include ActiveModel::Serializers::JSON
@@ -9,14 +11,12 @@ module ActiveRecord #:nodoc:
9
11
  end
10
12
 
11
13
  def serializable_hash(options = nil)
12
- options = options.try(:clone) || {}
14
+ options = options.try(:dup) || {}
13
15
 
14
- options[:except] = Array(options[:except]).map { |n| n.to_s }
16
+ options[:except] = Array(options[:except]).map(&:to_s)
15
17
  options[:except] |= Array(self.class.inheritance_column)
16
18
 
17
19
  super(options)
18
20
  end
19
21
  end
20
22
  end
21
-
22
- require 'active_record/serializers/xml_serializer'
@@ -1,5 +1,6 @@
1
- module ActiveRecord
1
+ # frozen_string_literal: true
2
2
 
3
+ module ActiveRecord
3
4
  # Statement cache is used to cache a single statement in order to avoid creating the AST again.
4
5
  # Initializing the cache is done by passing the statement in the create block:
5
6
  #
@@ -7,12 +8,14 @@ module ActiveRecord
7
8
  # Book.where(name: "my book").where("author_id > 3")
8
9
  # end
9
10
  #
10
- # The cached statement is executed by using the +execute+ method:
11
+ # The cached statement is executed by using the
12
+ # {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
11
13
  #
12
- # cache.execute([], Book, Book.connection)
14
+ # cache.execute([], Book.connection)
13
15
  #
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.
16
+ # The relation returned by the block is cached, and for each
17
+ # {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
18
+ # call the cached relation gets duped. Database is queried when +to_a+ is called on the relation.
16
19
  #
17
20
  # If you want to cache the statement without the values you can use the +bind+ method of the
18
21
  # block parameter.
@@ -23,7 +26,7 @@ module ActiveRecord
23
26
  #
24
27
  # And pass the bind values as the first argument of +execute+ call.
25
28
  #
26
- # cache.execute(["my book"], Book, Book.connection)
29
+ # cache.execute(["my book"], Book.connection)
27
30
  class StatementCache # :nodoc:
28
31
  class Substitute; end # :nodoc:
29
32
 
@@ -38,28 +41,27 @@ module ActiveRecord
38
41
  end
39
42
 
40
43
  class PartialQuery < Query # :nodoc:
41
- def initialize values
44
+ def initialize(values)
42
45
  @values = values
43
- @indexes = values.each_with_index.find_all { |thing,i|
46
+ @indexes = values.each_with_index.find_all { |thing, i|
44
47
  Arel::Nodes::BindParam === thing
45
48
  }.map(&:last)
46
49
  end
47
50
 
48
51
  def sql_for(binds, connection)
49
52
  val = @values.dup
50
- binds = binds.dup
51
- @indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
53
+ casted_binds = binds.map(&:value_for_database)
54
+ @indexes.each { |i| val[i] = connection.quote(casted_binds.shift) }
52
55
  val.join
53
56
  end
54
57
  end
55
58
 
56
- def self.query(visitor, ast)
57
- Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
59
+ def self.query(sql)
60
+ Query.new(sql)
58
61
  end
59
62
 
60
- def self.partial_query(visitor, ast, collector)
61
- collected = visitor.accept(ast, collector).value
62
- PartialQuery.new collected
63
+ def self.partial_query(values)
64
+ PartialQuery.new(values)
63
65
  end
64
66
 
65
67
  class Params # :nodoc:
@@ -67,45 +69,53 @@ module ActiveRecord
67
69
  end
68
70
 
69
71
  class BindMap # :nodoc:
70
- def initialize(bind_values)
71
- @indexes = []
72
- @bind_values = bind_values
72
+ def initialize(bound_attributes)
73
+ @indexes = []
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
 
88
- attr_reader :bind_map, :query_builder
89
-
90
- def self.create(connection, block = Proc.new)
91
- relation = block.call Params.new
92
- bind_map = BindMap.new relation.bind_values
93
- query_builder = connection.cacheable_query relation.arel
94
- new query_builder, bind_map
90
+ def self.create(connection, callable = nil, &block)
91
+ relation = (callable || block).call Params.new
92
+ query_builder, binds = connection.cacheable_query(self, relation.arel)
93
+ bind_map = BindMap.new(binds)
94
+ new(query_builder, bind_map, relation.klass)
95
95
  end
96
96
 
97
- def initialize(query_builder, bind_map)
97
+ def initialize(query_builder, bind_map, klass)
98
98
  @query_builder = query_builder
99
- @bind_map = bind_map
99
+ @bind_map = bind_map
100
+ @klass = klass
100
101
  end
101
102
 
102
- def execute(params, klass, connection)
103
+ def execute(params, connection, &block)
103
104
  bind_values = bind_map.bind params
104
105
 
105
106
  sql = query_builder.sql_for bind_values, connection
106
107
 
107
- klass.find_by_sql sql, bind_values
108
+ klass.find_by_sql(sql, bind_values, preparable: true, &block)
109
+ end
110
+
111
+ def self.unsupported_value?(value)
112
+ case value
113
+ when NilClass, Array, Range, Hash, Relation, Base then true
114
+ end
108
115
  end
109
- alias :call :execute
116
+
117
+ protected
118
+
119
+ attr_reader :query_builder, :bind_map, :klass
110
120
  end
111
121
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
2
4
 
3
5
  module ActiveRecord
4
6
  # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
@@ -15,11 +17,16 @@ module ActiveRecord
15
17
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
16
18
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
17
19
  #
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
20
+ # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
21
+ # the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
22
+ # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
20
23
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
21
24
  # using a symbol.
22
25
  #
26
+ # NOTE: The default validations with the exception of +uniqueness+ will work.
27
+ # For example, if you want to check for +uniqueness+ with +hstore+ you will
28
+ # need to use a custom validation to handle it.
29
+ #
23
30
  # Examples:
24
31
  #
25
32
  # class User < ActiveRecord::Base
@@ -39,7 +46,7 @@ module ActiveRecord
39
46
  # store_accessor :settings, :privileges, :servants
40
47
  # end
41
48
  #
42
- # The stored attribute names can be retrieved using +stored_attributes+.
49
+ # The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
43
50
  #
44
51
  # User.stored_attributes[:settings] # [:color, :homepage]
45
52
  #
@@ -73,7 +80,7 @@ module ActiveRecord
73
80
 
74
81
  module ClassMethods
75
82
  def store(store_attribute, options = {})
76
- serialize store_attribute, IndifferentCoder.new(options[:coder])
83
+ serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
77
84
  store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
78
85
  end
79
86
 
@@ -109,27 +116,26 @@ module ActiveRecord
109
116
 
110
117
  def stored_attributes
111
118
  parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
112
- if self.local_stored_attributes
113
- parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
119
+ if local_stored_attributes
120
+ parent.merge!(local_stored_attributes) { |k, a, b| a | b }
114
121
  end
115
122
  parent
116
123
  end
117
124
  end
118
125
 
119
- protected
120
- def read_store_attribute(store_attribute, key)
126
+ private
127
+ def read_store_attribute(store_attribute, key) # :doc:
121
128
  accessor = store_accessor_for(store_attribute)
122
129
  accessor.read(self, store_attribute, key)
123
130
  end
124
131
 
125
- def write_store_attribute(store_attribute, key, value)
132
+ def write_store_attribute(store_attribute, key, value) # :doc:
126
133
  accessor = store_accessor_for(store_attribute)
127
134
  accessor.write(self, store_attribute, key, value)
128
135
  end
129
136
 
130
- private
131
137
  def store_accessor_for(store_attribute)
132
- type_for_attribute(store_attribute.to_s).accessor
138
+ type_for_attribute(store_attribute).accessor
133
139
  end
134
140
 
135
141
  class HashAccessor # :nodoc:
@@ -172,34 +178,34 @@ module ActiveRecord
172
178
  end
173
179
  end
174
180
 
175
- class IndifferentCoder # :nodoc:
176
- def initialize(coder_or_class_name)
177
- @coder =
178
- if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
179
- coder_or_class_name
180
- else
181
- ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
182
- end
183
- end
181
+ class IndifferentCoder # :nodoc:
182
+ def initialize(attr_name, coder_or_class_name)
183
+ @coder =
184
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
185
+ coder_or_class_name
186
+ else
187
+ ActiveRecord::Coders::YAMLColumn.new(attr_name, coder_or_class_name || Object)
188
+ end
189
+ end
184
190
 
185
- def dump(obj)
186
- @coder.dump self.class.as_indifferent_hash(obj)
187
- end
191
+ def dump(obj)
192
+ @coder.dump self.class.as_indifferent_hash(obj)
193
+ end
188
194
 
189
- def load(yaml)
190
- self.class.as_indifferent_hash(@coder.load(yaml || ''))
191
- end
195
+ def load(yaml)
196
+ self.class.as_indifferent_hash(@coder.load(yaml || ""))
197
+ end
192
198
 
193
- def self.as_indifferent_hash(obj)
194
- case obj
195
- when ActiveSupport::HashWithIndifferentAccess
196
- obj
197
- when Hash
198
- obj.with_indifferent_access
199
- else
200
- ActiveSupport::HashWithIndifferentAccess.new
199
+ def self.as_indifferent_hash(obj)
200
+ case obj
201
+ when ActiveSupport::HashWithIndifferentAccess
202
+ obj
203
+ when Hash
204
+ obj.with_indifferent_access
205
+ else
206
+ ActiveSupport::HashWithIndifferentAccess.new
207
+ end
201
208
  end
202
209
  end
203
- end
204
210
  end
205
211
  end