active_admin_import 1.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +11 -5
- data/active_admin_import.gemspec +1 -2
- data/app/views/admin/import.html.erb +11 -7
- data/lib/active_admin_import.rb +10 -1
- data/lib/active_admin_import/dsl.rb +66 -22
- data/lib/active_admin_import/importer.rb +22 -15
- data/lib/active_admin_import/locales/en.yml +15 -0
- data/lib/active_admin_import/locales/it.yml +15 -0
- data/lib/active_admin_import/model.rb +27 -0
- data/lib/active_admin_import/version.rb +1 -1
- metadata +8 -16
data/README.md
CHANGED
@@ -14,14 +14,20 @@ https://github.com/zdennis/activerecord-import
|
|
14
14
|
|
15
15
|
# +back+:: resource action to redirect after processing
|
16
16
|
# +col_sep+:: column separator used for CSV parsing
|
17
|
-
# +validate+::
|
17
|
+
# +validate+:: true|false, means perfoem validations or not
|
18
18
|
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
|
19
|
-
# +before_import+:: proc for before import action, called with
|
20
|
-
# +
|
21
|
-
# +
|
19
|
+
# +before_import+:: proc for before import action, hook called with importer object
|
20
|
+
# +after_import+:: proc for after import action, hook called with importer object
|
21
|
+
# +before_batch_import+:: proc for before each batch action, called with importer object
|
22
|
+
# +after_batch_import+:: proc for after each batch action, called with importer object
|
22
23
|
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
23
24
|
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
24
25
|
# +ignore+:: true|false, tells activerecord-import toto use MySQL's INSERT IGNORE ability
|
26
|
+
# +params_keys+:: params values available in callbacks
|
27
|
+
# +template+:: custom template rendering
|
28
|
+
# +locals+:: local variables for template
|
29
|
+
# +resource_class+:: resource class name
|
30
|
+
# +resource_label+:: resource label value
|
25
31
|
|
26
32
|
|
27
33
|
#Example
|
@@ -30,7 +36,7 @@ https://github.com/zdennis/activerecord-import
|
|
30
36
|
active_admin_import :validate => false,
|
31
37
|
:col_sep => ',',
|
32
38
|
:back => :index ,
|
33
|
-
:before_import => proc{|
|
39
|
+
:before_import => proc{|importer| resource.delete_all},
|
34
40
|
:batch_size => 1000
|
35
41
|
|
36
42
|
|
data/active_admin_import.gemspec
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('../lib/active_admin_import/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Igor Fedoronchuk"]
|
6
6
|
gem.email = ["fedoronchuk@gmail.com"]
|
7
|
-
gem.description = "
|
7
|
+
gem.description = "The most efficient way to import for Active Admin"
|
8
8
|
gem.summary = "ActiveAdmin import based on activerecord-import gem."
|
9
9
|
gem.homepage = "http://github.com/Fivell/active_admin_import"
|
10
10
|
|
@@ -15,6 +15,5 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = ActiveAdminImport::VERSION
|
17
17
|
|
18
|
-
gem.add_runtime_dependency('chronic')
|
19
18
|
gem.add_runtime_dependency('activerecord-import','0.3.0')
|
20
19
|
end
|
@@ -1,7 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
<p>
|
2
|
+
<small> <%= raw(@active_admin_import_model.hint) %> </small>
|
3
|
+
</p>
|
4
|
+
<%= semantic_form_for @active_admin_import_model , :url => {:action => :do_import}, :html => {:multipart => true} do |f| %>
|
5
|
+
<%= f.inputs do %>
|
6
|
+
<%= f.input :file, :as=>:file %>
|
7
|
+
<% end %>
|
8
|
+
<%= f.actions do %>
|
9
|
+
<%= f.action :submit , :label =>t("active_admin_import.import_btn") ,:button_html => { :disable_with => t("active_admin_import.import_btn_disabled") }%>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
data/lib/active_admin_import.rb
CHANGED
@@ -3,5 +3,14 @@ require 'active_admin_import/version'
|
|
3
3
|
require 'active_admin_import/engine'
|
4
4
|
require 'active_admin_import/dsl'
|
5
5
|
require 'active_admin_import/importer'
|
6
|
-
|
6
|
+
require 'active_admin_import/model'
|
7
7
|
::ActiveAdmin::DSL.send(:include, ActiveAdminImport::DSL)
|
8
|
+
|
9
|
+
module ActiveAdminImport
|
10
|
+
class Railtie < ::Rails::Railtie
|
11
|
+
config.after_initialize do
|
12
|
+
require 'active_support/i18n'
|
13
|
+
I18n.load_path.unshift *Dir[File.expand_path('../active_admin_import/locales/*.yml', __FILE__)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -7,45 +7,89 @@ module ActiveAdminImport
|
|
7
7
|
# Options
|
8
8
|
# +back+:: resource action to redirect after processing
|
9
9
|
# +col_sep+:: column separator used for CSV parsing
|
10
|
-
# +validate+::
|
10
|
+
# +validate+:: true|false, means perfoem validations or not
|
11
11
|
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
|
12
|
-
# +before_import+:: proc for before import action, called with
|
13
|
-
# +
|
14
|
-
# +
|
12
|
+
# +before_import+:: proc for before import action, hook called with importer object
|
13
|
+
# +after_import+:: proc for after import action, hook called with importer object
|
14
|
+
# +before_batch_import+:: proc for before each batch action, called with importer object
|
15
|
+
# +after_batch_import+:: proc for after each batch action, called with importer object
|
15
16
|
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
16
17
|
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
17
18
|
# +ignore+:: true|false, tells activerecord-import toto use MySQL's INSERT IGNORE ability
|
19
|
+
# +fetch_extra_options_from_params+:: params values available in callbacks in importer.extra_options hash
|
20
|
+
# +template+:: custom template rendering
|
21
|
+
# +locals+:: local variables for template
|
22
|
+
# +resource_class+:: resource class name
|
23
|
+
# +resource_label+:: resource label value
|
24
|
+
#
|
18
25
|
def active_admin_import options = {}
|
19
26
|
default_options = {
|
20
|
-
|
21
|
-
|
27
|
+
:back => :import,
|
28
|
+
:col_sep => ',',
|
29
|
+
:template => "admin/import",
|
30
|
+
:template_object => ActiveAdminImport::Model.new,
|
31
|
+
:fetch_extra_options_from_params => [],
|
32
|
+
:resource_class => nil,
|
33
|
+
:resource_label => nil,
|
34
|
+
:headers_rewrites => {}
|
35
|
+
|
36
|
+
|
22
37
|
}
|
23
38
|
options = default_options.merge(options)
|
24
|
-
|
25
|
-
|
26
|
-
end
|
39
|
+
params_key = ActiveModel::Naming.param_key(options[:template_object])
|
40
|
+
|
27
41
|
|
28
42
|
collection_action :import, :method => :get do
|
29
|
-
|
43
|
+
@active_admin_import_model = options[:template_object]
|
44
|
+
render :template => options[:template]
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
action_item :only => :index do
|
49
|
+
link_to(I18n.t('active_admin_import.import_model',
|
50
|
+
:model => (options[:resource_label] || active_admin_config.resource_name)),
|
51
|
+
:action => 'import')
|
30
52
|
end
|
31
53
|
|
54
|
+
|
32
55
|
collection_action :do_import, :method => :post do
|
33
|
-
if params[
|
34
|
-
flash[:alert] =
|
35
|
-
return redirect_to :
|
56
|
+
if params[params_key].blank?
|
57
|
+
flash[:alert] = I18n.t('active_admin_import.no_file_error')
|
58
|
+
return redirect_to :back
|
36
59
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
60
|
+
content_types_allow = [
|
61
|
+
'text/csv',
|
62
|
+
'text/x-csv',
|
63
|
+
'text/comma-separated-values',
|
64
|
+
'application/csv',
|
65
|
+
'application/vnd.ms-excel',
|
66
|
+
'application/vnd.msexcel'
|
67
|
+
]
|
68
|
+
unless params[params_key]['file'].try(:content_type) && params[params_key]['file'].content_type.in?(content_types_allow)
|
69
|
+
flash[:alert] = I18n.t('active_admin_import.file_format_error')
|
70
|
+
return redirect_to :back
|
40
71
|
end
|
41
|
-
importer = Importer.new( active_admin_config.resource_class, params[:import][:file], options)
|
42
72
|
|
73
|
+
importer = Importer.new((options[:resource_class] || active_admin_config.resource_class),
|
74
|
+
params[params_key][:file],
|
75
|
+
options,
|
76
|
+
params[params_key].to_hash.slice(*options[:fetch_extra_options_from_params])
|
77
|
+
)
|
78
|
+
result = importer.import
|
79
|
+
|
80
|
+
flash[:notice] = I18n.t('active_admin_import.imported',
|
81
|
+
:count=> result[:imported].to_i,
|
82
|
+
:model => (options[:resource_label] || active_admin_config.resource_label).downcase,
|
83
|
+
:plural_model => (options[:resource_label].present? ? options[:resource_label].to_s.pluralize : active_admin_config.plural_resource_label).downcase
|
84
|
+
|
85
|
+
) if result[:imported].to_i > 0
|
86
|
+
|
87
|
+
flash[:error] = I18n.t('active_admin_import.failed',
|
88
|
+
:count=> result[:failed].count,
|
89
|
+
:model => (options[:resource_label] || active_admin_config.resource_label).downcase,
|
90
|
+
:plural_model => (options[:resource_label].present? ? options[:resource_label].to_s.pluralize : active_admin_config.plural_resource_label).downcase
|
43
91
|
|
44
|
-
|
45
|
-
flash[:notice] = "#{view_context.pluralize(result[:imported].to_i,active_admin_config.resource_name)} was imported"
|
46
|
-
unless result[:failed].count == 0
|
47
|
-
flash[:error] = "#{view_context.pluralize(result[:failed].count,active_admin_config.resource_name)} was failed to imported"
|
48
|
-
end
|
92
|
+
) if result[:failed].count > 0
|
49
93
|
|
50
94
|
redirect_to :action => options[:back]
|
51
95
|
end
|
@@ -2,28 +2,33 @@ require 'csv'
|
|
2
2
|
module ActiveAdminImport
|
3
3
|
class Importer
|
4
4
|
|
5
|
-
attr_reader :resource, :file, :options , :result
|
5
|
+
attr_reader :resource, :file, :options , :extra_options , :result ,
|
6
|
+
:cycle_data, :headers , :csv_lines
|
6
7
|
|
7
|
-
def store
|
8
|
+
def store
|
8
9
|
result = @resource.transaction do
|
9
|
-
options[:before_batch_import].call(
|
10
|
-
|
10
|
+
options[:before_batch_import].call(self) if options[:before_batch_import].is_a?(Proc)
|
11
|
+
|
12
|
+
|
13
|
+
result = resource.import headers.values, csv_lines, {
|
11
14
|
:validate => options[:validate],
|
12
15
|
:on_duplicate_key_update => options[:on_duplicate_key_update],
|
13
16
|
:ignore => options[:ignore],
|
14
17
|
:timestamps => options[:timestamps]
|
15
18
|
}
|
16
|
-
options[:after_batch_import].call(
|
19
|
+
options[:after_batch_import].call(self) if options[:after_batch_import].is_a?(Proc)
|
17
20
|
result
|
18
21
|
end
|
19
|
-
{:imported =>
|
22
|
+
{:imported => csv_lines.count - result.failed_instances.count , :failed => result.failed_instances}
|
20
23
|
end
|
21
24
|
|
22
25
|
def prepare_headers(headers)
|
23
|
-
Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })]
|
26
|
+
@headers = Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })]
|
27
|
+
@headers.merge!(options[:headers_rewrites])
|
28
|
+
@headers
|
24
29
|
end
|
25
30
|
|
26
|
-
def initialize resource, file, options
|
31
|
+
def initialize resource, file, options , extra_options = nil
|
27
32
|
@resource = resource
|
28
33
|
@file = file
|
29
34
|
@options = {
|
@@ -36,20 +41,22 @@ module ActiveAdminImport
|
|
36
41
|
:failed => [],
|
37
42
|
:imported => 0
|
38
43
|
}
|
44
|
+
@extra_options = extra_options
|
45
|
+
@csv_options = @options.slice(:col_sep, :row_sep)
|
39
46
|
end
|
40
47
|
|
41
48
|
def cycle(lines)
|
42
|
-
|
43
|
-
@result.merge!(self.store
|
49
|
+
@csv_lines = CSV.parse(lines.join, @csv_options)
|
50
|
+
@result.merge!(self.store){|key,val1,val2| val1+val2}
|
44
51
|
end
|
45
52
|
|
46
53
|
def import
|
47
|
-
options[:before_import].call(
|
54
|
+
options[:before_import].call(self) if options[:before_import].is_a?(Proc)
|
48
55
|
lines = []
|
49
|
-
batch_size =
|
56
|
+
batch_size = options[:batch_size].to_i
|
50
57
|
IO.foreach(file.path) do |line|
|
51
|
-
if
|
52
|
-
|
58
|
+
if headers.empty?
|
59
|
+
prepare_headers(CSV.parse(line, @csv_options).first)
|
53
60
|
else
|
54
61
|
lines << line
|
55
62
|
if lines.size >= batch_size
|
@@ -59,7 +66,7 @@ module ActiveAdminImport
|
|
59
66
|
end
|
60
67
|
end
|
61
68
|
cycle(lines) unless lines.blank?
|
62
|
-
options[:after_import].call(
|
69
|
+
options[:after_import].call(self) if options[:after_import].is_a?(Proc)
|
63
70
|
result
|
64
71
|
end
|
65
72
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
en:
|
2
|
+
active_admin:
|
3
|
+
import: "Import"
|
4
|
+
active_admin_import:
|
5
|
+
file_format_error: "You can import only valid csv file"
|
6
|
+
no_file_error: "Please, select file to import"
|
7
|
+
imported:
|
8
|
+
one: "Successfully imported 1 %{model}"
|
9
|
+
other: "Successfully imported %{count} %{plural_model}"
|
10
|
+
failed:
|
11
|
+
one: "Failed to import 1 %{model}"
|
12
|
+
other: "Failed to import %{count} %{plural_model}"
|
13
|
+
import_model: "Import %{model}"
|
14
|
+
import_btn: "Import"
|
15
|
+
import_btn_disabled: "Wait..."
|
@@ -0,0 +1,15 @@
|
|
1
|
+
it:
|
2
|
+
active_admin:
|
3
|
+
import: "Importa"
|
4
|
+
active_admin_import:
|
5
|
+
file_format_error: "Puoi importare solo CSV conformi"
|
6
|
+
no_file_error: "Per favore, seleziona un file da importare"
|
7
|
+
imported:
|
8
|
+
one: "Importato 1 %{model} con successo"
|
9
|
+
other: "Importati %{count} %{plural_model} con successo"
|
10
|
+
failed:
|
11
|
+
one: "Importazione di 1 %{model} fallita"
|
12
|
+
other: "Importazione di %{count} %{plural_model} fallita"
|
13
|
+
import_model: "Importa %{model}"
|
14
|
+
import_btn: "Importa"
|
15
|
+
import_btn_disabled: "In attesa..."
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveAdminImport
|
2
|
+
class Model
|
3
|
+
extend ActiveModel::Naming
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
|
6
|
+
attr_accessor :file
|
7
|
+
attr_accessor :hint
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
def initialize(attributes={})
|
11
|
+
self.hint= attributes.delete(:hint)
|
12
|
+
@attributes = attributes
|
13
|
+
@attributes.each do |key,value|
|
14
|
+
#generate methods for instance object by attributes
|
15
|
+
singleton_class.class_eval do
|
16
|
+
define_method(key) { self.attributes[key] } unless method_defined? key
|
17
|
+
define_method("#{key}=") { |new_value| @attributes[key] = new_value } unless method_defined? "#{key}="
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def persisted?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_admin_import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: chronic
|
16
|
-
requirement: &70313477456520 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70313477456520
|
25
14
|
- !ruby/object:Gem::Dependency
|
26
15
|
name: activerecord-import
|
27
|
-
requirement: &
|
16
|
+
requirement: &70124414344240 !ruby/object:Gem::Requirement
|
28
17
|
none: false
|
29
18
|
requirements:
|
30
19
|
- - =
|
@@ -32,8 +21,8 @@ dependencies:
|
|
32
21
|
version: 0.3.0
|
33
22
|
type: :runtime
|
34
23
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
description:
|
24
|
+
version_requirements: *70124414344240
|
25
|
+
description: The most efficient way to import for Active Admin
|
37
26
|
email:
|
38
27
|
- fedoronchuk@gmail.com
|
39
28
|
executables: []
|
@@ -51,6 +40,9 @@ files:
|
|
51
40
|
- lib/active_admin_import/dsl.rb
|
52
41
|
- lib/active_admin_import/engine.rb
|
53
42
|
- lib/active_admin_import/importer.rb
|
43
|
+
- lib/active_admin_import/locales/en.yml
|
44
|
+
- lib/active_admin_import/locales/it.yml
|
45
|
+
- lib/active_admin_import/model.rb
|
54
46
|
- lib/active_admin_import/version.rb
|
55
47
|
homepage: http://github.com/Fivell/active_admin_import
|
56
48
|
licenses: []
|