activerecord 4.2.11.1 → 5.2.4.5

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 +594 -1620
  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 +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 -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 -49
  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,91 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder # :nodoc:
3
- @handlers = []
4
-
5
- autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler'
6
- autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
7
-
8
- def self.resolve_column_aliases(klass, hash)
9
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
10
- # https://bugs.ruby-lang.org/issues/7166
11
- hash = Hash[hash]
12
- hash.keys.grep(Symbol) do |key|
13
- if klass.attribute_alias? key
14
- hash[klass.attribute_alias(key)] = hash.delete key
15
- end
16
- end
17
- hash
18
- end
19
-
20
- def self.build_from_hash(klass, attributes, default_table)
21
- queries = []
22
-
23
- attributes.each do |column, value|
24
- table = default_table
25
-
26
- if value.is_a?(Hash)
27
- if value.empty?
28
- queries << '1=0'
29
- else
30
- table = Arel::Table.new(column, default_table.engine)
31
- association = klass._reflect_on_association(column)
32
-
33
- value.each do |k, v|
34
- queries.concat expand(association && association.klass, table, k, v)
35
- end
36
- end
37
- else
38
- column = column.to_s
39
-
40
- if column.include?('.')
41
- table_name, column = column.split('.', 2)
42
- table = Arel::Table.new(table_name, default_table.engine)
43
- end
44
-
45
- queries.concat expand(klass, table, column, value)
46
- end
47
- end
48
-
49
- queries
50
- end
51
-
52
- def self.expand(klass, table, column, value)
53
- queries = []
54
-
55
- # Find the foreign key when using queries such as:
56
- # Post.where(author: author)
57
- #
58
- # For polymorphic relationships, find the foreign key and type:
59
- # PriceEstimate.where(estimate_of: treasure)
60
- if klass && reflection = klass._reflect_on_association(column)
61
- base_class = polymorphic_base_class_from_value(value)
62
-
63
- if reflection.polymorphic? && base_class
64
- queries << build(table[reflection.foreign_type], base_class)
65
- end
66
-
67
- column = reflection.foreign_key
68
-
69
- if base_class
70
- primary_key = reflection.association_primary_key(base_class)
71
- value = convert_value_to_association_ids(value, primary_key)
72
- end
73
- end
74
-
75
- queries << build(table[column], value)
76
- queries
5
+ delegate :resolve_column_aliases, to: :table
6
+
7
+ def initialize(table)
8
+ @table = table
9
+ @handlers = []
10
+
11
+ register_handler(BasicObject, BasicObjectHandler.new(self))
12
+ register_handler(Base, BaseHandler.new(self))
13
+ register_handler(Range, RangeHandler.new(self))
14
+ register_handler(Relation, RelationHandler.new)
15
+ register_handler(Array, ArrayHandler.new(self))
16
+ register_handler(Set, ArrayHandler.new(self))
77
17
  end
78
18
 
79
- def self.polymorphic_base_class_from_value(value)
80
- case value
81
- when Relation
82
- value.klass.base_class
83
- when Array
84
- val = value.compact.first
85
- val.class.base_class if val.is_a?(Base)
86
- when Base
87
- value.class.base_class
88
- end
19
+ def build_from_hash(attributes)
20
+ attributes = convert_dot_notation_to_hash(attributes)
21
+ expand_from_hash(attributes)
89
22
  end
90
23
 
91
24
  def self.references(attributes)
@@ -94,7 +27,7 @@ module ActiveRecord
94
27
  key
95
28
  else
96
29
  key = key.to_s
97
- key.split('.').first if key.include?('.')
30
+ key.split(".".freeze).first if key.include?(".".freeze)
98
31
  end
99
32
  end.compact
100
33
  end
@@ -109,47 +42,111 @@ module ActiveRecord
109
42
  # Arel::Nodes::And.new([range.start, range.end])
110
43
  # )
111
44
  # end
112
- # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
113
- def self.register_handler(klass, handler)
45
+ # ActiveRecord::PredicateBuilder.new("users").register_handler(MyCustomDateRange, handler)
46
+ def register_handler(klass, handler)
114
47
  @handlers.unshift([klass, handler])
115
48
  end
116
49
 
117
- BASIC_OBJECT_HANDLER = ->(attribute, value) { attribute.eq(value) } # :nodoc:
118
- register_handler(BasicObject, BASIC_OBJECT_HANDLER)
119
- # FIXME: I think we need to deprecate this behavior
120
- register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
121
- register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
122
- register_handler(Range, ->(attribute, value) { attribute.between(value) })
123
- register_handler(Relation, RelationHandler.new)
124
- register_handler(Array, ArrayHandler.new)
125
-
126
- def self.build(attribute, value)
127
- handler_for(value).call(attribute, value)
128
- end
129
- private_class_method :build
130
-
131
- def self.handler_for(object)
132
- @handlers.detect { |klass, _| klass === object }.last
133
- end
134
- private_class_method :handler_for
135
-
136
- def self.convert_value_to_association_ids(value, primary_key)
137
- case value
138
- when Relation
139
- value.select(primary_key)
140
- when Array
141
- value.map { |v| convert_value_to_association_ids(v, primary_key) }
142
- when Base
143
- value._read_attribute(primary_key)
50
+ def build(attribute, value)
51
+ if table.type(attribute.name).force_equality?(value)
52
+ bind = build_bind_attribute(attribute.name, value)
53
+ attribute.eq(bind)
144
54
  else
145
- value
55
+ handler_for(value).call(attribute, value)
146
56
  end
147
57
  end
148
58
 
149
- def self.can_be_bound?(value) # :nodoc:
150
- !value.nil? &&
151
- !value.is_a?(Hash) &&
152
- handler_for(value) == BASIC_OBJECT_HANDLER
59
+ def build_bind_attribute(column_name, value)
60
+ attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
61
+ Arel::Nodes::BindParam.new(attr)
153
62
  end
63
+
64
+ protected
65
+
66
+ attr_reader :table
67
+
68
+ def expand_from_hash(attributes)
69
+ return ["1=0"] if attributes.empty?
70
+
71
+ attributes.flat_map do |key, value|
72
+ if value.is_a?(Hash) && !table.has_column?(key)
73
+ associated_predicate_builder(key).expand_from_hash(value)
74
+ elsif table.associated_with?(key)
75
+ # Find the foreign key when using queries such as:
76
+ # Post.where(author: author)
77
+ #
78
+ # For polymorphic relationships, find the foreign key and type:
79
+ # PriceEstimate.where(estimate_of: treasure)
80
+ associated_table = table.associated_table(key)
81
+ if associated_table.polymorphic_association?
82
+ case value.is_a?(Array) ? value.first : value
83
+ when Base, Relation
84
+ value = [value] unless value.is_a?(Array)
85
+ klass = PolymorphicArrayValue
86
+ end
87
+ end
88
+
89
+ klass ||= AssociationQueryValue
90
+ queries = klass.new(associated_table, value).queries.map do |query|
91
+ expand_from_hash(query).reduce(&:and)
92
+ end
93
+ queries.reduce(&:or)
94
+ elsif table.aggregated_with?(key)
95
+ mapping = table.reflect_on_aggregation(key).mapping
96
+ values = value.nil? ? [nil] : Array.wrap(value)
97
+ if mapping.length == 1 || values.empty?
98
+ column_name, aggr_attr = mapping.first
99
+ values = values.map do |object|
100
+ object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
101
+ end
102
+ build(table.arel_attribute(column_name), values)
103
+ else
104
+ queries = values.map do |object|
105
+ mapping.map do |field_attr, aggregate_attr|
106
+ build(table.arel_attribute(field_attr), object.try!(aggregate_attr))
107
+ end.reduce(&:and)
108
+ end
109
+ queries.reduce(&:or)
110
+ end
111
+ else
112
+ build(table.arel_attribute(key), value)
113
+ end
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def associated_predicate_builder(association_name)
120
+ self.class.new(table.associated_table(association_name))
121
+ end
122
+
123
+ def convert_dot_notation_to_hash(attributes)
124
+ dot_notation = attributes.select do |k, v|
125
+ k.include?(".".freeze) && !v.is_a?(Hash)
126
+ end
127
+
128
+ dot_notation.each_key do |key|
129
+ table_name, column_name = key.split(".".freeze)
130
+ value = attributes.delete(key)
131
+ attributes[table_name] ||= {}
132
+
133
+ attributes[table_name] = attributes[table_name].merge(column_name => value)
134
+ end
135
+
136
+ attributes
137
+ end
138
+
139
+ def handler_for(object)
140
+ @handlers.detect { |klass, _| klass === object }.last
141
+ end
154
142
  end
155
143
  end
144
+
145
+ require "active_record/relation/predicate_builder/array_handler"
146
+ require "active_record/relation/predicate_builder/base_handler"
147
+ require "active_record/relation/predicate_builder/basic_object_handler"
148
+ require "active_record/relation/predicate_builder/range_handler"
149
+ require "active_record/relation/predicate_builder/relation_handler"
150
+
151
+ require "active_record/relation/predicate_builder/association_query_value"
152
+ require "active_record/relation/predicate_builder/polymorphic_array_value"
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/attribute"
4
+
5
+ module ActiveRecord
6
+ class Relation
7
+ class QueryAttribute < ActiveModel::Attribute # :nodoc:
8
+ def type_cast(value)
9
+ value
10
+ end
11
+
12
+ def value_for_database
13
+ @value_for_database ||= super
14
+ end
15
+
16
+ def with_cast_value(value)
17
+ QueryAttribute.new(name, value, type)
18
+ end
19
+
20
+ def nil?
21
+ unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
+ value_before_type_cast.nil? ||
23
+ type.respond_to?(:subtype, true) && value_for_database.nil?
24
+ end
25
+ end
26
+
27
+ def boundable?
28
+ return @_boundable if defined?(@_boundable)
29
+ value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
30
+ @_boundable = true
31
+ rescue ::RangeError
32
+ @_boundable = false
33
+ end
34
+
35
+ def infinity?
36
+ _infinity?(value_before_type_cast) || boundable? && _infinity?(value_for_database)
37
+ end
38
+
39
+ private
40
+ def _infinity?(value)
41
+ value.respond_to?(:infinite?) && value.infinite?
42
+ end
43
+ end
44
+ end
45
+ end