lib/data_seeder/loader.rb in data_seeder-0.0.5 vs lib/data_seeder/loader.rb in data_seeder-1.0.0
- old
+ new
@@ -1,140 +1,135 @@
require 'English'
module DataSeeder
module Loader
- attr_accessor :file_config, :key_attribute
- attr_reader :path, :path_minus_ext
+ attr_reader :seeder_config, :config, :key_attribute, :klass, :path, :path_minus_ext
- def initialize(options={})
- @only = options[:only]
- @except = options[:except]
- if options.has_key?(:purge)
- @purge = options[:purge]
- else
- @purge = true
- end
- @old_keys = []
+ def initialize(config)
+ @seeder_config = DataSeeder.config
+ @config = config
+ @key_attribute = config[:key_attribute] || :id
+ @klass = config[:klass]
+ @path = config[:path]
+ @path_minus_ext = config[:path_minus_ext]
+ # Default purge to true if unspecified
+ @config[:purge] = true unless config.has_key?(:purge)
+ @old_ids = Set.new
end
- def config
- DataSeeder.config
- end
-
def logger
- DataSeeder.logger
+ @seeder_config.logger
end
- def klass!
- @path_minus_ext.classify.constantize
- rescue NameError => e
- raise "#{@path} doesn't match a corresponding model"
+ def process(io)
+ setup
+ load(io)
+ teardown
end
- def klass
- # This should always translate to a class except for custom loaders
- @path_minus_ext.classify.constantize rescue nil
- end
-
- def process(path)
- @path = path
- dot_index = @path.rindex('.')
- @path_minus_ext = @path[0, dot_index]
- @file_config = {}
- cfg_file = "#{@path_minus_ext}.cfg"
- @file_config = eval(File.read(cfg_file)) if File.exist?(cfg_file)
- File.open(@path, 'r') do |fin|
- load_file_config(fin) if @file_config.empty?
- @file_config = ActiveSupport::HashWithIndifferentAccess.new(@file_config)
- setup
- load(fin)
- teardown
- end
- call_file_method(:teardown)
- end
-
def setup
- @key_attribute = self.file_config[:key_attribute] || :id
- @old_keys = self.klass!.all.pluck(@key_attribute).map(&:to_s) if @purge
- logger.info { "Loading #{@path}" }
- call_file_method(:setup)
+ @old_ids = klass.all.pluck(:id).to_set if config[:purge]
end
def teardown
- @old_keys.each do |key|
- if model = self.klass!.find_by(@key_attribute => key)
- logger.info { " Destroying #{model_info(model)}"}
- model.destroy
+ destroy_models(klass, @old_ids)
+ end
+
+ def destroy_models(klass, ids)
+ ids.each do |id|
+ if model = klass.find_by(id: id)
+ destroy_model(model)
end
end
end
# The information displayed when creating, updating, or destroying a model.
# The changes argument will be the model.changes on an update.
def model_info(model, changes=nil)
if changes
- attr = @file_config[:update_display_method] || @key_attribute
- "#{model.send(attr)}: #{changes.inspect}"
+ if attr = config[:update_display_method]
+ "#{model.send(attr)}: #{changes.inspect}"
+ elsif @key_attribute.kind_of?(Enumerable)
+ label = @key_attribute.map {|k| "#{k}=#{model.send(k)}"}.join(' ')
+ "#{label}: #{changes.inspect}"
+ else
+ "#{model.send(@key_attribute)}: #{changes.inspect}"
+ end
else
model.inspect
end
end
- def load_file_config(fin)
- config_line = fin.readline
- if match = config_line.match(/^\s*#\s*config:(.*)/)
- @file_config = eval(match[1])
- else
- fin.seek(0)
- if self.klass && self.klass.respond_to?(:data_seeder_config)
- @file_config = self.klass.data_seeder_config
- end
- end
- end
-
- def load(fin)
+ def load(io)
throw 'Must override load'
end
+ # This doesn't work in some versions of JRuby (version 9.0.3.0?)
def line_number
$INPUT_LINE_NUMBER
end
def save(attr)
- if @file_config[:use_line_number_as_id]
- key = self.line_number
+ attr = call_method(:postprocess, attr) || attr
+ if config[:use_line_number_as_id]
+ find_hash = { @key_attribute => self.line_number }
+ elsif @key_attribute.kind_of?(Enumerable)
+ find_hash = {}
+ @key_attribute.each do |k|
+ find_hash[k] = attr[k.to_s] || attr[k.to_sym]
+ end
else
key = attr[@key_attribute.to_s] || attr[@key_attribute.to_sym]
raise "No #{@key_attribute} in #{attr.inspect}" unless key
+ find_hash = { @key_attribute => key }
end
- if method = @file_config[:postprocess]
- method.call(attr)
- elsif self.klass!.respond_to?(:data_seeder_postprocess)
- self.klass!.send(:data_seeder_postprocess, attr)
- end
- @old_keys.delete(key.to_s)
- model = self.klass!.find_or_initialize_by(@key_attribute => key)
+ model = self.klass.find_or_initialize_by(find_hash)
model.attributes = attr
save_model(model)
end
def save_model(model)
if model.new_record?
- logger.info { " Saving #{model_info(model)}" }
+ log_save(model)
else
- changes = model.changes
- return if changes.empty?
- logger.info { " Updating #{model_info(model, changes)}" }
+ @old_ids.delete(model.id)
+ return unless model.changed?
+ log_update(model)
end
model.save!
end
- def call_file_method(name, *args)
- if method = @file_config[name]
- return method.call(*args)
- else
- class_method = "data_seeder_#{name}"
- return self.klass.send(class_method, *args) if @klass.respond_to?(class_method)
+ # Allow override for potential soft-delete
+ def destroy_model(model)
+ log_destroy(model)
+ model.destroy
+ end
+
+ def log_save(model)
+ logger.debug { "Saving #{model_info(model)}" }
+ end
+
+ def log_update(model)
+ logger.debug { "Updating #{model_info(model, model.changes)}" }
+ end
+
+ def log_destroy(model)
+ logger.debug { "Destroying #{model_info(model)}" }
+ end
+
+ def log_indent(&block)
+ @seeder_config.log_indent(&block)
+ end
+
+ def call_method(name, *args)
+ if self.respond_to?(name)
+ return send(name, *args)
+ elsif val = config[name]
+ if val.kind_of?(Proc)
+ return val.call(*args)
+ else
+ return val
+ end
end
return nil
end
end
end