active_admin_import 2.1.2 → 3.0.0.pre

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.
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