Class: ConfigScripts::Seeds::SeedType

Inherits:
Object
  • Object
show all
Defined in:
lib/config_scripts/seeds/seed_type.rb

Overview

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

Attributes collapse

Creation collapse

DSL collapse

Reading and Writing collapse

Fetching collapse

Seed Identifiers collapse

Constructor Details

#initialize(seed_set, klass, filename, &block) ⇒ SeedType

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.

Parameters:

  • seed_set (SeedSet)

    The seed set that this seed type is defined within.

  • klass (Class)

    The model class whose data we are running.

  • filename (String)

    The name of the file in which the seed data will be stored.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/config_scripts/seeds/seed_type.rb', line 71

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

Instance Attribute Details

#associationsArray (readonly)

The active record associations for the model class for this seed file.

Returns:

  • (Array)


34
35
36
# File 'lib/config_scripts/seeds/seed_type.rb', line 34

def associations
  @associations
end

#attributesArray<Symbol> (readonly)

The names of the attributes on the model object that we store in the seed file.

Returns:

  • (Array<Symbol>)


25
26
27
# File 'lib/config_scripts/seeds/seed_type.rb', line 25

def attributes
  @attributes
end

#dynamic_readersHash<Symbol, Proc> (readonly)

The attributes that we generate dynamically after loading the ones from the seed file.

Returns:

  • (Hash<Symbol, Proc>)


39
40
41
# File 'lib/config_scripts/seeds/seed_type.rb', line 39

def dynamic_readers
  @dynamic_readers
end

#dynamic_writersHash<Symbol, Proc> (readonly)

The attributes that we generate dynamically when writing things to the seed file.

Returns:

  • (Hash<Symbol, Proc>)


44
45
46
# File 'lib/config_scripts/seeds/seed_type.rb', line 44

def dynamic_writers
  @dynamic_writers
end

#filenameString (readonly)

The name of the file that we will store these records in.

Returns:

  • (String)


20
21
22
# File 'lib/config_scripts/seeds/seed_type.rb', line 20

def filename
  @filename
end

#identifier_attributesArray<Symbol> (readonly)

The names of the attributes used to compose a unique identifier for a record.

Returns:

  • (Array<Symbol>)


30
31
32
# File 'lib/config_scripts/seeds/seed_type.rb', line 30

def identifier_attributes
  @identifier_attributes
end

#klassClass (readonly)

The model class whose records we are storing in this seed file.

Returns:

  • (Class)


16
17
18
# File 'lib/config_scripts/seeds/seed_type.rb', line 16

def klass
  @klass
end

#scopesArray<Array> (readonly)

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.

Returns:

  • (Array<Array>)


53
54
55
# File 'lib/config_scripts/seeds/seed_type.rb', line 53

def scopes
  @scopes
end

#seed_setSeedSet (readonly)

The seed set that this type has been defined within.

Returns:



12
13
14
# File 'lib/config_scripts/seeds/seed_type.rb', line 12

def seed_set
  @seed_set
end

Instance Method Details

#allRelation

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.

Returns:

  • (Relation)


306
307
308
309
# File 'lib/config_scripts/seeds/seed_type.rb', line 306

def all
  version = Rails.version[0]
  records = version == '3' ? self.klass.scoped : self.klass.all
end

#has_attributes(*new_attributes) ⇒ Array<Symbol>

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

Parameters:

  • new_attributes (Array<Symbol>)

    The attributes to add.

Returns:

  • (Array<Symbol>)

    The full list of attributes after the new ones are added.



98
99
100
# File 'lib/config_scripts/seeds/seed_type.rb', line 98

def has_attributes(*new_attributes)
  @attributes += new_attributes
end

#has_identifier_attributes(*attributes) ⇒ Array<Symbol>

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

Parameters:

  • attributes (Array<Symbol>)

    The attributes that form the unique identifier.

Returns:

  • (Array<Symbol>)

    The attributes.



110
111
112
# File 'lib/config_scripts/seeds/seed_type.rb', line 110

def has_identifier_attributes(*attributes)
  @identifier_attributes = attributes
end

#has_scope(method, *args) ⇒ Array

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

Parameters:

  • method (Symbol)

    The name of the method to call on the relation.

  • args (Array)

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

Returns:

  • (Array)

    The full list of scopes.



126
127
128
# File 'lib/config_scripts/seeds/seed_type.rb', line 126

def has_scope(method, *args)
  @scopes << [method, args]
end

#itemsRelation

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.

Returns:

  • (Relation)


316
317
318
319
320
# File 'lib/config_scripts/seeds/seed_type.rb', line 316

def items
  records = self.all
  self.scopes.each { |method, args| records = records.send(method, *args) }
  records
end

#optionsHash

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

Returns:

  • (Hash)


326
327
328
# File 'lib/config_scripts/seeds/seed_type.rb', line 326

def options
  self.seed_set.options
end

#read_from_folder(folder) ⇒ Object

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.

Parameters:

  • folder (String)

    The full path to the folder with the seed files.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/config_scripts/seeds/seed_type.rb', line 192

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) ⇒ ActiveRecord::Base

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.

Parameters:

  • attribute (Symbol)

    The name of the association.

  • identifier (Array<String>)

    The components of the seed identifier.

Returns:

  • (ActiveRecord::Base)


289
290
291
292
293
294
295
296
# File 'lib/config_scripts/seeds/seed_type.rb', line 289

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) ⇒ Object

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.

Parameters:

  • value (String)

    The value from the CSV file.

  • attribute (Symbol)

    The name of the attribute that we are formatting.

Returns:

  • (Object)

    The value to set on the record.



264
265
266
267
268
269
270
271
272
273
274
# File 'lib/config_scripts/seeds/seed_type.rb', line 264

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) ⇒ ActiveRecord::Base

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

Parameters:

  • identifier (Array<String>)

    The identifier from the CSV file.

Returns:

  • (ActiveRecord::Base)

    The record



352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/config_scripts/seeds/seed_type.rb', line 352

def record_for_seed_identifier(identifier)
  return nil if identifier.blank?
  records = self.items
  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) ⇒ String

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

Parameters:

  • record (ActiveRecord::Base)

    The record

Returns:

  • (String)

    The identifier for the seed files.



340
341
342
# File 'lib/config_scripts/seeds/seed_type.rb', line 340

def seed_identifier_for_record(record)
  self.identifier_attributes.collect { |param| self.write_value_for_attribute(record, param) }.join("::")
end

#when_reading(attribute, &block) ⇒ Object

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.

Parameters:

  • attribute (Symbol)

    The attribute that we are reading.



139
140
141
# File 'lib/config_scripts/seeds/seed_type.rb', line 139

def when_reading(attribute, &block)
  @dynamic_readers[attribute] = block
end

#when_writing(attribute, &block) ⇒ Object

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.

Parameters:

  • attribute (Symbol)

    The attribute we are writing.



152
153
154
# File 'lib/config_scripts/seeds/seed_type.rb', line 152

def when_writing(attribute, &block)
  @dynamic_writers[attribute] = block
end

#write_to_folder(folder) ⇒ Object

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.

Parameters:

  • folder (String)

    The full path to the folder to write to.



169
170
171
172
173
174
175
176
177
178
# File 'lib/config_scripts/seeds/seed_type.rb', line 169

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) ⇒ String

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.

Parameters:

  • item (ActiveRecord::Base)

    The record whose value we are getting.

  • attribute (Symbol)

    The attribute we are getting.

Returns:

  • (String)

    The value to write.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/config_scripts/seeds/seed_type.rb', line 232

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
  end
  value
end