activerecord 3.2.22.5 → 4.0.0.beta1

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,49 +1,8 @@
1
- require 'active_support/core_ext/class/attribute'
1
+ require 'active_support/lazy_load_hooks'
2
2
 
3
3
  module ActiveRecord
4
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 the database adapter supports explain and auto explain is enabled,
15
- # this method triggers EXPLAIN logging for the queries triggered by the
16
- # block if it takes more than the threshold as a whole. That is, the
17
- # threshold is not checked against each individual query, but against the
18
- # duration of the entire block. This approach is convenient for relations.
19
-
20
- #
21
- # The available_queries_for_explain thread variable collects the queries
22
- # to be explained. If the value is nil, it means queries are not being
23
- # currently collected. A false value indicates collecting is turned
24
- # off. Otherwise it is an array of queries.
25
- def logging_query_plan # :nodoc:
26
- return yield unless logger
27
-
28
- threshold = auto_explain_threshold_in_seconds
29
- current = Thread.current
30
- if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
31
- begin
32
- queries = current[:available_queries_for_explain] = []
33
- start = Time.now
34
- result = yield
35
- logger.warn(exec_explain(queries)) if Time.now - start > threshold
36
- result
37
- ensure
38
- current[:available_queries_for_explain] = nil
39
- end
40
- else
41
- yield
42
- end
43
- end
44
-
45
- # Relation#explain needs to be able to collect the queries regardless of
46
- # whether auto explain is enabled. This method serves that purpose.
5
+ # Relation#explain needs to be able to collect the queries.
47
6
  def collecting_queries_for_explain # :nodoc:
48
7
  current = Thread.current
49
8
  original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
@@ -56,7 +15,7 @@ module ActiveRecord
56
15
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
57
16
  # Returns a formatted string ready to be logged.
58
17
  def exec_explain(queries) # :nodoc:
59
- queries && queries.map do |sql, bind|
18
+ str = queries && queries.map do |sql, bind|
60
19
  [].tap do |msg|
61
20
  msg << "EXPLAIN for: #{sql}"
62
21
  unless bind.empty?
@@ -66,21 +25,12 @@ module ActiveRecord
66
25
  msg << connection.explain(sql, bind)
67
26
  end.join("\n")
68
27
  end.join("\n")
69
- end
70
28
 
71
- # Silences automatic EXPLAIN logging for the duration of the block.
72
- #
73
- # This has high priority, no EXPLAINs will be run even if downwards
74
- # the threshold is set to 0.
75
- #
76
- # As the name of the method suggests this only applies to automatic
77
- # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
78
- def silence_auto_explain
79
- current = Thread.current
80
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
81
- yield
82
- ensure
83
- current[:available_queries_for_explain] = original
29
+ # Overriding inspect to be more human readable, specially in the console.
30
+ def str.inspect
31
+ self
32
+ end
33
+ str
84
34
  end
85
35
  end
86
36
  end
@@ -2,9 +2,12 @@ require 'active_support/notifications'
2
2
 
3
3
  module ActiveRecord
4
4
  class ExplainSubscriber # :nodoc:
5
- def call(*args)
5
+ def start(name, id, payload)
6
+ # unused
7
+ end
8
+
9
+ def finish(name, id, payload)
6
10
  if queries = Thread.current[:available_queries_for_explain]
7
- payload = args.last
8
11
  queries << payload.values_at(:sql, :binds) unless ignore_payload?(payload)
9
12
  end
10
13
  end
@@ -15,7 +18,7 @@ module ActiveRecord
15
18
  # On the other hand, we want to monitor the performance of our real database
16
19
  # queries, not the performance of the access to the query cache.
17
20
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
18
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
21
+ EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)/i
19
22
  def ignore_payload?(payload)
20
23
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
21
24
  end
@@ -0,0 +1,56 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
4
+ module ActiveRecord
5
+ class FixtureSet
6
+ class File # :nodoc:
7
+ include Enumerable
8
+
9
+ ##
10
+ # Open a fixture file named +file+. When called with a block, the block
11
+ # is called with the filehandle and the filehandle is automatically closed
12
+ # when the block finishes.
13
+ def self.open(file)
14
+ x = new file
15
+ block_given? ? yield(x) : x
16
+ end
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ @rows = nil
21
+ end
22
+
23
+ def each(&block)
24
+ rows.each(&block)
25
+ end
26
+
27
+ RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ] # :nodoc:
28
+
29
+ private
30
+ def rows
31
+ return @rows if @rows
32
+
33
+ begin
34
+ data = YAML.load(render(IO.read(@file)))
35
+ rescue *RESCUE_ERRORS => error
36
+ raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. 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
37
+ end
38
+ @rows = data ? validate(data).to_a : []
39
+ end
40
+
41
+ def render(content)
42
+ ERB.new(content).result
43
+ end
44
+
45
+ # Validate our unmarshalled data.
46
+ def validate(data)
47
+ unless Hash === data || YAML::Omap === data
48
+ raise Fixture::FormatError, 'fixture is not a hash'
49
+ end
50
+
51
+ raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
52
+ data
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,28 +1,14 @@
1
1
  require 'erb'
2
-
3
- begin
4
- require 'psych'
5
- rescue LoadError
6
- end
7
-
8
2
  require 'yaml'
9
3
  require 'zlib'
10
4
  require 'active_support/dependencies'
11
- require 'active_support/core_ext/array/wrap'
12
- require 'active_support/core_ext/object/blank'
13
- require 'active_support/core_ext/logger'
14
- require 'active_support/ordered_hash'
15
- require 'active_record/fixtures/file'
5
+ require 'active_record/fixture_set/file'
6
+ require 'active_record/errors'
16
7
 
17
- if defined? ActiveRecord
8
+ module ActiveRecord
18
9
  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
19
10
  end
20
- else
21
- class FixtureClassNotFound < StandardError #:nodoc:
22
- end
23
- end
24
11
 
25
- module ActiveRecord
26
12
  # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
27
13
  #
28
14
  # They are stored in YAML files, one file per model, which are placed in the directory
@@ -96,7 +82,7 @@ module ActiveRecord
96
82
  # end
97
83
  #
98
84
  # test "find_alt_method_2" do
99
- # assert_equal "Ruby on Rails", @rubyonrails.news
85
+ # assert_equal "Ruby on Rails", @rubyonrails.name
100
86
  # end
101
87
  #
102
88
  # In order to use these methods to access fixtured data within your testcases, you must specify one of the
@@ -262,7 +248,7 @@ module ActiveRecord
262
248
  #
263
249
  # ### in fruit.rb
264
250
  #
265
- # belongs_to :eater, :polymorphic => true
251
+ # belongs_to :eater, polymorphic: true
266
252
  #
267
253
  # ### in fruits.yml
268
254
  #
@@ -364,8 +350,8 @@ module ActiveRecord
364
350
  # to the rescue:
365
351
  #
366
352
  # george_reginald:
367
- # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
368
- # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
353
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
354
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
369
355
  #
370
356
  # == Support for YAML defaults
371
357
  #
@@ -377,22 +363,38 @@ module ActiveRecord
377
363
  #
378
364
  # first:
379
365
  # name: Smurf
380
- # *DEFAULTS
366
+ # <<: *DEFAULTS
381
367
  #
382
368
  # second:
383
369
  # name: Fraggle
384
- # *DEFAULTS
370
+ # <<: *DEFAULTS
385
371
  #
386
372
  # Any fixture labeled "DEFAULTS" is safely ignored.
387
- class Fixtures
373
+ class FixtureSet
374
+ #--
375
+ # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
376
+ #++
377
+
388
378
  MAX_ID = 2 ** 30 - 1
389
379
 
390
380
  @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
391
381
 
392
- def self.find_table_name(table_name) # :nodoc:
382
+ def self.find_table_name(fixture_set_name) # :nodoc:
383
+ ActiveSupport::Deprecation.warn(
384
+ "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
385
+ default_fixture_model_name(fixture_set_name)
386
+ end
387
+
388
+ def self.default_fixture_model_name(fixture_set_name) # :nodoc:
393
389
  ActiveRecord::Base.pluralize_table_names ?
394
- table_name.to_s.singularize.camelize :
395
- table_name.to_s.camelize
390
+ fixture_set_name.singularize.camelize :
391
+ fixture_set_name.camelize
392
+ end
393
+
394
+ def self.default_fixture_table_name(fixture_set_name) # :nodoc:
395
+ "#{ ActiveRecord::Base.table_name_prefix }"\
396
+ "#{ fixture_set_name.tr('/', '_') }"\
397
+ "#{ ActiveRecord::Base.table_name_suffix }".to_sym
396
398
  end
397
399
 
398
400
  def self.reset_cache
@@ -419,11 +421,7 @@ module ActiveRecord
419
421
  cache_for_connection(connection).update(fixtures_map)
420
422
  end
421
423
 
422
- #--
423
- # TODO:NOTE: in the next version, the __with_new_arity suffix and
424
- # the method with the old arity will be removed.
425
- #++
426
- def self.instantiate_fixtures__with_new_arity(object, fixture_set, load_instances = true) # :nodoc:
424
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true)
427
425
  if load_instances
428
426
  fixture_set.each do |fixture_name, fixture|
429
427
  begin
@@ -435,79 +433,60 @@ module ActiveRecord
435
433
  end
436
434
  end
437
435
 
438
- # The use with parameters <tt>(object, fixture_set_name, fixture_set, load_instances = true)</tt> is deprecated, +fixture_set_name+ parameter is not used.
439
- # Use as:
440
- #
441
- # instantiate_fixtures(object, fixture_set, load_instances = true)
442
- def self.instantiate_fixtures(object, fixture_set, load_instances = true, rails_3_2_compatibility_argument = true)
443
- unless load_instances == true || load_instances == false
444
- ActiveSupport::Deprecation.warn(
445
- "ActiveRecord::Fixtures.instantiate_fixtures with parameters (object, fixture_set_name, fixture_set, load_instances = true) is deprecated and shall be removed from future releases. Use it with parameters (object, fixture_set, load_instances = true) instead (skip fixture_set_name).",
446
- caller)
447
- fixture_set = load_instances
448
- load_instances = rails_3_2_compatibility_argument
449
- end
450
- instantiate_fixtures__with_new_arity(object, fixture_set, load_instances)
451
- end
452
-
453
436
  def self.instantiate_all_loaded_fixtures(object, load_instances = true)
454
437
  all_loaded_fixtures.each_value do |fixture_set|
455
- ActiveRecord::Fixtures.instantiate_fixtures(object, fixture_set, load_instances)
438
+ instantiate_fixtures(object, fixture_set, load_instances)
456
439
  end
457
440
  end
458
441
 
459
442
  cattr_accessor :all_loaded_fixtures
460
443
  self.all_loaded_fixtures = {}
461
444
 
462
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
463
- table_names = [table_names].flatten.map { |n| n.to_s }
464
- table_names.each { |n|
465
- class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
466
- }
445
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
446
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
447
+ class_names = class_names.stringify_keys
467
448
 
468
449
  # FIXME: Apparently JK uses this.
469
450
  connection = block_given? ? yield : ActiveRecord::Base.connection
470
451
 
471
- files_to_read = table_names.reject { |table_name|
472
- fixture_is_cached?(connection, table_name)
452
+ files_to_read = fixture_set_names.reject { |fs_name|
453
+ fixture_is_cached?(connection, fs_name)
473
454
  }
474
455
 
475
456
  unless files_to_read.empty?
476
457
  connection.disable_referential_integrity do
477
458
  fixtures_map = {}
478
459
 
479
- fixture_files = files_to_read.map do |path|
480
- table_name = path.tr '/', '_'
481
-
482
- fixtures_map[path] = ActiveRecord::Fixtures.new(
460
+ fixture_sets = files_to_read.map do |fs_name|
461
+ fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
483
462
  connection,
484
- table_name,
485
- class_names[table_name.to_sym] || table_name.classify,
486
- ::File.join(fixtures_directory, path))
463
+ fs_name,
464
+ class_names[fs_name] || default_fixture_model_name(fs_name),
465
+ ::File.join(fixtures_directory, fs_name))
487
466
  end
488
467
 
489
468
  all_loaded_fixtures.update(fixtures_map)
490
469
 
491
470
  connection.transaction(:requires_new => true) do
492
- fixture_files.each do |ff|
493
- conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
494
- table_rows = ff.table_rows
471
+ fixture_sets.each do |fs|
472
+ conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
473
+ table_rows = fs.table_rows
495
474
 
496
475
  table_rows.keys.each do |table|
497
476
  conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
498
477
  end
499
478
 
500
- table_rows.each do |table_name,rows|
479
+ table_rows.each do |fixture_set_name, rows|
501
480
  rows.each do |row|
502
- conn.insert_fixture(row, table_name)
481
+ conn.insert_fixture(row, fixture_set_name)
503
482
  end
504
483
  end
505
484
  end
506
485
 
507
486
  # Cap primary key sequences to max(pk).
508
487
  if connection.respond_to?(:reset_pk_sequence!)
509
- table_names.each do |table_name|
510
- connection.reset_pk_sequence!(table_name.tr('/', '_'))
488
+ fixture_sets.each do |fs|
489
+ connection.reset_pk_sequence!(fs.table_name)
511
490
  end
512
491
  end
513
492
  end
@@ -515,7 +494,7 @@ module ActiveRecord
515
494
  cache_fixtures(connection, fixtures_map)
516
495
  end
517
496
  end
518
- cached_fixtures(connection, table_names)
497
+ cached_fixtures(connection, fixture_set_names)
519
498
  end
520
499
 
521
500
  # Returns a consistent, platform-independent identifier for +label+.
@@ -526,25 +505,24 @@ module ActiveRecord
526
505
 
527
506
  attr_reader :table_name, :name, :fixtures, :model_class
528
507
 
529
- def initialize(connection, table_name, class_name, fixture_path)
530
- @connection = connection
531
- @table_name = table_name
532
- @fixture_path = fixture_path
533
- @name = table_name # preserve fixture base name
534
- @class_name = class_name
535
-
536
- @fixtures = ActiveSupport::OrderedHash.new
537
- @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
538
-
539
- # Should be an AR::Base type class
540
- if class_name.is_a?(Class)
541
- @table_name = class_name.table_name
542
- @connection = class_name.connection
543
- @model_class = class_name
508
+ def initialize(connection, name, class_name, path)
509
+ @fixtures = {} # Ordered hash
510
+ @name = name
511
+ @path = path
512
+
513
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
514
+ @model_class = class_name
544
515
  else
545
- @model_class = class_name.constantize rescue nil
516
+ @model_class = class_name.constantize rescue nil
546
517
  end
547
518
 
519
+ @connection = ( model_class.respond_to?(:connection) ?
520
+ model_class.connection : connection )
521
+
522
+ @table_name = ( model_class.respond_to?(:table_name) ?
523
+ model_class.table_name :
524
+ self.class.default_fixture_table_name(name) )
525
+
548
526
  read_fixture_files
549
527
  end
550
528
 
@@ -582,19 +560,19 @@ module ActiveRecord
582
560
  if model_class && model_class < ActiveRecord::Base
583
561
  # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
584
562
  if model_class.record_timestamps
585
- timestamp_column_names.each do |name|
586
- row[name] = now unless row.key?(name)
563
+ timestamp_column_names.each do |c_name|
564
+ row[c_name] = now unless row.key?(c_name)
587
565
  end
588
566
  end
589
567
 
590
568
  # interpolate the fixture label
591
569
  row.each do |key, value|
592
- row[key] = label if value.is_a?(String) && value == "$LABEL"
570
+ row[key] = label if value == "$LABEL"
593
571
  end
594
572
 
595
573
  # generate a primary key if necessary
596
574
  if has_primary_key_column? && !row.include?(primary_key_name)
597
- row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
575
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
598
576
  end
599
577
 
600
578
  # If STI is used, find the correct subclass for association reflection
@@ -617,15 +595,15 @@ module ActiveRecord
617
595
  row[association.foreign_type] = $1
618
596
  end
619
597
 
620
- row[fk_name] = ActiveRecord::Fixtures.identify(value)
598
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value)
621
599
  end
622
600
  when :has_and_belongs_to_many
623
601
  if (targets = row.delete(association.name.to_s))
624
602
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
625
- table_name = association.options[:join_table]
603
+ table_name = association.join_table
626
604
  rows[table_name].concat targets.map { |target|
627
605
  { association.foreign_key => row[primary_key_name],
628
- association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
606
+ association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
629
607
  }
630
608
  end
631
609
  end
@@ -661,25 +639,31 @@ module ActiveRecord
661
639
  end
662
640
 
663
641
  def read_fixture_files
664
- yaml_files = Dir["#{@fixture_path}/{**,*}/*.yml"].select { |f|
642
+ yaml_files = Dir["#{@path}/**/*.yml"].select { |f|
665
643
  ::File.file?(f)
666
644
  } + [yaml_file_path]
667
645
 
668
646
  yaml_files.each do |file|
669
- Fixtures::File.open(file) do |fh|
670
- fh.each do |name, row|
671
- fixtures[name] = ActiveRecord::Fixture.new(row, model_class)
647
+ FixtureSet::File.open(file) do |fh|
648
+ fh.each do |fixture_name, row|
649
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
672
650
  end
673
651
  end
674
652
  end
675
653
  end
676
654
 
677
655
  def yaml_file_path
678
- "#{@fixture_path}.yml"
656
+ "#{@path}.yml"
679
657
  end
680
658
 
681
659
  end
682
660
 
661
+ #--
662
+ # Deprecate 'Fixtures' in favor of 'FixtureSet'.
663
+ #++
664
+ # :nodoc:
665
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
666
+
683
667
  class Fixture #:nodoc:
684
668
  include Enumerable
685
669
 
@@ -732,7 +716,7 @@ module ActiveRecord
732
716
  class_attribute :fixture_table_names
733
717
  class_attribute :fixture_class_names
734
718
  class_attribute :use_transactional_fixtures
735
- class_attribute :use_instantiated_fixtures # true, false, or :no_instances
719
+ class_attribute :use_instantiated_fixtures # true, false, or :no_instances
736
720
  class_attribute :pre_loaded_fixtures
737
721
 
738
722
  self.fixture_table_names = []
@@ -740,27 +724,42 @@ module ActiveRecord
740
724
  self.use_instantiated_fixtures = false
741
725
  self.pre_loaded_fixtures = false
742
726
 
743
- self.fixture_class_names = Hash.new do |h, table_name|
744
- h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
727
+ self.fixture_class_names = Hash.new do |h, fixture_set_name|
728
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
745
729
  end
746
730
  end
747
731
 
748
732
  module ClassMethods
733
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
734
+ #
735
+ # Examples:
736
+ #
737
+ # set_fixture_class some_fixture: SomeModel,
738
+ # 'namespaced/fixture' => Another::Model
739
+ #
740
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
741
+ #--
742
+ # It is also possible to pass the class name instead of the class:
743
+ # set_fixture_class 'some_fixture' => 'SomeModel'
744
+ # I think this option is redundant, i propose to deprecate it.
745
+ # Isn't it easier to always pass the class itself?
746
+ # (2011-12-20 alexeymuranov)
747
+ #++
749
748
  def set_fixture_class(class_names = {})
750
- self.fixture_class_names = self.fixture_class_names.merge(class_names)
749
+ self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
751
750
  end
752
751
 
753
- def fixtures(*fixture_names)
754
- if fixture_names.first == :all
755
- fixture_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
756
- fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
752
+ def fixtures(*fixture_set_names)
753
+ if fixture_set_names.first == :all
754
+ fixture_set_names = Dir["#{fixture_path}/**/*.{yml}"]
755
+ fixture_set_names.map! { |f| f[(fixture_path.size + 1)..-5] }
757
756
  else
758
- fixture_names = fixture_names.flatten.map { |n| n.to_s }
757
+ fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
759
758
  end
760
759
 
761
- self.fixture_table_names |= fixture_names
762
- require_fixture_classes(fixture_names)
763
- setup_fixture_accessors(fixture_names)
760
+ self.fixture_table_names |= fixture_set_names
761
+ require_fixture_classes(fixture_set_names)
762
+ setup_fixture_accessors(fixture_set_names)
764
763
  end
765
764
 
766
765
  def try_to_load_dependency(file_name)
@@ -775,40 +774,45 @@ module ActiveRecord
775
774
  end
776
775
  end
777
776
 
778
- def require_fixture_classes(fixture_names = nil)
779
- (fixture_names || fixture_table_names).each do |fixture_name|
780
- file_name = fixture_name.to_s
777
+ def require_fixture_classes(fixture_set_names = nil)
778
+ if fixture_set_names
779
+ fixture_set_names = fixture_set_names.map { |n| n.to_s }
780
+ else
781
+ fixture_set_names = fixture_table_names
782
+ end
783
+
784
+ fixture_set_names.each do |file_name|
781
785
  file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
782
786
  try_to_load_dependency(file_name)
783
787
  end
784
788
  end
785
789
 
786
- def setup_fixture_accessors(fixture_names = nil)
787
- fixture_names = Array.wrap(fixture_names || fixture_table_names)
790
+ def setup_fixture_accessors(fixture_set_names = nil)
791
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
788
792
  methods = Module.new do
789
- fixture_names.each do |fixture_name|
790
- fixture_name = fixture_name.to_s.tr('./', '_')
793
+ fixture_set_names.each do |fs_name|
794
+ fs_name = fs_name.to_s
795
+ accessor_name = fs_name.tr('/', '_').to_sym
791
796
 
792
- define_method(fixture_name) do |*fixtures|
793
- force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
797
+ define_method(accessor_name) do |*fixture_names|
798
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
794
799
 
795
- @fixture_cache[fixture_name] ||= {}
800
+ @fixture_cache[fs_name] ||= {}
796
801
 
797
- instances = fixtures.map do |fixture|
798
- @fixture_cache[fixture_name].delete(fixture) if force_reload
802
+ instances = fixture_names.map do |f_name|
803
+ f_name = f_name.to_s
804
+ @fixture_cache[fs_name].delete(f_name) if force_reload
799
805
 
800
- if @loaded_fixtures[fixture_name][fixture.to_s]
801
- ActiveRecord::IdentityMap.without do
802
- @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
803
- end
806
+ if @loaded_fixtures[fs_name][f_name]
807
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
804
808
  else
805
- raise StandardError, "No fixture with name '#{fixture}' found for table '#{fixture_name}'"
809
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
806
810
  end
807
811
  end
808
812
 
809
813
  instances.size == 1 ? instances.first : instances
810
814
  end
811
- private fixture_name
815
+ private accessor_name
812
816
  end
813
817
  end
814
818
  include methods
@@ -831,7 +835,7 @@ module ActiveRecord
831
835
  end
832
836
 
833
837
  def setup_fixtures
834
- return unless !ActiveRecord::Base.configurations.blank?
838
+ return if ActiveRecord::Base.configurations.blank?
835
839
 
836
840
  if pre_loaded_fixtures && !use_transactional_fixtures
837
841
  raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
@@ -851,13 +855,11 @@ module ActiveRecord
851
855
  end
852
856
  @fixture_connections = enlist_fixture_connections
853
857
  @fixture_connections.each do |connection|
854
- connection.increment_open_transactions
855
- connection.transaction_joinable = false
856
- connection.begin_db_transaction
858
+ connection.begin_transaction joinable: false
857
859
  end
858
860
  # Load fixtures for every test.
859
861
  else
860
- ActiveRecord::Fixtures.reset_cache
862
+ ActiveRecord::FixtureSet.reset_cache
861
863
  @@already_loaded_fixtures[self.class] = nil
862
864
  @loaded_fixtures = load_fixtures
863
865
  end
@@ -867,32 +869,28 @@ module ActiveRecord
867
869
  end
868
870
 
869
871
  def teardown_fixtures
870
- return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
871
-
872
- unless run_in_transaction?
873
- ActiveRecord::Fixtures.reset_cache
874
- end
872
+ return if ActiveRecord::Base.configurations.blank?
875
873
 
876
874
  # Rollback changes if a transaction is active.
877
875
  if run_in_transaction?
878
876
  @fixture_connections.each do |connection|
879
- if connection.open_transactions != 0
880
- connection.rollback_db_transaction
881
- connection.decrement_open_transactions
882
- end
877
+ connection.rollback_transaction if connection.transaction_open?
883
878
  end
884
879
  @fixture_connections.clear
880
+ else
881
+ ActiveRecord::FixtureSet.reset_cache
885
882
  end
883
+
886
884
  ActiveRecord::Base.clear_active_connections!
887
885
  end
888
886
 
889
887
  def enlist_fixture_connections
890
- ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection)
888
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
891
889
  end
892
890
 
893
891
  private
894
892
  def load_fixtures
895
- fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
893
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
896
894
  Hash[fixtures.map { |f| [f.name, f] }]
897
895
  end
898
896
 
@@ -901,16 +899,16 @@ module ActiveRecord
901
899
 
902
900
  def instantiate_fixtures
903
901
  if pre_loaded_fixtures
904
- raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
902
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
905
903
  unless @@required_fixture_classes
906
- self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
904
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
907
905
  @@required_fixture_classes = true
908
906
  end
909
- ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
907
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
910
908
  else
911
909
  raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
912
910
  @loaded_fixtures.each_value do |fixture_set|
913
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
911
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
914
912
  end
915
913
  end
916
914
  end