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

  • Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet

  • Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it

  • Fork the project

  • Start a feature/bugfix branch

  • Commit and push until you are happy with your contribution

  • Make sure to add tests for it in the test/test_acts_as_importable project. This is important so I don’t break it in a future version unintentionally.

  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Credit

A big thanks goes to -

  • Ennova for giving us an opportunity to extract and release this functionality as a gem for the larger community

  • Bhavin Javia for helping with extracting, enhancing and releasing this gem

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