activerecord 4.2.11.3 → 5.0.0.beta1

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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -1,91 +1,49 @@
1
1
  module ActiveRecord
2
2
  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
3
+ require 'active_record/relation/predicate_builder/array_handler'
4
+ require 'active_record/relation/predicate_builder/association_query_handler'
5
+ require 'active_record/relation/predicate_builder/base_handler'
6
+ require 'active_record/relation/predicate_builder/basic_object_handler'
7
+ require 'active_record/relation/predicate_builder/class_handler'
8
+ require 'active_record/relation/predicate_builder/range_handler'
9
+ require 'active_record/relation/predicate_builder/relation_handler'
10
+
11
+ delegate :resolve_column_aliases, to: :table
12
+
13
+ def initialize(table)
14
+ @table = table
15
+ @handlers = []
16
+
17
+ register_handler(BasicObject, BasicObjectHandler.new(self))
18
+ register_handler(Class, ClassHandler.new(self))
19
+ register_handler(Base, BaseHandler.new(self))
20
+ register_handler(Range, RangeHandler.new(self))
21
+ register_handler(Relation, RelationHandler.new)
22
+ register_handler(Array, ArrayHandler.new(self))
23
+ register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
18
24
  end
19
25
 
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
26
+ def build_from_hash(attributes)
27
+ attributes = convert_dot_notation_to_hash(attributes)
28
+ expand_from_hash(attributes)
50
29
  end
51
30
 
52
- def self.expand(klass, table, column, value)
53
- queries = []
31
+ def create_binds(attributes)
32
+ attributes = convert_dot_notation_to_hash(attributes)
33
+ create_binds_for_hash(attributes)
34
+ end
54
35
 
36
+ def expand(column, value)
55
37
  # Find the foreign key when using queries such as:
56
38
  # Post.where(author: author)
57
39
  #
58
40
  # For polymorphic relationships, find the foreign key and type:
59
41
  # 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
42
+ if table.associated_with?(column)
43
+ value = AssociationQueryValue.new(table.associated_table(column), value)
73
44
  end
74
45
 
75
- queries << build(table[column], value)
76
- queries
77
- end
78
-
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
46
+ build(table.arel_attribute(column), value)
89
47
  end
90
48
 
91
49
  def self.references(attributes)
@@ -94,7 +52,7 @@ module ActiveRecord
94
52
  key
95
53
  else
96
54
  key = key.to_s
97
- key.split('.').first if key.include?('.')
55
+ key.split('.'.freeze).first if key.include?('.'.freeze)
98
56
  end
99
57
  end.compact
100
58
  end
@@ -109,47 +67,83 @@ module ActiveRecord
109
67
  # Arel::Nodes::And.new([range.start, range.end])
110
68
  # )
111
69
  # end
112
- # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
113
- def self.register_handler(klass, handler)
70
+ # ActiveRecord::PredicateBuilder.new("users").register_handler(MyCustomDateRange, handler)
71
+ def register_handler(klass, handler)
114
72
  @handlers.unshift([klass, handler])
115
73
  end
116
74
 
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)
75
+ def build(attribute, value)
127
76
  handler_for(value).call(attribute, value)
128
77
  end
129
- private_class_method :build
130
78
 
131
- def self.handler_for(object)
132
- @handlers.detect { |klass, _| klass === object }.last
79
+ protected
80
+
81
+ attr_reader :table
82
+
83
+ def expand_from_hash(attributes)
84
+ return ["1=0"] if attributes.empty?
85
+
86
+ attributes.flat_map do |key, value|
87
+ if value.is_a?(Hash)
88
+ associated_predicate_builder(key).expand_from_hash(value)
89
+ else
90
+ expand(key, value)
91
+ end
92
+ end
133
93
  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)
144
- else
145
- value
94
+
95
+
96
+ def create_binds_for_hash(attributes)
97
+ result = attributes.dup
98
+ binds = []
99
+
100
+ attributes.each do |column_name, value|
101
+ case value
102
+ when Hash
103
+ attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
104
+ result[column_name] = attrs
105
+ binds += bvs
106
+ when Relation
107
+ binds += value.bound_attributes
108
+ else
109
+ if can_be_bound?(column_name, value)
110
+ result[column_name] = Arel::Nodes::BindParam.new
111
+ binds << Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
112
+ end
113
+ end
146
114
  end
115
+
116
+ [result, binds]
117
+ end
118
+
119
+ private
120
+
121
+ def associated_predicate_builder(association_name)
122
+ self.class.new(table.associated_table(association_name))
123
+ end
124
+
125
+ def convert_dot_notation_to_hash(attributes)
126
+ dot_notation = attributes.keys.select { |s| s.include?(".".freeze) }
127
+
128
+ dot_notation.each 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
147
141
  end
148
142
 
149
- def self.can_be_bound?(value) # :nodoc:
143
+ def can_be_bound?(column_name, value)
150
144
  !value.nil? &&
151
- !value.is_a?(Hash) &&
152
- handler_for(value) == BASIC_OBJECT_HANDLER
145
+ handler_for(value).is_a?(BasicObjectHandler) &&
146
+ !table.associated_with?(column_name)
153
147
  end
154
148
  end
155
149
  end
@@ -1,23 +1,14 @@
1
- require 'active_support/core_ext/string/filters'
2
-
3
1
  module ActiveRecord
4
2
  class PredicateBuilder
5
3
  class ArrayHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
6
8
  def call(attribute, value)
7
9
  values = value.map { |x| x.is_a?(Base) ? x.id : x }
8
10
  nils, values = values.partition(&:nil?)
9
11
 
10
- if values.any? { |val| val.is_a?(Array) }
11
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
- Passing a nested array to Active Record finder methods is
13
- deprecated and will be removed. Flatten your array before using
14
- it for 'IN' conditions.
15
- MSG
16
-
17
- flat_values = values.flatten
18
- values = flat_values unless flat_values.include?(nil)
19
- end
20
-
21
12
  return attribute.in([]) if values.empty? && nils.empty?
22
13
 
23
14
  ranges, values = values.partition { |v| v.is_a?(Range) }
@@ -25,19 +16,23 @@ module ActiveRecord
25
16
  values_predicate =
26
17
  case values.length
27
18
  when 0 then NullPredicate
28
- when 1 then attribute.eq(values.first)
19
+ when 1 then predicate_builder.build(attribute, values.first)
29
20
  else attribute.in(values)
30
21
  end
31
22
 
32
23
  unless nils.empty?
33
- values_predicate = values_predicate.or(attribute.eq(nil))
24
+ values_predicate = values_predicate.or(predicate_builder.build(attribute, nil))
34
25
  end
35
26
 
36
- array_predicates = ranges.map { |range| attribute.between(range) }
27
+ array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
37
28
  array_predicates.unshift(values_predicate)
38
29
  array_predicates.inject { |composite, predicate| composite.or(predicate) }
39
30
  end
40
31
 
32
+ protected
33
+
34
+ attr_reader :predicate_builder
35
+
41
36
  module NullPredicate # :nodoc:
42
37
  def self.or(other)
43
38
  other
@@ -0,0 +1,78 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class AssociationQueryHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
8
+ def call(attribute, value)
9
+ queries = {}
10
+
11
+ table = value.associated_table
12
+ if value.base_class
13
+ queries[table.association_foreign_type.to_s] = value.base_class.name
14
+ end
15
+
16
+ queries[table.association_foreign_key.to_s] = value.ids
17
+ predicate_builder.build_from_hash(queries)
18
+ end
19
+
20
+ protected
21
+
22
+ attr_reader :predicate_builder
23
+ end
24
+
25
+ class AssociationQueryValue # :nodoc:
26
+ attr_reader :associated_table, :value
27
+
28
+ def initialize(associated_table, value)
29
+ @associated_table = associated_table
30
+ @value = value
31
+ end
32
+
33
+ def ids
34
+ case value
35
+ when Relation
36
+ value.select(primary_key)
37
+ when Array
38
+ value.map { |v| convert_to_id(v) }
39
+ else
40
+ convert_to_id(value)
41
+ end
42
+ end
43
+
44
+ def base_class
45
+ if associated_table.polymorphic_association?
46
+ @base_class ||= polymorphic_base_class_from_value
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def primary_key
53
+ associated_table.association_primary_key(base_class)
54
+ end
55
+
56
+ def polymorphic_base_class_from_value
57
+ case value
58
+ when Relation
59
+ value.klass.base_class
60
+ when Array
61
+ val = value.compact.first
62
+ val.class.base_class if val.is_a?(Base)
63
+ when Base
64
+ value.class.base_class
65
+ end
66
+ end
67
+
68
+ def convert_to_id(value)
69
+ case value
70
+ when Base
71
+ value._read_attribute(primary_key)
72
+ else
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class BaseHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
8
+ def call(attribute, value)
9
+ predicate_builder.build(attribute, value.id)
10
+ end
11
+
12
+ protected
13
+
14
+ attr_reader :predicate_builder
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class BasicObjectHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
8
+ def call(attribute, value)
9
+ attribute.eq(value)
10
+ end
11
+
12
+ protected
13
+
14
+ attr_reader :predicate_builder
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class ClassHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
8
+ def call(attribute, value)
9
+ print_deprecation_warning
10
+ predicate_builder.build(attribute, value.name)
11
+ end
12
+
13
+ protected
14
+
15
+ attr_reader :predicate_builder
16
+
17
+ private
18
+
19
+ def print_deprecation_warning
20
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
21
+ Passing a class as a value in an Active Record query is deprecated and
22
+ will be removed. Pass a string instead.
23
+ MSG
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class RangeHandler # :nodoc:
4
+ def initialize(predicate_builder)
5
+ @predicate_builder = predicate_builder
6
+ end
7
+
8
+ def call(attribute, value)
9
+ attribute.between(value)
10
+ end
11
+
12
+ protected
13
+
14
+ attr_reader :predicate_builder
15
+ end
16
+ end
17
+ end