ActsAsImportable

ActsAsImportable makes import/export of ActiveRecord models from csv files easier and less repetitive. It helps keep your code DRY, clean, and simple.

Usage

Add the following to your controller:

acts_as_importable :product

If model name is not specified, it will use the controller name to find the model name. So, the following is same as above if the controller name is “products”

acts_as_importable

Scoping imported objects:

acts_as_importable :product, :scoped => :current_store

This will import all products within the current_store object returned by the controller#current_store method. Product should have a belongs_to association to Store for this feature to work.

Updating existing objects:

acts_as_importable :product, :find_existing_by => :name

This will update existing products whose name matches with the value of name column in the csv row. However, if :find_by_existing is not specified, no attempt will be made to lookup existing products. e.g. the following will always create new products

acts_as_importable :product

Add the following to your Model:

acts_as_importable :import_fields => ["name", "price"]

This will populate the specified fields while importing the model records from csv.

acts_as_importable :import_fields => ["name", "price", "product_type.name"]

If there is a field which is not a direct attribute or is an association eg. ‘product_type.name’ in the example above, you have to define a method like the following and it will be called while importing the data:

def assign_product_type(data_row)
  name = data_row[2].strip
  product_type = ProductType.find_by_name(name)
  self.product_type_id = product_type.id if product_type
end

NOTE: Any field which has a ‘.’ in its name require a method with name like “assign_#{name_of_the_field}”. Also as a parameter you get the entire row from the csv file. So you can operate on multiple columns to generate the value for this field.

Another example of the method above:

def assign_category(data_row)
  category = Category.find_by_name(data_row[3].strip)
  self.category_id = category.id if category
end

Modify data before import:

class Product
  acts_as_importable :import_fields => ["name", "price"], :before_import => :modify_data
  self.modify_data data_row
    data_row[0] = "Modified data"
  end
end

If before_import is supplied, the given method (in this case ‘modify_data’) will be called on each row. Current row from csv file is passed to the given method as parameter, which can be modified here before object is created. The ‘data_row’ parameter is an array which represent a row in csv file.

Customizing the export fields:

You can specify a different list of fields to export if you want them to be different from the import fields e.g.

acts_as_importable :import_fields => ["name", "price", "product_type.name"],
                   :export_fields => ["name", "product_type.name"]

Add the following routes to your routes.rb:

resources :products do
  collection do
    post 'upload'
    get 'import'
    get 'export'
  end
end

Define the following constant (e.g. in an initializer):

UPLOADS_PATH = "#{Rails.root}/tmp/uploads"

NOTE: This can be any file system path writable by the Rails process.

Contributing to acts_as_importable

Credit

A big thanks goes to -

Copyright © 2010 Jagdeep Singh. See LICENSE.txt for further details.