activerecord 4.2.0 → 5.2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 +7 -9
  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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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