active_admin_import 2.1.2 → 3.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +4 -0
  3. data/.travis.yml +9 -0
  4. data/Gemfile +16 -0
  5. data/README.md +73 -173
  6. data/Rakefile +7 -2
  7. data/active_admin_import.gemspec +5 -3
  8. data/app/views/admin/import.html.erb +1 -1
  9. data/config/locales/en.yml +11 -4
  10. data/config/locales/es.yml +18 -0
  11. data/config/locales/it.yml +3 -1
  12. data/config/locales/zh-CN.yml +24 -0
  13. data/lib/active_admin_import/dsl.rb +39 -31
  14. data/lib/active_admin_import/import_result.rb +39 -0
  15. data/lib/active_admin_import/importer.rb +91 -44
  16. data/lib/active_admin_import/model.rb +80 -29
  17. data/lib/active_admin_import/options.rb +44 -0
  18. data/lib/active_admin_import/version.rb +1 -1
  19. data/lib/active_admin_import.rb +3 -0
  20. data/spec/fixtures/files/author.csv +2 -0
  21. data/spec/fixtures/files/author_broken_header.csv +2 -0
  22. data/spec/fixtures/files/author_invalid.csv +2 -0
  23. data/spec/fixtures/files/authors.csv +3 -0
  24. data/spec/fixtures/files/authors_bom.csv +3 -0
  25. data/spec/fixtures/files/authors_invalid_db.csv +3 -0
  26. data/spec/fixtures/files/authors_invalid_model.csv +3 -0
  27. data/spec/fixtures/files/authors_no_headers.csv +2 -0
  28. data/spec/fixtures/files/authors_win1251_win_endline.csv +3 -0
  29. data/spec/fixtures/files/authors_with_ids.csv +3 -0
  30. data/spec/fixtures/files/authors_with_semicolons.csv +3 -0
  31. data/spec/fixtures/files/empty.csv +0 -0
  32. data/spec/fixtures/files/only_headers.csv +1 -0
  33. data/spec/fixtures/files/posts.csv +4 -0
  34. data/spec/fixtures/files/posts_for_author.csv +3 -0
  35. data/spec/fixtures/files/posts_for_author_no_headers.csv +2 -0
  36. data/spec/import_result_spec.rb +32 -0
  37. data/spec/import_spec.rb +432 -0
  38. data/spec/model_spec.rb +5 -0
  39. data/spec/spec_helper.rb +72 -0
  40. data/spec/support/active_model_lint.rb +14 -0
  41. data/spec/support/admin.rb +20 -0
  42. data/spec/support/rails_template.rb +29 -0
  43. data/tasks/test.rake +6 -0
  44. metadata +80 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8f417cc9a85210d9b683c9153e081542a882820
4
- data.tar.gz: d91324749b99737df1100043f3d33aeeb1b81e25
3
+ metadata.gz: 46875662660bbc61d8dce4fa218234229d0c94d7
4
+ data.tar.gz: ba7347ef7348492d137e459e52a3430d5607f051
5
5
  SHA512:
6
- metadata.gz: afe63356cf3a67b25940824541b1e2dc4c7ae752fa7f93ac9d1e4a2403f2f963c0c4f081cc91d1cdf69e3c7e796e7ce4165dee9e8b6f31d762ed18209f45beba
7
- data.tar.gz: a3bd48e057590d3c85c01738f2399ae578153fdd2661eaba3f6124e3b5c87cbbab6ca2dbd50743cbdc377cb2b6a1aebc313d3a8cd0456db086e34a036b8b2f1d
6
+ metadata.gz: 390733a4c6297317e6ab84c0528d05a65e93b422c1ac84961cd38f302a3f859d5303852a937dbf88762e599a8b5d4d845f8597cdcfc50a9ed3f43e1125d86192
7
+ data.tar.gz: 459e49db7a02bdca7c5fa53d9cad1b7b8e2937ff2e223b479ee7551a8cd9b7e6993f5b4968c1a5c86e33f206467075afd9d7c7bb7c868906f4af050cae2b2754
data/.hound.yml ADDED
@@ -0,0 +1,4 @@
1
+ ruby:
2
+ enabled: true
3
+ StringLiterals:
4
+ EnforcedStyle: single_quotes
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ script: bundle exec rspec spec
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.5
6
+ - 2.2.0
7
+ before_install:
8
+ - gem update --system
9
+ - gem --version
data/Gemfile CHANGED
@@ -2,3 +2,19 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in active_admin_importable.gemspec
4
4
  gemspec
5
+ group :test do
6
+ gem 'rails', '4.1.9'
7
+ gem 'rspec-rails'
8
+ gem 'activeadmin', github: 'activeadmin' , ref: '54bede0558a99ab759f98f9b24e1b0144063a81e'
9
+
10
+ gem 'coveralls', require: false # Test coverage website. Go to https://coveralls.io
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
data/README.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # ActiveAdminImport
2
- The most fastest and efficient CSV import for Active Admin (based on activerecord-import gem)
3
- with support of validations and bulk inserts
2
+ [The most fastest and efficient CSV import for Active Admin
3
+ with support of validations, bulk inserts and encodings handling](http://activeadmin-plugins.github.io/active_admin_import/)
4
+
5
+
6
+
7
+ [![Build Status](https://img.shields.io/travis/activeadmin-plugins/active_admin_import.svg)](https://travis-ci.org/activeadmin-pluginsl/active_admin_import)
8
+ [![Dependency Status](http://img.shields.io/gemnasium/activeadmin-plugins/active_admin_import.svg)](https://gemnasium.com/activeadmin-plugins/active_admin_import)
9
+ [![Coverage Status](https://coveralls.io/repos/activeadmin-plugins/active_admin_import/badge.svg)](https://coveralls.io/r/activeadmin-plugins/active_admin_import)
4
10
 
11
+ [![Code Climate](http://img.shields.io/codeclimate/github/activeadmin-plugins/active_admin_import.svg)](https://codeclimate.com/github/activeadmin-plugins/active_admin_import)
12
+ [![Gem Version](http://img.shields.io/gem/v/active_admin_import.svg)](https://rubygems.org/gems/active_admin_import)
13
+ [![License](http://img.shields.io/:license-mit-blue.svg)](http://Fivell.mit-license.org)
5
14
 
6
- Should work with both AA 0.6 and 1.0.0
15
+ master can be used with AA 1.0.0 and Rails >= 4.1
7
16
 
8
17
 
9
18
  #Installation
@@ -11,203 +20,94 @@ Should work with both AA 0.6 and 1.0.0
11
20
  Add this line to your application's Gemfile:
12
21
 
13
22
  ```ruby
14
- gem "active_admin_import" , '2.1.1'
15
- ```
16
-
17
- And then execute:
18
-
19
- $ bundle
20
-
21
-
23
+ gem "active_admin_import" , '2.1.2'
22
24
 
23
- Why yet another import for ActiveAdmin ? Now with activerecord-import ....
24
-
25
- "Because plain-vanilla, out-of-the-box ActiveRecord doesn’t provide support for inserting large amounts of data efficiently"
26
-
27
- cool features of activerecord-import
28
-
29
- activerecord-import can perform validations (fast)
30
- activerecord-import can perform on duplicate key updates (requires mysql)
31
-
32
-
33
- So active_admin_import features
25
+ ```
26
+ or
34
27
 
35
- Encoding handling
36
- Two step importing (see example2)
37
- CSV options
38
- Ability to prepend CSV headers automatically
39
- Bulk import (activerecord-import)
40
- Ability to customize template
41
- Callbacks support
42
- Zip files import
43
- and more ....
28
+ ```ruby
29
+ gem "active_admin_import" , github: "activeadmin-plugins/active_admin_import"
44
30
 
45
- Options
31
+ ```
46
32
 
47
- # +back+:: resource action to redirect after processing
48
- # +csv_options+:: hash with column separator, row separator, etc
49
- # +validate+:: true|false, means perfoem validations or not
50
- # +batch_size+:: integer value of max record count inserted by 1 query/transaction
51
- # +before_import+:: proc for before import action, hook called with importer object
52
- # +after_import+:: proc for after import action, hook called with importer object
53
- # +before_batch_import+:: proc for before each batch action, called with importer object
54
- # +after_batch_import+:: proc for after each batch action, called with importer object
55
- # +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
56
- # +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
57
- # +ignore+:: true|false, tells activerecord-import toto use MySQL's INSERT IGNORE ability
58
- # +params_keys+:: params values available in callbacks
59
- # +template+:: custom template rendering
60
- # +template_object+:: object passing to view
61
- # +locals+:: local variables for template
62
- # +resource_class+:: resource class name
63
- # +resource_label+:: resource label value
64
- # +headers_rewrites+:: hash with key (csv header) - value (db column name) rows mapping
33
+ And then execute:
65
34
 
35
+ $ bundle
66
36
 
67
37
 
68
- Default options values
38
+ # active_admin_import features
39
+ <ol>
40
+ <li> Replacements/Updates support</li>
41
+ <li> Encoding handling</li>
42
+ <li> CSV options</li>
43
+ <li> Ability to describe/change CSV headers</li>
44
+ <li> Bulk import (activerecord-import)</li>
45
+ <li> Callbacks</li>
46
+ <li> Zip files</li>
47
+ <li> and more...</li>
48
+ </ol>
69
49
 
70
- ```ruby
71
- back: {action: :import},
72
- csv_options: {},
73
- template: "admin/import",
74
- fetch_extra_options_from_params: [],
75
- resource_class: config.resource_class,
76
- resource_label: config.resource_label,
77
- plural_resource_label: config.plural_resource_label,
78
- ```
50
+
79
51
 
80
- Example1
52
+ #### Basic usage
81
53
 
82
- ```ruby
83
- ActiveAdmin.register Post do
84
- active_admin_import :validate => false,
85
- :csv_options => {:col_sep => ";" },
86
- :before_import => proc{ Post.delete_all},
87
- :batch_size => 1000
88
-
89
-
90
- end
54
+ ```ruby
55
+ ActiveAdmin.register Post
56
+ active_admin_import options
57
+ end
91
58
  ```
92
59
 
93
60
 
94
- Example2 Importing to mediate table with insert select operation after import completion
95
-
96
- This config allows to replace data without downtime
61
+ #### Options
62
+ Tool | Description
63
+ --------------------- | -----------
64
+ :back |resource action to redirect after processing
65
+ :csv_options |hash with column separator, row separator, etc
66
+ :validate |bool means perform validations or not
67
+ :batch_size |integer value of max record count inserted by 1 query/transaction
68
+ :batch_transaction |bool (false by default), if transaction is used when batch importing and works when :validate is set to true
69
+ :before_import |proc for before import action, hook called with importer object
70
+ :after_import |proc for after import action, hook called with importer object
71
+ :before_batch_import |proc for before each batch action, called with importer object
72
+ :after_batch_import |proc for after each batch action, called with importer object
73
+ :on_duplicate_key_update|an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
74
+ :timestamps |bool, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
75
+ :ignore |bool, tells activerecord-import toto use MySQL's INSERT IGNORE ability
76
+ :template |custom template rendering
77
+ :template_object |object passing to view
78
+ :resource_class |resource class name
79
+ :resource_label |resource label value
80
+ :plural_resource_label |pluralized resource label value (default config.plural_resource_label)
81
+ :headers_rewrites |hash with key (csv header) - value (db column name) rows mapping
97
82
 
98
- ```ruby
99
- ActiveAdmin.register Post do
100
- active_admin_import :validate => false,
101
- :csv_options => {:col_sep => ";" },
102
- :resource_class => ImportedPost , # we import data into another resource
103
- :before_import => proc{ ImportedPost.delete_all },
104
- :after_import => proc{
105
- Post.transaction do
106
- Post.delete_all
107
- Post.connection.execute("INSERT INTO posts (SELECT * FROM import_posts)")
108
- end
109
- },
110
- :back => proc { config.namespace.resource_for(Post).route_collection_path } # redirect to post index
111
- end
112
- ```
113
83
 
114
84
 
115
- Example3 Importing file without headers, but we always know file format, so we can predefine it
116
-
117
- ```ruby
118
- ActiveAdmin.register Post do
119
- active_admin_import :validate => true,
120
- :template_object => ActiveAdminImport::Model.new(
121
- :hint => "file will be imported with such header format: 'body','title','author'",
122
- :csv_headers => ["body","title","author"]
123
- )
124
- end
125
- ```
126
-
127
- Example4 Importing without forcing to UTF-8 and disallow archives
85
+ #### Wiki
128
86
 
87
+ [Check various examples](https://github.com/activeadmin-plugins/active_admin_import/wiki)
129
88
 
130
- ```ruby
131
- ActiveAdmin.register Post do
132
- active_admin_import :validate => true,
133
- :template_object => ActiveAdminImport::Model.new(
134
- :hint => "file will be encoded to ISO-8859-1",
135
- :force_encoding => "ISO-8859-1",
136
- :allow_archive => false
137
- )
138
- end
139
- ```
89
+ ## Dependencies
140
90
 
91
+ Tool | Description
92
+ --------------------- | -----------
93
+ [rchardet] | Character encoding auto-detection in Ruby. As smart as your browser. Open source.
94
+ [activerecord-import] | Powerful library for bulk inserting data using ActiveRecord.
141
95
 
142
- Example5 Callbacks for each bulk insert iteration
96
+ [rchardet]: https://github.com/jmhodges/rchardet
97
+ [activerecord-import]: https://github.com/zdennis/activerecord-import
143
98
 
144
99
 
145
- ```ruby
146
- ActiveAdmin.register Post do
147
- active_admin_import :validate => true,
148
- :before_batch_import => proc { |import|
149
- import.file #current file used
150
- import.resource #ActiveRecord class to import to
151
- import.options # options
152
- import.result # result before bulk iteration
153
- import.headers # CSV headers
154
- import.csv_lines #lines to import
155
- import.model #template_object instance
156
- },
157
- :after_batch_import => proc{ |import|
158
- #the same
159
- }
160
- end
161
- ```
162
-
163
- Example6 dynamic CSV options, template overriding
164
-
165
- 1) put overrided template to app/views/import.html.erb
166
-
167
- ```erb
168
-
169
- <p>
170
- <%= raw(@active_admin_import_model.hint) %>
171
- </p>
172
- <%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
173
- <%= f.inputs do %>
174
- <%= f.input :file, as: :file %>
175
- <% end %>
176
- <%= f.inputs "CSV options", :for => [:csv_options, OpenStruct.new(@active_admin_import_model.csv_options)] do |csv| %>
177
- <% csv.with_options :input_html => {:style => 'width:40px;'} do |opts| %>
178
- <%= opts.input :col_sep %>
179
- <%= opts.input :row_sep %>
180
- <%= opts.input :quote_char %>
181
- <% end %>
182
- <% end %>
183
-
184
- <%= f.actions do %>
185
- <%= f.action :submit, label: t("active_admin_import.import_btn"), button_html: {disable_with: t("active_admin_import.import_btn_disabled")} %>
186
- <% end %>
187
- <% end %>
188
-
189
- ```
100
+ ## Contributing
190
101
 
191
- 2) call method with following parameters
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
192
107
 
193
- ```ruby
194
- ActiveAdmin.register Post do
195
- active_admin_import :validate => false,
196
- :template => 'import' ,
197
- :template_object => ActiveAdminImport::Model.new(
198
- :hint => "specify CSV options"
199
- :csv_options => {:col_sep => ";", :row_sep => nil, :quote_char => nil}
200
- )
201
- end
202
- ```
203
108
 
204
- #Links
205
- https://github.com/gregbell/active_admin
206
109
 
207
- https://github.com/zdennis/activerecord-import
208
110
 
209
- #Source Doc
210
- http://rubydoc.info/gems/active_admin_import/2.1.1/
211
111
 
212
112
 
213
113
 
data/Rakefile CHANGED
@@ -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 }
@@ -15,9 +15,11 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = ActiveAdminImport::VERSION
17
17
 
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'
21
18
 
19
+ gem.add_runtime_dependency 'activerecord-import', '~> 0.8.0'
20
+ gem.add_runtime_dependency 'rchardet', '~> 1.5'
21
+
22
+ gem.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0'
23
+ gem.add_dependency "rails", ">= 4.0"
22
24
 
23
25
  end
@@ -2,7 +2,7 @@
2
2
  <%= raw(@active_admin_import_model.hint) %>
3
3
  </p>
4
4
  <%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
5
- <%= f.inputs do %>
5
+ <%= f.inputs name: t("active_admin_import.details") do %>
6
6
  <%= f.input :file, as: :file %>
7
7
  <% end %>
8
8
  <%= f.actions do %>
@@ -2,15 +2,22 @@ en:
2
2
  active_admin:
3
3
  import: "Import"
4
4
  active_admin_import:
5
+ file: 'File'
6
+ file_error: "Error: %{message}"
5
7
  file_format_error: "You can import only valid csv file"
6
8
  file_empty_error: "You can't import empty file"
7
9
  no_file_error: "Please, select file to import"
10
+ details: "Please, select file to import"
8
11
  imported:
9
12
  one: "Successfully imported 1 %{model}"
10
13
  other: "Successfully imported %{count} %{plural_model}"
11
14
  failed:
12
- one: "Failed to import 1 %{model}"
13
- other: "Failed to import %{count} %{plural_model}"
14
- import_model: "Import %{model}"
15
+ one: "Failed to import 1 %{model}: %{message}"
16
+ other: "Failed to import %{count} %{plural_model}: %{message}"
17
+ import_model: "Import %{plural_model}"
15
18
  import_btn: "Import"
16
- import_btn_disabled: "Wait..."
19
+ import_btn_disabled: "Wait..."
20
+ csv_options: "CSV options"
21
+ col_sep: 'Col sep'
22
+ row_sep: 'Row sep'
23
+ quote_char: 'Quote char'
@@ -0,0 +1,18 @@
1
+ es:
2
+ active_admin:
3
+ import: "Importar"
4
+ active_admin_import:
5
+ file_error: "Error: %{message}"
6
+ file_format_error: "Sólo se puede importar un archivo CSV válido"
7
+ file_empty_error: "No puedes importar un archivo vacío"
8
+ no_file_error: "Seleccione un archivo para importar"
9
+ details: "Seleccione un archivo"
10
+ imported:
11
+ one: "Se ha importado exitosamente 1 %{model}"
12
+ other: "Se ha importado exitosamente %{count} %{plural_model}"
13
+ failed:
14
+ one: "No se pudo importar 1 %{model}"
15
+ other: "No se pudo importar %{count} %{plural_model}"
16
+ import_model: "Importar %{plural_model}"
17
+ import_btn: "Importar"
18
+ import_btn_disabled: "Espere..."
@@ -2,15 +2,17 @@ it:
2
2
  active_admin:
3
3
  import: "Importa"
4
4
  active_admin_import:
5
+ file_error: "Errore: %{message}"
5
6
  file_format_error: "Puoi importare solo CSV conformi"
6
7
  no_file_error: "Per favore, seleziona un file da importare"
7
8
  file_empty_error: "Non è possibile importare file vuoto"
9
+ details: "Per favore, seleziona un file da importare"
8
10
  imported:
9
11
  one: "Importato 1 %{model} con successo"
10
12
  other: "Importati %{count} %{plural_model} con successo"
11
13
  failed:
12
14
  one: "Importazione di 1 %{model} fallita"
13
15
  other: "Importazione di %{count} %{plural_model} fallita"
14
- import_model: "Importa %{model}"
16
+ import_model: "Importa %{plural_model}"
15
17
  import_btn: "Importa"
16
18
  import_btn_disabled: "In attesa..."
@@ -0,0 +1,24 @@
1
+ zh-CN:
2
+ active_admin:
3
+ import: "导入"
4
+ do_import: "导入"
5
+ active_admin_import:
6
+ file: '文件'
7
+ file_error: "导入错误: %{message}"
8
+ file_format_error: "请您上传 CSV 格式的文件"
9
+ file_empty_error: "您上传的文件为空"
10
+ no_file_error: "请您选择要上传的文件"
11
+ details: "请您选择要上传的文件"
12
+ imported:
13
+ one: "成功导入 1 份%{model}"
14
+ other: "成功导入 %{count} 份%{plural_model}"
15
+ failed:
16
+ one: "未能导入 1 份%{model}: %{message}"
17
+ other: "未能导入 %{count} 份%{plural_model}: %{message}"
18
+ import_model: "导入%{plural_model}"
19
+ import_btn: "导入"
20
+ import_btn_disabled: "请稍等..."
21
+ csv_options: "选项"
22
+ col_sep: '列分隔符'
23
+ row_sep: '行分隔符'
24
+ quote_char: '引用字符'
@@ -1,18 +1,19 @@
1
1
  module ActiveAdminImport
2
2
  module DSL
3
+
4
+
3
5
  # Declares import functionality
4
6
  #
5
7
  # Options
6
8
  # +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)
9
9
  # +csv_options+:: hash to override default CSV options
10
- # +validate+:: true|false, means perfoem validations or not
11
10
  # +batch_size+:: integer value of max record count inserted by 1 query/transaction
11
+ # +batch_transaction+:: bool (false by default), if transaction is used when batch importing and works when :validate is set to true
12
12
  # +before_import+:: proc for before import action, hook called with importer object
13
13
  # +after_import+:: proc for after import action, hook called with importer object
14
14
  # +before_batch_import+:: proc for before each batch action, called with importer object
15
15
  # +after_batch_import+:: proc for after each batch action, called with importer object
16
+ # +validate+:: true|false, means perform validations or not
16
17
  # +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
17
18
  # +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
18
19
  # +ignore+:: true|false, tells activerecord-import to use MySQL's INSERT IGNORE ability
@@ -20,58 +21,65 @@ module ActiveAdminImport
20
21
  # +template_object+:: object passing to view
21
22
  # +resource_class+:: resource class name, override to import to another table (default config.resource_class)
22
23
  # +resource_label+:: resource label value (default config.resource_label)
23
- # +plural_resource_label+:: plaralized resource label value (default config.plural_resource_label)
24
+ # +plural_resource_label+:: pluralized resource label value (default config.plural_resource_label)
24
25
  #
25
- def active_admin_import options = {}, &block
26
- default_options = {
27
- back: {action: :import},
28
- csv_options: {},
29
- template: "admin/import",
30
- resource_class: config.resource_class,
31
- resource_label: config.resource_label,
32
- plural_resource_label: config.plural_resource_label,
33
- headers_rewrites: {}
34
- }
35
- options = default_options.deep_merge(options)
36
- params_key = ActiveModel::Naming.param_key(options[:template_object] || ActiveAdminImport::Model.new)
26
+
27
+
28
+ DEFAULT_RESULT_PROC = ->(result, options) do
29
+
30
+ model_name = options[:resource_label].downcase
31
+ plural_model_name = options[:plural_resource_label].downcase
32
+ if result.empty?
33
+ flash[:warning] = I18n.t('active_admin_import.file_empty_error')
34
+ else
35
+ if result.has_failed?
36
+ flash[:error] = I18n.t('active_admin_import.failed', count: result.failed.count, model: model_name, plural_model: plural_model_name, message: result.failed_message(limit: 5))
37
+ return if options[:batch_transaction]
38
+ end
39
+ if result.has_imported?
40
+ flash[:notice] = I18n.t('active_admin_import.imported', count: result.imported_qty, model: model_name, plural_model: plural_model_name)
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ def active_admin_import(options = {}, &block)
47
+ options.assert_valid_keys(*Options::VALID_OPTIONS)
48
+
49
+ options = Options.options_for(config, options)
50
+ params_key = ActiveModel::Naming.param_key(options[:template_object])
37
51
 
38
52
  collection_action :import, method: :get do
39
53
  authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
40
-
41
- @active_admin_import_model = options[:template_object] || ActiveAdminImport::Model.new
54
+ @active_admin_import_model = options[:template_object]
42
55
  render template: options[:template]
43
56
  end
44
57
 
45
58
  action_item :import, only: :index do
46
59
  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')
60
+ link_to(I18n.t('active_admin_import.import_model', plural_model: options[:plural_resource_label]), action: :import)
48
61
  end
49
62
  end
50
63
 
51
64
  collection_action :do_import, method: :post do
52
65
  authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
53
66
 
54
- @active_admin_import_model = options[:template_object] || ActiveAdminImport::Model.new
67
+ @active_admin_import_model = options[:template_object]
55
68
  @active_admin_import_model.assign_attributes(params[params_key].try(:deep_symbolize_keys) || {})
56
69
  #go back to form
57
70
  return render template: options[:template] unless @active_admin_import_model.valid?
58
71
  @importer = Importer.new(options[:resource_class], @active_admin_import_model, options)
59
72
  begin
60
- @importer.import
73
+ result = @importer.import
74
+
61
75
  if block_given?
62
76
  instance_eval &block
63
77
  else
64
- model_name = options[:resource_label].downcase
65
- plural_model_name = options[:plural_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)
68
- 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)
71
- end
78
+ instance_exec result, options, &DEFAULT_RESULT_PROC
72
79
  end
73
- rescue ActiveRecord::Import::MissingColumnError, NoMethodError => e
74
- flash[:error] = e.message
80
+ rescue ActiveRecord::Import::MissingColumnError, NoMethodError, ActiveRecord::StatementInvalid => e
81
+ Rails.logger.error(I18n.t('active_admin_import.file_error', message: e.message))
82
+ flash[:error] = I18n.t('active_admin_import.file_error', message: e.message[0..200])
75
83
  end
76
84
  redirect_to options[:back]
77
85
  end
@@ -0,0 +1,39 @@
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
+ def empty?
28
+ total == 0
29
+ end
30
+
31
+ def failed_message(options = {})
32
+ limit = options.fetch(:limit, failed.count)
33
+ failed.first(limit).map{|record|
34
+ errors = record.errors
35
+ (errors.full_messages.zip errors.keys.map{|k| record.send k}).map{|ms| ms.join(' - ')}.join(', ')
36
+ }.join(" ; ")
37
+ end
38
+ end
39
+ end