Skip to content
Open
38 changes: 27 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,40 @@ Or install it yourself as:
Add the following line into your active admin resource:


active_admin_importable
active_admin_importable

The Import button should now appear. Click it and upload a CSV file with a header row corresponding to your model attributes. Press submit. Profit.

## Usefull options


active_admin_importable options


* ``:find_by => :id``

Try find row by ID (or specified field) and update or create it.

* ``:reset_pk_sequence => true``

After process primary key sequence will by reset (tested on PostgreSQL).

* ``:before_save => proc { |row| row[:salary] ||= 0 }``

Modify field values before write (and before find, if present).

## Custom Import Behavior

Need to do something special with the import? active_admin_importable accepts an optional block that will be called on each row, replacing the default functionality ( calling create! on the associated model). The associated model and a hash of the current row will get passed into the block. For example:

```
ActiveAdmin.register Product do
active_admin_importable do |model, hash|
store = Store.find_by_name(hash[:store_name])
hash[:store_id] = store.id
hash.delete(:store_name)
model.create!(hash)
end
end
```
ActiveAdmin.register Product do
active_admin_importable do |model, hash|
store = Store.find_by_name(hash[:store_name])
hash[:store_id] = store.id
hash.delete(:store_name)
model.create!(hash)
end
end

## Contributing

Expand Down
73 changes: 62 additions & 11 deletions app/models/csv_db.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,68 @@
require 'csv'
class CsvDb
class << self
def convert_save(target_model, csv_data, &block)
csv_file = csv_data.read
CSV.parse(csv_file, :headers => true, header_converters: :symbol ) do |row|
data = row.to_hash
if data.present?
if (block_given?)
block.call(target_model, data)
else
target_model.create!(data)
end
end
def char_code(c)
c.respond_to?(:ord) ? c.ord : c
end

def has_bom(file_data)
char_code(file_data[0]) == 0xEF &&
char_code(file_data[1]) == 0xBB &&
char_code(file_data[2]) == 0xBF
end

# @return [String]
def remove_bom(file_data)
has_bom(file_data) ? file_data[3..-1] : file_data
end

def convert_save(target_model, csv_data, options, &block)
csv_data = remove_bom(csv_data.read)
csv_data = csv_data.force_encoding('utf-8') if csv_data.respond_to?(:force_encoding)
parser_class = (RUBY_VERSION=='1.8.7') ? FasterCSV : CSV
begin
target_model.transaction do
parser_class.parse(csv_data, :headers => true, :header_converters => :symbol) do |row|
append_row(target_model, row, options, &block)
end
end
ensure
if options[:reset_pk_sequence]
target_model.connection.reset_pk_sequence! target_model.table_name
end
end
end

def append_row(target_model, row, options, &block)
data = row.to_hash
if data.present?
if (block_given?)
block.call(target_model, data)
else

options[:before_save].call(data) if options[:before_save]

role = options[:role] || :default
if key_field = options[:find_by]
create_or_update! target_model, data, key_field
else
if role == :default
target_model.create!(data)
else
target_model.create!(data, :as => role) # Old version ActiveRecord
end
end
end
end
end

def create_or_update!(target_model, values, key_field)
key_value = values[key_field]
scope = target_model.where(key_field => key_value)
if obj = scope.first
obj.update_attributes!(values)
else
scope.create!(values)
end
end
end
Expand Down
11 changes: 7 additions & 4 deletions lib/active_admin_importable/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module ActiveAdminImportable
module DSL
def active_admin_importable(&block)
action_item :only => :index do
def active_admin_importable(options = {}, &block)

action_item :edit, :only => :index do
link_to "Import #{active_admin_config.resource_name.to_s.pluralize}", :action => 'upload_csv'
end

Expand All @@ -10,8 +11,10 @@ def active_admin_importable(&block)
end

collection_action :import_csv, :method => :post do
CsvDb.convert_save(active_admin_config.resource_class, params[:dump][:file], &block)
redirect_to :action => :index, :notice => "#{active_admin_config.resource_name.to_s} imported successfully!"
role = resources_configuration[:self][:role]
CsvDb.convert_save(active_admin_config.resource_class, params[:dump][:file], options.merge(:role=>role), &block)
flash[:notice] = "#{active_admin_config.resource_name.to_s} imported successfully!"
redirect_to :action => :index
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/active_admin_importable/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ActiveAdminImportable
VERSION = "1.1.2"
VERSION = "1.1.3"
end