active_admin_import 2.0.1 → 2.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/README.md +143 -20
- data/active_admin_import.gemspec +16 -12
- data/app/views/admin/import.html.erb +8 -8
- data/lib/active_admin_import/dsl.rb +40 -59
- data/lib/active_admin_import/importer.rb +34 -31
- data/lib/active_admin_import/model.rb +107 -13
- data/lib/active_admin_import/version.rb +1 -1
- metadata +63 -15
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ODA4MTJlNTI0ODA4ZWUxZDcwODU4NjRhOWI4NzNlNzliNTE2MzliNA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZjkzY2M0MWZiOTk4OGNhZDg4ODllYWJiNjQyYjgyYWNiMWRlNzUzZg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
M2I5NTVkM2Y4NTI5MzgzNGMxNDU5MTYxYjc5NjY4MzU1Yzc5NTVkNjE3MDAx
|
10
|
+
MzQyMmFlMTgyNTRjMGJkNDYzZGFhZTM2MzJlOGRjZDcyNGQzYTFlMDgwMjYz
|
11
|
+
MjlhMjE2NGUwZDU1NjM4ZTNhZDM5ZGFkYTk3NzY3ZThiY2I0NWU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzQzZWNjYTkxYmM1NmE3NjBhNmUzOTIzM2Y2YjZkMWJkZWI1NDg1NTFiMjU4
|
14
|
+
ODM3NzRhNmE1M2FiM2I0OGFmMzM0OGZkNDhjNDE4MjJlNTlkZGY2ZmQzNGRm
|
15
|
+
MTI3ZjlkOTFiMDlmYmFjYjVhN2VjOGFkOGVmMjMwNDlhNDc0MDQ=
|
data/README.md
CHANGED
@@ -1,25 +1,42 @@
|
|
1
|
-
# ActiveAdminImport
|
1
|
+
# ActiveAdminImport
|
2
2
|
The most fastest and efficient CSV import for Active Admin (based on activerecord-import gem)
|
3
3
|
with support of validations and bulk inserts
|
4
4
|
|
5
5
|
|
6
|
-
|
7
|
-
https://github.com/gregbell/active_admin
|
6
|
+
Current master works with AA 0-6-stable branch
|
8
7
|
|
9
|
-
|
8
|
+
#Why yet another import for ActiveAdmin ? Now with activerecord-import ....
|
9
|
+
|
10
|
+
"Because plain-vanilla, out-of-the-box ActiveRecord doesn’t provide support for inserting large amounts of data efficiently"
|
11
|
+
|
12
|
+
cool features of activerecord-import
|
13
|
+
|
14
|
+
activerecord-import can perform validations (fast)
|
15
|
+
activerecord-import can perform on duplicate key updates (requires mysql)
|
10
16
|
|
11
17
|
|
18
|
+
#So active_admin_import features
|
19
|
+
|
20
|
+
Encoding handling
|
21
|
+
Support importing with ZIP file
|
22
|
+
Two step importing (see example2)
|
23
|
+
CSV options
|
24
|
+
Ability to prepend CSV headers automatically
|
25
|
+
Bulk import (activerecord-import)
|
26
|
+
Ability to customize template
|
27
|
+
Callbacks support
|
28
|
+
and more ....
|
29
|
+
|
12
30
|
#Options
|
13
31
|
|
14
32
|
# +back+:: resource action to redirect after processing
|
15
|
-
# +
|
33
|
+
# +csv_options+:: hash with column separator, row separator, etc
|
16
34
|
# +validate+:: true|false, means perfoem validations or not
|
17
35
|
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
|
18
36
|
# +before_import+:: proc for before import action, hook called with importer object
|
19
37
|
# +after_import+:: proc for after import action, hook called with importer object
|
20
38
|
# +before_batch_import+:: proc for before each batch action, called with importer object
|
21
39
|
# +after_batch_import+:: proc for after each batch action, called with importer object
|
22
|
-
# +fetch_extra_options_from_params+:: params available in callbacks ( importer.extra_options proprty hash )
|
23
40
|
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
24
41
|
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
25
42
|
# +ignore+:: true|false, tells activerecord-import toto use MySQL's INSERT IGNORE ability
|
@@ -33,24 +50,23 @@ https://github.com/zdennis/activerecord-import
|
|
33
50
|
|
34
51
|
|
35
52
|
|
36
|
-
Default options values
|
37
|
-
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
53
|
+
#Default options values
|
54
|
+
|
55
|
+
back: {action: :import},
|
56
|
+
csv_options: {},
|
57
|
+
template: "admin/import",
|
58
|
+
fetch_extra_options_from_params: [],
|
59
|
+
resource_class: config.resource_class,
|
60
|
+
resource_label: config.resource_label,
|
61
|
+
plural_resource_label: config.plural_resource_label,
|
45
62
|
|
46
63
|
|
47
|
-
#
|
64
|
+
#Example1
|
48
65
|
|
49
66
|
ActiveAdmin.register Post do
|
50
67
|
active_admin_import :validate => false,
|
51
|
-
:col_sep =>
|
52
|
-
:
|
53
|
-
:before_import => proc{|importer| resource.delete_all},
|
68
|
+
:csv_options => {:col_sep => ";" },
|
69
|
+
:before_import => proc{ Post.delete_all},
|
54
70
|
:batch_size => 1000
|
55
71
|
|
56
72
|
|
@@ -58,6 +74,113 @@ Default options values
|
|
58
74
|
|
59
75
|
|
60
76
|
|
77
|
+
#Example2 Importing to mediate table with insert select operation after import completion
|
78
|
+
|
79
|
+
This config allows to replace data without downtime
|
80
|
+
|
81
|
+
ActiveAdmin.register Post do
|
82
|
+
active_admin_import :validate => false,
|
83
|
+
:csv_options => {:col_sep => ";" },
|
84
|
+
:resource_class => ImportedPost , # we import data into another resource
|
85
|
+
:before_import => proc{ ImportedPost.delete_all },
|
86
|
+
:after_import => proc{
|
87
|
+
Post.transaction do
|
88
|
+
Post.delete_all
|
89
|
+
Post.connection.execute("INSERT INTO posts (SELECT * FROM import_posts)")
|
90
|
+
end
|
91
|
+
},
|
92
|
+
:back => proc { config.namespace.resource_for(Post).route_collection_path } # redirect to post index
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
#Example3 Importing file without headers, but we always know file format, so we can predefine it
|
98
|
+
|
99
|
+
ActiveAdmin.register Post do
|
100
|
+
active_admin_import :validate => true,
|
101
|
+
:template_object => ActiveAdminImport::Model.new(
|
102
|
+
:hint => "file will be imported with such header format: 'body','title','author'",
|
103
|
+
:csv_headers => ["body","title","author"]
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
#Example4 Importing without forcing to UTF-8 and disallow archives
|
109
|
+
|
110
|
+
ActiveAdmin.register Post do
|
111
|
+
active_admin_import :validate => true,
|
112
|
+
:template_object => ActiveAdminImport::Model.new(
|
113
|
+
:hint => "file will be encoded to ISO-8859-1",
|
114
|
+
:force_encoding => "ISO-8859-1",
|
115
|
+
:allow_archive => false
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
#Example5 Callbacks for each bulk insert iteration
|
122
|
+
|
123
|
+
ActiveAdmin.register Post do
|
124
|
+
active_admin_import :validate => true,
|
125
|
+
:before_batch_import => proc { |import|
|
126
|
+
import.file #current file used
|
127
|
+
import.resource #ActiveRecord class to import to
|
128
|
+
import.options # options
|
129
|
+
import.result # result before bulk iteration
|
130
|
+
import.headers # CSV headers
|
131
|
+
import.csv_lines #lines to import
|
132
|
+
import.model #template_object instance
|
133
|
+
},
|
134
|
+
:before_batch_import => proc{ |import|
|
135
|
+
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
#Example6 dynamic CSV options, template overriding
|
141
|
+
|
142
|
+
1) put overrided template to app/views/import.html.erb
|
143
|
+
|
144
|
+
|
145
|
+
<p>
|
146
|
+
<small> <%= raw(@active_admin_import_model.hint) %> </small>
|
147
|
+
</p>
|
148
|
+
<%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
|
149
|
+
<%= f.inputs do %>
|
150
|
+
<%= f.input :file, as: :file %>
|
151
|
+
<% end %>
|
152
|
+
<%= f.inputs "CSV options", :for => [:csv_options, OpenStruct.new(@active_admin_import_model.csv_options)] do |csv| %>
|
153
|
+
<% csv.with_options :input_html => {:style => 'width:40px;'} do |opts| %>
|
154
|
+
<%= opts.input :col_sep %>
|
155
|
+
<%= opts.input :row_sep %>
|
156
|
+
<%= opts.input :quote_char %>
|
157
|
+
<% end %>
|
158
|
+
<% end %>
|
159
|
+
|
160
|
+
<%= f.actions do %>
|
161
|
+
<%= f.action :submit, label: t("active_admin_import.import_btn"), button_html: {disable_with: t("active_admin_import.import_btn_disabled")} %>
|
162
|
+
<% end %>
|
163
|
+
<% end %>
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
2) call method with following parameters
|
168
|
+
|
169
|
+
ActiveAdmin.register Post do
|
170
|
+
active_admin_import :validate => false,
|
171
|
+
:template => 'import' ,
|
172
|
+
:template_object => ActiveAdminImport::Model.new(
|
173
|
+
:hint => "specify CSV options"
|
174
|
+
:csv_options => {:col_sep => ";", :row_sep => nil, :quote_char => nil}
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
#Links
|
180
|
+
https://github.com/gregbell/active_admin
|
181
|
+
|
182
|
+
https://github.com/zdennis/activerecord-import
|
183
|
+
|
61
184
|
#Source Doc
|
62
|
-
http://rubydoc.info/gems/active_admin_import/2.
|
185
|
+
http://rubydoc.info/gems/active_admin_import/2.1.0/
|
63
186
|
|
data/active_admin_import.gemspec
CHANGED
@@ -2,18 +2,22 @@
|
|
2
2
|
require File.expand_path('../lib/active_admin_import/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors
|
6
|
-
gem.email
|
7
|
-
gem.description
|
8
|
-
gem.summary
|
9
|
-
gem.homepage
|
10
|
-
|
11
|
-
gem.files
|
12
|
-
gem.executables
|
13
|
-
gem.test_files
|
14
|
-
gem.name
|
5
|
+
gem.authors = ["Igor Fedoronchuk"]
|
6
|
+
gem.email = ["fedoronchuk@gmail.com"]
|
7
|
+
gem.description = "The most efficient way to import for Active Admin"
|
8
|
+
gem.summary = "ActiveAdmin import based on activerecord-import gem."
|
9
|
+
gem.homepage = "http://github.com/Fivell/active_admin_import"
|
10
|
+
gem.license = 'MIT'
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "active_admin_import"
|
15
15
|
gem.require_paths = ["lib"]
|
16
|
-
gem.version
|
16
|
+
gem.version = ActiveAdminImport::VERSION << '.rc1'
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'activerecord-import', '~> 0.4', '>= 0.4.1'
|
19
|
+
gem.add_runtime_dependency 'activeadmin', '~> 0.6', '>= 0.6.0'
|
20
|
+
gem.add_runtime_dependency 'zip', '~> 1.0', '>= 1.0.0'
|
21
|
+
|
17
22
|
|
18
|
-
gem.add_runtime_dependency('activerecord-import','0.3.0')
|
19
23
|
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
<%= semantic_form_for @active_admin_import_model
|
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
5
|
<%= f.inputs do %>
|
6
|
-
|
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")} %>
|
7
10
|
<% 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
11
|
<% end %>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ActiveAdminImport
|
2
2
|
module DSL
|
3
|
-
|
4
|
-
|
5
3
|
# Declares import functionality
|
6
4
|
#
|
7
5
|
# Options
|
8
6
|
# +back+:: resource action to redirect after processing
|
9
|
-
# +col_sep+:: column separator used for CSV parsing
|
7
|
+
# +col_sep+:: column separator used for CSV parsing (deprecated)
|
8
|
+
# +row_sep+:: column separator used for CSV parsing (deprecated)
|
9
|
+
# +csv_options+:: hash to override default CSV options
|
10
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
12
|
# +before_import+:: proc for before import action, hook called with importer object
|
@@ -15,83 +15,64 @@ module ActiveAdminImport
|
|
15
15
|
# +after_batch_import+:: proc for after each batch action, called with importer object
|
16
16
|
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
17
17
|
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
18
|
-
# +ignore+:: true|false, tells activerecord-import
|
19
|
-
# +fetch_extra_options_from_params+:: params values available in callbacks in importer.extra_options hash
|
18
|
+
# +ignore+:: true|false, tells activerecord-import to use MySQL's INSERT IGNORE ability
|
20
19
|
# +template+:: custom template rendering
|
21
20
|
# +template_object+:: object passing to view
|
22
|
-
# +resource_class+:: resource class name
|
23
|
-
# +resource_label+:: resource label value
|
21
|
+
# +resource_class+:: resource class name, override to import to another table (default config.resource_class)
|
22
|
+
# +resource_label+:: resource label value (default config.resource_label)
|
23
|
+
# +plural_resource_label+:: plaralized resource label value (default config.plural_resource_label)
|
24
24
|
#
|
25
25
|
def active_admin_import options = {}
|
26
26
|
default_options = {
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
|
36
|
-
|
27
|
+
back: {action: :import},
|
28
|
+
csv_options: {},
|
29
|
+
template: "admin/import",
|
30
|
+
fetch_extra_options_from_params: [],
|
31
|
+
resource_class: config.resource_class,
|
32
|
+
resource_label: config.resource_label,
|
33
|
+
plural_resource_label: config.plural_resource_label,
|
34
|
+
headers_rewrites: {}
|
37
35
|
}
|
38
|
-
options = default_options.
|
36
|
+
options = default_options.deep_merge(options)
|
37
|
+
options[:template_object] = ActiveAdminImport::Model.new if options[:template_object].blank?
|
39
38
|
params_key = ActiveModel::Naming.param_key(options[:template_object])
|
40
39
|
|
41
|
-
|
42
|
-
collection_action :import, :method => :get do
|
40
|
+
collection_action :import, method: :get do
|
43
41
|
@active_admin_import_model = options[:template_object]
|
44
|
-
render :
|
42
|
+
render template: options[:template]
|
45
43
|
end
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
link_to(I18n.t('active_admin_import.import_model',
|
50
|
-
:model => (options[:resource_label] || active_admin_config.resource_name)),
|
51
|
-
:action => 'import')
|
45
|
+
action_item only: :index do
|
46
|
+
link_to(I18n.t('active_admin_import.import_model', model: options[:resource_label]), action: 'import')
|
52
47
|
end
|
53
48
|
|
49
|
+
collection_action :do_import, method: :post do
|
54
50
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
71
|
-
end
|
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])
|
51
|
+
@active_admin_import_model = options[:template_object]
|
52
|
+
@active_admin_import_model.assign_attributes(params[params_key].try(:deep_symbolize_keys) || {})
|
53
|
+
#go back to form
|
54
|
+
return render template: options[:template] unless @active_admin_import_model.valid?
|
55
|
+
|
56
|
+
importer = Importer.new(options[:resource_class],
|
57
|
+
@active_admin_import_model,
|
58
|
+
options
|
77
59
|
)
|
78
60
|
result = importer.import
|
79
|
-
|
61
|
+
model_name = options[:resource_label].downcase
|
62
|
+
plural_model_name = options[:resource_label].downcase
|
80
63
|
flash[:notice] = I18n.t('active_admin_import.imported',
|
81
|
-
:
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
) if result[:imported].to_i > 0
|
64
|
+
count: result[:imported].to_i,
|
65
|
+
model: model_name,
|
66
|
+
plural_model: plural_model_name
|
67
|
+
) if result[:imported].to_i > 0
|
86
68
|
|
87
69
|
flash[:error] = I18n.t('active_admin_import.failed',
|
88
|
-
:
|
89
|
-
|
90
|
-
|
91
|
-
|
70
|
+
count: result[:failed].count,
|
71
|
+
model: model_name,
|
72
|
+
plural_model: plural_model_name
|
92
73
|
) if result[:failed].count > 0
|
93
74
|
|
94
|
-
redirect_to
|
75
|
+
redirect_to options[:back]
|
95
76
|
end
|
96
77
|
|
97
78
|
end
|
@@ -2,65 +2,68 @@ require 'csv'
|
|
2
2
|
module ActiveAdminImport
|
3
3
|
class Importer
|
4
4
|
|
5
|
-
attr_reader :resource, :
|
6
|
-
:cycle_data, :headers , :csv_lines
|
5
|
+
attr_reader :resource, :options, :result, :headers, :csv_lines, :model
|
7
6
|
|
8
7
|
def store
|
9
8
|
result = @resource.transaction do
|
10
9
|
options[:before_batch_import].call(self) if options[:before_batch_import].is_a?(Proc)
|
11
10
|
|
12
|
-
|
13
11
|
result = resource.import headers.values, csv_lines, {
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
12
|
+
validate: options[:validate],
|
13
|
+
on_duplicate_key_update: options[:on_duplicate_key_update],
|
14
|
+
ignore: options[:ignore],
|
15
|
+
timestamps: options[:timestamps]
|
18
16
|
}
|
19
17
|
options[:after_batch_import].call(self) if options[:after_batch_import].is_a?(Proc)
|
20
18
|
result
|
21
19
|
end
|
22
|
-
{:
|
20
|
+
{imported: csv_lines.count - result.failed_instances.count, failed: result.failed_instances}
|
23
21
|
end
|
24
22
|
|
23
|
+
#
|
25
24
|
def prepare_headers(headers)
|
26
|
-
@headers =
|
25
|
+
@headers = Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })]
|
27
26
|
@headers.merge!(options[:headers_rewrites])
|
28
27
|
@headers
|
29
28
|
end
|
30
29
|
|
31
|
-
def initialize
|
30
|
+
def initialize(resource, model, options)
|
32
31
|
@resource = resource
|
33
|
-
@
|
34
|
-
@options = {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
45
|
-
@csv_options
|
32
|
+
@model = model
|
33
|
+
@options = {batch_size: 1000, validate: true}.merge(options)
|
34
|
+
@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? }
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def file
|
49
|
+
@model.file
|
46
50
|
end
|
47
51
|
|
48
52
|
def cycle(lines)
|
49
|
-
|
50
|
-
|
53
|
+
@csv_lines = CSV.parse(lines.join, @csv_options)
|
54
|
+
@result.merge!(self.store) { |key, val1, val2| val1+val2 }
|
51
55
|
end
|
52
56
|
|
53
57
|
def import
|
54
58
|
options[:before_import].call(self) if options[:before_import].is_a?(Proc)
|
55
59
|
lines = []
|
56
60
|
batch_size = options[:batch_size].to_i
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
else
|
61
|
+
File.open(file.path) do |f|
|
62
|
+
# capture headers if not exist
|
63
|
+
prepare_headers(headers.any? ? headers : CSV.parse(f.readline, @csv_options).first)
|
64
|
+
f.each_line do |line|
|
62
65
|
lines << line
|
63
|
-
if lines.size
|
66
|
+
if lines.size == batch_size || f.eof?
|
64
67
|
cycle lines
|
65
68
|
lines = []
|
66
69
|
end
|
@@ -2,26 +2,120 @@ module ActiveAdminImport
|
|
2
2
|
class Model
|
3
3
|
extend ActiveModel::Naming
|
4
4
|
include ActiveModel::Conversion
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include ActiveModel::Validations::Callbacks
|
7
|
+
|
8
|
+
validates :file, presence: {message: Proc.new { I18n.t('active_admin_import.no_file_error') }},
|
9
|
+
unless: proc { |me| me.new_record? }
|
10
|
+
|
11
|
+
validate :correct_content_type
|
12
|
+
|
13
|
+
before_validation :uncompress_file, if: proc { |me| me.archive? && me.allow_archive? }
|
14
|
+
before_validation :encode_file, if: proc { |me| me.force_encoding? && me.file.present? }
|
15
|
+
|
8
16
|
attr_reader :attributes
|
9
|
-
|
10
|
-
def initialize(
|
11
|
-
|
12
|
-
@attributes =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
def initialize(args={})
|
19
|
+
@new_record = true
|
20
|
+
@attributes = {}
|
21
|
+
assign_attributes(default_attributes.merge(args), true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def assign_attributes(args = {}, new_record = false)
|
25
|
+
@attributes.merge!(args)
|
26
|
+
@new_record = new_record
|
27
|
+
args.keys.each do |key|
|
28
|
+
key = key.to_sym
|
29
|
+
#generate methods for instance object by attributes
|
30
|
+
singleton_class.class_eval do
|
31
|
+
define_method(key) { self.attributes[key] } unless method_defined? key
|
32
|
+
define_method("#{key}=") { |new_value| @attributes[key] = new_value } unless method_defined? "#{key}="
|
19
33
|
end
|
34
|
+
end if args.is_a?(Hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_attribute_for_validation(key)
|
38
|
+
@attributes[key.to_sym]
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_attributes
|
42
|
+
{hint: '', file: nil, csv_headers: [], allow_archive: true, force_encoding: 'UTF-8'}
|
43
|
+
end
|
44
|
+
|
45
|
+
def allow_archive?
|
46
|
+
!!@attributes[:allow_archive]
|
47
|
+
end
|
48
|
+
|
49
|
+
def new_record?
|
50
|
+
!!@new_record
|
51
|
+
end
|
52
|
+
|
53
|
+
def force_encoding?
|
54
|
+
!!@attributes[:force_encoding]
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_hash
|
58
|
+
@attributes
|
20
59
|
end
|
21
60
|
|
22
61
|
def persisted?
|
23
62
|
false
|
24
63
|
end
|
25
64
|
|
65
|
+
def archive?
|
66
|
+
file_type == 'application/zip'
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def file_path
|
72
|
+
if file.is_a? ActionDispatch::Http::UploadedFile
|
73
|
+
file.tempfile.path
|
74
|
+
else
|
75
|
+
file.path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def encode_file
|
80
|
+
data = File.read(file_path).encode(force_encoding, invalid: :replace, undef: :replace)
|
81
|
+
File.open(file_path, 'w') do |f|
|
82
|
+
f.write(data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def uncompress_file
|
87
|
+
Zip::ZipFile.open(file_path) do |zip_file|
|
88
|
+
self.file = Tempfile.new("active-admin-import-unzipped")
|
89
|
+
data = zip_file.entries.select { |f| f.file? }.first.get_input_stream.read
|
90
|
+
data = data.encode(force_encoding, invalid: :replace, undef: :replace) if self.force_encoding?
|
91
|
+
self.file << data
|
92
|
+
self.file.close
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def csv_allowed_types
|
98
|
+
[
|
99
|
+
'text/csv',
|
100
|
+
'text/x-csv',
|
101
|
+
'text/comma-separated-values',
|
102
|
+
'application/csv',
|
103
|
+
'application/vnd.ms-excel',
|
104
|
+
'application/vnd.msexcel'
|
105
|
+
]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def correct_content_type
|
110
|
+
unless file.blank? || file.is_a?(Tempfile)
|
111
|
+
errors.add(:file, I18n.t('active_admin_import.file_format_error')) unless csv_allowed_types.include? file_type
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def file_type
|
116
|
+
file.try(:content_type).try(:chomp)
|
117
|
+
end
|
26
118
|
end
|
27
119
|
end
|
120
|
+
|
121
|
+
|
metadata
CHANGED
@@ -1,27 +1,75 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_admin_import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
5
|
-
prerelease:
|
4
|
+
version: 2.1.0.rc1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Igor Fedoronchuk
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-01-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activerecord-import
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
19
|
+
version: '0.4'
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.4.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.4'
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.4.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activeadmin
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.6'
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.6.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ~>
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.6'
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.6.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: zip
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.0'
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.0.0
|
22
63
|
type: :runtime
|
23
64
|
prerelease: false
|
24
|
-
version_requirements:
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.0'
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.0.0
|
25
73
|
description: The most efficient way to import for Active Admin
|
26
74
|
email:
|
27
75
|
- fedoronchuk@gmail.com
|
@@ -45,28 +93,28 @@ files:
|
|
45
93
|
- lib/active_admin_import/model.rb
|
46
94
|
- lib/active_admin_import/version.rb
|
47
95
|
homepage: http://github.com/Fivell/active_admin_import
|
48
|
-
licenses:
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
49
99
|
post_install_message:
|
50
100
|
rdoc_options: []
|
51
101
|
require_paths:
|
52
102
|
- lib
|
53
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
104
|
requirements:
|
56
105
|
- - ! '>='
|
57
106
|
- !ruby/object:Gem::Version
|
58
107
|
version: '0'
|
59
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
109
|
requirements:
|
62
|
-
- - ! '
|
110
|
+
- - ! '>'
|
63
111
|
- !ruby/object:Gem::Version
|
64
|
-
version:
|
112
|
+
version: 1.3.1
|
65
113
|
requirements: []
|
66
114
|
rubyforge_project:
|
67
|
-
rubygems_version:
|
115
|
+
rubygems_version: 2.2.0
|
68
116
|
signing_key:
|
69
|
-
specification_version:
|
117
|
+
specification_version: 4
|
70
118
|
summary: ActiveAdmin import based on activerecord-import gem.
|
71
119
|
test_files: []
|
72
120
|
has_rdoc:
|