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
@@ -62,6 +62,9 @@ module ActiveRecord
62
62
  # Post.order('id asc').only(:where) # discards the order condition
63
63
  # Post.order('id asc').only(:where, :order) # uses the specified order
64
64
  def only(*onlies)
65
+ if onlies.any? { |o| o == :where }
66
+ onlies << :bind
67
+ end
65
68
  relation_with values.slice(*onlies)
66
69
  end
67
70
 
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  class Result
32
32
  include Enumerable
33
33
 
34
- IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc:
34
+ IDENTITY_TYPE = Type::Value.new # :nodoc:
35
35
 
36
36
  attr_reader :columns, :rows, :column_types
37
37
 
@@ -42,12 +42,8 @@ module ActiveRecord
42
42
  @column_types = column_types
43
43
  end
44
44
 
45
- def identity_type # :nodoc:
46
- IDENTITY_TYPE
47
- end
48
-
49
- def column_type(name)
50
- @column_types[name] || identity_type
45
+ def length
46
+ @rows.length
51
47
  end
52
48
 
53
49
  def each
@@ -82,6 +78,15 @@ module ActiveRecord
82
78
  hash_rows.last
83
79
  end
84
80
 
81
+ def cast_values(type_overrides = {}) # :nodoc:
82
+ types = columns.map { |name| column_type(name, type_overrides) }
83
+ result = rows.map do |values|
84
+ types.zip(values).map { |type, value| type.type_cast_from_database(value) }
85
+ end
86
+
87
+ columns.one? ? result.map!(&:first) : result
88
+ end
89
+
85
90
  def initialize_copy(other)
86
91
  @columns = columns.dup
87
92
  @rows = rows.dup
@@ -91,6 +96,12 @@ module ActiveRecord
91
96
 
92
97
  private
93
98
 
99
+ def column_type(name, type_overrides = {})
100
+ type_overrides.fetch(name) do
101
+ column_types.fetch(name, IDENTITY_TYPE)
102
+ end
103
+ end
104
+
94
105
  def hash_rows
95
106
  @hash_rows ||=
96
107
  begin
@@ -87,12 +87,15 @@ module ActiveRecord
87
87
  # { address: Address.new("123 abc st.", "chicago") }
88
88
  # # => "address_street='123 abc st.' and address_city='chicago'"
89
89
  def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
90
+ ActiveSupport::Deprecation.warn(<<-EOWARN)
91
+ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
92
+ EOWARN
90
93
  attrs = PredicateBuilder.resolve_column_aliases self, attrs
91
94
  attrs = expand_hash_conditions_for_aggregates(attrs)
92
95
 
93
96
  table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
94
97
  PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
95
- connection.visitor.accept b
98
+ connection.visitor.compile b
96
99
  }.join(' AND ')
97
100
  end
98
101
  alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
@@ -107,6 +110,13 @@ module ActiveRecord
107
110
  end.join(', ')
108
111
  end
109
112
 
113
+ # Sanitizes a +string+ so that it is safe to use within an SQL
114
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%"
115
+ def sanitize_sql_like(string, escape_character = "\\")
116
+ pattern = Regexp.union(escape_character, "%", "_")
117
+ string.gsub(pattern) { |x| [escape_character, x].join }
118
+ end
119
+
110
120
  # Accepts an array of conditions. The array has each value
111
121
  # sanitized and interpolated into the SQL statement.
112
122
  # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
@@ -127,7 +137,7 @@ module ActiveRecord
127
137
  raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
128
138
  bound = values.dup
129
139
  c = connection
130
- statement.gsub('?') do
140
+ statement.gsub(/\?/) do
131
141
  replace_bind_variable(bound.shift, c)
132
142
  end
133
143
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  # = Active Record Schema
4
3
  #
@@ -91,16 +91,17 @@ HEADER
91
91
  end
92
92
 
93
93
  def tables(stream)
94
- @connection.tables.sort.each do |tbl|
95
- next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
96
- case ignored
97
- when String; remove_prefix_and_suffix(tbl) == ignored
98
- when Regexp; remove_prefix_and_suffix(tbl) =~ ignored
99
- else
100
- raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
101
- end
94
+ sorted_tables = @connection.tables.sort
95
+
96
+ sorted_tables.each do |table_name|
97
+ table(table_name, stream) unless ignored?(table_name)
98
+ end
99
+
100
+ # dump foreign keys at the end to make sure all dependent tables exist.
101
+ if @connection.supports_foreign_keys?
102
+ sorted_tables.each do |tbl|
103
+ foreign_keys(tbl, stream) unless ignored?(tbl)
102
104
  end
103
- table(tbl, stream)
104
105
  end
105
106
  end
106
107
 
@@ -110,26 +111,23 @@ HEADER
110
111
  tbl = StringIO.new
111
112
 
112
113
  # first dump primary key column
113
- if @connection.respond_to?(:pk_and_sequence_for)
114
- pk, _ = @connection.pk_and_sequence_for(table)
115
- end
116
- if !pk && @connection.respond_to?(:primary_key)
117
- pk = @connection.primary_key(table)
118
- end
114
+ pk = @connection.primary_key(table)
119
115
 
120
116
  tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
121
117
  pkcol = columns.detect { |c| c.name == pk }
122
118
  if pkcol
123
119
  if pk != 'id'
124
120
  tbl.print %Q(, primary_key: "#{pk}")
121
+ elsif pkcol.sql_type == 'bigint'
122
+ tbl.print ", id: :bigserial"
125
123
  elsif pkcol.sql_type == 'uuid'
126
124
  tbl.print ", id: :uuid"
127
- tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
125
+ tbl.print %Q(, default: #{pkcol.default_function.inspect})
128
126
  end
129
127
  else
130
128
  tbl.print ", id: false"
131
129
  end
132
- tbl.print ", force: true"
130
+ tbl.print ", force: :cascade"
133
131
  tbl.puts " do |t|"
134
132
 
135
133
  # then dump all non-primary key columns
@@ -187,34 +185,67 @@ HEADER
187
185
  if (indexes = @connection.indexes(table)).any?
188
186
  add_index_statements = indexes.map do |index|
189
187
  statement_parts = [
190
- ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
188
+ "add_index #{remove_prefix_and_suffix(index.table).inspect}",
191
189
  index.columns.inspect,
192
- ('name: ' + index.name.inspect),
190
+ "name: #{index.name.inspect}",
193
191
  ]
194
192
  statement_parts << 'unique: true' if index.unique
195
193
 
196
194
  index_lengths = (index.lengths || []).compact
197
- statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
195
+ statement_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
196
+
197
+ index_orders = index.orders || {}
198
+ statement_parts << "order: #{index.orders.inspect}" if index_orders.any?
199
+ statement_parts << "where: #{index.where.inspect}" if index.where
200
+ statement_parts << "using: #{index.using.inspect}" if index.using
201
+ statement_parts << "type: #{index.type.inspect}" if index.type
202
+
203
+ " #{statement_parts.join(', ')}"
204
+ end
205
+
206
+ stream.puts add_index_statements.sort.join("\n")
207
+ stream.puts
208
+ end
209
+ end
210
+
211
+ def foreign_keys(table, stream)
212
+ if (foreign_keys = @connection.foreign_keys(table)).any?
213
+ add_foreign_key_statements = foreign_keys.map do |foreign_key|
214
+ parts = [
215
+ "add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
216
+ remove_prefix_and_suffix(foreign_key.to_table).inspect,
217
+ ]
198
218
 
199
- index_orders = (index.orders || {})
200
- statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
219
+ if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table)
220
+ parts << "column: #{foreign_key.column.inspect}"
221
+ end
201
222
 
202
- statement_parts << ('where: ' + index.where.inspect) if index.where
223
+ if foreign_key.custom_primary_key?
224
+ parts << "primary_key: #{foreign_key.primary_key.inspect}"
225
+ end
203
226
 
204
- statement_parts << ('using: ' + index.using.inspect) if index.using
227
+ if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
228
+ parts << "name: #{foreign_key.name.inspect}"
229
+ end
205
230
 
206
- statement_parts << ('type: ' + index.type.inspect) if index.type
231
+ parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
232
+ parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
207
233
 
208
- ' ' + statement_parts.join(', ')
234
+ " #{parts.join(', ')}"
209
235
  end
210
236
 
211
- stream.puts add_index_statements.sort.join("\n")
212
- stream.puts
237
+ stream.puts add_foreign_key_statements.sort.join("\n")
213
238
  end
214
239
  end
215
240
 
216
241
  def remove_prefix_and_suffix(table)
217
242
  table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
218
243
  end
244
+
245
+ def ignored?(table_name)
246
+ ['schema_migrations', ignore_tables].flatten.any? do |ignored|
247
+ ignored === remove_prefix_and_suffix(table_name)
248
+ end
249
+ end
219
250
  end
220
251
  end
@@ -34,15 +34,16 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  def drop_table
37
- if table_exists?
38
- connection.remove_index table_name, name: index_name
39
- connection.drop_table(table_name)
40
- end
37
+ connection.drop_table table_name if table_exists?
41
38
  end
42
39
 
43
40
  def normalize_migration_number(number)
44
41
  "%.3d" % number.to_i
45
42
  end
43
+
44
+ def normalized_versions
45
+ pluck(:version).map { |v| normalize_migration_number v }
46
+ end
46
47
  end
47
48
 
48
49
  def version
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  end
12
12
 
13
13
  module ClassMethods
14
- # Returns a scope for the model without the +default_scope+.
14
+ # Returns a scope for the model without the previously set scopes.
15
15
  #
16
16
  # class Post < ActiveRecord::Base
17
17
  # def self.default_scope
@@ -19,11 +19,12 @@ module ActiveRecord
19
19
  # end
20
20
  # end
21
21
  #
22
- # Post.all # Fires "SELECT * FROM posts WHERE published = true"
23
- # Post.unscoped.all # Fires "SELECT * FROM posts"
22
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
23
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
24
+ # Post.where(published: false).unscoped.all # Fires "SELECT * FROM posts"
24
25
  #
25
26
  # This method also accepts a block. All queries inside the block will
26
- # not use the +default_scope+:
27
+ # not use the previously set scopes.
27
28
  #
28
29
  # Post.unscoped {
29
30
  # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
@@ -94,6 +95,7 @@ module ActiveRecord
94
95
  end
95
96
 
96
97
  def build_default_scope(base_rel = relation) # :nodoc:
98
+ return if abstract_class?
97
99
  if !Base.is_a?(method(:default_scope).owner)
98
100
  # The user has defined their own default scope method, so call that
99
101
  evaluate_default_scope { default_scope }
@@ -139,6 +139,10 @@ module ActiveRecord
139
139
  # Article.published.featured.latest_article
140
140
  # Article.featured.titles
141
141
  def scope(name, body, &block)
142
+ unless body.respond_to?(:call)
143
+ raise ArgumentError, 'The scope body needs to be callable.'
144
+ end
145
+
142
146
  if dangerous_class_method?(name)
143
147
  raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
144
148
  "on the model \"#{self.name}\", but Active Record already defined " \
@@ -180,13 +180,9 @@ module ActiveRecord #:nodoc:
180
180
  class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
181
181
  def compute_type
182
182
  klass = @serializable.class
183
- type = if klass.serialized_attributes.key?(name)
184
- super
185
- elsif klass.columns_hash.key?(name)
186
- klass.columns_hash[name].type
187
- else
188
- NilClass
189
- end
183
+ column = klass.columns_hash[name] || Type::Value.new
184
+
185
+ type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] || column.type
190
186
 
191
187
  { :text => :string,
192
188
  :time => :datetime }[type] || type
@@ -1,26 +1,111 @@
1
1
  module ActiveRecord
2
2
 
3
3
  # Statement cache is used to cache a single statement in order to avoid creating the AST again.
4
- # Initializing the cache is done by passing the statement in the initialization block:
4
+ # Initializing the cache is done by passing the statement in the create block:
5
5
  #
6
- # cache = ActiveRecord::StatementCache.new do
7
- # Book.where(name: "my book").limit(100)
6
+ # cache = StatementCache.create(Book.connection) do |params|
7
+ # Book.where(name: "my book").where("author_id > 3")
8
8
  # end
9
9
  #
10
10
  # The cached statement is executed by using the +execute+ method:
11
11
  #
12
- # cache.execute
12
+ # cache.execute([], Book, Book.connection)
13
13
  #
14
14
  # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
15
15
  # Database is queried when +to_a+ is called on the relation.
16
- class StatementCache
17
- def initialize
18
- @relation = yield
19
- raise ArgumentError.new("Statement cannot be nil") if @relation.nil?
16
+ #
17
+ # If you want to cache the statement without the values you can use the +bind+ method of the
18
+ # block parameter.
19
+ #
20
+ # cache = StatementCache.create(Book.connection) do |params|
21
+ # Book.where(name: params.bind)
22
+ # end
23
+ #
24
+ # And pass the bind values as the first argument of +execute+ call.
25
+ #
26
+ # cache.execute(["my book"], Book, Book.connection)
27
+ class StatementCache # :nodoc:
28
+ class Substitute; end # :nodoc:
29
+
30
+ class Query # :nodoc:
31
+ def initialize(sql)
32
+ @sql = sql
33
+ end
34
+
35
+ def sql_for(binds, connection)
36
+ @sql
37
+ end
38
+ end
39
+
40
+ class PartialQuery < Query # :nodoc:
41
+ def initialize values
42
+ @values = values
43
+ @indexes = values.each_with_index.find_all { |thing,i|
44
+ Arel::Nodes::BindParam === thing
45
+ }.map(&:last)
46
+ end
47
+
48
+ def sql_for(binds, connection)
49
+ val = @values.dup
50
+ binds = binds.dup
51
+ @indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
52
+ val.join
53
+ end
54
+ end
55
+
56
+ def self.query(visitor, ast)
57
+ Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
58
+ end
59
+
60
+ def self.partial_query(visitor, ast, collector)
61
+ collected = visitor.accept(ast, collector).value
62
+ PartialQuery.new collected
20
63
  end
21
64
 
22
- def execute
23
- @relation.dup.to_a
65
+ class Params # :nodoc:
66
+ def bind; Substitute.new; end
67
+ end
68
+
69
+ class BindMap # :nodoc:
70
+ def initialize(bind_values)
71
+ @indexes = []
72
+ @bind_values = bind_values
73
+
74
+ bind_values.each_with_index do |(_, value), i|
75
+ if Substitute === value
76
+ @indexes << i
77
+ end
78
+ end
79
+ end
80
+
81
+ def bind(values)
82
+ bvs = @bind_values.map { |pair| pair.dup }
83
+ @indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] }
84
+ bvs
85
+ end
86
+ end
87
+
88
+ attr_reader :bind_map, :query_builder
89
+
90
+ def self.create(connection, block = Proc.new)
91
+ relation = block.call Params.new
92
+ bind_map = BindMap.new relation.bind_values
93
+ query_builder = connection.cacheable_query relation.arel
94
+ new query_builder, bind_map
95
+ end
96
+
97
+ def initialize(query_builder, bind_map)
98
+ @query_builder = query_builder
99
+ @bind_map = bind_map
100
+ end
101
+
102
+ def execute(params, klass, connection)
103
+ bind_values = bind_map.bind params
104
+
105
+ sql = query_builder.sql_for bind_values, connection
106
+
107
+ klass.find_by_sql sql, bind_values
24
108
  end
109
+ alias :call :execute
25
110
  end
26
111
  end