active_admin_import 3.0.0.pre → 3.0.0

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.
@@ -1,9 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails'
2
3
 
3
4
  module ActiveAdminImport
4
5
  class Engine < ::Rails::Engine
5
-
6
6
  config.mount_at = '/'
7
-
8
7
  end
9
- end
8
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ActiveAdminImport
2
3
  class ImportResult
3
4
  attr_reader :failed, :total
@@ -16,12 +17,12 @@ module ActiveAdminImport
16
17
  total - failed.count
17
18
  end
18
19
 
19
- def has_imported?
20
+ def imported?
20
21
  imported_qty > 0
21
22
  end
22
23
 
23
- def has_failed?
24
- @failed.any?
24
+ def failed?
25
+ failed.any?
25
26
  end
26
27
 
27
28
  def empty?
@@ -29,11 +30,11 @@ module ActiveAdminImport
29
30
  end
30
31
 
31
32
  def failed_message(options = {})
32
- limit = options.fetch(:limit, failed.count)
33
- failed.first(limit).map{|record|
33
+ limit = options[:limit] || failed.count
34
+ failed.first(limit).map do |record|
34
35
  errors = record.errors
35
- (errors.full_messages.zip errors.keys.map{|k| record.send k}).map{|ms| ms.join(' - ')}.join(', ')
36
- }.join(" ; ")
36
+ (errors.full_messages.zip errors.keys.map { |k| record.send k }).map { |ms| ms.join(' - ') }.join(', ')
37
+ end.join(' ; ')
37
38
  end
38
39
  end
39
- end
40
+ end
@@ -1,23 +1,23 @@
1
+ # frozen_string_literal: true
1
2
  require 'csv'
2
3
  module ActiveAdminImport
3
4
  class Importer
4
-
5
5
  attr_reader :resource, :options, :result, :model
6
6
  attr_accessor :csv_lines, :headers
7
7
 
8
8
  OPTIONS = [
9
- :validate,
10
- :on_duplicate_key_update,
11
- :ignore,
12
- :timestamps,
13
- :before_import,
14
- :after_import,
15
- :before_batch_import,
16
- :after_batch_import,
17
- :headers_rewrites,
18
- :batch_size,
19
- :batch_transaction,
20
- :csv_options
9
+ :validate,
10
+ :on_duplicate_key_update,
11
+ :ignore,
12
+ :timestamps,
13
+ :before_import,
14
+ :after_import,
15
+ :before_batch_import,
16
+ :after_batch_import,
17
+ :headers_rewrites,
18
+ :batch_size,
19
+ :batch_transaction,
20
+ :csv_options
21
21
  ].freeze
22
22
 
23
23
  def initialize(resource, model, options)
@@ -55,7 +55,7 @@ module ActiveAdminImport
55
55
  index = header_index(header_key)
56
56
  csv_lines.map! do |line|
57
57
  from = line[index]
58
- line[index] = options[from] if options.has_key?(from)
58
+ line[index] = options[from] if options.key?(from)
59
59
  line
60
60
  end
61
61
  end
@@ -71,7 +71,8 @@ module ActiveAdminImport
71
71
  protected
72
72
 
73
73
  def process_file
74
- lines, batch_size = [], options[:batch_size].to_i
74
+ lines = []
75
+ batch_size = options[:batch_size].to_i
75
76
  File.open(file.path) do |f|
76
77
  # capture headers if not exist
77
78
  prepare_headers { CSV.parse(f.readline, @csv_options).first }
@@ -109,7 +110,7 @@ module ActiveAdminImport
109
110
  end
110
111
 
111
112
  def assign_options(options)
112
- @options = {batch_size: 1000, validate: true}.merge(options.slice(*OPTIONS))
113
+ @options = { batch_size: 1000, validate: true }.merge(options.slice(*OPTIONS))
113
114
  detect_csv_options
114
115
  end
115
116
 
@@ -118,8 +119,7 @@ module ActiveAdminImport
118
119
  model.csv_options
119
120
  else
120
121
  options[:csv_options] || {}
121
- end.reject { |_, value| value.blank? }
122
+ end.reject { |_, value| value.nil? || value == "" }
122
123
  end
123
-
124
124
  end
125
125
  end
@@ -1,17 +1,36 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rchardet'
5
+ require 'zip'
4
6
 
5
7
  module ActiveAdminImport
6
8
  class Model
7
-
8
9
  include ActiveModel::Model
9
10
  include ActiveModel::Validations
10
11
  include ActiveModel::Validations::Callbacks
11
12
 
13
+ module CONST
14
+ ZIP_TYPE = 'application/zip'.freeze
15
+ TMP_FILE = 'active-admin-import-unzipped'.freeze
16
+ CSV_TYPES = %w(
17
+ text/csv
18
+ text/x-csv
19
+ text/x-comma-separated-values
20
+ text/comma-separated-values
21
+ application/csv
22
+ application/vnd.ms-excel
23
+ application/vnd.msexcel
24
+ text/tsv
25
+ text/x-tsv
26
+ text/tab-separated-values
27
+ text/x-tab-separated-values
28
+ ).freeze
29
+ end
30
+
12
31
  validates :file, presence: {
13
- message: ->(*_){ I18n.t("active_admin_import.no_file_error") }
14
- }, unless: ->(me){ me.new_record? }
32
+ message: ->(*_) { I18n.t('active_admin_import.no_file_error') }
33
+ }, unless: ->(me) { me.new_record? }
15
34
 
16
35
  validate :correct_content_type, if: ->(me) { me.file.present? }
17
36
  validate :file_contents_present, if: ->(me) { me.file.present? }
@@ -21,7 +40,7 @@ module ActiveAdminImport
21
40
 
22
41
  attr_reader :attributes
23
42
 
24
- def initialize(args={})
43
+ def initialize(args = {})
25
44
  @new_record = true
26
45
  @attributes = {}
27
46
  assign_attributes(default_attributes.merge(args), true)
@@ -44,21 +63,21 @@ module ActiveAdminImport
44
63
  allow_archive: true,
45
64
  csv_headers: [],
46
65
  file: nil,
47
- force_encoding: "UTF-8",
48
- hint: ""
66
+ force_encoding: 'UTF-8',
67
+ hint: ''
49
68
  }
50
69
  end
51
70
 
52
71
  def allow_archive?
53
- !!attributes[:allow_archive]
72
+ attributes[:allow_archive].present?
54
73
  end
55
74
 
56
75
  def new_record?
57
- !!@new_record
76
+ @new_record.present?
58
77
  end
59
78
 
60
79
  def force_encoding?
61
- !!attributes[:force_encoding]
80
+ attributes[:force_encoding].present?
62
81
  end
63
82
 
64
83
  def persisted?
@@ -66,10 +85,10 @@ module ActiveAdminImport
66
85
  end
67
86
 
68
87
  def archive?
69
- file_type == 'application/zip'
88
+ file_type == CONST::ZIP_TYPE
70
89
  end
71
90
 
72
- alias :to_hash :attributes
91
+ alias to_hash attributes
73
92
 
74
93
  protected
75
94
 
@@ -90,32 +109,27 @@ module ActiveAdminImport
90
109
 
91
110
  def unzip_file
92
111
  Zip::File.open(file_path) do |zip_file|
93
- self.file = Tempfile.new('active-admin-import-unzipped')
94
- data = zip_file.entries.select { |f| f.file? }.first.get_input_stream.read
95
- self.file << data
96
- self.file.close
112
+ self.file = Tempfile.new(CONST::TMP_FILE)
113
+ data = zip_file.entries.select(&:file?).first.get_input_stream.read
114
+ file << data
115
+ file.close
97
116
  end
98
117
  end
99
118
 
100
119
  def csv_allowed_types
101
- [
102
- 'text/csv',
103
- 'text/x-csv',
104
- 'text/comma-separated-values',
105
- 'application/csv',
106
- 'application/vnd.ms-excel',
107
- 'application/vnd.msexcel'
108
- ]
120
+ CONST::CSV_TYPES
109
121
  end
110
122
 
111
123
  def correct_content_type
112
- unless file.blank? || file.is_a?(Tempfile)
113
- errors.add(:file, I18n.t('active_admin_import.file_format_error')) unless csv_allowed_types.include? file_type
114
- end
124
+ return if file.blank? ||
125
+ file.is_a?(Tempfile) ||
126
+ (csv_allowed_types.include? file_type)
127
+ errors.add(:file, I18n.t('active_admin_import.file_format_error'))
115
128
  end
116
129
 
117
130
  def file_contents_present
118
- errors.add(:file, I18n.t('active_admin_import.file_empty_error')) if File.zero?(file_path)
131
+ return unless File.zero?(file_path)
132
+ errors.add(:file, I18n.t('active_admin_import.file_empty_error'))
119
133
  end
120
134
 
121
135
  def file_type
@@ -126,10 +140,8 @@ module ActiveAdminImport
126
140
  end
127
141
  end
128
142
 
129
- protected
130
-
131
143
  def define_methods_for(attr_name)
132
- #generate methods for instance object by attributes
144
+ # generate methods for instance object by attributes
133
145
  singleton_class.class_eval do
134
146
  define_set_method(attr_name)
135
147
  define_get_method(attr_name)
@@ -143,7 +155,7 @@ module ActiveAdminImport
143
155
  invalid: :replace, undef: :replace, universal_newline: true
144
156
  )
145
157
  begin
146
- data.sub("\xEF\xBB\xBF", '') # bom
158
+ data.sub("\xEF\xBB\xBF", '') # bom
147
159
  rescue StandardError => _
148
160
  data
149
161
  end
@@ -168,15 +180,14 @@ module ActiveAdminImport
168
180
 
169
181
  class <<self
170
182
  def define_set_method(attr_name)
171
- define_method(attr_name) { self.attributes[attr_name] } unless method_defined? attr_name
183
+ return if method_defined? attr_name
184
+ define_method(attr_name) { attributes[attr_name] }
172
185
  end
173
186
 
174
187
  def define_get_method(attr_name)
175
- define_method("#{attr_name}=") { |new_value| @attributes[attr_name] = new_value } unless method_defined? "#{attr_name}="
188
+ return if method_defined? "#{attr_name}="
189
+ define_method("#{attr_name}=") { |new_value| @attributes[attr_name] = new_value }
176
190
  end
177
191
  end
178
-
179
192
  end
180
193
  end
181
-
182
-
@@ -1,44 +1,45 @@
1
+ # frozen_string_literal: true
1
2
  module ActiveAdminImport
2
-
3
3
  module Options
4
4
  VALID_OPTIONS = [
5
- :back,
6
- :csv_options,
7
- :validate,
8
- :batch_size,
9
- :batch_transaction,
10
- :before_import,
11
- :after_import,
12
- :before_batch_import,
13
- :after_batch_import,
14
- :on_duplicate_key_update,
15
- :timestamps,
16
- :ignore,
17
- :template,
18
- :template_object,
19
- :resource_class,
20
- :resource_label,
21
- :plural_resource_label,
22
- :headers_rewrites
5
+ :back,
6
+ :csv_options,
7
+ :validate,
8
+ :batch_size,
9
+ :batch_transaction,
10
+ :before_import,
11
+ :after_import,
12
+ :before_batch_import,
13
+ :after_batch_import,
14
+ :on_duplicate_key_update,
15
+ :timestamps,
16
+ :ignore,
17
+ :template,
18
+ :template_object,
19
+ :resource_class,
20
+ :resource_label,
21
+ :plural_resource_label,
22
+ :error_limit,
23
+ :headers_rewrites,
24
+ :if
23
25
  ].freeze
24
26
 
25
-
26
- def self.options_for(config, options= {})
27
- options[:template_object] = ActiveAdminImport::Model.new unless options.has_key? :template_object
27
+ def self.options_for(config, options = {})
28
+ unless options.key? :template_object
29
+ options[:template_object] = ActiveAdminImport::Model.new
30
+ end
28
31
 
29
32
  {
30
- back: {action: :import},
31
- csv_options: {},
32
- template: "admin/import",
33
- resource_class: config.resource_class,
34
- resource_label: config.resource_label,
35
- plural_resource_label: config.plural_resource_label,
36
- headers_rewrites: {}
33
+ back: { action: :import },
34
+ csv_options: {},
35
+ template: 'admin/import',
36
+ resource_class: config.resource_class,
37
+ resource_label: config.resource_label,
38
+ plural_resource_label: config.plural_resource_label,
39
+ error_limit: 5,
40
+ headers_rewrites: {},
41
+ if: true
37
42
  }.deep_merge(options)
38
-
39
-
40
-
41
43
  end
42
-
43
44
  end
44
45
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ActiveAdminImport
2
- VERSION = "3.0.0.pre"
3
+ VERSION = '3.0.0'
3
4
  end
@@ -0,0 +1,3 @@
1
+ Name Last name Birthday
2
+ John Doe 1986-05-01
3
+ Jane Roe 1988-11-16
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe ActiveAdminImport::ImportResult do
4
- context "failed_message" do
5
+ context 'failed_message' do
5
6
  let(:import_result) { ActiveAdminImport::ImportResult.new }
6
7
 
7
8
  before do
@@ -10,23 +11,28 @@ describe ActiveAdminImport::ImportResult do
10
11
 
11
12
  @result = double \
12
13
  failed_instances: [
13
- Author.create(name: 'Jim', last_name: "Doe"), # {:last_name=>["has already been taken"]}
14
- Author.create(name: nil, last_name: 'Doe') # {:name=>["can't be blank"], :last_name=>["has already been taken"]}
14
+ # {:last_name=>["has already been taken"]}
15
+ Author.create(name: 'Jim', last_name: 'Doe'),
16
+ # {:name=>["can't be blank"], :last_name=>["has already been taken"]}
17
+ Author.create(name: nil, last_name: 'Doe')
15
18
  ]
16
19
  end
17
20
 
18
- it "should work without any failed instances" do
19
- expect(import_result.failed_message).to eq("")
21
+ it 'should work without any failed instances' do
22
+ expect(import_result.failed_message).to eq('')
20
23
  end
21
24
 
22
- it "should work" do
25
+ it 'should work' do
23
26
  import_result.add(@result, 4)
24
- expect(import_result.failed_message).to eq("Last name has already been taken - Doe ; Name can't be blank - , Last name has already been taken - Doe")
27
+ expect(import_result.failed_message)
28
+ .to eq(
29
+ "Last name has already been taken - Doe ; Name can't be blank - , Last name has already been taken - Doe"
30
+ )
25
31
  end
26
32
 
27
- it "should work on limit param" do
33
+ it 'should work on limit param' do
28
34
  import_result.add(@result, 4)
29
- expect(import_result.failed_message(limit: 1)).to eq("Last name has already been taken - Doe")
35
+ expect(import_result.failed_message(limit: 1)).to eq('Last name has already been taken - Doe')
30
36
  end
31
37
  end
32
38
  end
@@ -1,7 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe 'import', type: :feature do
4
-
5
5
  shared_examples 'successful inserts' do |encoding, csv_file_name|
6
6
  let(:options) do
7
7
  attributes = { force_encoding: encoding }
@@ -12,8 +12,8 @@ describe 'import', type: :feature do
12
12
  upload_file!(csv_file_name)
13
13
  end
14
14
 
15
- it "should import file with many records" do
16
- expect(page).to have_content "Successfully imported 2 authors"
15
+ it 'should import file with many records' do
16
+ expect(page).to have_content 'Successfully imported 2 authors'
17
17
  expect(Author.count).to eq(2)
18
18
  Author.all.each do |author|
19
19
  expect(author).to be_valid
@@ -24,7 +24,6 @@ describe 'import', type: :feature do
24
24
  end
25
25
 
26
26
  def with_zipped_csv(name, &block)
27
-
28
27
  zip_file = File.expand_path("./spec/fixtures/files/#{name}.zip")
29
28
 
30
29
  begin
@@ -33,115 +32,119 @@ describe 'import', type: :feature do
33
32
  end
34
33
  instance_eval &block
35
34
  ensure
36
- File.delete zip_file rescue nil
35
+ begin
36
+ File.delete zip_file
37
+ rescue
38
+ nil
39
+ end
37
40
  end
38
41
  end
39
42
 
40
- def upload_file!(name, ext='csv')
43
+ def upload_file!(name, ext = 'csv')
41
44
  attach_file('active_admin_import_model_file', File.expand_path("./spec/fixtures/files/#{name}.#{ext}"))
42
45
  find_button('Import').click
43
46
  end
44
47
 
45
- context "posts index" do
48
+ context 'posts index' do
46
49
  before do
47
- Author.create!(name: "John", last_name: "Doe")
48
- Author.create!(name: "Jane", last_name: "Roe")
50
+ Author.create!(name: 'John', last_name: 'Doe')
51
+ Author.create!(name: 'Jane', last_name: 'Roe')
49
52
  end
50
53
 
51
- context "for csv for particular author" do
54
+ context 'for csv for particular author' do
52
55
  let(:author) { Author.take }
53
56
 
54
57
  shared_examples 'successful inserts for author' do
55
- it "should use predefined author_id" do
58
+ it 'should use predefined author_id' do
56
59
  expect(Post.where(author_id: author.id).count).to eq(Post.count)
57
60
  end
58
61
 
59
- it "should be imported" do
62
+ it 'should be imported' do
60
63
  expect(Post.count).to eq(2)
61
- expect(page).to have_content "Successfully imported 2 posts"
64
+ expect(page).to have_content 'Successfully imported 2 posts'
62
65
  end
63
66
  end
64
67
 
65
- context "no headers" do
68
+ context 'no headers' do
66
69
  before do
67
70
  add_post_resource(template_object: ActiveAdminImport::Model.new(author_id: author.id,
68
71
  csv_headers: [:title, :body, :author_id]),
69
72
  validate: true,
70
- before_batch_import: ->(importer) do
73
+ before_batch_import: lambda do |importer|
71
74
  importer.csv_lines.map! { |row| row << importer.model.author_id }
72
75
  end
73
- )
76
+ )
74
77
 
75
- visit "/admin/posts/import"
78
+ visit '/admin/posts/import'
76
79
  upload_file!(:posts_for_author_no_headers)
77
80
  end
78
81
  include_examples 'successful inserts for author'
79
82
  end
80
83
 
81
- context "with headers" do
84
+ context 'with headers' do
82
85
  before do
83
86
  add_post_resource(template_object: ActiveAdminImport::Model.new(author_id: author.id),
84
87
  validate: true,
85
- before_batch_import: ->(importer) do
88
+ before_batch_import: lambda do |importer|
86
89
  importer.csv_lines.map! { |row| row << importer.model.author_id }
87
- importer.headers.merge!({ :'Author Id' => :author_id })
90
+ importer.headers.merge!(:'Author Id' => :author_id)
88
91
  end
89
- )
92
+ )
90
93
 
91
- visit "/admin/posts/import"
94
+ visit '/admin/posts/import'
92
95
  upload_file!(:posts_for_author)
93
96
  end
94
97
  include_examples 'successful inserts for author'
95
98
  end
96
99
  end
97
100
 
98
- context "for csv with author name" do
101
+ context 'for csv with author name' do
99
102
  before do
100
103
  add_post_resource(
101
- validate: true,
102
- template_object: ActiveAdminImport::Model.new,
103
- headers_rewrites: { :'Author Name' => :author_id },
104
- before_batch_import: ->(importer) do
105
- authors_names = importer.values_at(:author_id)
106
- # replacing author name with author id
107
- authors = Author.where(name: authors_names).pluck(:name, :id)
108
- #{"Jane" => 2, "John" => 1}
109
- options = Hash[*authors.flatten]
110
- importer.batch_replace(:author_id, options)
111
- end
104
+ validate: true,
105
+ template_object: ActiveAdminImport::Model.new,
106
+ headers_rewrites: { :'Author Name' => :author_id },
107
+ before_batch_import: lambda do |importer|
108
+ authors_names = importer.values_at(:author_id)
109
+ # replacing author name with author id
110
+ authors = Author.where(name: authors_names).pluck(:name, :id)
111
+ # {"Jane" => 2, "John" => 1}
112
+ options = Hash[*authors.flatten]
113
+ importer.batch_replace(:author_id, options)
114
+ end
112
115
  )
113
- visit "/admin/posts/import"
116
+ visit '/admin/posts/import'
114
117
  upload_file!(:posts)
115
118
  end
116
119
 
117
- it "should resolve author_id by author name" do
120
+ it 'should resolve author_id by author name' do
118
121
  Post.all.each do |post|
119
122
  expect(Author.where(id: post.author.id)).to be_present
120
123
  end
121
124
  end
122
125
 
123
- it "should be imported" do
126
+ it 'should be imported' do
124
127
  expect(Post.count).to eq(2)
125
- expect(page).to have_content "Successfully imported 2 posts"
128
+ expect(page).to have_content 'Successfully imported 2 posts'
126
129
  end
127
130
  end
128
131
  end
129
132
 
130
- context "authors index" do
133
+ context 'authors index' do
131
134
  before do
132
135
  add_author_resource
133
136
  end
134
137
 
135
- it "should navigate to import page" do
136
- #todo: removing this causes undefined method `ransack' for #<ActiveRecord::Relation []>
138
+ it 'should navigate to import page' do
139
+ # TODO: removing this causes undefined method `ransack' for #<ActiveRecord::Relation []>
137
140
  allow_any_instance_of(Admin::AuthorsController).to receive(:find_collection).and_return(Author.all)
138
141
  visit '/admin/authors'
139
142
  find_link('Import Authors').click
140
- expect(current_path).to eq("/admin/authors/import")
143
+ expect(current_path).to eq('/admin/authors/import')
141
144
  end
142
145
  end
143
146
 
144
- context "with custom block" do
147
+ context 'with custom block' do
145
148
  before do
146
149
  add_author_resource({}) do
147
150
  flash[:notice] = 'some custom message'
@@ -149,44 +152,42 @@ describe 'import', type: :feature do
149
152
  visit '/admin/authors/import'
150
153
  end
151
154
 
152
- it "should display notice from custom block" do
155
+ it 'should display notice from custom block' do
153
156
  upload_file!(:author)
154
- expect(page).to have_content "some custom message"
157
+ expect(page).to have_content 'some custom message'
155
158
  end
156
-
157
159
  end
158
160
 
159
- context "authors already exist" do
161
+ context 'authors already exist' do
160
162
  before do
161
- Author.create!(id: 1, name: "Jane", last_name: "Roe")
162
- Author.create!(id: 2, name: "John", last_name: "Doe")
163
+ Author.create!(id: 1, name: 'Jane', last_name: 'Roe')
164
+ Author.create!(id: 2, name: 'John', last_name: 'Doe')
163
165
  end
164
166
 
165
- context "having authors with the same Id" do
167
+ context 'having authors with the same Id' do
166
168
  before do
167
169
  add_author_resource(
168
- before_batch_import: ->(importer) do
169
- Author.where(id: importer.values_at("id")).delete_all
170
- end
170
+ before_batch_import: lambda do |importer|
171
+ Author.where(id: importer.values_at('id')).delete_all
172
+ end
171
173
  )
172
- visit "/admin/authors/import"
174
+ visit '/admin/authors/import'
173
175
  upload_file!(:authors_with_ids)
174
176
  end
175
177
 
176
- it "should replace authors" do
177
- expect(page).to have_content "Successfully imported 2 authors"
178
+ it 'should replace authors' do
179
+ expect(page).to have_content 'Successfully imported 2 authors'
178
180
  expect(Author.count).to eq(2)
179
181
  end
180
182
 
181
- it "should replace authors by id" do
182
- expect(Author.find(1).name).to eq("John")
183
- expect(Author.find(2).name).to eq("Jane")
183
+ it 'should replace authors by id' do
184
+ expect(Author.find(1).name).to eq('John')
185
+ expect(Author.find(2).name).to eq('Jane')
184
186
  end
185
187
  end
186
188
  end
187
189
 
188
- context "with valid options" do
189
-
190
+ context 'with valid options' do
190
191
  let(:options) { {} }
191
192
 
192
193
  before do
@@ -194,32 +195,31 @@ describe 'import', type: :feature do
194
195
  visit '/admin/authors/import'
195
196
  end
196
197
 
197
- it "has valid form" do
198
+ it 'has valid form' do
198
199
  form = find('#new_active_admin_import_model')
199
- expect(form['action']).to eq("/admin/authors/do_import")
200
- expect(form['enctype']).to eq("multipart/form-data")
201
- file_input = form.find("input#active_admin_import_model_file")
202
- expect(file_input[:type]).to eq("file")
200
+ expect(form['action']).to eq('/admin/authors/do_import')
201
+ expect(form['enctype']).to eq('multipart/form-data')
202
+ file_input = form.find('input#active_admin_import_model_file')
203
+ expect(file_input[:type]).to eq('file')
203
204
  expect(file_input.value).to be_blank
204
- submit_input = form.find("#active_admin_import_model_submit_action input")
205
- expect(submit_input[:value]).to eq("Import")
206
- expect(submit_input[:type]).to eq("submit")
205
+ submit_input = form.find('#active_admin_import_model_submit_action input')
206
+ expect(submit_input[:value]).to eq('Import')
207
+ expect(submit_input[:type]).to eq('submit')
207
208
  end
208
209
 
209
- context "with hint defined" do
210
+ context 'with hint defined' do
210
211
  let(:options) do
211
- { template_object: ActiveAdminImport::Model.new(hint: "hint") }
212
+ { template_object: ActiveAdminImport::Model.new(hint: 'hint') }
212
213
  end
213
- it "renders hint at upload page" do
214
+ it 'renders hint at upload page' do
214
215
  expect(page).to have_content options[:template_object].hint
215
216
  end
216
217
  end
217
218
 
218
- context "when importing file" do
219
-
219
+ context 'when importing file' do
220
220
  [:empty, :only_headers].each do |file|
221
221
  context "when #{file} file" do
222
- it "should render warning" do
222
+ it 'should render warning' do
223
223
  upload_file!(file)
224
224
  expect(page).to have_content I18n.t('active_admin_import.file_empty_error')
225
225
  expect(Author.count).to eq(0)
@@ -227,142 +227,143 @@ describe 'import', type: :feature do
227
227
  end
228
228
  end
229
229
 
230
- context "when no file" do
231
- it "should render error" do
230
+ context 'when no file' do
231
+ it 'should render error' do
232
232
  find_button('Import').click
233
233
  expect(Author.count).to eq(0)
234
234
  expect(page).to have_content I18n.t('active_admin_import.no_file_error')
235
235
  end
236
236
  end
237
237
 
238
- context "auto detect encoding" do
238
+ context 'auto detect encoding' do
239
239
  include_examples 'successful inserts',
240
240
  :auto,
241
241
  :authors_win1251_win_endline
242
242
  end
243
243
 
244
- context "Win1251" do
244
+ context 'Win1251' do
245
245
  include_examples 'successful inserts',
246
246
  'windows-1251',
247
247
  :authors_win1251_win_endline
248
248
  end
249
249
 
250
- context "BOM" do
251
- it "should import file with many records" do
250
+ context 'BOM' do
251
+ it 'should import file with many records' do
252
252
  upload_file!(:authors_bom)
253
- expect(page).to have_content "Successfully imported 2 authors"
253
+ expect(page).to have_content 'Successfully imported 2 authors'
254
254
  expect(Author.count).to eq(2)
255
255
  end
256
256
  end
257
257
 
258
- context "with headers" do
259
- it "should import file with many records" do
258
+ context 'with headers' do
259
+ it 'should import file with many records' do
260
260
  upload_file!(:authors)
261
- expect(page).to have_content "Successfully imported 2 authors"
261
+ expect(page).to have_content 'Successfully imported 2 authors'
262
262
  expect(Author.count).to eq(2)
263
263
  end
264
264
 
265
- it "should import file with 1 record" do
265
+ it 'should import file with 1 record' do
266
266
  upload_file!(:author)
267
- expect(page).to have_content "Successfully imported 1 author"
267
+ expect(page).to have_content 'Successfully imported 1 author'
268
268
  expect(Author.count).to eq(1)
269
269
  end
270
270
  end
271
271
 
272
- context "without headers" do
273
- context "with known csv headers" do
272
+ context 'without headers' do
273
+ context 'with known csv headers' do
274
274
  let(:options) do
275
275
  attributes = { csv_headers: ['Name', 'Last name', 'Birthday'] }
276
276
  { template_object: ActiveAdminImport::Model.new(attributes) }
277
277
  end
278
278
 
279
- it "should import file" do
279
+ it 'should import file' do
280
280
  upload_file!(:authors_no_headers)
281
- expect(page).to have_content "Successfully imported 2 authors"
281
+ expect(page).to have_content 'Successfully imported 2 authors'
282
282
  expect(Author.count).to eq(2)
283
283
  end
284
284
  end
285
285
 
286
- context "with unknown csv headers" do
287
- it "should render error" do
286
+ context 'with unknown csv headers' do
287
+ it 'should render error' do
288
288
  upload_file!(:authors_no_headers)
289
- expect(page).to have_content "Error:"
289
+ expect(page).to have_content 'Error:'
290
290
  expect(Author.count).to eq(0)
291
291
  end
292
292
  end
293
293
  end
294
294
 
295
- context "with invalid data insert on DB constraint" do
295
+ context 'with invalid data insert on DB constraint' do
296
296
  # :name field has an uniq index
297
- it "should render error" do
297
+ it 'should render error' do
298
298
  upload_file!(:authors_invalid_db)
299
- expect(page).to have_content "Error:"
299
+ expect(page).to have_content 'Error:'
300
300
  expect(Author.count).to eq(0)
301
301
  end
302
302
  end
303
303
 
304
- context "with invalid data insert on model validation" do
304
+ context 'with invalid data insert on model validation' do
305
305
  let(:options) { { validate: true } }
306
306
 
307
307
  before do
308
- Author.create!(name: "John", last_name: "Doe")
308
+ Author.create!(name: 'John', last_name: 'Doe')
309
309
  end
310
310
 
311
- it "should render both successful and failed message" do
311
+ it 'should render both successful and failed message' do
312
312
  upload_file!(:authors_invalid_model)
313
- expect(page).to have_content "Failed to import 1 author"
314
- expect(page).to have_content "Successfully imported 1 author"
313
+ expect(page).to have_content 'Failed to import 1 author'
314
+ expect(page).to have_content 'Successfully imported 1 author'
315
+ expect(page).to have_content 'Last name has already been taken - Doe'
315
316
  expect(Author.count).to eq(2)
316
317
  end
317
318
 
318
- context "use batch_transaction to make transaction work on model validation" do
319
+ context 'use batch_transaction to make transaction work on model validation' do
319
320
  let(:options) { { validate: true, batch_transaction: true } }
320
321
 
321
- it "should render only the failed message" do
322
+ it 'should render only the failed message' do
322
323
  upload_file!(:authors_invalid_model)
323
- expect(page).to have_content "Failed to import 1 author"
324
- expect(page).to_not have_content "Successfully imported"
324
+ expect(page).to have_content 'Failed to import 1 author'
325
+ expect(page).to_not have_content 'Successfully imported'
325
326
  expect(Author.count).to eq(1)
326
327
  end
327
328
  end
328
329
  end
329
330
 
330
- context "with invalid records" do
331
- context "with validation" do
332
- it "should render error" do
331
+ context 'with invalid records' do
332
+ context 'with validation' do
333
+ it 'should render error' do
333
334
  upload_file!(:author_invalid)
334
- expect(page).to have_content "Failed to import 1 author"
335
+ expect(page).to have_content 'Failed to import 1 author'
335
336
  expect(Author.count).to eq(0)
336
337
  end
337
338
  end
338
339
 
339
- context "without validation" do
340
+ context 'without validation' do
340
341
  let(:options) { { validate: false } }
341
- it "should render error" do
342
+ it 'should render error' do
342
343
  upload_file!(:author_invalid)
343
- expect(page).to have_content "Successfully imported 1 author"
344
+ expect(page).to have_content 'Successfully imported 1 author'
344
345
  expect(Author.count).to eq(1)
345
346
  end
346
347
  end
347
348
  end
348
349
 
349
- context "when zipped" do
350
- context "when allowed" do
351
- it "should import file" do
350
+ context 'when zipped' do
351
+ context 'when allowed' do
352
+ it 'should import file' do
352
353
  with_zipped_csv(:authors) do
353
354
  upload_file!(:authors, :zip)
354
- expect(page).to have_content "Successfully imported 2 authors"
355
+ expect(page).to have_content 'Successfully imported 2 authors'
355
356
  expect(Author.count).to eq(2)
356
357
  end
357
358
  end
358
359
  end
359
360
 
360
- context "when not allowed" do
361
+ context 'when not allowed' do
361
362
  let(:options) do
362
363
  attributes = { allow_archive: false }
363
364
  { template_object: ActiveAdminImport::Model.new(attributes) }
364
365
  end
365
- it "should render error" do
366
+ it 'should render error' do
366
367
  with_zipped_csv(:authors) do
367
368
  upload_file!(:authors, :zip)
368
369
  expect(page).to have_content I18n.t('active_admin_import.file_format_error')
@@ -372,43 +373,56 @@ describe 'import', type: :feature do
372
373
  end
373
374
  end
374
375
 
375
- context "with different header attribute names" do
376
+ context 'with different header attribute names' do
376
377
  let(:options) do
377
378
  { headers_rewrites: { :'Second name' => :last_name } }
378
379
  end
379
380
 
380
- it "should import file" do
381
+ it 'should import file' do
381
382
  upload_file!(:author_broken_header)
382
- expect(page).to have_content "Successfully imported 1 author"
383
+ expect(page).to have_content 'Successfully imported 1 author'
383
384
  expect(Author.count).to eq(1)
384
385
  end
385
386
  end
386
387
 
387
- context "with semicolons separator" do
388
+ context 'with semicolons separator' do
388
389
  let(:options) do
389
- attributes = { csv_options: { col_sep: ";" } }
390
+ attributes = { csv_options: { col_sep: ';' } }
390
391
  { template_object: ActiveAdminImport::Model.new(attributes) }
391
392
  end
392
393
 
393
- it "should import file" do
394
+ it 'should import file' do
394
395
  upload_file!(:authors_with_semicolons)
395
- expect(page).to have_content "Successfully imported 2 authors"
396
+ expect(page).to have_content 'Successfully imported 2 authors'
397
+ expect(Author.count).to eq(2)
398
+ end
399
+ end
400
+
401
+ context 'with tab separator' do
402
+ let(:options) do
403
+ attributes = { csv_options: { col_sep: "\t" } }
404
+ { template_object: ActiveAdminImport::Model.new(attributes) }
405
+ end
406
+
407
+ it 'should import file' do
408
+ upload_file!(:authors_with_tabs, 'tsv')
409
+ expect(page).to have_content 'Successfully imported 2 authors'
396
410
  expect(Author.count).to eq(2)
397
411
  end
398
412
  end
399
413
  end
400
414
 
401
- context "with callback procs options" do
415
+ context 'with callback procs options' do
402
416
  let(:options) do
403
417
  {
404
- before_import: ->(_) { true },
405
- after_import: ->(_) { true },
406
- before_batch_import: ->(_) { true },
407
- after_batch_import: ->(_) { true }
418
+ before_import: ->(_) { true },
419
+ after_import: ->(_) { true },
420
+ before_batch_import: ->(_) { true },
421
+ after_batch_import: ->(_) { true }
408
422
  }
409
423
  end
410
424
 
411
- it "should call each callback" do
425
+ it 'should call each callback' do
412
426
  expect(options[:before_import]).to receive(:call).with(kind_of(ActiveAdminImport::Importer))
413
427
  expect(options[:after_import]).to receive(:call).with(kind_of(ActiveAdminImport::Importer))
414
428
  expect(options[:before_batch_import]).to receive(:call).with(kind_of(ActiveAdminImport::Importer))
@@ -417,16 +431,13 @@ describe 'import', type: :feature do
417
431
  expect(Author.count).to eq(2)
418
432
  end
419
433
  end
420
-
421
434
  end
422
435
 
423
- context "with invalid options" do
436
+ context 'with invalid options' do
424
437
  let(:options) { { invalid_option: :invalid_value } }
425
438
 
426
- it "should raise TypeError" do
439
+ it 'should raise TypeError' do
427
440
  expect { add_author_resource(options) }.to raise_error(ArgumentError)
428
441
  end
429
-
430
442
  end
431
-
432
443
  end