activerecord 3.1.12 → 3.2.0.rc1

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 (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -1,8 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_support/core_ext/kernel/requires'
3
2
  require 'active_record/connection_adapters/statement_pool'
4
3
  require 'active_support/core_ext/string/encoding'
5
- require 'arel/visitors/bind_visitor'
6
4
 
7
5
  module ActiveRecord
8
6
  module ConnectionAdapters #:nodoc:
@@ -86,25 +84,12 @@ module ActiveRecord
86
84
  end
87
85
  end
88
86
 
89
- class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
90
- include Arel::Visitors::BindVisitor
91
- end
92
-
93
87
  def initialize(connection, logger, config)
94
88
  super(connection, logger)
95
89
  @statements = StatementPool.new(@connection,
96
90
  config.fetch(:statement_limit) { 1000 })
97
91
  @config = config
98
- end
99
-
100
- def self.visitor_for(pool) # :nodoc:
101
- config = pool.spec.config
102
-
103
- if config.fetch(:prepared_statements) { true }
104
- Arel::Visitors::SQLite.new pool
105
- else
106
- BindSubstitution.new pool
107
- end
92
+ @visitor = Arel::Visitors::SQLite.new self
108
93
  end
109
94
 
110
95
  def adapter_name #:nodoc:
@@ -137,6 +122,11 @@ module ActiveRecord
137
122
  true
138
123
  end
139
124
 
125
+ # Returns true.
126
+ def supports_explain?
127
+ true
128
+ end
129
+
140
130
  def requires_reloading?
141
131
  true
142
132
  end
@@ -169,6 +159,10 @@ module ActiveRecord
169
159
  sqlite_version >= '3.1.0'
170
160
  end
171
161
 
162
+ def supports_index_sort_order?
163
+ sqlite_version >= '3.3.0'
164
+ end
165
+
172
166
  def native_database_types #:nodoc:
173
167
  {
174
168
  :primary_key => default_primary_key_type,
@@ -215,7 +209,7 @@ module ActiveRecord
215
209
 
216
210
  value = super
217
211
  if column.type == :string && value.encoding == Encoding::ASCII_8BIT
218
- logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
212
+ @logger.error "Binary data inserted for `string` type on column `#{column.name}`"
219
213
  value.encode! 'utf-8'
220
214
  end
221
215
  value
@@ -230,6 +224,25 @@ module ActiveRecord
230
224
 
231
225
  # DATABASE STATEMENTS ======================================
232
226
 
227
+ def explain(arel, binds = [])
228
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel)}"
229
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
230
+ end
231
+
232
+ class ExplainPrettyPrinter
233
+ # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
234
+ # the output of the SQLite shell:
235
+ #
236
+ # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
237
+ # 0|1|1|SCAN TABLE posts (~100000 rows)
238
+ #
239
+ def pp(result) # :nodoc:
240
+ result.rows.map do |row|
241
+ row.join('|')
242
+ end.join("\n") + "\n"
243
+ end
244
+ end
245
+
233
246
  def exec_query(sql, name = nil, binds = [])
234
247
  log(sql, name, binds) do
235
248
 
@@ -303,31 +316,36 @@ module ActiveRecord
303
316
  end
304
317
 
305
318
  def begin_db_transaction #:nodoc:
306
- @connection.transaction
319
+ log('begin transaction',nil) { @connection.transaction }
307
320
  end
308
321
 
309
322
  def commit_db_transaction #:nodoc:
310
- @connection.commit
323
+ log('commit transaction',nil) { @connection.commit }
311
324
  end
312
325
 
313
326
  def rollback_db_transaction #:nodoc:
314
- @connection.rollback
327
+ log('rollback transaction',nil) { @connection.rollback }
315
328
  end
316
329
 
317
330
  # SCHEMA STATEMENTS ========================================
318
331
 
319
- def tables(name = 'SCHEMA') #:nodoc:
332
+ def tables(name = 'SCHEMA', table_name = nil) #:nodoc:
320
333
  sql = <<-SQL
321
334
  SELECT name
322
335
  FROM sqlite_master
323
336
  WHERE type = 'table' AND NOT name = 'sqlite_sequence'
324
337
  SQL
338
+ sql << " AND name = #{quote_table_name(table_name)}" if table_name
325
339
 
326
340
  exec_query(sql, name).map do |row|
327
341
  row['name']
328
342
  end
329
343
  end
330
344
 
345
+ def table_exists?(name)
346
+ name && tables('SCHEMA', name).any?
347
+ end
348
+
331
349
  # Returns an array of +SQLiteColumn+ objects for the table specified by +table_name+.
332
350
  def columns(table_name, name = nil) #:nodoc:
333
351
  table_structure(table_name).map do |field|
@@ -393,7 +411,7 @@ module ActiveRecord
393
411
  end
394
412
 
395
413
  def remove_column(table_name, *column_names) #:nodoc:
396
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
414
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
397
415
  column_names.flatten.each do |column_name|
398
416
  alter_table(table_name) do |definition|
399
417
  definition.columns.delete(definition[column_name])
@@ -425,6 +443,8 @@ module ActiveRecord
425
443
  self.limit = options[:limit] if options.include?(:limit)
426
444
  self.default = options[:default] if include_default
427
445
  self.null = options[:null] if options.include?(:null)
446
+ self.precision = options[:precision] if options.include?(:precision)
447
+ self.scale = options[:scale] if options.include?(:scale)
428
448
  end
429
449
  end
430
450
  end
@@ -479,6 +499,7 @@ module ActiveRecord
479
499
 
480
500
  @definition.column(column_name, column.type,
481
501
  :limit => column.limit, :default => column.default,
502
+ :precision => column.precision, :scale => column.scale,
482
503
  :null => column.null)
483
504
  end
484
505
  @definition.primary_key(primary_key(from)) if primary_key(from)
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  # = Active Record Counter Cache
3
3
  module CounterCache
4
4
  # Resets one or more counter caches to their correct value using an SQL
5
- # count query. This is useful when adding new counter caches, or if the
5
+ # count query. This is useful when adding new counter caches, or if the
6
6
  # counter has been corrupted or modified directly by SQL.
7
7
  #
8
8
  # ==== Parameters
@@ -0,0 +1,79 @@
1
+ module ActiveRecord
2
+ module DynamicMatchers
3
+ def respond_to?(method_id, include_private = false)
4
+ if match = DynamicFinderMatch.match(method_id)
5
+ return true if all_attributes_exists?(match.attribute_names)
6
+ elsif match = DynamicScopeMatch.match(method_id)
7
+ return true if all_attributes_exists?(match.attribute_names)
8
+ end
9
+
10
+ super
11
+ end
12
+
13
+ private
14
+
15
+ # Enables dynamic finders like <tt>User.find_by_user_name(user_name)</tt> and
16
+ # <tt>User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders
17
+ # section at the top of this file for more detailed information.
18
+ #
19
+ # It's even possible to use all the additional parameters to +find+. For example, the
20
+ # full interface for +find_all_by_amount+ is actually <tt>find_all_by_amount(amount, options)</tt>.
21
+ #
22
+ # Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
23
+ # is first invoked, so that future attempts to use it do not run through method_missing.
24
+ def method_missing(method_id, *arguments, &block)
25
+ if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
26
+ attribute_names = match.attribute_names
27
+ super unless all_attributes_exists?(attribute_names)
28
+ if arguments.size < attribute_names.size
29
+ method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
30
+ backtrace = [method_trace] + caller
31
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
32
+ end
33
+ if match.respond_to?(:scope?) && match.scope?
34
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
35
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
36
+ attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
37
+ #
38
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
39
+ end # end
40
+ METHOD
41
+ send(method_id, *arguments)
42
+ elsif match.finder?
43
+ options = arguments.extract_options!
44
+ relation = options.any? ? scoped(options) : scoped
45
+ relation.send :find_by_attributes, match, attribute_names, *arguments, &block
46
+ elsif match.instantiator?
47
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
48
+ end
49
+ else
50
+ super
51
+ end
52
+ end
53
+
54
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
55
+ def expand_attribute_names_for_aggregates(attribute_names)
56
+ attribute_names.map { |attribute_name|
57
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
58
+ aggregate_mapping(aggregation).map do |field_attr, _|
59
+ field_attr.to_sym
60
+ end
61
+ else
62
+ attribute_name.to_sym
63
+ end
64
+ }.flatten
65
+ end
66
+
67
+ def all_attributes_exists?(attribute_names)
68
+ (expand_attribute_names_for_aggregates(attribute_names) -
69
+ column_methods_hash.keys).empty?
70
+ end
71
+
72
+ def aggregate_mapping(reflection)
73
+ mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
74
+ mapping.first.is_a?(Array) ? mapping : [mapping]
75
+ end
76
+
77
+
78
+ end
79
+ end
@@ -87,7 +87,7 @@ module ActiveRecord
87
87
  #
88
88
  # For example, in
89
89
  #
90
- # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
90
+ # Location.where("lat = ? AND lng = ?", 53.7362)
91
91
  #
92
92
  # two placeholders are given but only one variable to fill them.
93
93
  class PreparedStatementInvalid < ActiveRecordError
@@ -99,6 +99,16 @@ module ActiveRecord
99
99
  #
100
100
  # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
101
101
  class StaleObjectError < ActiveRecordError
102
+ attr_reader :record, :attempted_action
103
+
104
+ def initialize(record, attempted_action)
105
+ @record = record
106
+ @attempted_action = attempted_action
107
+ end
108
+
109
+ def message
110
+ "Attempted to #{attempted_action} a stale object: #{record.class.name}"
111
+ end
102
112
  end
103
113
 
104
114
  # Raised when association is being configured improperly or
@@ -0,0 +1,83 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
3
+ module ActiveRecord
4
+ module Explain
5
+ def self.extended(base)
6
+ base.class_eval do
7
+ # If a query takes longer than these many seconds we log its query plan
8
+ # automatically. nil disables this feature.
9
+ class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false
10
+ self.auto_explain_threshold_in_seconds = nil
11
+ end
12
+ end
13
+
14
+ # If auto explain is enabled, this method triggers EXPLAIN logging for the
15
+ # queries triggered by the block if it takes more than the threshold as a
16
+ # whole. That is, the threshold is not checked against each individual
17
+ # query, but against the duration of the entire block. This approach is
18
+ # convenient for relations.
19
+ #
20
+ # The available_queries_for_explain thread variable collects the queries
21
+ # to be explained. If the value is nil, it means queries are not being
22
+ # currently collected. A false value indicates collecting is turned
23
+ # off. Otherwise it is an array of queries.
24
+ def logging_query_plan # :nodoc:
25
+ threshold = auto_explain_threshold_in_seconds
26
+ current = Thread.current
27
+ if threshold && current[:available_queries_for_explain].nil?
28
+ begin
29
+ queries = current[:available_queries_for_explain] = []
30
+ start = Time.now
31
+ result = yield
32
+ logger.warn(exec_explain(queries)) if Time.now - start > threshold
33
+ result
34
+ ensure
35
+ current[:available_queries_for_explain] = nil
36
+ end
37
+ else
38
+ yield
39
+ end
40
+ end
41
+
42
+ # Relation#explain needs to be able to collect the queries regardless of
43
+ # whether auto explain is enabled. This method serves that purpose.
44
+ def collecting_queries_for_explain # :nodoc:
45
+ current = Thread.current
46
+ original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
47
+ return yield, current[:available_queries_for_explain]
48
+ ensure
49
+ # Note that the return value above does not depend on this assigment.
50
+ current[:available_queries_for_explain] = original
51
+ end
52
+
53
+ # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
54
+ # Returns a formatted string ready to be logged.
55
+ def exec_explain(queries) # :nodoc:
56
+ queries && queries.map do |sql, bind|
57
+ [].tap do |msg|
58
+ msg << "EXPLAIN for: #{sql}"
59
+ unless bind.empty?
60
+ bind_msg = bind.map {|col, val| [col.name, val]}.inspect
61
+ msg.last << " #{bind_msg}"
62
+ end
63
+ msg << connection.explain(sql, bind)
64
+ end.join("\n")
65
+ end.join("\n")
66
+ end
67
+
68
+ # Silences automatic EXPLAIN logging for the duration of the block.
69
+ #
70
+ # This has high priority, no EXPLAINs will be run even if downwards
71
+ # the threshold is set to 0.
72
+ #
73
+ # As the name of the method suggests this only applies to automatic
74
+ # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
75
+ def silence_auto_explain
76
+ current = Thread.current
77
+ original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
78
+ yield
79
+ ensure
80
+ current[:available_queries_for_explain] = original
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_support/notifications'
2
+
3
+ module ActiveRecord
4
+ class ExplainSubscriber # :nodoc:
5
+ def call(*args)
6
+ if queries = Thread.current[:available_queries_for_explain]
7
+ payload = args.last
8
+ queries << payload.values_at(:sql, :binds) unless ignore_payload?(payload)
9
+ end
10
+ end
11
+
12
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
13
+ # our own EXPLAINs now matter how loopingly beautiful that would be.
14
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
15
+ def ignore_payload?(payload)
16
+ payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name])
17
+ end
18
+
19
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
20
+ end
21
+ end
@@ -6,14 +6,13 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  require 'yaml'
9
- require 'csv'
10
9
  require 'zlib'
11
10
  require 'active_support/dependencies'
12
11
  require 'active_support/core_ext/array/wrap'
13
12
  require 'active_support/core_ext/object/blank'
14
13
  require 'active_support/core_ext/logger'
15
14
  require 'active_support/ordered_hash'
16
- require 'active_support/core_ext/module/deprecation'
15
+ require 'active_record/fixtures/file'
17
16
 
18
17
  if defined? ActiveRecord
19
18
  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
@@ -23,8 +22,6 @@ else
23
22
  end
24
23
  end
25
24
 
26
- class FixturesFileNotFound < StandardError; end
27
-
28
25
  module ActiveRecord
29
26
  # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
30
27
  #
@@ -467,7 +464,7 @@ module ActiveRecord
467
464
  connection,
468
465
  table_name,
469
466
  class_names[table_name.to_sym] || table_name.classify,
470
- File.join(fixtures_directory, path))
467
+ ::File.join(fixtures_directory, path))
471
468
  end
472
469
 
473
470
  all_loaded_fixtures.update(fixtures_map)
@@ -645,80 +642,25 @@ module ActiveRecord
645
642
  end
646
643
 
647
644
  def read_fixture_files
648
- if File.file?(yaml_file_path)
649
- read_yaml_fixture_files
650
- elsif File.file?(csv_file_path)
651
- read_csv_fixture_files
652
- else
653
- raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
654
- end
655
- end
656
-
657
- def read_yaml_fixture_files
658
- yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
659
- File.file?(f)
660
- } + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
661
-
662
- if yaml = parse_yaml_string(yaml_string)
663
- # If the file is an ordered map, extract its children.
664
- yaml_value =
665
- if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
666
- yaml.value
667
- else
668
- [yaml]
669
- end
670
-
671
- yaml_value.each do |fixture|
672
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
673
- fixture.each do |name, data|
674
- unless data
675
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
676
- end
677
-
678
- fixtures[name] = ActiveRecord::Fixture.new(data, model_class)
645
+ yaml_files = Dir["#{@fixture_path}/**/*.yml"].select { |f|
646
+ ::File.file?(f)
647
+ } + [yaml_file_path]
648
+
649
+ yaml_files.each do |file|
650
+ Fixtures::File.open(file) do |fh|
651
+ fh.each do |name, row|
652
+ fixtures[name] = ActiveRecord::Fixture.new(row, model_class)
679
653
  end
680
654
  end
681
655
  end
682
656
  end
683
657
 
684
- def read_csv_fixture_files
685
- reader = CSV.parse(erb_render(IO.read(csv_file_path)))
686
- header = reader.shift
687
- i = 0
688
- reader.each do |row|
689
- data = {}
690
- row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
691
- fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = ActiveRecord::Fixture.new(data, model_class)
692
- end
693
- end
694
- deprecate :read_csv_fixture_files
695
-
696
658
  def yaml_file_path
697
659
  "#{@fixture_path}.yml"
698
660
  end
699
661
 
700
- def csv_file_path
701
- @fixture_path + ".csv"
702
- end
703
-
704
662
  def yaml_fixtures_key(path)
705
- File.basename(@fixture_path).split(".").first
706
- end
707
-
708
- RESCUE_ERRORS = [ ArgumentError ]
709
-
710
- if defined?(Psych) && defined?(Psych::SyntaxError)
711
- RESCUE_ERRORS << Psych::SyntaxError
712
- end
713
-
714
- def parse_yaml_string(fixture_content)
715
- YAML::load(erb_render(fixture_content))
716
- rescue *RESCUE_ERRORS => error
717
- raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
718
- end
719
-
720
- def erb_render(fixture_content)
721
- ERB.new(fixture_content).result
663
+ ::File.basename(@fixture_path).split(".").first
722
664
  end
723
665
  end
724
666
 
@@ -794,7 +736,7 @@ module ActiveRecord
794
736
 
795
737
  def fixtures(*fixture_names)
796
738
  if fixture_names.first == :all
797
- fixture_names = Dir["#{fixture_path}/**/*.{yml,csv}"]
739
+ fixture_names = Dir["#{fixture_path}/**/*.{yml}"]
798
740
  fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
799
741
  else
800
742
  fixture_names = fixture_names.flatten.map { |n| n.to_s }
@@ -880,6 +822,7 @@ module ActiveRecord
880
822
  end
881
823
 
882
824
  @fixture_cache = {}
825
+ @fixture_connections = []
883
826
  @@already_loaded_fixtures ||= {}
884
827
 
885
828
  # Load fixtures once and begin transaction.
@@ -890,9 +833,12 @@ module ActiveRecord
890
833
  @loaded_fixtures = load_fixtures
891
834
  @@already_loaded_fixtures[self.class] = @loaded_fixtures
892
835
  end
893
- ActiveRecord::Base.connection.increment_open_transactions
894
- ActiveRecord::Base.connection.transaction_joinable = false
895
- ActiveRecord::Base.connection.begin_db_transaction
836
+ @fixture_connections = enlist_fixture_connections
837
+ @fixture_connections.each do |connection|
838
+ connection.increment_open_transactions
839
+ connection.transaction_joinable = false
840
+ connection.begin_db_transaction
841
+ end
896
842
  # Load fixtures for every test.
897
843
  else
898
844
  ActiveRecord::Fixtures.reset_cache
@@ -912,13 +858,22 @@ module ActiveRecord
912
858
  end
913
859
 
914
860
  # Rollback changes if a transaction is active.
915
- if run_in_transaction? && ActiveRecord::Base.connection.open_transactions != 0
916
- ActiveRecord::Base.connection.rollback_db_transaction
917
- ActiveRecord::Base.connection.decrement_open_transactions
861
+ if run_in_transaction?
862
+ @fixture_connections.each do |connection|
863
+ if connection.open_transactions != 0
864
+ connection.rollback_db_transaction
865
+ connection.decrement_open_transactions
866
+ end
867
+ end
868
+ @fixture_connections.clear
918
869
  end
919
870
  ActiveRecord::Base.clear_active_connections!
920
871
  end
921
872
 
873
+ def enlist_fixture_connections
874
+ ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection)
875
+ end
876
+
922
877
  private
923
878
  def load_fixtures
924
879
  fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)