activerecord 4.1.15 → 4.2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  @hash = hash
14
14
  end
15
15
 
16
- def merge
16
+ def merge #:nodoc:
17
17
  Merger.new(relation, other).merge
18
18
  end
19
19
 
@@ -51,7 +51,8 @@ module ActiveRecord
51
51
 
52
52
  NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
53
53
  Relation::MULTI_VALUE_METHODS -
54
- [:joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
54
+ [:includes, :preload, :joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
55
+
55
56
 
56
57
  def normal_values
57
58
  NORMAL_VALUES
@@ -75,6 +76,7 @@ module ActiveRecord
75
76
 
76
77
  merge_multi_values
77
78
  merge_single_values
79
+ merge_preloads
78
80
  merge_joins
79
81
 
80
82
  relation
@@ -82,13 +84,34 @@ module ActiveRecord
82
84
 
83
85
  private
84
86
 
87
+ def merge_preloads
88
+ return if other.preload_values.empty? && other.includes_values.empty?
89
+
90
+ if other.klass == relation.klass
91
+ relation.preload!(*other.preload_values) unless other.preload_values.empty?
92
+ relation.includes!(other.includes_values) unless other.includes_values.empty?
93
+ else
94
+ reflection = relation.klass.reflect_on_all_associations.find do |r|
95
+ r.class_name == other.klass.name
96
+ end || return
97
+
98
+ unless other.preload_values.empty?
99
+ relation.preload! reflection.name => other.preload_values
100
+ end
101
+
102
+ unless other.includes_values.empty?
103
+ relation.includes! reflection.name => other.includes_values
104
+ end
105
+ end
106
+ end
107
+
85
108
  def merge_joins
86
- return if values[:joins].blank?
109
+ return if other.joins_values.blank?
87
110
 
88
111
  if other.klass == relation.klass
89
- relation.joins!(*values[:joins])
112
+ relation.joins!(*other.joins_values)
90
113
  else
91
- joins_dependency, rest = values[:joins].partition do |join|
114
+ joins_dependency, rest = other.joins_values.partition do |join|
92
115
  case join
93
116
  when Hash, Symbol, Array
94
117
  true
@@ -108,49 +131,36 @@ module ActiveRecord
108
131
 
109
132
  def merge_multi_values
110
133
  lhs_wheres = relation.where_values
111
- rhs_wheres = values[:where] || []
134
+ rhs_wheres = other.where_values
112
135
 
113
136
  lhs_binds = relation.bind_values
114
- rhs_binds = values[:bind] || []
137
+ rhs_binds = other.bind_values
115
138
 
116
139
  removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
117
140
 
118
141
  where_values = kept + rhs_wheres
119
142
  bind_values = filter_binds(lhs_binds, removed) + rhs_binds
120
143
 
121
- conn = relation.klass.connection
122
- bv_index = 0
123
- where_values.map! do |node|
124
- if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
125
- substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
126
- bv_index += 1
127
- Arel::Nodes::Equality.new(node.left, substitute)
128
- else
129
- node
130
- end
131
- end
132
-
133
144
  relation.where_values = where_values
134
145
  relation.bind_values = bind_values
135
146
 
136
- if values[:reordering]
147
+ if other.reordering_value
137
148
  # override any order specified in the original relation
138
- relation.reorder! values[:order]
139
- elsif values[:order]
149
+ relation.reorder! other.order_values
150
+ elsif other.order_values
140
151
  # merge in order_values from relation
141
- relation.order! values[:order]
152
+ relation.order! other.order_values
142
153
  end
143
154
 
144
- relation.extend(*values[:extending]) unless values[:extending].blank?
155
+ relation.extend(*other.extending_values) unless other.extending_values.blank?
145
156
  end
146
157
 
147
158
  def merge_single_values
148
- relation.from_value = values[:from] unless relation.from_value
149
- relation.lock_value = values[:lock] unless relation.lock_value
150
- relation.reverse_order_value = values[:reverse_order]
159
+ relation.from_value = other.from_value unless relation.from_value
160
+ relation.lock_value = other.lock_value unless relation.lock_value
151
161
 
152
- unless values[:create_with].blank?
153
- relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
162
+ unless other.create_with_value.blank?
163
+ relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
154
164
  end
155
165
  end
156
166
 
@@ -6,7 +6,9 @@ module ActiveRecord
6
6
  autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
7
7
 
8
8
  def self.resolve_column_aliases(klass, hash)
9
- hash = hash.dup
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]
10
12
  hash.keys.grep(Symbol) do |key|
11
13
  if klass.attribute_alias? key
12
14
  hash[klass.attribute_alias(key)] = hash.delete key
@@ -26,7 +28,7 @@ module ActiveRecord
26
28
  queries << '1=0'
27
29
  else
28
30
  table = Arel::Table.new(column, default_table.engine)
29
- association = klass._reflect_on_association(column.to_sym)
31
+ association = klass._reflect_on_association(column)
30
32
 
31
33
  value.each do |k, v|
32
34
  queries.concat expand(association && association.klass, table, k, v)
@@ -55,8 +57,9 @@ module ActiveRecord
55
57
  #
56
58
  # For polymorphic relationships, find the foreign key and type:
57
59
  # PriceEstimate.where(estimate_of: treasure)
58
- if klass && reflection = klass._reflect_on_association(column.to_sym)
60
+ if klass && reflection = klass._reflect_on_association(column)
59
61
  base_class = polymorphic_base_class_from_value(value)
62
+
60
63
  if reflection.polymorphic? && base_class
61
64
  queries << build(table[reflection.foreign_type], base_class)
62
65
  end
@@ -111,16 +114,15 @@ module ActiveRecord
111
114
  @handlers.unshift([klass, handler])
112
115
  end
113
116
 
114
- register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
117
+ BASIC_OBJECT_HANDLER = ->(attribute, value) { attribute.eq(value) } # :nodoc:
118
+ register_handler(BasicObject, BASIC_OBJECT_HANDLER)
115
119
  # FIXME: I think we need to deprecate this behavior
116
120
  register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
117
121
  register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
118
- register_handler(Range, ->(attribute, value) { attribute.in(value) })
122
+ register_handler(Range, ->(attribute, value) { attribute.between(value) })
119
123
  register_handler(Relation, RelationHandler.new)
120
124
  register_handler(Array, ArrayHandler.new)
121
125
 
122
- private
123
-
124
126
  def self.build(attribute, value)
125
127
  handler_for(value).call(attribute, value)
126
128
  end
@@ -138,10 +140,16 @@ module ActiveRecord
138
140
  when Array
139
141
  value.map { |v| convert_value_to_association_ids(v, primary_key) }
140
142
  when Base
141
- value.read_attribute(primary_key)
143
+ value._read_attribute(primary_key)
142
144
  else
143
145
  value
144
146
  end
145
147
  end
148
+
149
+ def self.can_be_bound?(value) # :nodoc:
150
+ !value.nil? &&
151
+ !value.is_a?(Hash) &&
152
+ handler_for(value) == BASIC_OBJECT_HANDLER
153
+ end
146
154
  end
147
155
  end
@@ -1,29 +1,48 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class ArrayHandler # :nodoc:
4
6
  def call(attribute, value)
5
7
  values = value.map { |x| x.is_a?(Base) ? x.id : x }
6
- ranges, values = values.partition { |v| v.is_a?(Range) }
8
+ nils, values = values.partition(&:nil?)
9
+
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
7
16
 
8
- values_predicate = if values.include?(nil)
9
- values = values.compact
17
+ flat_values = values.flatten
18
+ values = flat_values unless flat_values.include?(nil)
19
+ end
20
+
21
+ return attribute.in([]) if values.empty? && nils.empty?
10
22
 
23
+ ranges, values = values.partition { |v| v.is_a?(Range) }
24
+
25
+ values_predicate =
11
26
  case values.length
12
- when 0
13
- attribute.eq(nil)
14
- when 1
15
- attribute.eq(values.first).or(attribute.eq(nil))
16
- else
17
- attribute.in(values).or(attribute.eq(nil))
27
+ when 0 then NullPredicate
28
+ when 1 then attribute.eq(values.first)
29
+ else attribute.in(values)
18
30
  end
19
- else
20
- attribute.in(values)
31
+
32
+ unless nils.empty?
33
+ values_predicate = values_predicate.or(attribute.eq(nil))
21
34
  end
22
35
 
23
- array_predicates = ranges.map { |range| attribute.in(range) }
24
- array_predicates << values_predicate
36
+ array_predicates = ranges.map { |range| attribute.between(range) }
37
+ array_predicates.unshift(values_predicate)
25
38
  array_predicates.inject { |composite, predicate| composite.or(predicate) }
26
39
  end
40
+
41
+ module NullPredicate # :nodoc:
42
+ def self.or(other)
43
+ other
44
+ end
45
+ end
27
46
  end
28
47
  end
29
48
  end
@@ -6,11 +6,7 @@ module ActiveRecord
6
6
  value = value.select(value.klass.arel_table[value.klass.primary_key])
7
7
  end
8
8
 
9
- value = value.dup
10
- value.where_values = value.where_values.map do |node|
11
- node.dup rescue node
12
- end
13
- attribute.in(value.arel.ast)
9
+ attribute.in(value.arel)
14
10
  end
15
11
  end
16
12
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/string/filters'
2
3
  require 'active_model/forbidden_attributes_protection'
3
4
 
4
5
  module ActiveRecord
@@ -67,6 +68,7 @@ module ActiveRecord
67
68
  #
68
69
  def #{name}_values=(values) # def select_values=(values)
69
70
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
71
+ check_cached_relation
70
72
  @values[:#{name}] = values # @values[:select] = values
71
73
  end # end
72
74
  CODE
@@ -84,11 +86,22 @@ module ActiveRecord
84
86
  class_eval <<-CODE, __FILE__, __LINE__ + 1
85
87
  def #{name}_value=(value) # def readonly_value=(value)
86
88
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
89
+ check_cached_relation
87
90
  @values[:#{name}] = value # @values[:readonly] = value
88
91
  end # end
89
92
  CODE
90
93
  end
91
94
 
95
+ def check_cached_relation # :nodoc:
96
+ if defined?(@arel) && @arel
97
+ @arel = nil
98
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
+ Modifying already cached Relation. The cache will be reset. Use a
100
+ cloned Relation to prevent this warning.
101
+ MSG
102
+ end
103
+ end
104
+
92
105
  def create_with_value # :nodoc:
93
106
  @values[:create_with] || {}
94
107
  end
@@ -173,7 +186,7 @@ module ActiveRecord
173
186
 
174
187
  # Use to indicate that the given +table_names+ are referenced by an SQL string,
175
188
  # and should therefore be JOINed in any query rather than loaded separately.
176
- # This method only works in conjuction with +includes+.
189
+ # This method only works in conjunction with +includes+.
177
190
  # See #includes for more details.
178
191
  #
179
192
  # User.includes(:posts).where("posts.name = 'foo'")
@@ -207,7 +220,7 @@ module ActiveRecord
207
220
  # fields are retrieved:
208
221
  #
209
222
  # Model.select(:field)
210
- # # => [#<Model field:value>]
223
+ # # => [#<Model id: nil, field: "value">]
211
224
  #
212
225
  # Although in the above example it looks as though this method returns an
213
226
  # array, it actually returns a relation object and can have other query
@@ -216,12 +229,12 @@ module ActiveRecord
216
229
  # The argument to the method can also be an array of fields.
217
230
  #
218
231
  # Model.select(:field, :other_field, :and_one_more)
219
- # # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
232
+ # # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
220
233
  #
221
234
  # You can also use one or more strings, which will be used unchanged as SELECT fields.
222
235
  #
223
236
  # Model.select('field AS field_one', 'other_field AS field_two')
224
- # # => [#<Model field: "value", other_field: "value">]
237
+ # # => [#<Model id: nil, field: "value", other_field: "value">]
225
238
  #
226
239
  # If an alias was specified, it will be accessible from the resulting objects:
227
240
  #
@@ -229,7 +242,7 @@ module ActiveRecord
229
242
  # # => "value"
230
243
  #
231
244
  # Accessing attributes of an object that do not have fields retrieved by a select
232
- # will throw <tt>ActiveModel::MissingAttributeError</tt>:
245
+ # except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
233
246
  #
234
247
  # Model.select(:field).first.other_field
235
248
  # # => ActiveModel::MissingAttributeError: missing attribute: other_field
@@ -245,7 +258,7 @@ module ActiveRecord
245
258
  def _select!(*fields) # :nodoc:
246
259
  fields.flatten!
247
260
  fields.map! do |field|
248
- klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
261
+ klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
249
262
  end
250
263
  self.select_values += fields
251
264
  self
@@ -266,6 +279,10 @@ module ActiveRecord
266
279
  #
267
280
  # User.group('name AS grouped_name, age')
268
281
  # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
282
+ #
283
+ # Passing in an array of attributes to group by is also supported.
284
+ # User.select([:id, :first_name]).group(:id, :first_name).first(3)
285
+ # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
269
286
  def group(*args)
270
287
  check_if_method_has_arguments!(:group, args)
271
288
  spawn.group!(*args)
@@ -280,15 +297,6 @@ module ActiveRecord
280
297
 
281
298
  # Allows to specify an order attribute:
282
299
  #
283
- # User.order('name')
284
- # => SELECT "users".* FROM "users" ORDER BY name
285
- #
286
- # User.order('name DESC')
287
- # => SELECT "users".* FROM "users" ORDER BY name DESC
288
- #
289
- # User.order('name DESC, email')
290
- # => SELECT "users".* FROM "users" ORDER BY name DESC, email
291
- #
292
300
  # User.order(:name)
293
301
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
294
302
  #
@@ -297,6 +305,15 @@ module ActiveRecord
297
305
  #
298
306
  # User.order(:name, email: :desc)
299
307
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
308
+ #
309
+ # User.order('name')
310
+ # => SELECT "users".* FROM "users" ORDER BY name
311
+ #
312
+ # User.order('name DESC')
313
+ # => SELECT "users".* FROM "users" ORDER BY name DESC
314
+ #
315
+ # User.order('name DESC, email')
316
+ # => SELECT "users".* FROM "users" ORDER BY name DESC, email
300
317
  def order(*args)
301
318
  check_if_method_has_arguments!(:order, args)
302
319
  spawn.order!(*args)
@@ -410,19 +427,17 @@ module ActiveRecord
410
427
  # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
411
428
  def joins(*args)
412
429
  check_if_method_has_arguments!(:joins, args)
413
-
414
- args.compact!
415
- args.flatten!
416
-
417
430
  spawn.joins!(*args)
418
431
  end
419
432
 
420
433
  def joins!(*args) # :nodoc:
434
+ args.compact!
435
+ args.flatten!
421
436
  self.joins_values += args
422
437
  self
423
438
  end
424
439
 
425
- def bind(value)
440
+ def bind(value) # :nodoc:
426
441
  spawn.bind!(value)
427
442
  end
428
443
 
@@ -560,18 +575,14 @@ module ActiveRecord
560
575
  end
561
576
  end
562
577
 
563
- def where!(opts = :chain, *rest) # :nodoc:
564
- if opts == :chain
565
- WhereChain.new(self)
566
- else
567
- if Hash === opts
568
- opts = sanitize_forbidden_attributes(opts)
569
- references!(PredicateBuilder.references(opts))
570
- end
571
-
572
- self.where_values += build_where(opts, rest)
573
- self
578
+ def where!(opts, *rest) # :nodoc:
579
+ if Hash === opts
580
+ opts = sanitize_forbidden_attributes(opts)
581
+ references!(PredicateBuilder.references(opts))
574
582
  end
583
+
584
+ self.where_values += build_where(opts, rest)
585
+ self
575
586
  end
576
587
 
577
588
  # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
@@ -746,6 +757,9 @@ module ActiveRecord
746
757
 
747
758
  def from!(value, subquery_name = nil) # :nodoc:
748
759
  self.from_value = [value, subquery_name]
760
+ if value.is_a? Relation
761
+ self.bind_values = value.arel.bind_values + value.bind_values + bind_values
762
+ end
749
763
  self
750
764
  end
751
765
 
@@ -833,7 +847,9 @@ module ActiveRecord
833
847
  end
834
848
 
835
849
  def reverse_order! # :nodoc:
836
- self.reverse_order_value = !reverse_order_value
850
+ orders = order_values.uniq
851
+ orders.reject!(&:blank?)
852
+ self.order_values = reverse_sql_order(orders)
837
853
  self
838
854
  end
839
855
 
@@ -849,7 +865,7 @@ module ActiveRecord
849
865
 
850
866
  build_joins(arel, joins_values.flatten) unless joins_values.empty?
851
867
 
852
- collapse_wheres(arel, (where_values - ['']).uniq)
868
+ collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
853
869
 
854
870
  arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
855
871
 
@@ -865,15 +881,6 @@ module ActiveRecord
865
881
  arel.from(build_from) if from_value
866
882
  arel.lock(lock_value) if lock_value
867
883
 
868
- # Reorder bind indexes if joins produced bind values
869
- bvs = arel.bind_values + bind_values
870
- if bvs.any?
871
- arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
872
- column = bvs[i].first
873
- bp.replace connection.substitute_at(column, i)
874
- end
875
- end
876
-
877
884
  arel
878
885
  end
879
886
 
@@ -887,8 +894,9 @@ module ActiveRecord
887
894
 
888
895
  case scope
889
896
  when :order
890
- self.reverse_order_value = false
891
897
  result = []
898
+ when :where
899
+ self.bind_values = []
892
900
  else
893
901
  result = [] unless single_val_method
894
902
  end
@@ -899,9 +907,9 @@ module ActiveRecord
899
907
  def where_unscoping(target_value)
900
908
  target_value = target_value.to_s
901
909
 
902
- where_values.reject! do |rel|
910
+ self.where_values = where_values.reject do |rel|
903
911
  case rel
904
- when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
912
+ 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
905
913
  subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
906
914
  subrelation.name == target_value
907
915
  end
@@ -939,18 +947,14 @@ module ActiveRecord
939
947
  def build_where(opts, other = [])
940
948
  case opts
941
949
  when String, Array
942
- #TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
943
- values = Hash === other.first ? other.first.values : other
944
-
945
- values.grep(ActiveRecord::Relation) do |rel|
946
- self.bind_values += rel.bind_values
947
- end
948
-
949
950
  [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
950
951
  when Hash
951
952
  opts = PredicateBuilder.resolve_column_aliases(klass, opts)
952
- attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
953
953
 
954
+ tmp_opts, bind_values = create_binds(opts)
955
+ self.bind_values += bind_values
956
+
957
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
954
958
  add_relations_to_bind_values(attributes)
955
959
 
956
960
  PredicateBuilder.build_from_hash(klass, attributes, table)
@@ -959,12 +963,50 @@ module ActiveRecord
959
963
  end
960
964
  end
961
965
 
966
+ def create_binds(opts)
967
+ bindable, non_binds = opts.partition do |column, value|
968
+ PredicateBuilder.can_be_bound?(value) &&
969
+ @klass.columns_hash.include?(column.to_s) &&
970
+ !@klass.reflect_on_aggregation(column)
971
+ end
972
+
973
+ association_binds, non_binds = non_binds.partition do |column, value|
974
+ value.is_a?(Hash) && association_for_table(column)
975
+ end
976
+
977
+ new_opts = {}
978
+ binds = []
979
+
980
+ connection = self.connection
981
+
982
+ bindable.each do |(column,value)|
983
+ binds.push [@klass.columns_hash[column.to_s], value]
984
+ new_opts[column] = connection.substitute_at(column)
985
+ end
986
+
987
+ association_binds.each do |(column, value)|
988
+ association_relation = association_for_table(column).klass.send(:relation)
989
+ association_new_opts, association_bind = association_relation.send(:create_binds, value)
990
+ new_opts[column] = association_new_opts
991
+ binds += association_bind
992
+ end
993
+
994
+ non_binds.each { |column,value| new_opts[column] = value }
995
+
996
+ [new_opts, binds]
997
+ end
998
+
999
+ def association_for_table(table_name)
1000
+ table_name = table_name.to_s
1001
+ @klass._reflect_on_association(table_name) ||
1002
+ @klass._reflect_on_association(table_name.singularize)
1003
+ end
1004
+
962
1005
  def build_from
963
1006
  opts, name = from_value
964
1007
  case opts
965
1008
  when Relation
966
1009
  name ||= 'subquery'
967
- self.bind_values = opts.bind_values + self.bind_values
968
1010
  opts.arel.as(name.to_s)
969
1011
  else
970
1012
  opts
@@ -1000,9 +1042,12 @@ module ActiveRecord
1000
1042
  join_list
1001
1043
  )
1002
1044
 
1003
- joins = join_dependency.join_constraints stashed_association_joins
1045
+ join_infos = join_dependency.join_constraints stashed_association_joins
1004
1046
 
1005
- joins.each { |join| manager.from(join) }
1047
+ join_infos.each do |info|
1048
+ info.joins.each { |join| manager.from(join) }
1049
+ manager.bind_values.concat info.binds
1050
+ end
1006
1051
 
1007
1052
  manager.join_sources.concat(join_list)
1008
1053
 
@@ -1019,8 +1064,10 @@ module ActiveRecord
1019
1064
 
1020
1065
  def arel_columns(columns)
1021
1066
  columns.map do |field|
1022
- if columns_hash.key?(field.to_s)
1067
+ if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_value
1023
1068
  arel_table[field]
1069
+ elsif Symbol === field
1070
+ connection.quote_table_name(field.to_s)
1024
1071
  else
1025
1072
  field
1026
1073
  end
@@ -1052,15 +1099,19 @@ module ActiveRecord
1052
1099
  def build_order(arel)
1053
1100
  orders = order_values.uniq
1054
1101
  orders.reject!(&:blank?)
1055
- orders = reverse_sql_order(orders) if reverse_order_value
1056
1102
 
1057
1103
  arel.order(*orders) unless orders.empty?
1058
1104
  end
1059
1105
 
1106
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1107
+ 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1108
+
1060
1109
  def validate_order_args(args)
1061
- args.grep(Hash) do |h|
1062
- unless (h.values - [:asc, :desc]).empty?
1063
- raise ArgumentError, 'Direction should be :asc or :desc'
1110
+ args.each do |arg|
1111
+ next unless arg.is_a?(Hash)
1112
+ arg.each do |_key, value|
1113
+ raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
1114
+ "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1064
1115
  end
1065
1116
  end
1066
1117
  end
@@ -1082,7 +1133,7 @@ module ActiveRecord
1082
1133
  when Hash
1083
1134
  arg.map { |field, dir|
1084
1135
  field = klass.attribute_alias(field) if klass.attribute_alias?(field)
1085
- table[field].send(dir)
1136
+ table[field].send(dir.downcase)
1086
1137
  }
1087
1138
  else
1088
1139
  arg
@@ -1112,13 +1163,11 @@ module ActiveRecord
1112
1163
  end
1113
1164
  end
1114
1165
 
1115
- # This function is recursive just for better readablity.
1116
- # #where argument doesn't support more than one level nested hash in real world.
1117
1166
  def add_relations_to_bind_values(attributes)
1118
1167
  if attributes.is_a?(Hash)
1119
1168
  attributes.each_value do |value|
1120
1169
  if value.is_a?(ActiveRecord::Relation)
1121
- self.bind_values += value.bind_values
1170
+ self.bind_values += value.arel.bind_values + value.bind_values
1122
1171
  else
1123
1172
  add_relations_to_bind_values(value)
1124
1173
  end