Skip to content

Commit 50ac9c9

Browse files
committed
update dependencies, refactoring, basic specs
1 parent 307b12a commit 50ac9c9

File tree

19 files changed

+371
-51
lines changed

19 files changed

+371
-51
lines changed

.travis.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
script: bundle exec rspec spec
2+
rvm:
3+
- 1.9.3
4+
- 2.0.0
5+
before_install:
6+
- gem update --system
7+
- gem --version

Gemfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,19 @@ source 'https://rubygems.org'
22

33
# Specify your gem's dependencies in active_admin_importable.gemspec
44
gemspec
5+
group :test do
6+
gem 'rails', '4.1.9'
7+
gem 'rspec-rails'
8+
gem 'activeadmin', github: 'activeadmin' , ref: '54bede0558a99ab759f98f9b24e1b0144063a81e'
9+
10+
11+
12+
gem 'devise'
13+
gem 'sass-rails'
14+
gem 'sqlite3'
15+
gem 'launchy'
16+
gem 'database_cleaner'
17+
gem 'capybara'
18+
gem 'selenium-webdriver'
19+
gem 'poltergeist'
20+
end

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ The most fastest and efficient CSV import for Active Admin (based on activerecor
33
with support of validations and bulk inserts
44

55

6-
Should work with both AA 0.6 and 1.0.0
6+
7+
[![Build Status](http://img.shields.io/travis/Fivell/active_admin_import.svg)](https://travis-ci.org/Fivell/active_admin_import)
8+
[![Dependency Status](http://img.shields.io/gemnasium/Fivell/active_admin_import.svg)](https://gemnasium.com/Fivell/active_admin_import)
9+
10+
branch for AA 1.0.0 and Rails >= 4.1
711

812

913
#Installation

Rakefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
#!/usr/bin/env rake
2-
require "bundler/gem_tasks"
1+
require "bundler"
2+
require 'rake'
3+
Bundler.setup
4+
Bundler::GemHelper.install_tasks
5+
6+
# Import all our rake tasks
7+
FileList['tasks/**/*.rake'].each { |task| import task }

active_admin_import.gemspec

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ Gem::Specification.new do |gem|
1515
gem.require_paths = ["lib"]
1616
gem.version = ActiveAdminImport::VERSION
1717

18-
gem.add_runtime_dependency 'activerecord-import', '~> 0.4', '>= 0.4.1'
19-
gem.add_runtime_dependency 'activeadmin', '>= 0.6.0'
20-
gem.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0'
2118

19+
gem.add_runtime_dependency 'activerecord-import', '~> 0.7.0'
20+
21+
gem.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0'
22+
gem.add_dependency "rails", ">= 4.0"
2223

2324
end

app/views/admin/import.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<p>
2-
<small> <%= raw(@active_admin_import_model.hint) %> </small>
1+
<p class="active_admin_import_hint">
2+
<%= raw(@active_admin_import_model.hint) %>
33
</p>
44
<%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
55
<%= f.inputs do %>

lib/active_admin_import.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
require 'active_admin'
33
require 'active_admin_import/version'
44
require 'active_admin_import/engine'
5+
require 'active_admin_import/import_result'
6+
require 'active_admin_import/options'
57
require 'active_admin_import/dsl'
68
require 'active_admin_import/importer'
79
require 'active_admin_import/model'
810
require 'active_admin_import/authorization'
911
::ActiveAdmin::DSL.send(:include, ActiveAdminImport::DSL)
1012

13+

lib/active_admin_import/dsl.rb

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module ActiveAdminImport
22
module DSL
3+
4+
35
# Declares import functionality
46
#
57
# Options
68
# +back+:: resource action to redirect after processing
7-
# +col_sep+:: column separator used for CSV parsing (deprecated)
8-
# +row_sep+:: column separator used for CSV parsing (deprecated)
99
# +csv_options+:: hash to override default CSV options
1010
# +validate+:: true|false, means perfoem validations or not
1111
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
@@ -22,7 +22,10 @@ module DSL
2222
# +resource_label+:: resource label value (default config.resource_label)
2323
# +plural_resource_label+:: plaralized resource label value (default config.plural_resource_label)
2424
#
25-
def active_admin_import options = {}, &block
25+
26+
def active_admin_import(options = {}, &block)
27+
options.assert_valid_keys(*VALID_OPTIONS)
28+
2629
default_options = {
2730
back: {action: :import},
2831
csv_options: {},
@@ -36,18 +39,30 @@ def active_admin_import options = {}, &block
3639
params_key = ActiveModel::Naming.param_key(options[:template_object] || ActiveAdminImport::Model.new)
3740

3841
collection_action :import, method: :get do
42+
3943
authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
4044

4145
@active_admin_import_model = options[:template_object] || ActiveAdminImport::Model.new
4246
render template: options[:template]
4347
end
4448

45-
action_item only: :index do
49+
50+
action_item :import, only: :index do
4651
if authorized?(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
47-
link_to(I18n.t('active_admin_import.import_model', model: options[:resource_label]), action: 'import')
52+
link_to(I18n.t('active_admin_import.import_model', model: options[:resource_label]), action: :import)
4853
end
54+
55+
56+
@active_admin_import_model = options[:template_object]
57+
render template: options[:template]
58+
end
59+
60+
action_item :import, only: :index do
61+
link_to(I18n.t('active_admin_import.import_model', model: options[:resource_label]), action: :import)
4962
end
5063

64+
65+
5166
collection_action :do_import, method: :post do
5267
authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
5368

@@ -57,17 +72,21 @@ def active_admin_import options = {}, &block
5772
return render template: options[:template] unless @active_admin_import_model.valid?
5873
@importer = Importer.new(options[:resource_class], @active_admin_import_model, options)
5974
begin
60-
@importer.import
75+
result = @importer.import
6176
if block_given?
6277
instance_eval &block
6378
else
79+
6480
model_name = options[:resource_label].downcase
65-
plural_model_name = options[:resource_label].downcase
66-
if @importer.result[:imported].to_i > 0
67-
flash[:notice] = I18n.t('active_admin_import.imported', count: @importer.result[:imported].to_i, model: model_name, plural_model: plural_model_name)
81+
plural_model_name = options[:plural_resource_label].downcase
82+
83+
84+
85+
if result.has_imported?
86+
flash[:notice] = I18n.t('active_admin_import.imported', count: result.imported_qty, model: model_name, plural_model: plural_model_name)
6887
end
69-
if @importer.result[:failed].count > 0
70-
flash[:error] = I18n.t('active_admin_import.failed', count: @importer.result[:failed].count, model: model_name, plural_model: plural_model_name)
88+
if result.has_failed?
89+
flash[:error] = I18n.t('active_admin_import.failed', count: result.failed.count, model: model_name, plural_model: plural_model_name)
7190
end
7291
end
7392
rescue ActiveRecord::Import::MissingColumnError, NoMethodError => e
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module ActiveAdminImport
2+
class ImportResult
3+
attr_reader :failed, :total
4+
5+
def initialize
6+
@failed = []
7+
@total = 0
8+
end
9+
10+
def add(result, qty)
11+
@failed += result.failed_instances
12+
@total+=qty
13+
end
14+
15+
def imported_qty
16+
total - failed.count
17+
end
18+
19+
def has_imported?
20+
imported_qty > 0
21+
end
22+
23+
def has_failed?
24+
@failed.any?
25+
end
26+
27+
end
28+
end

lib/active_admin_import/importer.rb

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,45 @@
22
module ActiveAdminImport
33
class Importer
44

5-
attr_reader :resource, :options, :result, :headers, :csv_lines, :model
5+
6+
attr_reader :resource, :options, :result, :headers, :csv_lines, :model
7+
8+
9+
OPTIONS = [
10+
:validate,
11+
:on_duplicate_key_update,
12+
:ignore,
13+
:timestamps,
14+
:before_import,
15+
:after_import,
16+
:before_batch_import,
17+
:after_batch_import,
18+
:headers_rewrites,
19+
:batch_size,
20+
:csv_options
21+
].freeze
22+
623

724
def store
825
result = @resource.transaction do
9-
options[:before_batch_import].call(self) if options[:before_batch_import].is_a?(Proc)
10-
11-
result = resource.import headers.values, csv_lines, {
12-
validate: options[:validate],
13-
on_duplicate_key_update: options[:on_duplicate_key_update],
14-
ignore: options[:ignore],
15-
timestamps: options[:timestamps]
16-
}
17-
options[:after_batch_import].call(self) if options[:after_batch_import].is_a?(Proc)
26+
run_callback(:before_batch_import)
27+
result = resource.import(headers.values, csv_lines, options.slice(:validate, :on_duplicate_key_update, :ignore, :timestamps))
28+
run_callback(:after_batch_import)
1829
result
1930
end
2031
{imported: csv_lines.count - result.failed_instances.count, failed: result.failed_instances}
2132
end
2233

23-
#
24-
def prepare_headers(headers)
25-
@headers = Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })]
26-
@headers.merge!(options[:headers_rewrites])
27-
@headers
28-
end
2934

3035
def initialize(resource, model, options)
3136
@resource = resource
3237
@model = model
33-
@options = {batch_size: 1000, validate: true}.merge(options)
3438
@headers = model.respond_to?(:csv_headers) ? model.csv_headers : []
35-
@result= {failed: [], imported: 0}
36-
if @options.has_key?(:col_sep) || @options.has_key?(:row_sep)
37-
ActiveSupport::Deprecation.warn "row_sep and col_sep options are deprecated, use csv_options to override default CSV options"
38-
@csv_options = @options.slice(:col_sep, :row_sep)
39-
else
40-
@csv_options = @options[:csv_options] || {}
41-
end
42-
#override csv options from model if it respond_to csv_options
43-
@csv_options = model.csv_options if model.respond_to?(:csv_options)
44-
@csv_options.reject! {| key, value | value.blank? }
39+
assign_options(options)
40+
end
4541

42+
def import_result
43+
@import_result ||= ImportResult.new
4644
end
4745

4846
def file
@@ -51,11 +49,11 @@ def file
5149

5250
def cycle(lines)
5351
@csv_lines = CSV.parse(lines.join, @csv_options)
54-
@result.merge!(self.store) { |key, val1, val2| val1+val2 }
52+
import_result.add(batch_import, lines.count)
5553
end
5654

5755
def import
58-
options[:before_import].call(self) if options[:before_import].is_a?(Proc)
56+
run_callback(:before_import)
5957
lines = []
6058
batch_size = options[:batch_size].to_i
6159
File.open(file.path) do |f|
@@ -65,14 +63,56 @@ def import
6563
next if line.blank?
6664
lines << line
6765
if lines.size == batch_size || f.eof?
68-
cycle lines
66+
cycle(lines)
6967
lines = []
7068
end
7169
end
7270
end
7371
cycle(lines) unless lines.blank?
74-
options[:after_import].call(self) if options[:after_import].is_a?(Proc)
75-
result
72+
run_callback(:after_import)
73+
import_result
74+
end
75+
76+
def import_options
77+
@import_options ||= options.slice(:validate, :on_duplicate_key_update, :ignore, :timestamps)
78+
end
79+
80+
protected
81+
82+
def prepare_headers(headers)
83+
@headers = Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })]
84+
@headers.merge!(options[:headers_rewrites])
85+
@headers
86+
end
87+
88+
def run_callback(name)
89+
options[name].call(self) if options[name].is_a?(Proc)
90+
end
91+
92+
def batch_import
93+
@resource.transaction do
94+
run_callback(:before_batch_import)
95+
batch_result = resource.import(headers.values, csv_lines, import_options)
96+
run_callback(:after_batch_import)
97+
batch_result
98+
end
99+
end
100+
101+
102+
private
103+
104+
def assign_options(options)
105+
@options = {batch_size: 1000, validate: true}.merge(options.slice(*OPTIONS))
106+
detect_csv_options
107+
end
108+
109+
def detect_csv_options
110+
@csv_options = if model.respond_to?(:csv_options)
111+
model.csv_options
112+
else
113+
options[:csv_options] || {}
114+
end.reject { |_, value| value.blank? }
76115
end
116+
77117
end
78118
end

0 commit comments

Comments
 (0)