active_admin_import 5.1.0 → 7.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +199 -31
- data/Gemfile +28 -12
- data/README.md +253 -15
- data/Rakefile +1 -4
- data/active_admin_import.gemspec +4 -4
- data/lib/active_admin_import/dsl.rb +26 -15
- data/lib/active_admin_import/importer.rb +3 -2
- data/lib/active_admin_import/model.rb +2 -1
- data/lib/active_admin_import/options.rb +7 -2
- data/lib/active_admin_import/version.rb +1 -1
- data/spec/fixtures/files/author_invalid_format.txt +2 -0
- data/spec/fixtures/files/post_comments.csv +3 -0
- data/spec/import_spec.rb +153 -10
- data/spec/spec_helper.rb +11 -18
- data/spec/support/admin.rb +16 -0
- data/spec/support/import_form_selectors.rb +24 -0
- data/spec/support/rails_template.rb +58 -16
- data/spec/support/test_app_paths.rb +14 -0
- data/tasks/test.rake +27 -4
- metadata +20 -40
- data/CHANGELOG.md +0 -33
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f72286b16c17763984dba1b9da29ada72f8f7be7e64e5ae64010a1dcd7926af3
|
|
4
|
+
data.tar.gz: 2f50c17a8aed4cf2f02d8f28e2e83976e1af4fb593d5fcd763339f4a221be7ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8921862e1b2de5bfa6d72f5655fe3cc0fc9d2ac7b4410d8ded785521443daf4b582d8e9b2978645c66bb0446b6d11478d7e72e452c70433274d118baa35e6127
|
|
7
|
+
data.tar.gz: 332150892d0732efeabf1b7a3de70fa81c9910e6f235ac08b83710eb993709281fb62aec6c21388f6ea9786cf7afb9791dffabdd7c651ac4275cd1ffebd57b3f
|
data/.github/workflows/test.yml
CHANGED
|
@@ -1,43 +1,211 @@
|
|
|
1
|
-
name:
|
|
2
|
-
|
|
1
|
+
name: CI
|
|
3
2
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
pull_request:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
6
11
|
|
|
7
12
|
jobs:
|
|
8
|
-
|
|
13
|
+
test:
|
|
14
|
+
name: Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / AA ${{ matrix.activeadmin }} / SQLite
|
|
9
15
|
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
16
|
strategy:
|
|
17
|
+
fail-fast: false
|
|
12
18
|
matrix:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- '3.0'
|
|
17
|
-
rails_version:
|
|
18
|
-
- '5.2.6'
|
|
19
|
-
- '6.0.4'
|
|
20
|
-
- '6.1.4'
|
|
21
|
-
- '7.0.0'
|
|
19
|
+
ruby: ['3.2', '3.3', '3.4']
|
|
20
|
+
rails: ['7.1.0', '7.2.0', '8.0.0']
|
|
21
|
+
activeadmin: ['3.2.0', '3.3.0', '3.4.0', '3.5.1']
|
|
22
22
|
exclude:
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
- rails: '8.0.0'
|
|
24
|
+
activeadmin: '3.2.0'
|
|
25
|
+
include:
|
|
26
|
+
- ruby: '3.4'
|
|
27
|
+
rails: '7.2.0'
|
|
28
|
+
activeadmin: '4.0.0.beta22'
|
|
29
|
+
- ruby: '3.4'
|
|
30
|
+
rails: '8.0.0'
|
|
31
|
+
activeadmin: '4.0.0.beta22'
|
|
30
32
|
env:
|
|
31
|
-
RAILS: ${{ matrix.
|
|
32
|
-
|
|
33
|
+
RAILS: ${{ matrix.rails }}
|
|
34
|
+
AA: ${{ matrix.activeadmin }}
|
|
33
35
|
steps:
|
|
34
|
-
- uses: actions/checkout@
|
|
35
|
-
|
|
36
|
-
- name: Setup Ruby
|
|
37
|
-
uses: ruby/setup-ruby@v1
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: ruby/setup-ruby@v1
|
|
38
38
|
with:
|
|
39
|
-
ruby-version: ${{ matrix.
|
|
39
|
+
ruby-version: ${{ matrix.ruby }}
|
|
40
40
|
bundler-cache: true
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
- name: AA v4 environment summary
|
|
42
|
+
if: matrix.activeadmin == '4.0.0.beta22'
|
|
43
|
+
run: |
|
|
44
|
+
bundle exec gem list activeadmin
|
|
45
|
+
bundle info activeadmin
|
|
46
|
+
- name: Run tests
|
|
47
|
+
run: bundle exec rspec spec
|
|
48
|
+
test-mysql:
|
|
49
|
+
name: Ruby 3.4 / Rails 8.0.0 / AA 3.5.1 / MySQL 8.0
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
env:
|
|
52
|
+
RAILS: '8.0.0'
|
|
53
|
+
AA: '3.5.1'
|
|
54
|
+
DB: mysql
|
|
55
|
+
DB_HOST: 127.0.0.1
|
|
56
|
+
DB_PORT: 3306
|
|
57
|
+
DB_USERNAME: root
|
|
58
|
+
DB_PASSWORD: root
|
|
59
|
+
services:
|
|
60
|
+
mysql:
|
|
61
|
+
image: mysql:8.0
|
|
62
|
+
env:
|
|
63
|
+
MYSQL_ROOT_PASSWORD: root
|
|
64
|
+
MYSQL_DATABASE: active_admin_import_test
|
|
65
|
+
ports:
|
|
66
|
+
- 3306:3306
|
|
67
|
+
options: >-
|
|
68
|
+
--health-cmd="mysqladmin ping -h localhost -uroot -proot"
|
|
69
|
+
--health-interval=10s
|
|
70
|
+
--health-timeout=5s
|
|
71
|
+
--health-retries=10
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
- uses: ruby/setup-ruby@v1
|
|
75
|
+
with:
|
|
76
|
+
ruby-version: '3.4'
|
|
77
|
+
bundler-cache: true
|
|
78
|
+
- name: Run tests
|
|
79
|
+
run: bundle exec rspec spec
|
|
80
|
+
test-postgres:
|
|
81
|
+
name: Ruby 3.4 / Rails 8.0.0 / AA 3.5.1 / PostgreSQL 16
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
env:
|
|
84
|
+
RAILS: '8.0.0'
|
|
85
|
+
AA: '3.5.1'
|
|
86
|
+
DB: postgres
|
|
87
|
+
DB_HOST: 127.0.0.1
|
|
88
|
+
DB_PORT: 5432
|
|
89
|
+
DB_USERNAME: postgres
|
|
90
|
+
DB_PASSWORD: postgres
|
|
91
|
+
services:
|
|
92
|
+
postgres:
|
|
93
|
+
image: postgres:16
|
|
94
|
+
env:
|
|
95
|
+
POSTGRES_USER: postgres
|
|
96
|
+
POSTGRES_PASSWORD: postgres
|
|
97
|
+
POSTGRES_DB: active_admin_import_test
|
|
98
|
+
ports:
|
|
99
|
+
- 5432:5432
|
|
100
|
+
options: >-
|
|
101
|
+
--health-cmd="pg_isready -U postgres"
|
|
102
|
+
--health-interval=10s
|
|
103
|
+
--health-timeout=5s
|
|
104
|
+
--health-retries=10
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v4
|
|
107
|
+
- uses: ruby/setup-ruby@v1
|
|
108
|
+
with:
|
|
109
|
+
ruby-version: '3.4'
|
|
110
|
+
bundler-cache: true
|
|
111
|
+
- name: Run tests
|
|
43
112
|
run: bundle exec rspec spec
|
|
113
|
+
test-postgres-aa4:
|
|
114
|
+
name: Ruby 3.4 / Rails 8.0.0 / AA 4.0.0.beta22 / PostgreSQL 16
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
env:
|
|
117
|
+
RAILS: '8.0.0'
|
|
118
|
+
AA: '4.0.0.beta22'
|
|
119
|
+
DB: postgres
|
|
120
|
+
DB_HOST: 127.0.0.1
|
|
121
|
+
DB_PORT: 5432
|
|
122
|
+
DB_USERNAME: postgres
|
|
123
|
+
DB_PASSWORD: postgres
|
|
124
|
+
services:
|
|
125
|
+
postgres:
|
|
126
|
+
image: postgres:16
|
|
127
|
+
env:
|
|
128
|
+
POSTGRES_USER: postgres
|
|
129
|
+
POSTGRES_PASSWORD: postgres
|
|
130
|
+
POSTGRES_DB: active_admin_import_test
|
|
131
|
+
ports:
|
|
132
|
+
- 5432:5432
|
|
133
|
+
options: >-
|
|
134
|
+
--health-cmd="pg_isready -U postgres"
|
|
135
|
+
--health-interval=10s
|
|
136
|
+
--health-timeout=5s
|
|
137
|
+
--health-retries=10
|
|
138
|
+
steps:
|
|
139
|
+
- uses: actions/checkout@v4
|
|
140
|
+
- uses: ruby/setup-ruby@v1
|
|
141
|
+
with:
|
|
142
|
+
ruby-version: '3.4'
|
|
143
|
+
bundler-cache: true
|
|
144
|
+
- name: AA v4 environment summary
|
|
145
|
+
run: |
|
|
146
|
+
bundle exec gem list activeadmin
|
|
147
|
+
bundle info activeadmin
|
|
148
|
+
- name: Run tests
|
|
149
|
+
run: bundle exec rspec spec
|
|
150
|
+
coverage:
|
|
151
|
+
name: Coverage
|
|
152
|
+
runs-on: ubuntu-latest
|
|
153
|
+
steps:
|
|
154
|
+
- uses: actions/checkout@v4
|
|
155
|
+
- uses: ruby/setup-ruby@v1
|
|
156
|
+
with:
|
|
157
|
+
ruby-version: '3.4'
|
|
158
|
+
bundler-cache: true
|
|
159
|
+
- name: Run tests with coverage
|
|
160
|
+
run: bundle exec rspec spec
|
|
161
|
+
- name: Upload coverage
|
|
162
|
+
uses: actions/upload-artifact@v4
|
|
163
|
+
with:
|
|
164
|
+
name: coverage
|
|
165
|
+
path: coverage/
|
|
166
|
+
|
|
167
|
+
- name: Generate badge.json
|
|
168
|
+
run: |
|
|
169
|
+
LAST_RUN="coverage/.last_run.json"
|
|
170
|
+
if [ ! -f "$LAST_RUN" ]; then
|
|
171
|
+
mkdir -p badge
|
|
172
|
+
echo '{"schemaVersion":1,"label":"coverage","message":"unknown","color":"lightgrey"}' > badge/badge.json
|
|
173
|
+
exit 0
|
|
174
|
+
fi
|
|
175
|
+
PERCENT=$(ruby -rjson -e "puts JSON.parse(File.read('$LAST_RUN')).dig('result','line').round(1)")
|
|
176
|
+
PERCENT_NUM=$(ruby -rjson -e "puts JSON.parse(File.read('$LAST_RUN')).dig('result','line')")
|
|
177
|
+
if ruby -e "exit(($PERCENT_NUM >= 90) ? 0 : 1)"; then COLOR="brightgreen"
|
|
178
|
+
elif ruby -e "exit(($PERCENT_NUM >= 75) ? 0 : 1)"; then COLOR="green"
|
|
179
|
+
elif ruby -e "exit(($PERCENT_NUM >= 60) ? 0 : 1)"; then COLOR="yellow"
|
|
180
|
+
else COLOR="red"; fi
|
|
181
|
+
mkdir -p badge
|
|
182
|
+
echo "{\"schemaVersion\":1,\"label\":\"coverage\",\"message\":\"${PERCENT}%\",\"color\":\"${COLOR}\"}" > badge/badge.json
|
|
183
|
+
|
|
184
|
+
- name: Upload badge artifact
|
|
185
|
+
uses: actions/upload-artifact@v4
|
|
186
|
+
with:
|
|
187
|
+
name: coverage-badge
|
|
188
|
+
path: badge
|
|
189
|
+
|
|
190
|
+
deploy-coverage:
|
|
191
|
+
needs: coverage
|
|
192
|
+
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
environment:
|
|
195
|
+
name: github-pages
|
|
196
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
197
|
+
steps:
|
|
198
|
+
- name: Download coverage badge
|
|
199
|
+
uses: actions/download-artifact@v4
|
|
200
|
+
with:
|
|
201
|
+
name: coverage-badge
|
|
202
|
+
path: .
|
|
203
|
+
- name: Setup Pages
|
|
204
|
+
uses: actions/configure-pages@v5
|
|
205
|
+
- name: Upload Pages artifact
|
|
206
|
+
uses: actions/upload-pages-artifact@v3
|
|
207
|
+
with:
|
|
208
|
+
path: .
|
|
209
|
+
- name: Deploy to GitHub Pages
|
|
210
|
+
id: deployment
|
|
211
|
+
uses: actions/deploy-pages@v4
|
data/Gemfile
CHANGED
|
@@ -1,21 +1,37 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
source 'https://rubygems.org'
|
|
3
|
-
|
|
4
|
-
# Specify your gem's dependencies in active_admin_importable.gemspec
|
|
5
2
|
gemspec
|
|
6
3
|
|
|
4
|
+
default_rails_version = '7.1.0'
|
|
5
|
+
default_activeadmin_version = '3.2.0'
|
|
6
|
+
|
|
7
|
+
# `~> 4.0.0.beta22` would admit 4.0.0 GA — pin prereleases exactly so the
|
|
8
|
+
# CI cell tests the AA build it claims to test.
|
|
9
|
+
aa_version = ENV['AA'] || default_activeadmin_version
|
|
10
|
+
aa_op = aa_version.match?(/[a-z]/) ? '=' : '~>'
|
|
11
|
+
|
|
12
|
+
gem 'rails', "~> #{ENV['RAILS'] || default_rails_version}"
|
|
13
|
+
gem 'activeadmin', "#{aa_op} #{aa_version}"
|
|
14
|
+
|
|
15
|
+
if ENV['AA']&.start_with?('4')
|
|
16
|
+
# AA 4 uses Tailwind + importmap; sass-rails conflicts with Tailwind v4.
|
|
17
|
+
gem 'cssbundling-rails'
|
|
18
|
+
gem 'importmap-rails'
|
|
19
|
+
else
|
|
20
|
+
gem 'sprockets-rails'
|
|
21
|
+
gem 'sass-rails'
|
|
22
|
+
end
|
|
7
23
|
|
|
8
24
|
group :test do
|
|
9
|
-
|
|
10
|
-
rails_version = ENV['RAILS'] || default_rails_version
|
|
11
|
-
gem 'sassc-rails'
|
|
12
|
-
gem 'rails', rails_version
|
|
25
|
+
gem 'simplecov', require: false
|
|
13
26
|
gem 'rspec-rails'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
case ENV['DB']
|
|
28
|
+
when 'mysql'
|
|
29
|
+
gem 'mysql2'
|
|
30
|
+
when 'postgres', 'postgresql'
|
|
31
|
+
gem 'pg'
|
|
32
|
+
else
|
|
33
|
+
gem 'sqlite3', '~> 2.0'
|
|
34
|
+
end
|
|
17
35
|
gem 'database_cleaner'
|
|
18
36
|
gem 'capybara'
|
|
19
|
-
gem 'poltergeist'
|
|
20
|
-
gem 'jquery-ui-rails', '~> 5.0'
|
|
21
37
|
end
|
data/README.md
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# ActiveAdminImport
|
|
2
2
|
|
|
3
|
-
[![Build Status
|
|
4
|
-
|
|
5
|
-
[![Code Climate
|
|
6
|
-
[![Gem Version
|
|
7
|
-
[![License
|
|
3
|
+
[![Build Status][build_badge]][build_link]
|
|
4
|
+
![Coverage][coverage_badge]
|
|
5
|
+
[![Code Climate][codeclimate_badge]][codeclimate_link]
|
|
6
|
+
[![Gem Version][rubygems_badge]][rubygems_link]
|
|
7
|
+
[![License][license_badge]][license_link]
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
The
|
|
11
|
-
|
|
12
|
-
For more about ActiveAdminImport installation and usage, check [Documentation website](http://activeadmin-plugins.github.io/active_admin_import/) and [Wiki pages](https://github.com/activeadmin-plugins/active_admin_import/wiki) for some specific cases and caveats.
|
|
10
|
+
The fastest and most efficient CSV import for Active Admin with support for validations, bulk inserts, and encoding handling.
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
## Installation
|
|
@@ -44,7 +42,7 @@ And then execute:
|
|
|
44
42
|
#### Basic usage
|
|
45
43
|
|
|
46
44
|
```ruby
|
|
47
|
-
ActiveAdmin.register Post
|
|
45
|
+
ActiveAdmin.register Post do
|
|
48
46
|
active_admin_import options
|
|
49
47
|
end
|
|
50
48
|
```
|
|
@@ -68,18 +66,259 @@ Tool | Description
|
|
|
68
66
|
:timestamps |bool, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
|
69
67
|
:template |custom template rendering
|
|
70
68
|
:template_object |object passing to view
|
|
69
|
+
:result_class |custom `ImportResult` subclass to collect data from each batch (e.g. inserted ids). Must respond to `add(batch_result, qty)` plus the readers used in flash messages (`failed`, `total`, `imported_qty`, `imported?`, `failed?`, `empty?`, `failed_message`).
|
|
71
70
|
:resource_class |resource class name
|
|
72
71
|
:resource_label |resource label value
|
|
73
72
|
:plural_resource_label |pluralized resource label value (default config.plural_resource_label)
|
|
74
73
|
:error_limit |Limit the number of errors reported (default `5`, set to `nil` for all)
|
|
75
74
|
:headers_rewrites |hash with key (csv header) - value (db column name) rows mapping
|
|
76
75
|
:if |Controls whether the 'Import' button is displayed. It supports a proc to be evaluated into a boolean value within the activeadmin render context.
|
|
76
|
+
:action_item_html_options |HTML options passed to the index-page "Import …" action_item link. Defaults to `{ class: 'action-item-button' }` so the link matches AA 4's built-in action_items; the class is a no-op on AA 3. Override to drop the class or add your own (`{ class: 'my-btn' }`, `{ class: '', data: { turbo: false } }`, etc.).
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
#### Custom ImportResult
|
|
81
|
+
|
|
82
|
+
To collect extra data from each batch (for example the ids of inserted rows so you can enqueue background jobs against them), pass a subclass of `ActiveAdminImport::ImportResult` via `:result_class`:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
class ImportResultWithIds < ActiveAdminImport::ImportResult
|
|
86
|
+
attr_reader :ids
|
|
87
|
+
|
|
88
|
+
def initialize
|
|
89
|
+
super
|
|
90
|
+
@ids = []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def add(batch_result, qty)
|
|
94
|
+
super
|
|
95
|
+
@ids.concat(Array(batch_result.ids))
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
ActiveAdmin.register Author do
|
|
100
|
+
active_admin_import result_class: ImportResultWithIds do |result, options|
|
|
101
|
+
EnqueueAuthorsJob.perform_later(result.ids) if result.imported?
|
|
102
|
+
instance_exec(result, options, &ActiveAdminImport::DSL::DEFAULT_RESULT_PROC)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
```
|
|
77
106
|
|
|
107
|
+
The action block is invoked via `instance_exec` with `result` and `options` as block arguments, so you can either capture them with `do |result, options|` or read them as locals when no arguments are declared.
|
|
78
108
|
|
|
109
|
+
Note: which batch-result attributes are populated depends on the database adapter and the import options. `activerecord-import` returns ids reliably on PostgreSQL; on MySQL/SQLite the behavior depends on the adapter and options like `on_duplicate_key_update`. Putting the collection logic in your own subclass keeps these adapter quirks in your application code.
|
|
79
110
|
|
|
80
|
-
#### Wiki
|
|
81
111
|
|
|
82
|
-
|
|
112
|
+
#### Authorization
|
|
113
|
+
|
|
114
|
+
The current user must be authorized to perform imports. With CanCanCan:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
class Ability
|
|
118
|
+
include CanCan::Ability
|
|
119
|
+
|
|
120
|
+
def initialize(user)
|
|
121
|
+
can :import, Post
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
#### Per-request context
|
|
128
|
+
|
|
129
|
+
Define an `active_admin_import_context` method on the controller to inject request-derived attributes into every import (current user, parent resource id, request IP, etc.). The returned hash is merged into the import model after form params, so it always wins for the keys it provides:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
ActiveAdmin.register PostComment do
|
|
133
|
+
belongs_to :post
|
|
134
|
+
|
|
135
|
+
controller do
|
|
136
|
+
def active_admin_import_context
|
|
137
|
+
{ post_id: parent.id, request_ip: request.remote_ip }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
active_admin_import before_batch_import: ->(importer) {
|
|
142
|
+
importer.csv_lines.map! { |row| row << importer.model.post_id }
|
|
143
|
+
importer.headers.merge!(:'Post Id' => :post_id)
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
#### Examples
|
|
150
|
+
|
|
151
|
+
##### Files without CSV headers
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
ActiveAdmin.register Post do
|
|
155
|
+
active_admin_import validate: true,
|
|
156
|
+
template_object: ActiveAdminImport::Model.new(
|
|
157
|
+
hint: "expected header order: body, title, author",
|
|
158
|
+
csv_headers: %w[body title author]
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
##### Auto-detect file encoding
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
ActiveAdmin.register Post do
|
|
167
|
+
active_admin_import validate: true,
|
|
168
|
+
template_object: ActiveAdminImport::Model.new(force_encoding: :auto)
|
|
169
|
+
end
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
##### Force a specific (non-UTF-8) encoding
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
ActiveAdmin.register Post do
|
|
176
|
+
active_admin_import validate: true,
|
|
177
|
+
template_object: ActiveAdminImport::Model.new(
|
|
178
|
+
hint: "file is encoded in ISO-8859-1",
|
|
179
|
+
force_encoding: "ISO-8859-1"
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
##### Disallow ZIP upload
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
ActiveAdmin.register Post do
|
|
188
|
+
active_admin_import validate: true,
|
|
189
|
+
template_object: ActiveAdminImport::Model.new(
|
|
190
|
+
hint: "upload a CSV file",
|
|
191
|
+
allow_archive: false
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
##### Skip CSV columns
|
|
197
|
+
|
|
198
|
+
Useful when the CSV file has columns that don't exist on the table. Available since 3.1.0.
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
ActiveAdmin.register Post do
|
|
202
|
+
active_admin_import before_batch_import: ->(importer) {
|
|
203
|
+
importer.batch_slice_columns(['name', 'last_name'])
|
|
204
|
+
}
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Tip: pass `Post.column_names` to keep only the columns that exist on the table.
|
|
209
|
+
|
|
210
|
+
##### Resolve associations on the fly
|
|
211
|
+
|
|
212
|
+
Replace an `Author name` column in the CSV with the matching `author_id` before insert:
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
ActiveAdmin.register Post do
|
|
216
|
+
active_admin_import validate: true,
|
|
217
|
+
headers_rewrites: { 'Author name': :author_id },
|
|
218
|
+
before_batch_import: ->(importer) {
|
|
219
|
+
names = importer.values_at(:author_id)
|
|
220
|
+
mapping = Author.where(name: names).pluck(:name, :id).to_h
|
|
221
|
+
importer.batch_replace(:author_id, mapping)
|
|
222
|
+
}
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
##### Update existing records by id
|
|
227
|
+
|
|
228
|
+
Delete colliding rows just before each batch insert:
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
ActiveAdmin.register Post do
|
|
232
|
+
active_admin_import before_batch_import: ->(importer) {
|
|
233
|
+
Post.where(id: importer.values_at('id')).delete_all
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
For databases that support upserts you can use `:on_duplicate_key_update` instead.
|
|
239
|
+
|
|
240
|
+
##### Tune batch size
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
ActiveAdmin.register Post do
|
|
244
|
+
active_admin_import validate: false,
|
|
245
|
+
csv_options: { col_sep: ";" },
|
|
246
|
+
batch_size: 1000
|
|
247
|
+
end
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
##### Import into an intermediate table
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
ActiveAdmin.register Post do
|
|
254
|
+
active_admin_import validate: false,
|
|
255
|
+
csv_options: { col_sep: ";" },
|
|
256
|
+
resource_class: ImportedPost, # write to a staging table
|
|
257
|
+
before_import: ->(_) { ImportedPost.delete_all },
|
|
258
|
+
after_import: ->(_) {
|
|
259
|
+
Post.transaction do
|
|
260
|
+
Post.delete_all
|
|
261
|
+
Post.connection.execute("INSERT INTO posts (SELECT * FROM imported_posts)")
|
|
262
|
+
end
|
|
263
|
+
},
|
|
264
|
+
back: ->(_) { config.namespace.resource_for(Post).route_collection_path }
|
|
265
|
+
end
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
##### Allow user input for CSV options (custom template)
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
ActiveAdmin.register Post do
|
|
272
|
+
active_admin_import validate: false,
|
|
273
|
+
template: 'admin/posts/import',
|
|
274
|
+
template_object: ActiveAdminImport::Model.new(
|
|
275
|
+
hint: "you can configure CSV options",
|
|
276
|
+
csv_options: { col_sep: ";", row_sep: nil, quote_char: nil }
|
|
277
|
+
)
|
|
278
|
+
end
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`app/views/admin/posts/import.html.erb`:
|
|
282
|
+
|
|
283
|
+
```erb
|
|
284
|
+
<p><%= raw(@active_admin_import_model.hint) %></p>
|
|
285
|
+
|
|
286
|
+
<%= semantic_form_for @active_admin_import_model, url: { action: :do_import }, html: { multipart: true } do |f| %>
|
|
287
|
+
<%= f.inputs do %>
|
|
288
|
+
<%= f.input :file, as: :file %>
|
|
289
|
+
<% end %>
|
|
290
|
+
|
|
291
|
+
<%= f.inputs "CSV options", for: [:csv_options, OpenStruct.new(@active_admin_import_model.csv_options)] do |csv| %>
|
|
292
|
+
<% csv.with_options input_html: { style: 'width:40px;' } do |opts| %>
|
|
293
|
+
<%= opts.input :col_sep %>
|
|
294
|
+
<%= opts.input :row_sep %>
|
|
295
|
+
<%= opts.input :quote_char %>
|
|
296
|
+
<% end %>
|
|
297
|
+
<% end %>
|
|
298
|
+
|
|
299
|
+
<%= f.actions do %>
|
|
300
|
+
<%= f.action :submit,
|
|
301
|
+
label: t("active_admin_import.import_btn"),
|
|
302
|
+
button_html: { disable_with: t("active_admin_import.import_btn_disabled") } %>
|
|
303
|
+
<% end %>
|
|
304
|
+
<% end %>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
##### Inspecting the importer in batch callbacks
|
|
308
|
+
|
|
309
|
+
Both `before_batch_import` and `after_batch_import` receive the `Importer` instance:
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
active_admin_import before_batch_import: ->(importer) {
|
|
313
|
+
importer.file # the uploaded file
|
|
314
|
+
importer.resource # the ActiveRecord class being imported into
|
|
315
|
+
importer.options # the resolved options hash
|
|
316
|
+
importer.headers # CSV headers (mutable)
|
|
317
|
+
importer.csv_lines # parsed CSV rows for the current batch (mutable)
|
|
318
|
+
importer.model # the template_object instance
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
83
322
|
|
|
84
323
|
## Dependencies
|
|
85
324
|
|
|
@@ -93,14 +332,13 @@ Tool | Description
|
|
|
93
332
|
|
|
94
333
|
[build_badge]: https://github.com/activeadmin-plugins/active_admin_import/actions/workflows/test.yml/badge.svg
|
|
95
334
|
[build_link]: https://github.com/activeadmin-plugins/active_admin_import/actions
|
|
96
|
-
[
|
|
97
|
-
[coveralls_link]: https://coveralls.io/github/activeadmin-plugins/active_admin_import
|
|
335
|
+
[coverage_badge]: https://img.shields.io/endpoint?url=https://activeadmin-plugins.github.io/active_admin_import/badge.json
|
|
98
336
|
[codeclimate_badge]: https://codeclimate.com/github/activeadmin-plugins/active_admin_import/badges/gpa.svg
|
|
99
337
|
[codeclimate_link]: https://codeclimate.com/github/activeadmin-plugins/active_admin_import
|
|
100
338
|
[rubygems_badge]: https://badge.fury.io/rb/active_admin_import.svg
|
|
101
339
|
[rubygems_link]: https://rubygems.org/gems/active_admin_import
|
|
102
|
-
[license_badge]:
|
|
103
|
-
[license_link]:
|
|
340
|
+
[license_badge]: https://img.shields.io/:license-mit-blue.svg
|
|
341
|
+
[license_link]: https://Fivell.mit-license.org
|
|
104
342
|
|
|
105
343
|
|
|
106
344
|
## Contributing
|
data/Rakefile
CHANGED
data/active_admin_import.gemspec
CHANGED
|
@@ -7,16 +7,16 @@ Gem::Specification.new do |gem|
|
|
|
7
7
|
gem.email = ['fedoronchuk@gmail.com']
|
|
8
8
|
gem.description = 'The most efficient way to import for Active Admin'
|
|
9
9
|
gem.summary = 'ActiveAdmin import based on activerecord-import gem.'
|
|
10
|
-
gem.homepage = '
|
|
10
|
+
gem.homepage = 'https://github.com/activeadmin-plugins/active_admin_import'
|
|
11
11
|
gem.license = 'MIT'
|
|
12
|
+
gem.required_ruby_version = '>= 3.1.0'
|
|
12
13
|
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
|
13
14
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
14
|
-
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
15
15
|
gem.name = 'active_admin_import'
|
|
16
16
|
gem.require_paths = ['lib']
|
|
17
17
|
gem.version = ActiveAdminImport::VERSION
|
|
18
|
-
gem.add_runtime_dependency 'activerecord-import', '>= 0
|
|
18
|
+
gem.add_runtime_dependency 'activerecord-import', '>= 2.0'
|
|
19
19
|
gem.add_runtime_dependency 'rchardet', '>= 1.6'
|
|
20
20
|
gem.add_runtime_dependency 'rubyzip', '>= 1.2'
|
|
21
|
-
gem.add_dependency 'activeadmin', '>=
|
|
21
|
+
gem.add_dependency 'activeadmin', '>= 3.0', '< 4.1'
|
|
22
22
|
end
|