activerecord 6.0.0 → 6.1.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1045 -575
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +5 -6
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations.rb +120 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +33 -9
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -77
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  95. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  97. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  99. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  100. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  103. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  104. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  106. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  107. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  108. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  109. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  110. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  111. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  112. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  113. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  114. data/lib/active_record/connection_handling.rb +219 -81
  115. data/lib/active_record/core.rb +253 -67
  116. data/lib/active_record/counter_cache.rb +4 -1
  117. data/lib/active_record/database_configurations.rb +124 -85
  118. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  119. data/lib/active_record/database_configurations/database_config.rb +52 -9
  120. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  121. data/lib/active_record/database_configurations/url_config.rb +15 -41
  122. data/lib/active_record/delegated_type.rb +209 -0
  123. data/lib/active_record/destroy_association_async_job.rb +36 -0
  124. data/lib/active_record/dynamic_matchers.rb +2 -3
  125. data/lib/active_record/enum.rb +82 -38
  126. data/lib/active_record/errors.rb +47 -12
  127. data/lib/active_record/explain.rb +9 -5
  128. data/lib/active_record/explain_subscriber.rb +1 -1
  129. data/lib/active_record/fixture_set/file.rb +10 -17
  130. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  131. data/lib/active_record/fixture_set/render_context.rb +1 -1
  132. data/lib/active_record/fixture_set/table_row.rb +2 -3
  133. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  134. data/lib/active_record/fixtures.rb +58 -12
  135. data/lib/active_record/gem_version.rb +2 -2
  136. data/lib/active_record/inheritance.rb +40 -21
  137. data/lib/active_record/insert_all.rb +39 -10
  138. data/lib/active_record/integration.rb +3 -5
  139. data/lib/active_record/internal_metadata.rb +16 -7
  140. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  141. data/lib/active_record/locking/optimistic.rb +33 -18
  142. data/lib/active_record/locking/pessimistic.rb +6 -2
  143. data/lib/active_record/log_subscriber.rb +28 -9
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  147. data/lib/active_record/migration.rb +115 -85
  148. data/lib/active_record/migration/command_recorder.rb +53 -45
  149. data/lib/active_record/migration/compatibility.rb +71 -20
  150. data/lib/active_record/migration/join_table.rb +0 -1
  151. data/lib/active_record/model_schema.rb +120 -15
  152. data/lib/active_record/nested_attributes.rb +2 -5
  153. data/lib/active_record/no_touching.rb +1 -1
  154. data/lib/active_record/null_relation.rb +0 -1
  155. data/lib/active_record/persistence.rb +50 -46
  156. data/lib/active_record/query_cache.rb +15 -5
  157. data/lib/active_record/querying.rb +12 -7
  158. data/lib/active_record/railtie.rb +65 -45
  159. data/lib/active_record/railties/console_sandbox.rb +2 -4
  160. data/lib/active_record/railties/databases.rake +277 -97
  161. data/lib/active_record/readonly_attributes.rb +4 -0
  162. data/lib/active_record/reflection.rb +77 -63
  163. data/lib/active_record/relation.rb +107 -67
  164. data/lib/active_record/relation/batches.rb +38 -32
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  166. data/lib/active_record/relation/calculations.rb +102 -45
  167. data/lib/active_record/relation/delegation.rb +9 -7
  168. data/lib/active_record/relation/finder_methods.rb +55 -17
  169. data/lib/active_record/relation/from_clause.rb +5 -1
  170. data/lib/active_record/relation/merger.rb +27 -26
  171. data/lib/active_record/relation/predicate_builder.rb +59 -40
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  175. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  176. data/lib/active_record/relation/query_methods.rb +343 -180
  177. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  178. data/lib/active_record/relation/spawn_methods.rb +8 -8
  179. data/lib/active_record/relation/where_clause.rb +107 -61
  180. data/lib/active_record/result.rb +41 -34
  181. data/lib/active_record/runtime_registry.rb +2 -2
  182. data/lib/active_record/sanitization.rb +6 -17
  183. data/lib/active_record/schema_dumper.rb +34 -4
  184. data/lib/active_record/schema_migration.rb +2 -8
  185. data/lib/active_record/scoping.rb +0 -1
  186. data/lib/active_record/scoping/default.rb +0 -1
  187. data/lib/active_record/scoping/named.rb +7 -18
  188. data/lib/active_record/secure_token.rb +16 -8
  189. data/lib/active_record/serialization.rb +5 -3
  190. data/lib/active_record/signed_id.rb +116 -0
  191. data/lib/active_record/statement_cache.rb +20 -4
  192. data/lib/active_record/store.rb +3 -3
  193. data/lib/active_record/suppressor.rb +2 -2
  194. data/lib/active_record/table_metadata.rb +42 -36
  195. data/lib/active_record/tasks/database_tasks.rb +140 -113
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  199. data/lib/active_record/test_databases.rb +5 -4
  200. data/lib/active_record/test_fixtures.rb +38 -16
  201. data/lib/active_record/timestamp.rb +4 -7
  202. data/lib/active_record/touch_later.rb +20 -21
  203. data/lib/active_record/transactions.rb +26 -73
  204. data/lib/active_record/type.rb +8 -2
  205. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  206. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  207. data/lib/active_record/type/serialized.rb +6 -3
  208. data/lib/active_record/type/time.rb +10 -0
  209. data/lib/active_record/type/type_map.rb +0 -1
  210. data/lib/active_record/type/unsigned_integer.rb +0 -1
  211. data/lib/active_record/type_caster/connection.rb +0 -1
  212. data/lib/active_record/type_caster/map.rb +8 -5
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record/validations/associated.rb +1 -2
  215. data/lib/active_record/validations/numericality.rb +35 -0
  216. data/lib/active_record/validations/uniqueness.rb +24 -4
  217. data/lib/arel.rb +15 -12
  218. data/lib/arel/attributes/attribute.rb +4 -0
  219. data/lib/arel/collectors/bind.rb +5 -0
  220. data/lib/arel/collectors/composite.rb +8 -0
  221. data/lib/arel/collectors/sql_string.rb +7 -0
  222. data/lib/arel/collectors/substitute_binds.rb +7 -0
  223. data/lib/arel/nodes.rb +3 -1
  224. data/lib/arel/nodes/binary.rb +82 -8
  225. data/lib/arel/nodes/bind_param.rb +8 -0
  226. data/lib/arel/nodes/casted.rb +21 -9
  227. data/lib/arel/nodes/equality.rb +6 -9
  228. data/lib/arel/nodes/grouping.rb +3 -0
  229. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  230. data/lib/arel/nodes/in.rb +8 -1
  231. data/lib/arel/nodes/infix_operation.rb +13 -1
  232. data/lib/arel/nodes/join_source.rb +1 -1
  233. data/lib/arel/nodes/node.rb +7 -6
  234. data/lib/arel/nodes/ordering.rb +27 -0
  235. data/lib/arel/nodes/sql_literal.rb +3 -0
  236. data/lib/arel/nodes/table_alias.rb +7 -3
  237. data/lib/arel/nodes/unary.rb +0 -1
  238. data/lib/arel/predications.rb +17 -24
  239. data/lib/arel/select_manager.rb +1 -2
  240. data/lib/arel/table.rb +13 -5
  241. data/lib/arel/visitors.rb +0 -7
  242. data/lib/arel/visitors/dot.rb +14 -3
  243. data/lib/arel/visitors/mysql.rb +11 -1
  244. data/lib/arel/visitors/postgresql.rb +15 -5
  245. data/lib/arel/visitors/sqlite.rb +0 -1
  246. data/lib/arel/visitors/to_sql.rb +89 -79
  247. data/lib/arel/visitors/visitor.rb +0 -1
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration.rb +6 -2
  250. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  251. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  252. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -43,13 +43,13 @@ module ActiveRecord
43
43
  # The validation context can be changed by passing <tt>context: context</tt>.
44
44
  # The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
45
45
  # with this when the validations module is mixed in, which it is by default.
46
- def save(options = {})
46
+ def save(**options)
47
47
  perform_validations(options) ? super : false
48
48
  end
49
49
 
50
50
  # Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
51
51
  # will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
52
- def save!(options = {})
52
+ def save!(**options)
53
53
  perform_validations(options) ? super : raise_validation_error
54
54
  end
55
55
 
@@ -72,7 +72,6 @@ module ActiveRecord
72
72
  alias_method :validate, :valid?
73
73
 
74
74
  private
75
-
76
75
  def default_validation_context
77
76
  new_record? ? :create : :update
78
77
  end
@@ -92,3 +91,4 @@ require "active_record/validations/uniqueness"
92
91
  require "active_record/validations/presence"
93
92
  require "active_record/validations/absence"
94
93
  require "active_record/validations/length"
94
+ require "active_record/validations/numericality"
@@ -5,12 +5,11 @@ module ActiveRecord
5
5
  class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
6
6
  def validate_each(record, attribute, value)
7
7
  if Array(value).reject { |r| valid_object?(r) }.any?
8
- record.errors.add(attribute, :invalid, options.merge(value: value))
8
+ record.errors.add(attribute, :invalid, **options.merge(value: value))
9
9
  end
10
10
  end
11
11
 
12
12
  private
13
-
14
13
  def valid_object?(record)
15
14
  (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
16
15
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Validations
5
+ class NumericalityValidator < ActiveModel::Validations::NumericalityValidator # :nodoc:
6
+ def validate_each(record, attribute, value, precision: nil, scale: nil)
7
+ precision = [column_precision_for(record, attribute) || BigDecimal.double_fig, BigDecimal.double_fig].min
8
+ scale = column_scale_for(record, attribute)
9
+ super(record, attribute, value, precision: precision, scale: scale)
10
+ end
11
+
12
+ private
13
+ def column_precision_for(record, attribute)
14
+ record.class.type_for_attribute(attribute.to_s)&.precision
15
+ end
16
+
17
+ def column_scale_for(record, attribute)
18
+ record.class.type_for_attribute(attribute.to_s)&.scale
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ # Validates whether the value of the specified attribute is numeric by
24
+ # trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
25
+ # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\z/</tt>
26
+ # (if <tt>only_integer</tt> is set to +true+). Kernel.Float precision
27
+ # defaults to the column's precision value or 15.
28
+ #
29
+ # See ActiveModel::Validations::HelperMethods.validates_numericality_of for more information.
30
+ def validates_numericality_of(*attr_names)
31
+ validates_with NumericalityValidator, _merge_attributes(attr_names)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -29,13 +29,22 @@ module ActiveRecord
29
29
  end
30
30
  end
31
31
  relation = scope_relation(record, relation)
32
- relation = relation.merge(options[:conditions]) if options[:conditions]
32
+
33
+ if options[:conditions]
34
+ conditions = options[:conditions]
35
+
36
+ relation = if conditions.arity.zero?
37
+ relation.instance_exec(&conditions)
38
+ else
39
+ relation.instance_exec(record, &conditions)
40
+ end
41
+ end
33
42
 
34
43
  if relation.exists?
35
44
  error_options = options.except(:case_sensitive, :scope, :conditions)
36
45
  error_options[:value] = value
37
46
 
38
- record.errors.add(attribute, :taken, error_options)
47
+ record.errors.add(attribute, :taken, **error_options)
39
48
  end
40
49
  end
41
50
 
@@ -61,7 +70,7 @@ module ActiveRecord
61
70
  return relation.none! if bind.unboundable?
62
71
 
63
72
  if !options.key?(:case_sensitive) || bind.nil?
64
- klass.connection.default_uniqueness_comparison(attr, bind, klass)
73
+ klass.connection.default_uniqueness_comparison(attr, bind)
65
74
  elsif options[:case_sensitive]
66
75
  klass.connection.case_sensitive_comparison(attr, bind)
67
76
  else
@@ -78,7 +87,7 @@ module ActiveRecord
78
87
  scope_value = if record.class._reflect_on_association(scope_item)
79
88
  record.association(scope_item).reader
80
89
  else
81
- record._read_attribute(scope_item)
90
+ record.read_attribute(scope_item)
82
91
  end
83
92
  relation = relation.where(scope_item => scope_value)
84
93
  end
@@ -126,6 +135,17 @@ module ActiveRecord
126
135
  # validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
127
136
  # end
128
137
  #
138
+ # To build conditions based on the record's state, define the conditions
139
+ # callable with a parameter, which will be the record itself. This
140
+ # example validates the title is unique for the year of publication:
141
+ #
142
+ # class Article < ActiveRecord::Base
143
+ # validates_uniqueness_of :title, conditions: ->(article) {
144
+ # published_at = article.published_at
145
+ # where(published_at: published_at.beginning_of_year..published_at.end_of_year)
146
+ # }
147
+ # end
148
+ #
129
149
  # When the record is created, a check is performed to make sure that no
130
150
  # record exists in the database with the given value for the specified
131
151
  # attribute (that maps to a column). When the record is updated,
data/lib/arel.rb CHANGED
@@ -12,7 +12,7 @@ require "arel/math"
12
12
  require "arel/alias_predication"
13
13
  require "arel/order_predications"
14
14
  require "arel/table"
15
- require "arel/attributes"
15
+ require "arel/attributes/attribute"
16
16
 
17
17
  require "arel/visitors"
18
18
  require "arel/collectors/sql_string"
@@ -24,28 +24,31 @@ require "arel/update_manager"
24
24
  require "arel/delete_manager"
25
25
  require "arel/nodes"
26
26
 
27
- module Arel # :nodoc: all
27
+ module Arel
28
28
  VERSION = "10.0.0"
29
29
 
30
+ # Wrap a known-safe SQL string for passing to query methods, e.g.
31
+ #
32
+ # Post.order(Arel.sql("length(title)")).last
33
+ #
34
+ # Great caution should be taken to avoid SQL injection vulnerabilities.
35
+ # This method should not be used with unsafe values such as request
36
+ # parameters or model attributes.
30
37
  def self.sql(raw_sql)
31
38
  Arel::Nodes::SqlLiteral.new raw_sql
32
39
  end
33
40
 
34
- def self.star
41
+ def self.star # :nodoc:
35
42
  sql "*"
36
43
  end
37
44
 
38
- def self.arel_node?(value)
39
- value.is_a?(Arel::Node) || value.is_a?(Arel::Attribute) || value.is_a?(Arel::Nodes::SqlLiteral)
45
+ def self.arel_node?(value) # :nodoc:
46
+ value.is_a?(Arel::Nodes::Node) || value.is_a?(Arel::Attribute) || value.is_a?(Arel::Nodes::SqlLiteral)
40
47
  end
41
48
 
42
- def self.fetch_attribute(value)
43
- case value
44
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
45
- yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
49
+ def self.fetch_attribute(value, &block) # :nodoc:
50
+ unless String === value
51
+ value.fetch_attribute(&block)
46
52
  end
47
53
  end
48
-
49
- ## Convenience Alias
50
- Node = Arel::Nodes::Node
51
54
  end
@@ -9,6 +9,10 @@ module Arel # :nodoc: all
9
9
  include Arel::OrderPredications
10
10
  include Arel::Math
11
11
 
12
+ def type_caster
13
+ relation.type_for_attribute(name)
14
+ end
15
+
12
16
  ###
13
17
  # Create a node for lowering this attribute
14
18
  def lower
@@ -16,6 +16,11 @@ module Arel # :nodoc: all
16
16
  self
17
17
  end
18
18
 
19
+ def add_binds(binds)
20
+ @binds.concat binds
21
+ self
22
+ end
23
+
19
24
  def value
20
25
  @binds
21
26
  end
@@ -3,6 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module Collectors
5
5
  class Composite
6
+ attr_accessor :preparable
7
+
6
8
  def initialize(left, right)
7
9
  @left = left
8
10
  @right = right
@@ -20,6 +22,12 @@ module Arel # :nodoc: all
20
22
  self
21
23
  end
22
24
 
25
+ def add_binds(binds, &block)
26
+ left.add_binds(binds, &block)
27
+ right.add_binds(binds, &block)
28
+ self
29
+ end
30
+
23
31
  def value
24
32
  [left.value, right.value]
25
33
  end
@@ -5,6 +5,8 @@ require "arel/collectors/plain_string"
5
5
  module Arel # :nodoc: all
6
6
  module Collectors
7
7
  class SQLString < PlainString
8
+ attr_accessor :preparable
9
+
8
10
  def initialize(*)
9
11
  super
10
12
  @bind_index = 1
@@ -15,6 +17,11 @@ module Arel # :nodoc: all
15
17
  @bind_index += 1
16
18
  self
17
19
  end
20
+
21
+ def add_binds(binds, &block)
22
+ self << (@bind_index...@bind_index += binds.size).map(&block).join(", ")
23
+ self
24
+ end
18
25
  end
19
26
  end
20
27
  end
@@ -3,6 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module Collectors
5
5
  class SubstituteBinds
6
+ attr_accessor :preparable
7
+
6
8
  def initialize(quoter, delegate_collector)
7
9
  @quoter = quoter
8
10
  @delegate = delegate_collector
@@ -14,9 +16,14 @@ module Arel # :nodoc: all
14
16
  end
15
17
 
16
18
  def add_bind(bind)
19
+ bind = bind.value_for_database if bind.respond_to?(:value_for_database)
17
20
  self << quoter.quote(bind)
18
21
  end
19
22
 
23
+ def add_binds(binds)
24
+ self << binds.map { |bind| quoter.quote(bind) }.join(", ")
25
+ end
26
+
20
27
  def value
21
28
  delegate.value
22
29
  end
data/lib/arel/nodes.rb CHANGED
@@ -18,6 +18,8 @@ require "arel/nodes/false"
18
18
  # unary
19
19
  require "arel/nodes/unary"
20
20
  require "arel/nodes/grouping"
21
+ require "arel/nodes/homogeneous_in"
22
+ require "arel/nodes/ordering"
21
23
  require "arel/nodes/ascending"
22
24
  require "arel/nodes/descending"
23
25
  require "arel/nodes/unqualified_column"
@@ -26,7 +28,7 @@ require "arel/nodes/with"
26
28
  # binary
27
29
  require "arel/nodes/binary"
28
30
  require "arel/nodes/equality"
29
- require "arel/nodes/in" # Why is this subclassed from equality?
31
+ require "arel/nodes/in"
30
32
  require "arel/nodes/join_source"
31
33
  require "arel/nodes/delete_statement"
32
34
  require "arel/nodes/table_alias"
@@ -29,18 +29,92 @@ module Arel # :nodoc: all
29
29
  alias :== :eql?
30
30
  end
31
31
 
32
+ module FetchAttribute
33
+ def fetch_attribute
34
+ if left.is_a?(Arel::Attributes::Attribute)
35
+ yield left
36
+ elsif right.is_a?(Arel::Attributes::Attribute)
37
+ yield right
38
+ end
39
+ end
40
+ end
41
+
42
+ class Between < Binary; include FetchAttribute; end
43
+
44
+ class GreaterThan < Binary
45
+ include FetchAttribute
46
+
47
+ def invert
48
+ Arel::Nodes::LessThanOrEqual.new(left, right)
49
+ end
50
+ end
51
+
52
+ class GreaterThanOrEqual < Binary
53
+ include FetchAttribute
54
+
55
+ def invert
56
+ Arel::Nodes::LessThan.new(left, right)
57
+ end
58
+ end
59
+
60
+ class LessThan < Binary
61
+ include FetchAttribute
62
+
63
+ def invert
64
+ Arel::Nodes::GreaterThanOrEqual.new(left, right)
65
+ end
66
+ end
67
+
68
+ class LessThanOrEqual < Binary
69
+ include FetchAttribute
70
+
71
+ def invert
72
+ Arel::Nodes::GreaterThan.new(left, right)
73
+ end
74
+ end
75
+
76
+ class IsDistinctFrom < Binary
77
+ include FetchAttribute
78
+
79
+ def invert
80
+ Arel::Nodes::IsNotDistinctFrom.new(left, right)
81
+ end
82
+ end
83
+
84
+ class IsNotDistinctFrom < Binary
85
+ include FetchAttribute
86
+
87
+ def invert
88
+ Arel::Nodes::IsDistinctFrom.new(left, right)
89
+ end
90
+ end
91
+
92
+ class NotEqual < Binary
93
+ include FetchAttribute
94
+
95
+ def invert
96
+ Arel::Nodes::Equality.new(left, right)
97
+ end
98
+ end
99
+
100
+ class NotIn < Binary
101
+ include FetchAttribute
102
+
103
+ def invert
104
+ Arel::Nodes::In.new(left, right)
105
+ end
106
+ end
107
+
108
+ class Or < Binary
109
+ def fetch_attribute(&block)
110
+ left.fetch_attribute(&block) && right.fetch_attribute(&block)
111
+ end
112
+ end
113
+
32
114
  %w{
33
115
  As
34
116
  Assignment
35
- Between
36
- GreaterThan
37
- GreaterThanOrEqual
38
117
  Join
39
- LessThan
40
- LessThanOrEqual
41
- NotEqual
42
- NotIn
43
- Or
44
118
  Union
45
119
  UnionAll
46
120
  Intersect
@@ -24,6 +24,14 @@ module Arel # :nodoc: all
24
24
  value.nil?
25
25
  end
26
26
 
27
+ def value_before_type_cast
28
+ if value.respond_to?(:value_before_type_cast)
29
+ value.value_before_type_cast
30
+ else
31
+ value
32
+ end
33
+ end
34
+
27
35
  def infinite?
28
36
  value.respond_to?(:infinite?) && value.infinite?
29
37
  end
@@ -3,30 +3,42 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class Casted < Arel::Nodes::NodeExpression # :nodoc:
6
- attr_reader :val, :attribute
7
- def initialize(val, attribute)
8
- @val = val
6
+ attr_reader :value, :attribute
7
+ alias :value_before_type_cast :value
8
+
9
+ def initialize(value, attribute)
10
+ @value = value
9
11
  @attribute = attribute
10
12
  super()
11
13
  end
12
14
 
13
- def nil?; @val.nil?; end
15
+ def nil?; value.nil?; end
16
+
17
+ def value_for_database
18
+ if attribute.able_to_type_cast?
19
+ attribute.type_cast_for_database(value)
20
+ else
21
+ value
22
+ end
23
+ end
14
24
 
15
25
  def hash
16
- [self.class, val, attribute].hash
26
+ [self.class, value, attribute].hash
17
27
  end
18
28
 
19
29
  def eql?(other)
20
30
  self.class == other.class &&
21
- self.val == other.val &&
22
- self.attribute == other.attribute
31
+ self.value == other.value &&
32
+ self.attribute == other.attribute
23
33
  end
24
34
  alias :== :eql?
25
35
  end
26
36
 
27
37
  class Quoted < Arel::Nodes::Unary # :nodoc:
28
- alias :val :value
29
- def nil?; val.nil?; end
38
+ alias :value_for_database :value
39
+ alias :value_before_type_cast :value
40
+
41
+ def nil?; value.nil?; end
30
42
 
31
43
  def infinite?
32
44
  value.respond_to?(:infinite?) && value.infinite?