activerecord 4.1.16 → 4.2.11.3

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1801
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +19 -11
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +19 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +55 -69
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  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
@@ -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
@@ -99,7 +99,7 @@ module ActiveRecord
99
99
  self.local_stored_attributes[store_attribute] |= keys
100
100
  end
101
101
 
102
- def _store_accessors_module
102
+ def _store_accessors_module # :nodoc:
103
103
  @_store_accessors_module ||= begin
104
104
  mod = Module.new
105
105
  include mod
@@ -129,10 +129,10 @@ module ActiveRecord
129
129
 
130
130
  private
131
131
  def store_accessor_for(store_attribute)
132
- @column_types[store_attribute.to_s].accessor
132
+ type_for_attribute(store_attribute.to_s).accessor
133
133
  end
134
134
 
135
- class HashAccessor
135
+ class HashAccessor # :nodoc:
136
136
  def self.read(object, attribute, key)
137
137
  prepare(object, attribute)
138
138
  object.public_send(attribute)[key]
@@ -151,7 +151,7 @@ module ActiveRecord
151
151
  end
152
152
  end
153
153
 
154
- class StringKeyedHashAccessor < HashAccessor
154
+ class StringKeyedHashAccessor < HashAccessor # :nodoc:
155
155
  def self.read(object, attribute, key)
156
156
  super object, attribute, key.to_s
157
157
  end
@@ -161,7 +161,7 @@ module ActiveRecord
161
161
  end
162
162
  end
163
163
 
164
- class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
164
+ class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
165
165
  def self.prepare(object, store_attribute)
166
166
  attribute = object.send(store_attribute)
167
167
  unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  module Tasks # :nodoc:
3
5
  class DatabaseAlreadyExists < StandardError; end # :nodoc:
@@ -6,7 +8,7 @@ module ActiveRecord
6
8
  # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
7
9
  # logic behind common tasks used to manage database and migrations.
8
10
  #
9
- # The tasks defined here are used in rake tasks provided by Active Record.
11
+ # The tasks defined here are used with Rake tasks provided by Active Record.
10
12
  #
11
13
  # In order to use DatabaseTasks, a few config values need to be set. All the needed
12
14
  # config values are set by Rails already, so it's necessary to do it only if you
@@ -14,7 +16,6 @@ module ActiveRecord
14
16
  # (in such case after configuring the database tasks, you can also use the rake tasks
15
17
  # defined in Active Record).
16
18
  #
17
- #
18
19
  # The possible config values are:
19
20
  #
20
21
  # * +env+: current environment (like Rails.env).
@@ -28,7 +29,7 @@ module ActiveRecord
28
29
  # Example usage of +DatabaseTasks+ outside Rails could look as such:
29
30
  #
30
31
  # include ActiveRecord::Tasks
31
- # DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
32
+ # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
32
33
  # DatabaseTasks.db_dir = 'db'
33
34
  # # other settings...
34
35
  #
@@ -59,7 +60,11 @@ module ActiveRecord
59
60
  end
60
61
 
61
62
  def fixtures_path
62
- @fixtures_path ||= File.join(root, 'test', 'fixtures')
63
+ @fixtures_path ||= if ENV['FIXTURES_PATH']
64
+ File.join(root, ENV['FIXTURES_PATH'])
65
+ else
66
+ File.join(root, 'test', 'fixtures')
67
+ end
63
68
  end
64
69
 
65
70
  def root
@@ -107,6 +112,8 @@ module ActiveRecord
107
112
  def drop(*arguments)
108
113
  configuration = arguments.first
109
114
  class_for_adapter(configuration['adapter']).new(*arguments).drop
115
+ rescue ActiveRecord::NoDatabaseError
116
+ $stderr.puts "Database '#{configuration['database']}' does not exist"
110
117
  rescue Exception => error
111
118
  $stderr.puts error, *(error.backtrace)
112
119
  $stderr.puts "Couldn't drop #{configuration['database']}"
@@ -122,6 +129,21 @@ module ActiveRecord
122
129
  }
123
130
  end
124
131
 
132
+ def migrate
133
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
134
+
135
+ verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
136
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
137
+ scope = ENV['SCOPE']
138
+ verbose_was, Migration.verbose = Migration.verbose, verbose
139
+ Migrator.migrate(migrations_paths, version) do |migration|
140
+ scope.blank? || scope == migration.scope
141
+ end
142
+ ActiveRecord::Base.clear_cache!
143
+ ensure
144
+ Migration.verbose = verbose_was
145
+ end
146
+
125
147
  def charset_current(environment = env)
126
148
  charset ActiveRecord::Base.configurations[environment]
127
149
  end
@@ -144,6 +166,19 @@ module ActiveRecord
144
166
  class_for_adapter(configuration['adapter']).new(configuration).purge
145
167
  end
146
168
 
169
+ def purge_all
170
+ each_local_configuration { |configuration|
171
+ purge configuration
172
+ }
173
+ end
174
+
175
+ def purge_current(environment = env)
176
+ each_current_configuration(environment) { |configuration|
177
+ purge configuration
178
+ }
179
+ ActiveRecord::Base.establish_connection(environment.to_sym)
180
+ end
181
+
147
182
  def structure_dump(*arguments)
148
183
  configuration = arguments.first
149
184
  filename = arguments.delete_at 1
@@ -157,20 +192,34 @@ module ActiveRecord
157
192
  end
158
193
 
159
194
  def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
195
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
196
+ This method will act on a specific connection in the future.
197
+ To act on the current connection, use `load_schema_current` instead.
198
+ MSG
199
+
160
200
  load_schema_current(format, file)
161
201
  end
162
202
 
203
+ def schema_file(format = ActiveRecord::Base.schema_format)
204
+ case format
205
+ when :ruby
206
+ File.join(db_dir, "schema.rb")
207
+ when :sql
208
+ File.join(db_dir, "structure.sql")
209
+ end
210
+ end
211
+
163
212
  # This method is the successor of +load_schema+. We should rename it
164
213
  # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
165
214
  def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
215
+ file ||= schema_file(format)
216
+
166
217
  case format
167
218
  when :ruby
168
- file ||= File.join(db_dir, "schema.rb")
169
219
  check_schema_file(file)
170
220
  ActiveRecord::Base.establish_connection(configuration)
171
221
  load(file)
172
222
  when :sql
173
- file ||= File.join(db_dir, "structure.sql")
174
223
  check_schema_file(file)
175
224
  structure_load(configuration, file)
176
225
  else
@@ -178,6 +227,12 @@ module ActiveRecord
178
227
  end
179
228
  end
180
229
 
230
+ def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
231
+ if File.exist?(file || schema_file(format))
232
+ load_schema_current(format, file, environment)
233
+ end
234
+ end
235
+
181
236
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
182
237
  each_current_configuration(environment) { |configuration|
183
238
  load_schema_for configuration, format, file