activerecord 4.0.13 → 4.1.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +745 -2700
  3. data/README.rdoc +2 -2
  4. data/examples/performance.rb +30 -18
  5. data/examples/simple.rb +4 -4
  6. data/lib/active_record.rb +2 -6
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +0 -4
  9. data/lib/active_record/associations.rb +87 -43
  10. data/lib/active_record/associations/alias_tracker.rb +1 -3
  11. data/lib/active_record/associations/association.rb +8 -16
  12. data/lib/active_record/associations/association_scope.rb +5 -16
  13. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  15. data/lib/active_record/associations/builder/association.rb +78 -54
  16. data/lib/active_record/associations/builder/belongs_to.rb +91 -58
  17. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
  19. data/lib/active_record/associations/builder/has_many.rb +2 -2
  20. data/lib/active_record/associations/builder/has_one.rb +5 -7
  21. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  22. data/lib/active_record/associations/collection_association.rb +68 -105
  23. data/lib/active_record/associations/collection_proxy.rb +12 -15
  24. data/lib/active_record/associations/has_many_association.rb +11 -9
  25. data/lib/active_record/associations/has_many_through_association.rb +16 -12
  26. data/lib/active_record/associations/has_one_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +204 -165
  28. data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
  29. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  31. data/lib/active_record/associations/join_helper.rb +2 -11
  32. data/lib/active_record/associations/preloader.rb +89 -34
  33. data/lib/active_record/associations/preloader/association.rb +43 -25
  34. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  38. data/lib/active_record/associations/singular_association.rb +6 -5
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +5 -2
  41. data/lib/active_record/attribute_methods.rb +45 -40
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +8 -22
  44. data/lib/active_record/attribute_methods/primary_key.rb +1 -7
  45. data/lib/active_record/attribute_methods/read.rb +55 -28
  46. data/lib/active_record/attribute_methods/serialization.rb +12 -33
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
  48. data/lib/active_record/attribute_methods/write.rb +37 -12
  49. data/lib/active_record/autosave_association.rb +207 -207
  50. data/lib/active_record/base.rb +5 -1
  51. data/lib/active_record/callbacks.rb +2 -2
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
  76. data/lib/active_record/connection_handling.rb +2 -2
  77. data/lib/active_record/core.rb +22 -43
  78. data/lib/active_record/counter_cache.rb +7 -7
  79. data/lib/active_record/enum.rb +100 -0
  80. data/lib/active_record/errors.rb +10 -5
  81. data/lib/active_record/fixture_set/file.rb +2 -1
  82. data/lib/active_record/fixtures.rb +171 -74
  83. data/lib/active_record/inheritance.rb +16 -22
  84. data/lib/active_record/integration.rb +52 -1
  85. data/lib/active_record/locking/optimistic.rb +7 -2
  86. data/lib/active_record/locking/pessimistic.rb +1 -1
  87. data/lib/active_record/log_subscriber.rb +5 -12
  88. data/lib/active_record/migration.rb +62 -46
  89. data/lib/active_record/migration/command_recorder.rb +7 -13
  90. data/lib/active_record/model_schema.rb +7 -14
  91. data/lib/active_record/nested_attributes.rb +10 -8
  92. data/lib/active_record/no_touching.rb +52 -0
  93. data/lib/active_record/null_relation.rb +3 -3
  94. data/lib/active_record/persistence.rb +16 -34
  95. data/lib/active_record/querying.rb +14 -12
  96. data/lib/active_record/railtie.rb +0 -50
  97. data/lib/active_record/railties/databases.rake +12 -15
  98. data/lib/active_record/readonly_attributes.rb +0 -6
  99. data/lib/active_record/reflection.rb +189 -75
  100. data/lib/active_record/relation.rb +69 -94
  101. data/lib/active_record/relation/batches.rb +57 -23
  102. data/lib/active_record/relation/calculations.rb +36 -43
  103. data/lib/active_record/relation/delegation.rb +54 -39
  104. data/lib/active_record/relation/finder_methods.rb +107 -62
  105. data/lib/active_record/relation/merger.rb +7 -20
  106. data/lib/active_record/relation/predicate_builder.rb +57 -38
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  109. data/lib/active_record/relation/query_methods.rb +110 -98
  110. data/lib/active_record/relation/spawn_methods.rb +1 -2
  111. data/lib/active_record/result.rb +45 -6
  112. data/lib/active_record/runtime_registry.rb +5 -0
  113. data/lib/active_record/sanitization.rb +6 -8
  114. data/lib/active_record/schema_dumper.rb +16 -5
  115. data/lib/active_record/schema_migration.rb +24 -25
  116. data/lib/active_record/scoping/default.rb +5 -18
  117. data/lib/active_record/scoping/named.rb +8 -29
  118. data/lib/active_record/store.rb +56 -28
  119. data/lib/active_record/tasks/database_tasks.rb +8 -4
  120. data/lib/active_record/timestamp.rb +4 -4
  121. data/lib/active_record/transactions.rb +8 -10
  122. data/lib/active_record/validations/presence.rb +1 -1
  123. data/lib/active_record/validations/uniqueness.rb +1 -6
  124. data/lib/active_record/version.rb +1 -1
  125. data/lib/rails/generators/active_record.rb +2 -8
  126. data/lib/rails/generators/active_record/migration.rb +18 -0
  127. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  128. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  129. metadata +32 -45
  130. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  131. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  132. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  133. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  134. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  135. data/lib/active_record/test_case.rb +0 -102
@@ -0,0 +1,100 @@
1
+ module ActiveRecord
2
+ # Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example:
3
+ #
4
+ # class Conversation < ActiveRecord::Base
5
+ # enum status: [ :active, :archived ]
6
+ # end
7
+ #
8
+ # # conversation.update! status: 0
9
+ # conversation.active!
10
+ # conversation.active? # => true
11
+ # conversation.status # => "active"
12
+ #
13
+ # # conversation.update! status: 1
14
+ # conversation.archived!
15
+ # conversation.archived? # => true
16
+ # conversation.status # => "archived"
17
+ #
18
+ # # conversation.update! status: 1
19
+ # conversation.status = "archived"
20
+ #
21
+ # You can set the default value from the database declaration, like:
22
+ #
23
+ # create_table :conversations do |t|
24
+ # t.column :status, :integer, default: 0
25
+ # end
26
+ #
27
+ # Good practice is to let the first declared status be the default.
28
+ #
29
+ # Finally, it's also possible to explicitly map the relation between attribute and
30
+ # database integer with a +Hash+:
31
+ #
32
+ # class Conversation < ActiveRecord::Base
33
+ # enum status: { active: 0, archived: 1 }
34
+ # end
35
+ #
36
+ # Note that when an +Array+ is used, the implicit mapping from the values to database
37
+ # integers is derived from the order the values appear in the array. In the example,
38
+ # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
39
+ # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
40
+ # database.
41
+ #
42
+ # Therefore, once a value is added to the enum array, its position in the array must
43
+ # be maintained, and new values should only be added to the end of the array. To
44
+ # remove unused values, the explicit +Hash+ syntax should be used.
45
+ #
46
+ # In rare circumstances you might need to access the mapping directly.
47
+ # The mappings are exposed through a constant with the attributes name:
48
+ #
49
+ # Conversation::STATUS # => { "active" => 0, "archived" => 1 }
50
+ #
51
+ # Use that constant when you need to know the ordinal value of an enum:
52
+ #
53
+ # Conversation.where("status <> ?", Conversation::STATUS[:archived])
54
+ module Enum
55
+ def enum(definitions)
56
+ klass = self
57
+ definitions.each do |name, values|
58
+ # DIRECTION = { }
59
+ enum_values = _enum_methods_module.const_set name.to_s.upcase, ActiveSupport::HashWithIndifferentAccess.new
60
+ name = name.to_sym
61
+
62
+ _enum_methods_module.module_eval do
63
+ # def direction=(value) self[:direction] = DIRECTION[value] end
64
+ define_method("#{name}=") { |value|
65
+ unless enum_values.has_key?(value)
66
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
67
+ end
68
+ self[name] = enum_values[value]
69
+ }
70
+
71
+ # def direction() DIRECTION.key self[:direction] end
72
+ define_method(name) { enum_values.key self[name] }
73
+
74
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
75
+ pairs.each do |value, i|
76
+ enum_values[value] = i
77
+
78
+ # scope :incoming, -> { where direction: 0 }
79
+ klass.scope value, -> { klass.where name => i }
80
+
81
+ # def incoming?() direction == 0 end
82
+ define_method("#{value}?") { self[name] == i }
83
+
84
+ # def incoming! update! direction: :incoming end
85
+ define_method("#{value}!") { update! name => value }
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ private
92
+ def _enum_methods_module
93
+ @_enum_methods_module ||= begin
94
+ mod = Module.new
95
+ include mod
96
+ mod
97
+ end
98
+ end
99
+ end
100
+ end
@@ -69,10 +69,6 @@ module ActiveRecord
69
69
  end
70
70
  end
71
71
 
72
- # Raised when SQL statement is invalid and the application gets a blank result.
73
- class ThrowResult < ActiveRecordError
74
- end
75
-
76
72
  # Defunct wrapper class kept for compatibility.
77
73
  # +StatementInvalid+ wraps the original exception now.
78
74
  class WrappedDatabaseException < StatementInvalid
@@ -159,6 +155,15 @@ module ActiveRecord
159
155
 
160
156
  # Raised when unknown attributes are supplied via mass assignment.
161
157
  class UnknownAttributeError < NoMethodError
158
+
159
+ attr_reader :record, :attribute
160
+
161
+ def initialize(record, attribute)
162
+ @record = record
163
+ @attribute = attribute.to_s
164
+ super("unknown attribute: #{attribute}")
165
+ end
166
+
162
167
  end
163
168
 
164
169
  # Raised when an error occurred while doing a mass assignment to an attribute through the
@@ -183,7 +188,7 @@ module ActiveRecord
183
188
  end
184
189
  end
185
190
 
186
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
191
+ # Raised when a primary key is needed, but not specified in the schema or model.
187
192
  class UnknownPrimaryKey < ActiveRecordError
188
193
  attr_reader :model
189
194
 
@@ -38,7 +38,8 @@ module ActiveRecord
38
38
  end
39
39
 
40
40
  def render(content)
41
- ERB.new(content).result
41
+ context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
42
+ ERB.new(content).result(context.get_binding)
42
43
  end
43
44
 
44
45
  # Validate our unmarshalled data.
@@ -119,6 +119,23 @@ module ActiveRecord
119
119
  # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
120
120
  # in fixtures are to be considered a code smell.
121
121
  #
122
+ # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
123
+ # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
124
+ # that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
125
+ #
126
+ # - define a helper method in `test_helper.rb`
127
+ # class FixtureFileHelpers
128
+ # def file_sha(path)
129
+ # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
130
+ # end
131
+ # end
132
+ # ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
133
+ #
134
+ # - use the helper method in a fixture
135
+ # photo:
136
+ # name: kitten.png
137
+ # sha: <%= file_sha 'files/kitten.png' %>
138
+ #
122
139
  # = Transactional Fixtures
123
140
  #
124
141
  # Test cases can use begin+rollback to isolate their changes to the database instead of having to
@@ -379,22 +396,16 @@ module ActiveRecord
379
396
 
380
397
  @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
381
398
 
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:
389
- ActiveRecord::Base.pluralize_table_names ?
399
+ def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
400
+ config.pluralize_table_names ?
390
401
  fixture_set_name.singularize.camelize :
391
402
  fixture_set_name.camelize
392
403
  end
393
404
 
394
- def self.default_fixture_table_name(fixture_set_name) # :nodoc:
395
- "#{ ActiveRecord::Base.table_name_prefix }"\
405
+ def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
406
+ "#{ config.table_name_prefix }"\
396
407
  "#{ fixture_set_name.tr('/', '_') }"\
397
- "#{ ActiveRecord::Base.table_name_suffix }".to_sym
408
+ "#{ config.table_name_suffix }".to_sym
398
409
  end
399
410
 
400
411
  def self.reset_cache
@@ -442,9 +453,47 @@ module ActiveRecord
442
453
  cattr_accessor :all_loaded_fixtures
443
454
  self.all_loaded_fixtures = {}
444
455
 
445
- def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
456
+ class ClassCache
457
+ def initialize(class_names, config)
458
+ @class_names = class_names.stringify_keys
459
+ @config = config
460
+
461
+ # Remove string values that aren't constants or subclasses of AR
462
+ @class_names.delete_if { |k,klass|
463
+ unless klass.is_a? Class
464
+ klass = klass.safe_constantize
465
+ ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
466
+ end
467
+ !insert_class(@class_names, k, klass)
468
+ }
469
+ end
470
+
471
+ def [](fs_name)
472
+ @class_names.fetch(fs_name) {
473
+ klass = default_fixture_model(fs_name, @config).safe_constantize
474
+ insert_class(@class_names, fs_name, klass)
475
+ }
476
+ end
477
+
478
+ private
479
+
480
+ def insert_class(class_names, name, klass)
481
+ # We only want to deal with AR objects.
482
+ if klass && klass < ActiveRecord::Base
483
+ class_names[name] = klass
484
+ else
485
+ class_names[name] = nil
486
+ end
487
+ end
488
+
489
+ def default_fixture_model(fs_name, config)
490
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
491
+ end
492
+ end
493
+
494
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
446
495
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
447
- class_names = class_names.stringify_keys
496
+ class_names = ClassCache.new class_names, config
448
497
 
449
498
  # FIXME: Apparently JK uses this.
450
499
  connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -458,10 +507,12 @@ module ActiveRecord
458
507
  fixtures_map = {}
459
508
 
460
509
  fixture_sets = files_to_read.map do |fs_name|
510
+ klass = class_names[fs_name]
511
+ conn = klass ? klass.connection : connection
461
512
  fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
462
- connection,
513
+ conn,
463
514
  fs_name,
464
- class_names[fs_name] || default_fixture_model_name(fs_name),
515
+ klass,
465
516
  ::File.join(fixtures_directory, fs_name))
466
517
  end
467
518
 
@@ -470,16 +521,8 @@ module ActiveRecord
470
521
  connection.transaction(:requires_new => true) do
471
522
  fixture_sets.each do |fs|
472
523
  conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
473
- table_rows = fs.table_rows
474
-
475
- table_rows.keys.each do |table|
476
- conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
477
- end
478
-
479
- table_rows.each do |fixture_set_name, rows|
480
- rows.each do |row|
481
- conn.insert_fixture(row, fixture_set_name)
482
- end
524
+ fs.fixture_sql(conn).each do |stmt|
525
+ conn.execute stmt
483
526
  end
484
527
  end
485
528
 
@@ -503,27 +546,36 @@ module ActiveRecord
503
546
  Zlib.crc32(label.to_s) % MAX_ID
504
547
  end
505
548
 
506
- attr_reader :table_name, :name, :fixtures, :model_class
549
+ # Superclass for the evaluation contexts used by ERB fixtures.
550
+ def self.context_class
551
+ @context_class ||= Class.new
552
+ end
553
+
554
+ attr_reader :table_name, :name, :fixtures, :model_class, :config
507
555
 
508
- def initialize(connection, name, class_name, path)
509
- @fixtures = {} # Ordered hash
556
+ def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
510
557
  @name = name
511
558
  @path = path
559
+ @config = config
560
+ @model_class = nil
561
+
562
+ if class_name.is_a?(String)
563
+ ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
564
+ end
512
565
 
513
566
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
514
567
  @model_class = class_name
515
568
  else
516
- @model_class = class_name.constantize rescue nil
569
+ @model_class = class_name.safe_constantize if class_name
517
570
  end
518
571
 
519
- @connection = ( model_class.respond_to?(:connection) ?
520
- model_class.connection : connection )
572
+ @connection = connection
521
573
 
522
574
  @table_name = ( model_class.respond_to?(:table_name) ?
523
575
  model_class.table_name :
524
- self.class.default_fixture_table_name(name) )
576
+ self.class.default_fixture_table_name(name, config) )
525
577
 
526
- read_fixture_files
578
+ @fixtures = read_fixture_files path, @model_class
527
579
  end
528
580
 
529
581
  def [](x)
@@ -542,10 +594,20 @@ module ActiveRecord
542
594
  fixtures.size
543
595
  end
544
596
 
597
+ def fixture_sql(conn)
598
+ table_rows = self.table_rows
599
+
600
+ table_rows.keys.map { |table|
601
+ "DELETE FROM #{conn.quote_table_name(table)}"
602
+ }.concat table_rows.flat_map { |fixture_set_name, rows|
603
+ rows.map { |row| conn.fixture_sql(row, fixture_set_name) }
604
+ }
605
+ end
606
+
545
607
  # Return a hash of rows to be inserted. The key is the table, the value is
546
608
  # a list of rows to insert to that table.
547
609
  def table_rows
548
- now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
610
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
549
611
  now = now.to_s(:db)
550
612
 
551
613
  # allow a standard key to be used for doing defaults in YAML
@@ -557,7 +619,7 @@ module ActiveRecord
557
619
  rows[table_name] = fixtures.map do |label, fixture|
558
620
  row = fixture.to_hash
559
621
 
560
- if model_class && model_class < ActiveRecord::Base
622
+ if model_class
561
623
  # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
562
624
  if model_class.record_timestamps
563
625
  timestamp_column_names.each do |c_name|
@@ -567,7 +629,7 @@ module ActiveRecord
567
629
 
568
630
  # interpolate the fixture label
569
631
  row.each do |key, value|
570
- row[key] = label if value == "$LABEL"
632
+ row[key] = label if "$LABEL" == value
571
633
  end
572
634
 
573
635
  # generate a primary key if necessary
@@ -597,14 +659,9 @@ module ActiveRecord
597
659
 
598
660
  row[fk_name] = ActiveRecord::FixtureSet.identify(value)
599
661
  end
600
- when :has_and_belongs_to_many
601
- if (targets = row.delete(association.name.to_s))
602
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
603
- table_name = association.join_table
604
- rows[table_name].concat targets.map { |target|
605
- { association.foreign_key => row[primary_key_name],
606
- association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
607
- }
662
+ when :has_many
663
+ if association.options[:through]
664
+ add_join_records(rows, row, HasManyThroughProxy.new(association))
608
665
  end
609
666
  end
610
667
  end
@@ -615,11 +672,50 @@ module ActiveRecord
615
672
  rows
616
673
  end
617
674
 
675
+ class ReflectionProxy # :nodoc:
676
+ def initialize(association)
677
+ @association = association
678
+ end
679
+
680
+ def join_table
681
+ @association.join_table
682
+ end
683
+
684
+ def name
685
+ @association.name
686
+ end
687
+ end
688
+
689
+ class HasManyThroughProxy < ReflectionProxy # :nodoc:
690
+ def rhs_key
691
+ @association.foreign_key
692
+ end
693
+
694
+ def lhs_key
695
+ @association.through_reflection.foreign_key
696
+ end
697
+ end
698
+
618
699
  private
619
700
  def primary_key_name
620
701
  @primary_key_name ||= model_class && model_class.primary_key
621
702
  end
622
703
 
704
+ def add_join_records(rows, row, association)
705
+ # This is the case when the join table has no fixtures file
706
+ if (targets = row.delete(association.name.to_s))
707
+ table_name = association.join_table
708
+ lhs_key = association.lhs_key
709
+ rhs_key = association.rhs_key
710
+
711
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
712
+ rows[table_name].concat targets.map { |target|
713
+ { lhs_key => row[primary_key_name],
714
+ rhs_key => ActiveRecord::FixtureSet.identify(target) }
715
+ }
716
+ end
717
+ end
718
+
623
719
  def has_primary_key_column?
624
720
  @has_primary_key_column ||= primary_key_name &&
625
721
  model_class.columns.any? { |c| c.name == primary_key_name }
@@ -638,12 +734,12 @@ module ActiveRecord
638
734
  @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
639
735
  end
640
736
 
641
- def read_fixture_files
642
- yaml_files = Dir["#{@path}/{**,*}/*.yml"].select { |f|
737
+ def read_fixture_files(path, model_class)
738
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
643
739
  ::File.file?(f)
644
- } + [yaml_file_path]
740
+ } + [yaml_file_path(path)]
645
741
 
646
- yaml_files.each do |file|
742
+ yaml_files.each_with_object({}) do |file, fixtures|
647
743
  FixtureSet::File.open(file) do |fh|
648
744
  fh.each do |fixture_name, row|
649
745
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -652,8 +748,8 @@ module ActiveRecord
652
748
  end
653
749
  end
654
750
 
655
- def yaml_file_path
656
- "#{@path}.yml"
751
+ def yaml_file_path(path)
752
+ "#{path}.yml"
657
753
  end
658
754
 
659
755
  end
@@ -696,7 +792,7 @@ module ActiveRecord
696
792
 
697
793
  def find
698
794
  if model_class
699
- model_class.unscoped.find(fixture[model_class.primary_key])
795
+ model_class.find(fixture[model_class.primary_key])
700
796
  else
701
797
  raise FixtureClassNotFound, "No class attached to find."
702
798
  end
@@ -725,14 +821,16 @@ module ActiveRecord
725
821
  class_attribute :use_transactional_fixtures
726
822
  class_attribute :use_instantiated_fixtures # true, false, or :no_instances
727
823
  class_attribute :pre_loaded_fixtures
824
+ class_attribute :config
728
825
 
729
826
  self.fixture_table_names = []
730
827
  self.use_transactional_fixtures = true
731
828
  self.use_instantiated_fixtures = false
732
829
  self.pre_loaded_fixtures = false
830
+ self.config = ActiveRecord::Base
733
831
 
734
832
  self.fixture_class_names = Hash.new do |h, fixture_set_name|
735
- h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
833
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name, self.config)
736
834
  end
737
835
  end
738
836
 
@@ -745,13 +843,6 @@ module ActiveRecord
745
843
  # 'namespaced/fixture' => Another::Model
746
844
  #
747
845
  # The keys must be the fixture names, that coincide with the short paths to the fixture files.
748
- #--
749
- # It is also possible to pass the class name instead of the class:
750
- # set_fixture_class 'some_fixture' => 'SomeModel'
751
- # I think this option is redundant, i propose to deprecate it.
752
- # Isn't it easier to always pass the class itself?
753
- # (2011-12-20 alexeymuranov)
754
- #++
755
846
  def set_fixture_class(class_names = {})
756
847
  self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
757
848
  end
@@ -765,7 +856,7 @@ module ActiveRecord
765
856
  end
766
857
 
767
858
  self.fixture_table_names |= fixture_set_names
768
- require_fixture_classes(fixture_set_names)
859
+ require_fixture_classes(fixture_set_names, self.config)
769
860
  setup_fixture_accessors(fixture_set_names)
770
861
  end
771
862
 
@@ -780,7 +871,7 @@ module ActiveRecord
780
871
  end
781
872
  end
782
873
 
783
- def require_fixture_classes(fixture_set_names = nil)
874
+ def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
784
875
  if fixture_set_names
785
876
  fixture_set_names = fixture_set_names.map { |n| n.to_s }
786
877
  else
@@ -788,7 +879,7 @@ module ActiveRecord
788
879
  end
789
880
 
790
881
  fixture_set_names.each do |file_name|
791
- file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
882
+ file_name = file_name.singularize if config.pluralize_table_names
792
883
  try_to_load_dependency(file_name)
793
884
  end
794
885
  end
@@ -840,9 +931,7 @@ module ActiveRecord
840
931
  !self.class.uses_transaction?(method_name)
841
932
  end
842
933
 
843
- def setup_fixtures
844
- return if ActiveRecord::Base.configurations.blank?
845
-
934
+ def setup_fixtures(config = ActiveRecord::Base)
846
935
  if pre_loaded_fixtures && !use_transactional_fixtures
847
936
  raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
848
937
  end
@@ -856,7 +945,7 @@ module ActiveRecord
856
945
  if @@already_loaded_fixtures[self.class]
857
946
  @loaded_fixtures = @@already_loaded_fixtures[self.class]
858
947
  else
859
- @loaded_fixtures = load_fixtures
948
+ @loaded_fixtures = load_fixtures(config)
860
949
  @@already_loaded_fixtures[self.class] = @loaded_fixtures
861
950
  end
862
951
  @fixture_connections = enlist_fixture_connections
@@ -867,16 +956,14 @@ module ActiveRecord
867
956
  else
868
957
  ActiveRecord::FixtureSet.reset_cache
869
958
  @@already_loaded_fixtures[self.class] = nil
870
- @loaded_fixtures = load_fixtures
959
+ @loaded_fixtures = load_fixtures(config)
871
960
  end
872
961
 
873
962
  # Instantiate fixtures for every test if requested.
874
- instantiate_fixtures if use_instantiated_fixtures
963
+ instantiate_fixtures(config) if use_instantiated_fixtures
875
964
  end
876
965
 
877
966
  def teardown_fixtures
878
- return if ActiveRecord::Base.configurations.blank?
879
-
880
967
  # Rollback changes if a transaction is active.
881
968
  if run_in_transaction?
882
969
  @fixture_connections.each do |connection|
@@ -895,19 +982,19 @@ module ActiveRecord
895
982
  end
896
983
 
897
984
  private
898
- def load_fixtures
899
- fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
985
+ def load_fixtures(config)
986
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
900
987
  Hash[fixtures.map { |f| [f.name, f] }]
901
988
  end
902
989
 
903
990
  # for pre_loaded_fixtures, only require the classes once. huge speed improvement
904
991
  @@required_fixture_classes = false
905
992
 
906
- def instantiate_fixtures
993
+ def instantiate_fixtures(config)
907
994
  if pre_loaded_fixtures
908
995
  raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
909
996
  unless @@required_fixture_classes
910
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
997
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
911
998
  @@required_fixture_classes = true
912
999
  end
913
1000
  ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
@@ -924,3 +1011,13 @@ module ActiveRecord
924
1011
  end
925
1012
  end
926
1013
  end
1014
+
1015
+ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
1016
+ def self.create_subclass
1017
+ Class.new ActiveRecord::FixtureSet.context_class do
1018
+ def get_binding
1019
+ binding()
1020
+ end
1021
+ end
1022
+ end
1023
+ end