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