Class: ActiveRecord::Fixtures

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/fixtures.rb

Constant Summary collapse

MAX_ID =
2 ** 30 - 1
@@all_cached_fixtures =
Hash.new { |h,k| h[k] = {} }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection, table_name, class_name, fixture_path) ⇒ Fixtures

Returns a new instance of Fixtures.



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/active_record/fixtures.rb', line 523

def initialize(connection, table_name, class_name, fixture_path)
  @connection   = connection
  @table_name   = table_name
  @fixture_path = fixture_path
  @name         = table_name # preserve fixture base name
  @class_name   = class_name

  @fixtures     = ActiveSupport::OrderedHash.new
  @table_name   = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"

  # Should be an AR::Base type class
  if class_name.is_a?(Class)
    @table_name   = class_name.table_name
    @connection   = class_name.connection
    @model_class  = class_name
  else
    @model_class  = class_name.constantize rescue nil
  end

  read_fixture_files
end

Instance Attribute Details

#fixturesObject (readonly)

Returns the value of attribute fixtures.



521
522
523
# File 'lib/active_record/fixtures.rb', line 521

def fixtures
  @fixtures
end

#model_classObject (readonly)

Returns the value of attribute model_class.



521
522
523
# File 'lib/active_record/fixtures.rb', line 521

def model_class
  @model_class
end

#nameObject (readonly)

Returns the value of attribute name.



521
522
523
# File 'lib/active_record/fixtures.rb', line 521

def name
  @name
end

#table_nameObject (readonly)

Returns the value of attribute table_name.



521
522
523
# File 'lib/active_record/fixtures.rb', line 521

def table_name
  @table_name
end

Class Method Details

.cache_fixtures(connection, fixtures_map) ⇒ Object



431
432
433
# File 'lib/active_record/fixtures.rb', line 431

def self.cache_fixtures(connection, fixtures_map)
  cache_for_connection(connection).update(fixtures_map)
end

.cache_for_connection(connection) ⇒ Object



415
416
417
# File 'lib/active_record/fixtures.rb', line 415

def self.cache_for_connection(connection)
  @@all_cached_fixtures[connection]
end

.cached_fixtures(connection, keys_to_fetch = nil) ⇒ Object



423
424
425
426
427
428
429
# File 'lib/active_record/fixtures.rb', line 423

def self.cached_fixtures(connection, keys_to_fetch = nil)
  if keys_to_fetch
    cache_for_connection(connection).values_at(*keys_to_fetch)
  else
    cache_for_connection(connection).values
  end
end

.create_fixtures(fixtures_directory, table_names, class_names = {}) ⇒ Object



456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/active_record/fixtures.rb', line 456

def self.create_fixtures(fixtures_directory, table_names, class_names = {})
  table_names = [table_names].flatten.map { |n| n.to_s }
  table_names.each { |n|
    class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
  }

  # FIXME: Apparently JK uses this.
  connection = block_given? ? yield : ActiveRecord::Base.connection

  files_to_read = table_names.reject { |table_name|
    fixture_is_cached?(connection, table_name)
  }

  unless files_to_read.empty?
    connection.disable_referential_integrity do
      fixtures_map = {}

      fixture_files = files_to_read.map do |path|
        table_name = path.tr '/', '_'

        fixtures_map[path] = ActiveRecord::Fixtures.new(
          connection,
          table_name,
          class_names[table_name.to_sym] || table_name.classify,
          File.join(fixtures_directory, path))
      end

      all_loaded_fixtures.update(fixtures_map)

      connection.transaction(:requires_new => true) do
        fixture_files.each do |ff|
          conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
          table_rows = ff.table_rows

          table_rows.keys.each do |table|
            conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
          end

          table_rows.each do |table_name,rows|
            rows.each do |row|
              conn.insert_fixture(row, table_name)
            end
          end
        end

        # Cap primary key sequences to max(pk).
        if connection.respond_to?(:reset_pk_sequence!)
          table_names.each do |table_name|
            connection.reset_pk_sequence!(table_name.tr('/', '_'))
          end
        end
      end

      cache_fixtures(connection, fixtures_map)
    end
  end
  cached_fixtures(connection, table_names)
end

.find_table_name(table_name) ⇒ Object

:nodoc:



405
406
407
408
409
# File 'lib/active_record/fixtures.rb', line 405

def self.find_table_name(table_name) # :nodoc:
  ActiveRecord::Base.pluralize_table_names ?
    table_name.to_s.singularize.camelize :
    table_name.to_s.camelize
end

.fixture_is_cached?(connection, table_name) ⇒ Boolean

Returns:

  • (Boolean)


419
420
421
# File 'lib/active_record/fixtures.rb', line 419

def self.fixture_is_cached?(connection, table_name)
  cache_for_connection(connection)[table_name]
end

.identify(label) ⇒ Object

Returns a consistent, platform-independent identifier for label. Identifiers are positive integers less than 2^32.



517
518
519
# File 'lib/active_record/fixtures.rb', line 517

def self.identify(label)
  Zlib.crc32(label.to_s) % MAX_ID
end

.instantiate_all_loaded_fixtures(object, load_instances = true) ⇒ Object



447
448
449
450
451
# File 'lib/active_record/fixtures.rb', line 447

def self.instantiate_all_loaded_fixtures(object, load_instances = true)
  all_loaded_fixtures.each do |table_name, fixtures|
    ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
  end
end

.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true) ⇒ Object



435
436
437
438
439
440
441
442
443
444
445
# File 'lib/active_record/fixtures.rb', line 435

def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
  if load_instances
    fixtures.each do |name, fixture|
      begin
        object.instance_variable_set "@#{name}", fixture.find
      rescue FixtureClassNotFound
        nil
      end
    end
  end
end

.reset_cacheObject



411
412
413
# File 'lib/active_record/fixtures.rb', line 411

def self.reset_cache
  @@all_cached_fixtures.clear
end

Instance Method Details

#[](x) ⇒ Object



545
546
547
# File 'lib/active_record/fixtures.rb', line 545

def [](x)
  fixtures[x]
end

#[]=(k, v) ⇒ Object



549
550
551
# File 'lib/active_record/fixtures.rb', line 549

def []=(k,v)
  fixtures[k] = v
end

#each(&block) ⇒ Object



553
554
555
# File 'lib/active_record/fixtures.rb', line 553

def each(&block)
  fixtures.each(&block)
end

#sizeObject



557
558
559
# File 'lib/active_record/fixtures.rb', line 557

def size
  fixtures.size
end

#table_rowsObject

Return a hash of rows to be inserted. The key is the table, the value is a list of rows to insert to that table.



563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/active_record/fixtures.rb', line 563

def table_rows
  now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
  now = now.to_s(:db)

  # allow a standard key to be used for doing defaults in YAML
  fixtures.delete('DEFAULTS')

  # track any join tables we need to insert later
  rows = Hash.new { |h,table| h[table] = [] }

  rows[table_name] = fixtures.map do |label, fixture|
    row = fixture.to_hash

    if model_class && model_class < ActiveRecord::Base
      # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
      if model_class.record_timestamps
        timestamp_column_names.each do |name|
          row[name] = now unless row.key?(name)
        end
      end

      # interpolate the fixture label
      row.each do |key, value|
        row[key] = label if value == "$LABEL"
      end

      # generate a primary key if necessary
      if has_primary_key_column? && !row.include?(primary_key_name)
        row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
      end

      # If STI is used, find the correct subclass for association reflection
      reflection_class =
        if row.include?(inheritance_column_name)
          row[inheritance_column_name].constantize rescue model_class
        else
          model_class
        end

      reflection_class.reflect_on_all_associations.each do |association|
        case association.macro
        when :belongs_to
          # Do not replace association name with association foreign key if they are named the same
          fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s

          if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
            if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
              # support polymorphic belongs_to as "label (Type)"
              row[association.foreign_type] = $1
            end

            row[fk_name] = ActiveRecord::Fixtures.identify(value)
          end
        when :has_and_belongs_to_many
          if (targets = row.delete(association.name.to_s))
            targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
            table_name = association.options[:join_table]
            rows[table_name].concat targets.map { |target|
              { association.foreign_key             => row[primary_key_name],
                association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
            }
          end
        end
      end
    end

    row
  end
  rows
end