pcreux/csv-importer

Using after_save in block change class after_save_blocks array

Closed this issue · 5 comments

I use cscv-importer in conroller:

    import = Importers::ImportMemberCSV.new(file: params[:import_file]) do
      after_save { |member| list.subscribe(member) }
    end
    import.run!

Nonetheless I use after_save in block, it adds proc into Importers::ImportMemberCSV.config.after_save_blocks array

class Importers::ImportMemberCSV
  include CSVImporter

  model Member

  column :email, to: ->(email) { email.downcase }, required: true
  column :first_name
  column :last_name
  column :ip

  when_invalid :skip
end

Hello @ysynesis,

Can you confirm that the issue you're running into is that the after_save block you define for one instance of ImportMemberCSV is being added to the class instead of the instance?

class MyImport
  # ...
end

MyImport.new(file: file) do
  after_save { puts "Hi!" }
end
import.run!
# => "Hi!"

MyImport.new(file: file).run!
# expected: ""
# actual: "Hi!"

@pcreux yes, exactly

OK, I'll look into this when I get a chance. As usual, any pull request is
greatly appreciated. 😉

On Sep 2, 2016 18:54, "ysynesis" notifications@github.com wrote:

@pcreux https://github.com/pcreux yes, exactly


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#44 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AACw8x4Hm54oE0GeEUkqlXbQZrkh44QIks5qmFTUgaJpZM4JiVBx
.

Hi, I'm having the same issue.
The problem is here:
CsvImporter

def initialize(*args, &block)
    @csv = CSVReader.new(*args)
    @config = self.class.config.dup

the #dup call does a shallow copy so it doesn't copy a new Array for after_save and after_build blocks.

Thanks for your work, it's a very useful gem.

I have a improvement suggestion. Id like to add a 3rd option to this case where the block passed uses the column_definition too. This allows to do custom conversion with metadata stored in the column name. For example the columns header is defined as "name, birthdate[MM/DD/YY], address". So the column name matches trough a regex and you can capture the metadata inside []

        case to_proc.arity
        when 1 # to: ->(email) { email.downcase }
          model.public_send("#{column_definition.name}=", to_proc.call(csv_value))
        when 2 # to: ->(published, post) { post.published_at = Time.now if published == "true" }
          to_proc.call(csv_value, model)
        else
          raise ArgumentError, "`to` proc can only have 1 or 2 arguments"

What do you think?

Issue was fixed in 0.3.2. I still have to think about the "3rd option" to capture metadata inside header.