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.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 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

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_fetcherProc (readonly)

The block that will be run when fetching items.

Returns:

  • (Proc)


48
49
50
# File 'lib/config_scripts/seeds/seed_type.rb', line 48

def dynamic_fetcher
  @dynamic_fetcher
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>)


57
58
59
# File 'lib/config_scripts/seeds/seed_type.rb', line 57

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)


320
321
322
323
# 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) ⇒ 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.



102
103
104
# File 'lib/config_scripts/seeds/seed_type.rb', line 102

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.



114
115
116
# File 'lib/config_scripts/seeds/seed_type.rb', line 114

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.



130
131
132
# File 'lib/config_scripts/seeds/seed_type.rb', line 130

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)


330
331
332
333
334
335
# 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

#optionsHash

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

Returns:

  • (Hash)


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

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.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# 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) ⇒ 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)


303
304
305
306
307
308
309
310
# 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) ⇒ 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.



278
279
280
281
282
283
284
285
286
287
288
# 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) ⇒ 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



367
368
369
370
371
372
373
374
375
376
377
378
379
380
# 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) ⇒ 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.



355
356
357
# 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) ⇒ Object

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



162
163
164
# File 'lib/config_scripts/seeds/seed_type.rb', line 162

def when_fetching(&block)
  @dynamic_fetcher = block
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.



143
144
145
# File 'lib/config_scripts/seeds/seed_type.rb', line 143

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.



156
157
158
# File 'lib/config_scripts/seeds/seed_type.rb', line 156

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.



179
180
181
182
183
184
185
186
187
188
# 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) ⇒ 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.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# 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