activerecord 3.1.0.beta1 → 3.1.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 (35) hide show
  1. data/CHANGELOG +123 -30
  2. data/README.rdoc +2 -2
  3. data/lib/active_record/associations.rb +10 -9
  4. data/lib/active_record/associations/alias_tracker.rb +2 -2
  5. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  6. data/lib/active_record/associations/builder/singular_association.rb +19 -6
  7. data/lib/active_record/associations/collection_association.rb +61 -52
  8. data/lib/active_record/associations/collection_proxy.rb +30 -3
  9. data/lib/active_record/associations/has_one_association.rb +1 -1
  10. data/lib/active_record/associations/join_dependency/join_association.rb +3 -3
  11. data/lib/active_record/associations/join_helper.rb +1 -1
  12. data/lib/active_record/associations/singular_association.rb +13 -12
  13. data/lib/active_record/associations/through_association.rb +4 -1
  14. data/lib/active_record/base.rb +95 -46
  15. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -9
  16. data/lib/active_record/connection_adapters/column.rb +1 -1
  17. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -3
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +28 -26
  19. data/lib/active_record/fixtures.rb +294 -339
  20. data/lib/active_record/identity_map.rb +34 -8
  21. data/lib/active_record/locking/optimistic.rb +16 -16
  22. data/lib/active_record/locking/pessimistic.rb +2 -2
  23. data/lib/active_record/observer.rb +3 -3
  24. data/lib/active_record/persistence.rb +1 -1
  25. data/lib/active_record/railties/controller_runtime.rb +10 -1
  26. data/lib/active_record/railties/databases.rake +9 -9
  27. data/lib/active_record/relation/calculations.rb +5 -6
  28. data/lib/active_record/relation/finder_methods.rb +9 -4
  29. data/lib/active_record/serialization.rb +1 -1
  30. data/lib/active_record/session_store.rb +4 -2
  31. data/lib/active_record/test_case.rb +7 -0
  32. data/lib/active_record/validations.rb +3 -3
  33. data/lib/active_record/version.rb +1 -1
  34. data/lib/rails/generators/active_record/model/templates/migration.rb +1 -1
  35. metadata +6 -6
@@ -39,6 +39,16 @@ module ActiveRecord
39
39
  # :stopdoc:
40
40
  class << self
41
41
  attr_accessor :money_precision
42
+ def string_to_time(string)
43
+ return string unless String === string
44
+
45
+ case string
46
+ when 'infinity' then 1.0 / 0.0
47
+ when '-infinity' then -1.0 / 0.0
48
+ else
49
+ super
50
+ end
51
+ end
42
52
  end
43
53
  # :startdoc:
44
54
 
@@ -349,6 +359,9 @@ module ActiveRecord
349
359
  return super unless column
350
360
 
351
361
  case value
362
+ when Float
363
+ return super unless value.infinite? && column.type == :datetime
364
+ "'#{value.to_s.downcase}'"
352
365
  when Numeric
353
366
  return super unless column.sql_type == 'money'
354
367
  # Not truly string input, so doesn't require (or allow) escape string syntax.
@@ -688,11 +701,11 @@ module ActiveRecord
688
701
  schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
689
702
  result = query(<<-SQL, name)
690
703
  SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
691
- FROM pg_class t, pg_class i, pg_index d
704
+ FROM pg_class t
705
+ INNER JOIN pg_index d ON t.oid = d.indrelid
706
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
692
707
  WHERE i.relkind = 'i'
693
- AND d.indexrelid = i.oid
694
708
  AND d.indisprimary = 'f'
695
- AND t.oid = d.indrelid
696
709
  AND t.relname = '#{table_name}'
697
710
  AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
698
711
  ORDER BY i.relname
@@ -807,19 +820,13 @@ module ActiveRecord
807
820
  # given table's primary key.
808
821
  result = exec_query(<<-end_sql, 'SCHEMA').rows.first
809
822
  SELECT attr.attname, seq.relname
810
- FROM pg_class seq,
811
- pg_attribute attr,
812
- pg_depend dep,
813
- pg_namespace name,
814
- pg_constraint cons
815
- WHERE seq.oid = dep.objid
816
- AND seq.relkind = 'S'
817
- AND attr.attrelid = dep.refobjid
818
- AND attr.attnum = dep.refobjsubid
819
- AND attr.attrelid = cons.conrelid
820
- AND attr.attnum = cons.conkey[1]
821
- AND cons.contype = 'p'
822
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
823
+ FROM pg_class seq
824
+ INNER JOIN pg_depend dep ON seq.oid = dep.objid
825
+ INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
826
+ INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
827
+ WHERE seq.relkind = 'S'
828
+ AND cons.contype = 'p'
829
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
823
830
  end_sql
824
831
 
825
832
  # [primary_key, sequence]
@@ -832,16 +839,11 @@ module ActiveRecord
832
839
  def primary_key(table)
833
840
  row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first
834
841
  SELECT DISTINCT(attr.attname)
835
- FROM pg_attribute attr,
836
- pg_depend dep,
837
- pg_namespace name,
838
- pg_constraint cons
839
- WHERE attr.attrelid = dep.refobjid
840
- AND attr.attnum = dep.refobjsubid
841
- AND attr.attrelid = cons.conrelid
842
- AND attr.attnum = cons.conkey[1]
843
- AND cons.contype = 'p'
844
- AND dep.refobjid = $1::regclass
842
+ FROM pg_attribute attr
843
+ INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
844
+ INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
845
+ WHERE cons.contype = 'p'
846
+ AND dep.refobjid = $1::regclass
845
847
  end_sql
846
848
 
847
849
  row && row.first
@@ -13,6 +13,7 @@ require 'active_support/core_ext/array/wrap'
13
13
  require 'active_support/core_ext/object/blank'
14
14
  require 'active_support/core_ext/logger'
15
15
  require 'active_support/ordered_hash'
16
+ require 'active_support/core_ext/module/deprecation'
16
17
 
17
18
  if defined? ActiveRecord
18
19
  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
@@ -28,11 +29,9 @@ class FixturesFileNotFound < StandardError; end
28
29
  #
29
30
  # = Fixture formats
30
31
  #
31
- # Fixtures come in 3 flavors:
32
+ # Fixtures come in 1 flavor:
32
33
  #
33
34
  # 1. YAML fixtures
34
- # 2. CSV fixtures
35
- # 3. Single-file fixtures
36
35
  #
37
36
  # == YAML fixtures
38
37
  #
@@ -74,56 +73,6 @@ class FixturesFileNotFound < StandardError; end
74
73
  # parent_id: 1
75
74
  # title: Child
76
75
  #
77
- # == CSV fixtures
78
- #
79
- # Fixtures can also be kept in the Comma Separated Value (CSV) format. Akin to YAML fixtures, CSV fixtures are stored
80
- # in a single file, but instead end with the <tt>.csv</tt> file extension
81
- # (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
82
- #
83
- # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
84
- # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the
85
- # file is then comprised
86
- # of the actual data (1 per line). Here's an example:
87
- #
88
- # id, name, url
89
- # 1, Ruby On Rails, http://www.rubyonrails.org
90
- # 2, Google, http://www.google.com
91
- #
92
- # Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
93
- # need to use a double quote character, you must escape it with another double quote.
94
- #
95
- # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
96
- # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
97
- # number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
98
- # "web_site_2".
99
- #
100
- # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
101
- # have existing data somewhere already.
102
- #
103
- # == Single-file fixtures
104
- #
105
- # This type of fixture was the original format for Active Record that has since been deprecated in
106
- # favor of the YAML and CSV formats.
107
- # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model)
108
- # to the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
109
- # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
110
- # like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
111
- #
112
- # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
113
- # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension.
114
- # Here's what the above example might look like:
115
- #
116
- # web_sites/google
117
- # web_sites/yahoo.txt
118
- # web_sites/ruby-on-rails
119
- #
120
- # The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
121
- # of "name => value". Here's an example of the ruby-on-rails fixture above:
122
- #
123
- # id => 1
124
- # name => Ruby on Rails
125
- # url => http://www.rubyonrails.org
126
- #
127
76
  # = Using fixtures in testcases
128
77
  #
129
78
  # Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
@@ -176,7 +125,7 @@ class FixturesFileNotFound < StandardError; end
176
125
  # = Dynamic fixtures with ERB
177
126
  #
178
127
  # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
179
- # mix ERB in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
128
+ # mix ERB in with your YAML fixtures to create a bunch of fixtures for load testing, like:
180
129
  #
181
130
  # <% for i in 1..1000 %>
182
131
  # fix_<%= i %>:
@@ -423,8 +372,8 @@ class FixturesFileNotFound < StandardError; end
423
372
  # to the rescue:
424
373
  #
425
374
  # george_reginald:
426
- # monkey_id: <%= Fixtures.identify(:reginald) %>
427
- # pirate_id: <%= Fixtures.identify(:george) %>
375
+ # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
376
+ # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
428
377
  #
429
378
  # == Support for YAML defaults
430
379
  #
@@ -444,369 +393,375 @@ class FixturesFileNotFound < StandardError; end
444
393
  #
445
394
  # Any fixture labeled "DEFAULTS" is safely ignored.
446
395
 
447
- class Fixtures
448
- MAX_ID = 2 ** 30 - 1
396
+ Fixture = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixture', 'ActiveRecord::Fixture')
397
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixtures', 'ActiveRecord::Fixtures')
449
398
 
450
- @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
399
+ module ActiveRecord
400
+ class Fixtures
401
+ MAX_ID = 2 ** 30 - 1
451
402
 
452
- def self.find_table_name(table_name) # :nodoc:
453
- ActiveRecord::Base.pluralize_table_names ?
454
- table_name.to_s.singularize.camelize :
455
- table_name.to_s.camelize
456
- end
403
+ @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
457
404
 
458
- def self.reset_cache
459
- @@all_cached_fixtures.clear
460
- end
405
+ def self.find_table_name(table_name) # :nodoc:
406
+ ActiveRecord::Base.pluralize_table_names ?
407
+ table_name.to_s.singularize.camelize :
408
+ table_name.to_s.camelize
409
+ end
461
410
 
462
- def self.cache_for_connection(connection)
463
- @@all_cached_fixtures[connection]
464
- end
411
+ def self.reset_cache
412
+ @@all_cached_fixtures.clear
413
+ end
465
414
 
466
- def self.fixture_is_cached?(connection, table_name)
467
- cache_for_connection(connection)[table_name]
468
- end
415
+ def self.cache_for_connection(connection)
416
+ @@all_cached_fixtures[connection]
417
+ end
469
418
 
470
- def self.cached_fixtures(connection, keys_to_fetch = nil)
471
- if keys_to_fetch
472
- cache_for_connection(connection).values_at(*keys_to_fetch)
473
- else
474
- cache_for_connection(connection).values
419
+ def self.fixture_is_cached?(connection, table_name)
420
+ cache_for_connection(connection)[table_name]
475
421
  end
476
- end
477
422
 
478
- def self.cache_fixtures(connection, fixtures_map)
479
- cache_for_connection(connection).update(fixtures_map)
480
- end
423
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
424
+ if keys_to_fetch
425
+ cache_for_connection(connection).values_at(*keys_to_fetch)
426
+ else
427
+ cache_for_connection(connection).values
428
+ end
429
+ end
430
+
431
+ def self.cache_fixtures(connection, fixtures_map)
432
+ cache_for_connection(connection).update(fixtures_map)
433
+ end
481
434
 
482
- def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
483
- if load_instances
484
- fixtures.each do |name, fixture|
485
- begin
486
- object.instance_variable_set "@#{name}", fixture.find
487
- rescue FixtureClassNotFound
488
- nil
435
+ def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
436
+ if load_instances
437
+ fixtures.each do |name, fixture|
438
+ begin
439
+ object.instance_variable_set "@#{name}", fixture.find
440
+ rescue FixtureClassNotFound
441
+ nil
442
+ end
489
443
  end
490
444
  end
491
445
  end
492
- end
493
446
 
494
- def self.instantiate_all_loaded_fixtures(object, load_instances = true)
495
- all_loaded_fixtures.each do |table_name, fixtures|
496
- Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
447
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
448
+ all_loaded_fixtures.each do |table_name, fixtures|
449
+ ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
450
+ end
497
451
  end
498
- end
499
452
 
500
- cattr_accessor :all_loaded_fixtures
501
- self.all_loaded_fixtures = {}
453
+ cattr_accessor :all_loaded_fixtures
454
+ self.all_loaded_fixtures = {}
502
455
 
503
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
504
- table_names = [table_names].flatten.map { |n| n.to_s }
505
- table_names.each { |n|
506
- class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
507
- }
456
+ def self.create_fixtures(fixtures_directory, table_names, class_names = {})
457
+ table_names = [table_names].flatten.map { |n| n.to_s }
458
+ table_names.each { |n|
459
+ class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
460
+ }
508
461
 
509
- # FIXME: Apparently JK uses this.
510
- connection = block_given? ? yield : ActiveRecord::Base.connection
462
+ # FIXME: Apparently JK uses this.
463
+ connection = block_given? ? yield : ActiveRecord::Base.connection
511
464
 
512
- files_to_read = table_names.reject { |table_name|
513
- fixture_is_cached?(connection, table_name)
514
- }
465
+ files_to_read = table_names.reject { |table_name|
466
+ fixture_is_cached?(connection, table_name)
467
+ }
515
468
 
516
- unless files_to_read.empty?
517
- connection.disable_referential_integrity do
518
- fixtures_map = {}
469
+ unless files_to_read.empty?
470
+ connection.disable_referential_integrity do
471
+ fixtures_map = {}
519
472
 
520
- fixture_files = files_to_read.map do |path|
521
- table_name = path.tr '/', '_'
473
+ fixture_files = files_to_read.map do |path|
474
+ table_name = path.tr '/', '_'
522
475
 
523
- fixtures_map[path] = Fixtures.new(
524
- connection,
525
- table_name,
526
- class_names[table_name.to_sym] || table_name.classify,
527
- File.join(fixtures_directory, path))
528
- end
476
+ fixtures_map[path] = ActiveRecord::Fixtures.new(
477
+ connection,
478
+ table_name,
479
+ class_names[table_name.to_sym] || table_name.classify,
480
+ File.join(fixtures_directory, path))
481
+ end
529
482
 
530
- all_loaded_fixtures.update(fixtures_map)
483
+ all_loaded_fixtures.update(fixtures_map)
531
484
 
532
- connection.transaction(:requires_new => true) do
533
- fixture_files.each do |ff|
534
- conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
535
- table_rows = ff.table_rows
485
+ connection.transaction(:requires_new => true) do
486
+ fixture_files.each do |ff|
487
+ conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
488
+ table_rows = ff.table_rows
536
489
 
537
- table_rows.keys.each do |table|
538
- conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
539
- end
490
+ table_rows.keys.each do |table|
491
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
492
+ end
540
493
 
541
- table_rows.each do |table_name,rows|
542
- rows.each do |row|
543
- conn.insert_fixture(row, table_name)
494
+ table_rows.each do |table_name,rows|
495
+ rows.each do |row|
496
+ conn.insert_fixture(row, table_name)
497
+ end
544
498
  end
545
499
  end
546
- end
547
500
 
548
- # Cap primary key sequences to max(pk).
549
- if connection.respond_to?(:reset_pk_sequence!)
550
- table_names.each do |table_name|
551
- connection.reset_pk_sequence!(table_name.tr('/', '_'))
501
+ # Cap primary key sequences to max(pk).
502
+ if connection.respond_to?(:reset_pk_sequence!)
503
+ table_names.each do |table_name|
504
+ connection.reset_pk_sequence!(table_name.tr('/', '_'))
505
+ end
552
506
  end
553
507
  end
554
- end
555
508
 
556
- cache_fixtures(connection, fixtures_map)
509
+ cache_fixtures(connection, fixtures_map)
510
+ end
557
511
  end
512
+ cached_fixtures(connection, table_names)
558
513
  end
559
- cached_fixtures(connection, table_names)
560
- end
561
-
562
- # Returns a consistent, platform-independent identifier for +label+.
563
- # Identifiers are positive integers less than 2^32.
564
- def self.identify(label)
565
- Zlib.crc32(label.to_s) % MAX_ID
566
- end
567
514
 
568
- attr_reader :table_name, :name, :fixtures, :model_class
569
-
570
- def initialize(connection, table_name, class_name, fixture_path)
571
- @connection = connection
572
- @table_name = table_name
573
- @fixture_path = fixture_path
574
- @name = table_name # preserve fixture base name
575
- @class_name = class_name
576
-
577
- @fixtures = ActiveSupport::OrderedHash.new
578
- @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
579
-
580
- # Should be an AR::Base type class
581
- if class_name.is_a?(Class)
582
- @table_name = class_name.table_name
583
- @connection = class_name.connection
584
- @model_class = class_name
585
- else
586
- @model_class = class_name.constantize rescue nil
515
+ # Returns a consistent, platform-independent identifier for +label+.
516
+ # Identifiers are positive integers less than 2^32.
517
+ def self.identify(label)
518
+ Zlib.crc32(label.to_s) % MAX_ID
587
519
  end
588
520
 
589
- read_fixture_files
590
- end
521
+ attr_reader :table_name, :name, :fixtures, :model_class
591
522
 
592
- def [](x)
593
- fixtures[x]
594
- end
523
+ def initialize(connection, table_name, class_name, fixture_path)
524
+ @connection = connection
525
+ @table_name = table_name
526
+ @fixture_path = fixture_path
527
+ @name = table_name # preserve fixture base name
528
+ @class_name = class_name
595
529
 
596
- def []=(k,v)
597
- fixtures[k] = v
598
- end
530
+ @fixtures = ActiveSupport::OrderedHash.new
531
+ @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
599
532
 
600
- def each(&block)
601
- fixtures.each(&block)
602
- end
533
+ # Should be an AR::Base type class
534
+ if class_name.is_a?(Class)
535
+ @table_name = class_name.table_name
536
+ @connection = class_name.connection
537
+ @model_class = class_name
538
+ else
539
+ @model_class = class_name.constantize rescue nil
540
+ end
603
541
 
604
- def size
605
- fixtures.size
606
- end
542
+ read_fixture_files
543
+ end
607
544
 
608
- # Return a hash of rows to be inserted. The key is the table, the value is
609
- # a list of rows to insert to that table.
610
- def table_rows
611
- now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
612
- now = now.to_s(:db)
545
+ def [](x)
546
+ fixtures[x]
547
+ end
613
548
 
614
- # allow a standard key to be used for doing defaults in YAML
615
- fixtures.delete('DEFAULTS')
549
+ def []=(k,v)
550
+ fixtures[k] = v
551
+ end
616
552
 
617
- # track any join tables we need to insert later
618
- rows = Hash.new { |h,table| h[table] = [] }
553
+ def each(&block)
554
+ fixtures.each(&block)
555
+ end
619
556
 
620
- rows[table_name] = fixtures.map do |label, fixture|
621
- row = fixture.to_hash
557
+ def size
558
+ fixtures.size
559
+ end
622
560
 
623
- if model_class && model_class < ActiveRecord::Base
624
- # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
625
- if model_class.record_timestamps
626
- timestamp_column_names.each do |name|
627
- row[name] = now unless row.key?(name)
628
- end
629
- end
561
+ # Return a hash of rows to be inserted. The key is the table, the value is
562
+ # a list of rows to insert to that table.
563
+ def table_rows
564
+ now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
565
+ now = now.to_s(:db)
630
566
 
631
- # interpolate the fixture label
632
- row.each do |key, value|
633
- row[key] = label if value == "$LABEL"
634
- end
567
+ # allow a standard key to be used for doing defaults in YAML
568
+ fixtures.delete('DEFAULTS')
635
569
 
636
- # generate a primary key if necessary
637
- if has_primary_key_column? && !row.include?(primary_key_name)
638
- row[primary_key_name] = Fixtures.identify(label)
639
- end
570
+ # track any join tables we need to insert later
571
+ rows = Hash.new { |h,table| h[table] = [] }
572
+
573
+ rows[table_name] = fixtures.map do |label, fixture|
574
+ row = fixture.to_hash
640
575
 
641
- # If STI is used, find the correct subclass for association reflection
642
- reflection_class =
643
- if row.include?(inheritance_column_name)
644
- row[inheritance_column_name].constantize rescue model_class
645
- else
646
- model_class
576
+ if model_class && model_class < ActiveRecord::Base
577
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
578
+ if model_class.record_timestamps
579
+ timestamp_column_names.each do |name|
580
+ row[name] = now unless row.key?(name)
581
+ end
647
582
  end
648
583
 
649
- reflection_class.reflect_on_all_associations.each do |association|
650
- case association.macro
651
- when :belongs_to
652
- # Do not replace association name with association foreign key if they are named the same
653
- fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
584
+ # interpolate the fixture label
585
+ row.each do |key, value|
586
+ row[key] = label if value == "$LABEL"
587
+ end
654
588
 
655
- if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
656
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
657
- # support polymorphic belongs_to as "label (Type)"
658
- row[association.foreign_type] = $1
659
- end
589
+ # generate a primary key if necessary
590
+ if has_primary_key_column? && !row.include?(primary_key_name)
591
+ row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
592
+ end
660
593
 
661
- row[fk_name] = Fixtures.identify(value)
594
+ # If STI is used, find the correct subclass for association reflection
595
+ reflection_class =
596
+ if row.include?(inheritance_column_name)
597
+ row[inheritance_column_name].constantize rescue model_class
598
+ else
599
+ model_class
662
600
  end
663
- when :has_and_belongs_to_many
664
- if (targets = row.delete(association.name.to_s))
665
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
666
- table_name = association.options[:join_table]
667
- rows[table_name].concat targets.map { |target|
668
- { association.foreign_key => row[primary_key_name],
669
- association.association_foreign_key => Fixtures.identify(target) }
670
- }
601
+
602
+ reflection_class.reflect_on_all_associations.each do |association|
603
+ case association.macro
604
+ when :belongs_to
605
+ # Do not replace association name with association foreign key if they are named the same
606
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
607
+
608
+ if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
609
+ if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
610
+ # support polymorphic belongs_to as "label (Type)"
611
+ row[association.foreign_type] = $1
612
+ end
613
+
614
+ row[fk_name] = ActiveRecord::Fixtures.identify(value)
615
+ end
616
+ when :has_and_belongs_to_many
617
+ if (targets = row.delete(association.name.to_s))
618
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
619
+ table_name = association.options[:join_table]
620
+ rows[table_name].concat targets.map { |target|
621
+ { association.foreign_key => row[primary_key_name],
622
+ association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
623
+ }
624
+ end
671
625
  end
672
626
  end
673
627
  end
674
- end
675
-
676
- row
677
- end
678
- rows
679
- end
680
628
 
681
- private
682
- def primary_key_name
683
- @primary_key_name ||= model_class && model_class.primary_key
629
+ row
630
+ end
631
+ rows
684
632
  end
685
633
 
686
- def has_primary_key_column?
687
- @has_primary_key_column ||= primary_key_name &&
688
- model_class.columns.any? { |c| c.name == primary_key_name }
689
- end
634
+ private
635
+ def primary_key_name
636
+ @primary_key_name ||= model_class && model_class.primary_key
637
+ end
690
638
 
691
- def timestamp_column_names
692
- @timestamp_column_names ||=
693
- %w(created_at created_on updated_at updated_on) & column_names
694
- end
639
+ def has_primary_key_column?
640
+ @has_primary_key_column ||= primary_key_name &&
641
+ model_class.columns.any? { |c| c.name == primary_key_name }
642
+ end
695
643
 
696
- def inheritance_column_name
697
- @inheritance_column_name ||= model_class && model_class.inheritance_column
698
- end
644
+ def timestamp_column_names
645
+ @timestamp_column_names ||=
646
+ %w(created_at created_on updated_at updated_on) & column_names
647
+ end
699
648
 
700
- def column_names
701
- @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
702
- end
649
+ def inheritance_column_name
650
+ @inheritance_column_name ||= model_class && model_class.inheritance_column
651
+ end
703
652
 
704
- def read_fixture_files
705
- if File.file?(yaml_file_path)
706
- read_yaml_fixture_files
707
- elsif File.file?(csv_file_path)
708
- read_csv_fixture_files
709
- else
710
- raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
653
+ def column_names
654
+ @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
711
655
  end
712
- end
713
656
 
714
- def read_yaml_fixture_files
715
- yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
716
- File.file?(f)
717
- } + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
718
-
719
- if yaml = parse_yaml_string(yaml_string)
720
- # If the file is an ordered map, extract its children.
721
- yaml_value =
722
- if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
723
- yaml.value
724
- else
725
- [yaml]
726
- end
657
+ def read_fixture_files
658
+ if File.file?(yaml_file_path)
659
+ read_yaml_fixture_files
660
+ elsif File.file?(csv_file_path)
661
+ read_csv_fixture_files
662
+ else
663
+ raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
664
+ end
665
+ end
727
666
 
728
- yaml_value.each do |fixture|
729
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
730
- fixture.each do |name, data|
731
- unless data
732
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
667
+ def read_yaml_fixture_files
668
+ yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
669
+ File.file?(f)
670
+ } + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
671
+
672
+ if yaml = parse_yaml_string(yaml_string)
673
+ # If the file is an ordered map, extract its children.
674
+ yaml_value =
675
+ if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
676
+ yaml.value
677
+ else
678
+ [yaml]
733
679
  end
734
680
 
735
- fixtures[name] = Fixture.new(data, model_class)
681
+ yaml_value.each do |fixture|
682
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
683
+ fixture.each do |name, data|
684
+ unless data
685
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
686
+ end
687
+
688
+ fixtures[name] = ActiveRecord::Fixture.new(data, model_class)
689
+ end
736
690
  end
737
691
  end
738
692
  end
739
- end
740
693
 
741
- def read_csv_fixture_files
742
- reader = CSV.parse(erb_render(IO.read(csv_file_path)))
743
- header = reader.shift
744
- i = 0
745
- reader.each do |row|
746
- data = {}
747
- row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
748
- fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class)
694
+ def read_csv_fixture_files
695
+ reader = CSV.parse(erb_render(IO.read(csv_file_path)))
696
+ header = reader.shift
697
+ i = 0
698
+ reader.each do |row|
699
+ data = {}
700
+ row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
701
+ fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = ActiveRecord::Fixture.new(data, model_class)
702
+ end
749
703
  end
750
- end
704
+ deprecate :read_csv_fixture_files
751
705
 
752
- def yaml_file_path
753
- "#{@fixture_path}.yml"
754
- end
706
+ def yaml_file_path
707
+ "#{@fixture_path}.yml"
708
+ end
755
709
 
756
- def csv_file_path
757
- @fixture_path + ".csv"
758
- end
710
+ def csv_file_path
711
+ @fixture_path + ".csv"
712
+ end
759
713
 
760
- def yaml_fixtures_key(path)
761
- File.basename(@fixture_path).split(".").first
762
- end
714
+ def yaml_fixtures_key(path)
715
+ File.basename(@fixture_path).split(".").first
716
+ end
763
717
 
764
- def parse_yaml_string(fixture_content)
765
- YAML::load(erb_render(fixture_content))
766
- rescue => error
767
- 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}"
768
- end
718
+ def parse_yaml_string(fixture_content)
719
+ YAML::load(erb_render(fixture_content))
720
+ rescue => error
721
+ 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}"
722
+ end
769
723
 
770
- def erb_render(fixture_content)
771
- ERB.new(fixture_content).result
772
- end
773
- end
724
+ def erb_render(fixture_content)
725
+ ERB.new(fixture_content).result
726
+ end
727
+ end
774
728
 
775
- class Fixture #:nodoc:
776
- include Enumerable
729
+ class Fixture #:nodoc:
730
+ include Enumerable
777
731
 
778
- class FixtureError < StandardError #:nodoc:
779
- end
732
+ class FixtureError < StandardError #:nodoc:
733
+ end
780
734
 
781
- class FormatError < FixtureError #:nodoc:
782
- end
735
+ class FormatError < FixtureError #:nodoc:
736
+ end
783
737
 
784
- attr_reader :model_class, :fixture
738
+ attr_reader :model_class, :fixture
785
739
 
786
- def initialize(fixture, model_class)
787
- @fixture = fixture
788
- @model_class = model_class
789
- end
740
+ def initialize(fixture, model_class)
741
+ @fixture = fixture
742
+ @model_class = model_class
743
+ end
790
744
 
791
- def class_name
792
- model_class.name if model_class
793
- end
745
+ def class_name
746
+ model_class.name if model_class
747
+ end
794
748
 
795
- def each
796
- fixture.each { |item| yield item }
797
- end
749
+ def each
750
+ fixture.each { |item| yield item }
751
+ end
798
752
 
799
- def [](key)
800
- fixture[key]
801
- end
753
+ def [](key)
754
+ fixture[key]
755
+ end
802
756
 
803
- alias :to_hash :fixture
757
+ alias :to_hash :fixture
804
758
 
805
- def find
806
- if model_class
807
- model_class.find(fixture[model_class.primary_key])
808
- else
809
- raise FixtureClassNotFound, "No class attached to find."
759
+ def find
760
+ if model_class
761
+ model_class.find(fixture[model_class.primary_key])
762
+ else
763
+ raise FixtureClassNotFound, "No class attached to find."
764
+ end
810
765
  end
811
766
  end
812
767
  end
@@ -832,7 +787,7 @@ module ActiveRecord
832
787
  self.pre_loaded_fixtures = false
833
788
 
834
789
  self.fixture_class_names = Hash.new do |h, table_name|
835
- h[table_name] = Fixtures.find_table_name(table_name)
790
+ h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
836
791
  end
837
792
  end
838
793
 
@@ -944,7 +899,7 @@ module ActiveRecord
944
899
  ActiveRecord::Base.connection.begin_db_transaction
945
900
  # Load fixtures for every test.
946
901
  else
947
- Fixtures.reset_cache
902
+ ActiveRecord::Fixtures.reset_cache
948
903
  @@already_loaded_fixtures[self.class] = nil
949
904
  @loaded_fixtures = load_fixtures
950
905
  end
@@ -957,7 +912,7 @@ module ActiveRecord
957
912
  return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
958
913
 
959
914
  unless run_in_transaction?
960
- Fixtures.reset_cache
915
+ ActiveRecord::Fixtures.reset_cache
961
916
  end
962
917
 
963
918
  # Rollback changes if a transaction is active.
@@ -970,7 +925,7 @@ module ActiveRecord
970
925
 
971
926
  private
972
927
  def load_fixtures
973
- fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
928
+ fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
974
929
  Hash[fixtures.map { |f| [f.name, f] }]
975
930
  end
976
931
 
@@ -979,16 +934,16 @@ module ActiveRecord
979
934
 
980
935
  def instantiate_fixtures
981
936
  if pre_loaded_fixtures
982
- raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
937
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
983
938
  unless @@required_fixture_classes
984
- self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
939
+ self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
985
940
  @@required_fixture_classes = true
986
941
  end
987
- Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
942
+ ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
988
943
  else
989
944
  raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
990
945
  @loaded_fixtures.each do |fixture_name, fixtures|
991
- Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
946
+ ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
992
947
  end
993
948
  end
994
949
  end