class ConfigScripts::Seeds::SeedType

This class encapsulates information about how to write seeds for a class to a seed file.

Attributes

associations[R]

@return [Array] The active record associations for the model class for this seed file.

attributes[R]

@return [Array<Symbol>] The names of the attributes on the model object that we store in the seed file.

dynamic_fetcher[R]

@return [Proc] The block that will be run when fetching items.

dynamic_readers[R]

@return [Hash<Symbol, Proc>] The attributes that we generate dynamically after loading the ones from the seed file.

dynamic_writers[R]

@return [Hash<Symbol, Proc>] The attributes that we generate dynamically when writing things to the seed file.

filename[R]

@return [String] The name of the file that we will store these records in.

identifier_attributes[R]

@return [Array<Symbol>] The names of the attributes used to compose a unique identifier for a record.

klass[R]

@return [Class] The model class whose records we are storing in this seed file.

scopes[R]

@return [Array<Array>] The scopes that we apply when fetching items.

Each entry will be an array. The first entry in the inner arrays will be a symbol, the name of a method that can be run on a relation. The result of the array will be passed in when running the method on the scope.

seed_set[R]

@return [SeedSet] The seed set that this type has been defined within.

Public Class Methods

new(seed_set, klass, filename, &block) click to toggle source

This method creates a new seed type.

This method should be given a block, which will be run in the instance context of the new seed type. That block should use the DSL methods to fill in the details for the seed type.

@param [SeedSet] seed_set

The seed set that this seed type is defined within.

@param [Class] klass

The model class whose data we are running.

@param [String] filename

The name of the file in which the seed data will be stored.
# File lib/config_scripts/seeds/seed_type.rb, line 75
def initialize(seed_set, klass, filename, &block)
  @seed_set = seed_set
  @klass = klass
  @filename = filename
  @attributes = []
  @identifier_attributes = [:id]
  @scopes = []
  @dynamic_writers = {}
  @dynamic_readers = {}

  @associations = {}
  @klass.reflect_on_all_associations.each do |association|
    @associations[association.name] = association.klass rescue nil
  end

  self.instance_eval(&block) if block_given?
end

Public Instance Methods

all() click to toggle source

This method gets a relation encompassing all the records in the class.

We encapsulate this here so that we can use different calls in Rails 3 and Rails 4.

@return [Relation]

# File lib/config_scripts/seeds/seed_type.rb, line 320
def all
  version = Rails.version[0]
  records = version == '3' ? self.klass.scoped : self.klass.all
end
has_attributes(*new_attributes) click to toggle source

This method adds new attributes to the ones written in this seed type.

@param [Array<Symbol>] new_attributes

The attributes to add.

@return [Array<Symbol>]

The full list of attributes after the new ones are added.
# File lib/config_scripts/seeds/seed_type.rb, line 102
def has_attributes(*new_attributes)
  @attributes += new_attributes
end
has_identifier_attributes(*attributes) click to toggle source

This method defines the attributes used to generate a unique identifier for a record.

@param [Array<Symbol>] attributes

The attributes that form the unique identifier.

@return [Array<Symbol>]

The attributes.
# File lib/config_scripts/seeds/seed_type.rb, line 114
def has_identifier_attributes(*attributes)
  @identifier_attributes = attributes
end
has_scope(method, *args) click to toggle source

This method adds a scope to the list used to filter the records for writing.

@param [Symbol] method

The name of the method to call on the relation.

@param [Array] args

The arguments that will be passed into the scope method on the
relation.

@return [Array]

The full list of scopes.
# File lib/config_scripts/seeds/seed_type.rb, line 130
def has_scope(method, *args)
  @scopes << [method, args]
end
items() click to toggle source

This method gets the items that we should write to our seed file.

It will start with the scoped list for the model class, and then apply all the scopes in our {#scopes} list.

@return [Relation]

# File lib/config_scripts/seeds/seed_type.rb, line 330
def items
  return @dynamic_fetcher.call if @dynamic_fetcher
  records = self.all
  self.scopes.each { |method, args| records = records.send(method, *args) }
  records
end
options() click to toggle source

This method gets the additional information passed in when defining our seed set.

@return [Hash]

# File lib/config_scripts/seeds/seed_type.rb, line 341
def options
  self.seed_set.options
end
read_from_folder(folder) click to toggle source

This method reads the seed data from a file, and creates new records from it.

This will extract all the rows from the CSV file, and use {#read_value_for_attribute} to get the attributes for the record from each cell in the CSV file.

If this seed type has no attributes, this method will not try to read the file.

@param [String] folder

The full path to the folder with the seed files.
# File lib/config_scripts/seeds/seed_type.rb, line 202
def read_from_folder(folder)
  return unless attributes.any?
  CSV.open(File.join(folder, "#{self.filename}.csv"), headers: true) do |csv|
    csv.each do |row|
      record = self.klass.new
      row.each do |attribute, value|
        attribute = attribute.to_sym
        value = self.read_value_for_attribute(value, attribute)
        record.send("#{attribute}=", value)
      end
      
      begin
        record.save!
      rescue
        puts "#{self.filename}.csv"
        puts "#{row}"
        raise
      end
    end
  end
end
read_value_for_association(attribute, identifier) click to toggle source

This method gets the value that we will assign to an attribute for an association.

This will remove as many entries from the identifier as it needs to get the value.

@param [Symbol] attribute

The name of the association.

@param [Array<String>] identifier

The components of the seed identifier.

@return [ActiveRecord::Base]

# File lib/config_scripts/seeds/seed_type.rb, line 303
def read_value_for_association(attribute, identifier)
  klass = @associations[attribute]
  unless klass
    class_name = identifier.shift
    klass = class_name.constantize
  end
  value = self.seed_set.record_for_seed_identifier(klass, identifier)
end
read_value_for_attribute(value, attribute) click to toggle source

This method takes a value from the CSV file and gives back the value that should be set on the record.

If the attribute is an association, this will pass it to the seed set as a seed identifier. If it is a polymorphic association, it will use the first part of the seed identifier as a class name.

@param [String] value

The value from the CSV file.

@param [Symbol] attribute

The name of the attribute that we are formatting.

@return [Object]

The value to set on the record.
# File lib/config_scripts/seeds/seed_type.rb, line 278
def read_value_for_attribute(value, attribute)
  if @dynamic_readers[attribute]
    value = @dynamic_readers[attribute].call(value)
  end

  if @associations.has_key?(attribute)
    return nil if value.blank?
    value = self.read_value_for_association(attribute, value.split("::"))
  end
  value
end
record_for_seed_identifier(identifier) click to toggle source

This method finds a record for our model class based on the unique seed identifier.

@param [Array<String>] identifier

The identifier from the CSV file.

@return [ActiveRecord::Base]

The record
# File lib/config_scripts/seeds/seed_type.rb, line 367
def record_for_seed_identifier(identifier)
  return nil if identifier.blank?
  records = self.all
  self.identifier_attributes.each_with_index do |attribute, index|
    if self.associations.has_key?(attribute)
      value = self.read_value_for_association(attribute, identifier)
      records = records.where("#{attribute}_id" => value.try(:id))
    else
      value = self.read_value_for_attribute(identifier.shift, attribute)
      records = records.where(attribute => value)
    end
  end
  records.first
end
seed_identifier_for_record(record) click to toggle source

This method gets the unique identifier for a record of the class that this seed type handles.

@param [ActiveRecord::Base] record

The record

@return [String]

The identifier for the seed files.
# File lib/config_scripts/seeds/seed_type.rb, line 355
def seed_identifier_for_record(record)
  self.identifier_attributes.collect { |param| self.write_value_for_attribute(record, param) }.join("::")
end
when_fetching(&block) click to toggle source

This method registers a custom block that will be run when fetching records.

# File lib/config_scripts/seeds/seed_type.rb, line 162
def when_fetching(&block)
  @dynamic_fetcher = block
end
when_reading(attribute, &block) click to toggle source

This method registers a custom block that will be run when reading a value from the seed file.

This method takes a block that will be run on the value from the seed file. The return value of the block will be used in place of the original value from the seed file.

@param [Symbol] attribute

The attribute that we are reading.
# File lib/config_scripts/seeds/seed_type.rb, line 143
def when_reading(attribute, &block)
  @dynamic_readers[attribute] = block
end
when_writing(attribute, &block) click to toggle source

This method registers a custom block that will be run when writing a value to the seed file.

This method takes a block that will be run on the item whose values we are writing. The return value of the block will be used instead of running the method on the record.

@param [Symbol] attribute

The attribute we are writing.
# File lib/config_scripts/seeds/seed_type.rb, line 156
def when_writing(attribute, &block)
  @dynamic_writers[attribute] = block
end
write_to_folder(folder) click to toggle source

This method writes the seed data file to a folder.

This will write a header row with the names of the attributes, and then write a row for each item from the {#items} method. It will use the {#write_value_for_attribute} method to get the values for the CSV file.

If this seed type has no attributes, this method will not write anything.

@param [String] folder

The full path to the folder to write to.
# File lib/config_scripts/seeds/seed_type.rb, line 179
def write_to_folder(folder)
  return unless attributes.any?
  CSV.open(File.join(folder, "#{self.filename}.csv"), 'w') do |csv|
    csv << self.attributes
    self.items.each do |item|
      data = self.attributes.collect { |attribute| self.write_value_for_attribute(item, attribute) }
      csv << data
    end
  end
end
write_value_for_attribute(item, attribute) click to toggle source

This method gets the value that we should write into the CSV file for an attribute.

If the value for that attribute is another model record, this will get its seed identifier from the seed set. Otherwise, it will just use the value.

If the attribute is a polymorphic foreign key, this will prefix the seed identifier with the class name.

@param [ActiveRecord::Base] item

The record whose value we are getting.

@param [Symbol] attribute

The attribute we are getting.

@return [String]

The value to write.
# File lib/config_scripts/seeds/seed_type.rb, line 242
def write_value_for_attribute(item, attribute)
  if @dynamic_writers[attribute]
    value = @dynamic_writers[attribute].call(item)
  else
    value = item.send(attribute)
  end

  if value.is_a?(ActiveRecord::Base)
    identifier = self.seed_set.seed_identifier_for_record(value)
    if !self.associations[attribute]
      identifier = "#{value.class.name}::#{identifier}"
    end
    value = identifier
  elsif value.is_a?(TrueClass)
    value = '1'
  elsif value.is_a?(FalseClass)
    value = '0'
  end
  value
end