activerecord 4.2.0 → 5.0.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -0,0 +1,49 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ module RecordFetchWarning
4
+ # When this module is prepended to ActiveRecord::Relation and
5
+ # `config.active_record.warn_on_records_fetched_greater_than` is
6
+ # set to an integer, if the number of records a query returns is
7
+ # greater than the value of `warn_on_records_fetched_greater_than`,
8
+ # a warning is logged. This allows for the detection of queries that
9
+ # return a large number of records, which could cause memory bloat.
10
+ #
11
+ # In most cases, fetching large number of records can be performed
12
+ # efficiently using the ActiveRecord::Batches methods.
13
+ # See active_record/lib/relation/batches.rb for more information.
14
+ def exec_queries
15
+ QueryRegistry.reset
16
+
17
+ super.tap do
18
+ if logger && warn_on_records_fetched_greater_than
19
+ if @records.length > warn_on_records_fetched_greater_than
20
+ logger.warn "Query fetched #{@records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ # :stopdoc:
27
+ ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
28
+ QueryRegistry.queries << payload[:sql]
29
+ end
30
+ # :startdoc:
31
+
32
+ class QueryRegistry # :nodoc:
33
+ extend ActiveSupport::PerThreadRegistry
34
+
35
+ attr_reader :queries
36
+
37
+ def initialize
38
+ @queries = []
39
+ end
40
+
41
+ def reset
42
+ @queries.clear
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ ActiveRecord::Relation.prepend ActiveRecord::Relation::RecordFetchWarning
@@ -10,8 +10,9 @@ module ActiveRecord
10
10
  clone
11
11
  end
12
12
 
13
- # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
13
+ # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
14
14
  # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
15
+ #
15
16
  # Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
16
17
  # # Performs a single join query with both where conditions.
17
18
  #
@@ -28,20 +29,23 @@ module ActiveRecord
28
29
  # This is mainly intended for sharing common conditions between multiple associations.
29
30
  def merge(other)
30
31
  if other.is_a?(Array)
31
- to_a & other
32
+ records & other
32
33
  elsif other
33
34
  spawn.merge!(other)
34
35
  else
35
- self
36
+ raise ArgumentError, "invalid argument: #{other.inspect}."
36
37
  end
37
38
  end
38
39
 
39
40
  def merge!(other) # :nodoc:
40
- if !other.is_a?(Relation) && other.respond_to?(:to_proc)
41
+ if other.is_a?(Hash)
42
+ Relation::HashMerger.new(self, other).merge
43
+ elsif other.is_a?(Relation)
44
+ Relation::Merger.new(self, other).merge
45
+ elsif other.respond_to?(:to_proc)
41
46
  instance_exec(&other)
42
47
  else
43
- klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
44
- klass.new(self, other).merge
48
+ raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
45
49
  end
46
50
  end
47
51
 
@@ -58,16 +62,13 @@ module ActiveRecord
58
62
  # Post.order('id asc').only(:where) # discards the order condition
59
63
  # Post.order('id asc').only(:where, :order) # uses the specified order
60
64
  def only(*onlies)
61
- if onlies.any? { |o| o == :where }
62
- onlies << :bind
63
- end
64
65
  relation_with values.slice(*onlies)
65
66
  end
66
67
 
67
68
  private
68
69
 
69
70
  def relation_with(values) # :nodoc:
70
- result = Relation.create(klass, table, values)
71
+ result = Relation.create(klass, table, predicate_builder, values)
71
72
  result.extend(*extending_values) if extending_values.any?
72
73
  result
73
74
  end
@@ -0,0 +1,174 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ class WhereClause # :nodoc:
4
+ attr_reader :binds
5
+
6
+ delegate :any?, :empty?, to: :predicates
7
+
8
+ def initialize(predicates, binds)
9
+ @predicates = predicates
10
+ @binds = binds
11
+ end
12
+
13
+ def +(other)
14
+ WhereClause.new(
15
+ predicates + other.predicates,
16
+ binds + other.binds,
17
+ )
18
+ end
19
+
20
+ def merge(other)
21
+ WhereClause.new(
22
+ predicates_unreferenced_by(other) + other.predicates,
23
+ non_conflicting_binds(other) + other.binds,
24
+ )
25
+ end
26
+
27
+ def except(*columns)
28
+ WhereClause.new(
29
+ predicates_except(columns),
30
+ binds_except(columns),
31
+ )
32
+ end
33
+
34
+ def or(other)
35
+ if empty?
36
+ self
37
+ elsif other.empty?
38
+ other
39
+ else
40
+ WhereClause.new(
41
+ [ast.or(other.ast)],
42
+ binds + other.binds
43
+ )
44
+ end
45
+ end
46
+
47
+ def to_h(table_name = nil)
48
+ equalities = predicates.grep(Arel::Nodes::Equality)
49
+ if table_name
50
+ equalities = equalities.select do |node|
51
+ node.left.relation.name == table_name
52
+ end
53
+ end
54
+
55
+ binds = self.binds.map { |attr| [attr.name, attr.value] }.to_h
56
+
57
+ equalities.map { |node|
58
+ name = node.left.name
59
+ [name, binds.fetch(name.to_s) {
60
+ case node.right
61
+ when Array then node.right.map(&:val)
62
+ when Arel::Nodes::Casted, Arel::Nodes::Quoted
63
+ node.right.val
64
+ end
65
+ }]
66
+ }.to_h
67
+ end
68
+
69
+ def ast
70
+ Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
71
+ end
72
+
73
+ def ==(other)
74
+ other.is_a?(WhereClause) &&
75
+ predicates == other.predicates &&
76
+ binds == other.binds
77
+ end
78
+
79
+ def invert
80
+ WhereClause.new(inverted_predicates, binds)
81
+ end
82
+
83
+ def self.empty
84
+ @empty ||= new([], [])
85
+ end
86
+
87
+ protected
88
+
89
+ attr_reader :predicates
90
+
91
+ def referenced_columns
92
+ @referenced_columns ||= begin
93
+ equality_nodes = predicates.select { |n| equality_node?(n) }
94
+ Set.new(equality_nodes, &:left)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def predicates_unreferenced_by(other)
101
+ predicates.reject do |n|
102
+ equality_node?(n) && other.referenced_columns.include?(n.left)
103
+ end
104
+ end
105
+
106
+ def equality_node?(node)
107
+ node.respond_to?(:operator) && node.operator == :==
108
+ end
109
+
110
+ def non_conflicting_binds(other)
111
+ conflicts = referenced_columns & other.referenced_columns
112
+ conflicts.map! { |node| node.name.to_s }
113
+ binds.reject { |attr| conflicts.include?(attr.name) }
114
+ end
115
+
116
+ def inverted_predicates
117
+ predicates.map { |node| invert_predicate(node) }
118
+ end
119
+
120
+ def invert_predicate(node)
121
+ case node
122
+ when NilClass
123
+ raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
124
+ when Arel::Nodes::In
125
+ Arel::Nodes::NotIn.new(node.left, node.right)
126
+ when Arel::Nodes::Equality
127
+ Arel::Nodes::NotEqual.new(node.left, node.right)
128
+ when String
129
+ Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
130
+ else
131
+ Arel::Nodes::Not.new(node)
132
+ end
133
+ end
134
+
135
+ def predicates_except(columns)
136
+ predicates.reject do |node|
137
+ case node
138
+ 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
139
+ subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
140
+ columns.include?(subrelation.name.to_s)
141
+ end
142
+ end
143
+ end
144
+
145
+ def binds_except(columns)
146
+ binds.reject do |attr|
147
+ columns.include?(attr.name)
148
+ end
149
+ end
150
+
151
+ def predicates_with_wrapped_sql_literals
152
+ non_empty_predicates.map do |node|
153
+ if Arel::Nodes::Equality === node
154
+ node
155
+ else
156
+ wrap_sql_literal(node)
157
+ end
158
+ end
159
+ end
160
+
161
+ ARRAY_WITH_EMPTY_STRING = ['']
162
+ def non_empty_predicates
163
+ predicates - ARRAY_WITH_EMPTY_STRING
164
+ end
165
+
166
+ def wrap_sql_literal(node)
167
+ if ::String === node
168
+ node = Arel.sql(node)
169
+ end
170
+ Arel::Nodes::Grouping.new(node)
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ class WhereClauseFactory # :nodoc:
4
+ def initialize(klass, predicate_builder)
5
+ @klass = klass
6
+ @predicate_builder = predicate_builder
7
+ end
8
+
9
+ def build(opts, other)
10
+ binds = []
11
+
12
+ case opts
13
+ when String, Array
14
+ parts = [klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
15
+ when Hash
16
+ attributes = predicate_builder.resolve_column_aliases(opts)
17
+ attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
18
+ attributes.stringify_keys!
19
+
20
+ attributes, binds = predicate_builder.create_binds(attributes)
21
+
22
+ parts = predicate_builder.build_from_hash(attributes)
23
+ when Arel::Nodes::Node
24
+ parts = [opts]
25
+ binds = other
26
+ else
27
+ raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
28
+ end
29
+
30
+ WhereClause.new(parts, binds)
31
+ end
32
+
33
+ protected
34
+
35
+ attr_reader :klass, :predicate_builder
36
+ end
37
+ end
38
+ end