ixport 0.1.0 → 0.2.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/README.md +85 -4
- data/app/controllers/ixport/application_controller.rb +5 -1
- data/app/controllers/ixport/export_templates_controller.rb +52 -0
- data/app/controllers/ixport/exports_controller.rb +18 -0
- data/app/controllers/ixport/imports_controller.rb +4 -3
- data/app/controllers/ixport/previews_controller.rb +2 -2
- data/app/controllers/ixport/runs_controller.rb +13 -1
- data/app/helpers/ixport/imports_helper.rb +13 -0
- data/app/models/ixport/csv_writer.rb +19 -0
- data/app/models/ixport/export_template.rb +19 -0
- data/app/models/ixport/import.rb +71 -19
- data/app/models/ixport/null_reporter.rb +11 -0
- data/app/models/ixport/row_walk.rb +1 -1
- data/app/models/ixport/wallflower_runner.rb +10 -0
- data/app/models/ixport/wallflower_task_runner.rb +14 -0
- data/app/views/ixport/export_templates/edit.html.erb +14 -0
- data/app/views/ixport/export_templates/new.html.erb +11 -0
- data/app/views/ixport/exports/index.html.erb +23 -0
- data/app/views/ixport/imports/index.html.erb +17 -0
- data/app/views/ixport/imports/show.html.erb +30 -15
- data/config/routes.rb +4 -0
- data/lib/generators/ixport/install/install_generator.rb +3 -0
- data/lib/generators/ixport/install/templates/add_error_message_to_ixport_imports.rb.erb +5 -0
- data/lib/generators/ixport/install/templates/add_wallflower_task_to_ixport_imports.rb.erb +6 -0
- data/lib/generators/ixport/install/templates/create_ixport_export_templates.rb.erb +12 -0
- data/lib/generators/ixport/update/update_generator.rb +28 -0
- data/lib/ixport/background_runner.rb +23 -0
- data/lib/ixport/configuration.rb +2 -1
- data/lib/ixport/engine.rb +4 -1
- data/lib/ixport/exporter.rb +27 -0
- data/lib/ixport/registrations.rb +29 -3
- data/lib/ixport/version.rb +1 -1
- data/lib/ixport.rb +2 -0
- data/the_local/agents/ixport-develop.md +224 -29
- data/the_local/agents/ixport-info.md +55 -22
- data/the_local/agents/ixport-install.md +63 -38
- data/the_local/interface.yml +36 -1
- metadata +18 -1
|
@@ -1,23 +1,40 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ixport-info
|
|
3
|
-
description: Use to learn what ixport offers — CSV import through a mounted Rails engine, the mapped imports a host app registers, the importer classes behind them, uploading a file, matching its columns, previewing which rows will add, update or be refused, running the import, its result page, and the vocabulary the install and develop locals assume.
|
|
3
|
+
description: Use to learn what ixport offers — CSV import and export through a mounted Rails engine, the mapped imports and the fixed and chosen exports a host app registers, the importer and exporter classes behind them, uploading a file, matching its columns, previewing which rows will add, update or be refused, running the import inside the request or in the background through wallflower or the app's own runner, its result page, what a person sees when a file is refused or a run fails, the list of a person's past imports, downloading an export as a CSV file, saving named templates of picked columns for a chosen export, limiting each import or export to the people a rule allows, and the vocabulary the install and develop locals assume.
|
|
4
4
|
tools: Read
|
|
5
|
-
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports the host app registers, each backed by an importer class the host writes
|
|
5
|
+
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports and the exports the host app registers, each import backed by an importer class and each export by an exporter class the host writes, where an export is either fixed or chosen, and a chosen export lets the account save named templates of picked columns
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This local explains what ixport is and routes you to the local that does the work. It makes no changes and gives no steps.
|
|
9
9
|
|
|
10
10
|
## What ixport is
|
|
11
11
|
|
|
12
|
-
ixport is a Rails engine for loading a host app's records from CSV files. The host app mounts it at a path of its choosing
|
|
12
|
+
ixport is a Rails engine for loading a host app's records from CSV files and downloading them back out as CSV files. The host app mounts it at a path of its choosing. A signed-in person who visits that path sees the list of imports the host has set up, and a signed-in person who visits its exports page sees the list of exports, each shown by its title.
|
|
13
13
|
|
|
14
14
|
A person picks an import, uploads a CSV file, matches each column of the file to one of the columns the import reads or skips it, and then sees a preview. The preview counts how many rows will add a record, how many will update one, and how many will be refused, and lists the rows that will update and the rows that will be refused with the reason for each. Viewing the preview saves nothing in the host app, and a Back link returns to the mapping so the person can change it.
|
|
15
15
|
|
|
16
|
-
From the preview the person runs the import. The run saves every added and updated record in the host app, records each refused row with its reason, and sends the person to the import's result page, which shows how many rows were added and updated and lists each refused row.
|
|
16
|
+
From the preview the person runs the import. The run saves every added and updated record in the host app, records each refused row with its reason, and sends the person to the import's result page, which shows how many rows were added and updated and lists each refused row. An import that has finished is never run again. Opening the preview of a running or finished import sends the person to its result page instead, so no Run button is offered for it, and opening the result page of an import that has not started running sends the person to its preview.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
A run that stops on an error is marked Failed. Every row saved before the error stays saved, and the result page is headed Import failed, shows the error's message, and shows how many rows were added and updated before it stopped along with each refused row. ixport also reports the error to the host app's error reporting. A failed import is never run again.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Where the run happens depends on the host's background runner. With none, the run happens inside the request, as above. With one, every import runs through it whatever its size: running the import marks it Running and hands it to the runner, and its result page says the import is running until the run finishes. An import is handed to the runner only once: asking to run it again while it is running, or after it has finished, sends the person to its result page.
|
|
21
|
+
|
|
22
|
+
- **Wallflower** — the host can choose wallflower as the runner. Running an import then starts a wallflower task and takes the person to the task's page, which shows the run's progress and each refused row with its reason, and links to the import's result page once the run finishes. When the run fails, the task is marked failed with the import's error message and still links to the import's result page.
|
|
23
|
+
- **The app's own runner** — the host can instead name a class of its own. ixport hands each import to that class, and the class runs it later by calling ixport back, after which the result page shows the counts and refused rows as usual. Running the import takes the person to the import's result page.
|
|
24
|
+
|
|
25
|
+
ixport refuses a file on the upload page, before any import record is kept, when it is not UTF-8 text, when a line's quotes stop it being read, naming that line, or when it has no header row. With no background runner, it also refuses a file with more rows than the host's row limit there. Each refusal says what is wrong with the file.
|
|
26
|
+
|
|
27
|
+
Below the list of imports, the same page lists the person's own past imports in the current account, newest first. Each shows the import's title, the uploaded file's name, its status, the date it was uploaded, and, once it has finished, how many rows were added, updated and refused. A failed import shows its status as Failed and no counts. Each title opens the import at the step it has reached: the mapping when no mapping is saved, the preview when it has a mapping and has not started running, and the result page once it is running, finished or failed. Imports uploaded by other people, or by the same person in another account, are not listed, and neither are the person's imports of a kind their access rule now refuses.
|
|
28
|
+
|
|
29
|
+
An export is either fixed or chosen. A fixed export's title on the exports page links to a CSV download holding every column the exporter declares. A chosen export's title is not a link. Under it the page lists the templates the account has saved for that export, each linking to a download and to its edit page, with a button to make a new template.
|
|
30
|
+
|
|
31
|
+
A template is a name and an ordered pick of the exporter's columns. A person makes one on its own page, entering a name and choosing a column, or None, for each slot. Its download holds only the picked columns, in the order picked, and the file is named after the template. On the edit page a person can change the name and the picked columns, or delete the template. A template belongs to the account, so everyone working in that account sees, downloads, edits and deletes the same templates.
|
|
32
|
+
|
|
33
|
+
Every export file's first row holds the column labels, and each later row holds one of the host's records. The host decides which records an export holds, so an export can be limited to the person's current account. The file is built inside the request and nothing about it is stored.
|
|
34
|
+
|
|
35
|
+
By default every signed-in person can use every import and export. The host can give any import or export an access rule, which is handed the signed-in person and the current account and allows or refuses them, and which can ask the app's own permission checks, such as a Pundit policy. A person the rule refuses does not see that import or export listed, does not see their past imports of it, and is answered not found on every one of its pages and downloads, including its upload page, an existing import's mapping, preview, run and result pages, and a chosen export's template pages and downloads. The rule is checked again on every request, so a person whose permissions change is let in or kept out from their next page.
|
|
36
|
+
|
|
37
|
+
The engine owns the screens, the access check, and its own tables of import records, refused rows and export templates, with the uploaded file stored through Active Storage. The host owns what each import and export means: which columns it reads or writes, which column identifies an existing record, and which of the app's records a row is checked against, saved into, or read from. Reach for ixport when a Rails app needs people to load data from spreadsheets or take it out into one, rather than writing a one-off upload or download page per record type.
|
|
21
38
|
|
|
22
39
|
The engine renders its pages with Keystone UI inside a layout the host chooses, and it runs its access check through a method the host already has.
|
|
23
40
|
|
|
@@ -25,35 +42,51 @@ The engine renders its pages with Keystone UI inside a layout the host chooses,
|
|
|
25
42
|
|
|
26
43
|
ixport declares nothing for this local to document. Its surface is split between the other two locals:
|
|
27
44
|
|
|
28
|
-
- **ixport-install** owns hooking ixport into a host app: adding the gem, running its install generator, mounting the engine, and the configuration that names the host's sign-in check, current person, current account, layout and
|
|
29
|
-
- **ixport-develop** owns building against ixport: registering a mapped import
|
|
45
|
+
- **ixport-install** owns hooking ixport into a host app: adding the gem, running its install generator, running its update generator to bring an existing install's tables up to date, mounting the engine, and the configuration that names the host's sign-in check, current person, current account, layout, row limit and background runner.
|
|
46
|
+
- **ixport-develop** owns building against ixport: registering a mapped import and the importer class behind it, registering a fixed or chosen export and the exporter class behind it, giving an import or export an access rule, writing the app's own background runner and running an import from it, the error a wallflower task raises when its import fails, the errors raised when a registration names a class that does not exist, and the engine's pages for imports, exports and export templates.
|
|
30
47
|
|
|
31
48
|
## How to use it
|
|
32
49
|
|
|
33
|
-
- The app does not have ixport yet, or you are changing how ixport reaches the app's sign-in, people, accounts, layout or row limit: use **ixport-install**.
|
|
34
|
-
- ixport is already installed and you are adding or changing an import, changing how
|
|
50
|
+
- The app does not have ixport yet, the app installed ixport before export templates or background runs existed and needs their tables, you are choosing whether imports run inside the request, through wallflower or through the app's own runner, or you are changing how ixport reaches the app's sign-in, people, accounts, layout or row limit: use **ixport-install**.
|
|
51
|
+
- ixport is already installed and you are adding or changing an import or an export, making an export chosen or fixed, changing how an import's rows are matched, checked or saved, changing which records or columns an export holds, limiting an import or export to some people, writing the app's own runner that runs imports in the background, linking to the engine's pages, or the app fails to boot naming a missing importer or exporter: use **ixport-develop**.
|
|
35
52
|
|
|
36
53
|
## Conventions
|
|
37
54
|
|
|
38
55
|
- **Mapped import** — one kind of import the host registers, made of a key, a title shown to people, and the name of its importer class.
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
56
|
+
- **Fixed export** — one kind of export the host registers, made of a key, a title shown to people, and the name of its exporter class, whose download always holds every declared column. An export is fixed unless the host registers it as chosen.
|
|
57
|
+
- **Chosen export** — an export registered the same way and marked as chosen, which has no download of its own and is downloaded only through the account's templates.
|
|
58
|
+
- **Export template** — a named, ordered pick of a chosen export's columns, saved by one person and shared by everyone in their account.
|
|
59
|
+
- **Picked columns** — the columns a template holds, in the order they were chosen. A slot left on None is dropped, a column picked twice is kept once, and a column the exporter no longer declares is left out of the download.
|
|
60
|
+
- **Key** — the short name an import or export is registered and looked up by, held as a symbol. A fixed export's file is named after its key.
|
|
61
|
+
- **Title** — the words a person sees for the import or export on the engine's pages.
|
|
41
62
|
- **Importer** — a class the host app writes that declares the columns the import reads, each with a key and a label, names the column that identifies an existing record, and supplies the set of the app's records the rows are checked against and saved into.
|
|
42
|
-
- **
|
|
63
|
+
- **Exporter** — a class the host app writes that declares the columns the export writes, each with a key and a label, and supplies the set of the app's records the file holds.
|
|
64
|
+
- **Export column** — one column of an export's file, headed by its label and filled from the attribute its key names on each record.
|
|
65
|
+
- **Through** — an export column can be read from a record related to each row's record rather than from the record itself, and it is left blank when that related record is missing.
|
|
66
|
+
- **Match column** — the one declared import column whose value decides whether a row is an existing record or a new one.
|
|
43
67
|
- **Import record** — one upload by one person, stored by the engine with the key of its mapped import, the uploaded file, the mapping once it is saved, and the added and updated counts once it has run.
|
|
44
68
|
- **Headers** — the first row of the uploaded file, read as the names of its columns.
|
|
45
69
|
- **Mapping** — which of the importer's columns each header in the file fills, stored by the header's position in the row. A header left on "Skip" is not stored.
|
|
46
|
-
- **Row limit** — the most data rows an uploaded file may have, not counting the header row. It defaults to 1,000 and the host can change it.
|
|
70
|
+
- **Row limit** — the most data rows an uploaded file may have, not counting the header row. It defaults to 1,000 and the host can change it. It applies only when the host has no background runner, and it does not apply to exports.
|
|
71
|
+
- **Background runner** — what runs an import outside the request: none, wallflower, or a class the host app writes. Having wallflower in the app does not choose it, the host has to.
|
|
72
|
+
- **Wallflower** — a separate gem that runs long tasks in the background and gives each task a page showing its progress. ixport registers its imports with it as a kind of task titled Import.
|
|
47
73
|
- **Row number** — a row's line in the file, counting the header as row 1, so the first data row is row 2.
|
|
48
74
|
- **Add** — a row whose match column value is found in no existing record, so it creates one.
|
|
49
75
|
- **Update** — a row whose match column value is found in an existing record, so it changes that record.
|
|
50
76
|
- **Refused** — a row whose record, new or changed, fails the host model's own validations. Its reason is that model's error messages.
|
|
51
77
|
- **Preview** — the page that shows the add, update and refused counts and lists the update and refused rows by row number. It builds each row's record in memory and saves none of them.
|
|
52
|
-
- **Run** — the step that saves the added and updated records and stores each refused row's number and reason. A run that raises an error
|
|
53
|
-
- **
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
59
|
-
-
|
|
78
|
+
- **Run** — the step that saves the added and updated records one row at a time and stores each refused row's number and reason. A run that raises an error stops there and keeps the rows it already saved, and an import that has finished or failed is never run a second time.
|
|
79
|
+
- **Status** — an import record is Pending until it is run, Running while a background runner has it, and then Finished, or Failed when the run stopped on an error.
|
|
80
|
+
- **Running** — the state of an import record handed to a background runner and not yet finished. Its result page says it is running and shows no counts, its preview sends the person to that result page, and it is never handed to the runner a second time.
|
|
81
|
+
- **Finished** — the state of an import record once it has run to the end.
|
|
82
|
+
- **Failed** — the state of an import record whose run stopped on an error. It keeps the error's message and the added and updated counts reached before it stopped, and its result page shows them.
|
|
83
|
+
- **Refused file** — an upload ixport turns away on the upload page because it is not UTF-8, cannot be read past a line, has no header row, or has too many rows. Nothing is stored for it, unlike a refused row, which belongs to an import that ran.
|
|
84
|
+
- **Past imports** — the person's own import records in the current account, listed newest first under the imports on the engine's front page.
|
|
85
|
+
- **Step** — how far an import record has got: mapping when no mapping is saved, preview once one is, and result once it is running, finished or failed.
|
|
86
|
+
- **Result page** — the import's own page, showing the added and updated counts and each refused row as they were stored by the run, and the error's message when the run failed, so it shows the same thing each time it is opened.
|
|
87
|
+
- **Person** — whoever is signed in and running the import or downloading the export. ixport reaches them through a method the host names, so the host's user model needs no particular name.
|
|
88
|
+
- **Account** — the optional owner the person is working inside, such as a team or company, reached the same way. An app with no accounts runs without one. Both the importer and the exporter are given the person and the account.
|
|
89
|
+
- **Ownership** — an import record belongs to the person and account that uploaded it, and anyone else who opens it, including the same person from another account, is answered not found. An export template belongs to its account, and anyone opening it from another account is answered not found.
|
|
90
|
+
- **Access rule** — an optional check the host gives one import or export when registering it, handed the person and the account and answering whether they may use it. It is checked on top of ownership, so even the person who uploaded an import is answered not found for it once the rule refuses them. An import or export with no rule is open to every signed-in person.
|
|
91
|
+
- **Boot check** — when the app starts, ixport checks that every registered importer and exporter class exists and refuses to boot when one does not, so a misspelled class name fails at startup rather than when someone runs the import or downloads the export.
|
|
92
|
+
- Imports and exports are registered by the host app, never discovered: an importer or exporter class that is not registered does not appear on the page, asking for an export key that is not registered is answered not found, and asking to make a template for an export that is not chosen is answered not found.
|
|
@@ -1,82 +1,107 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ixport-install
|
|
3
|
-
description: Use to hook ixport into a project — adding the gem, checking Active Storage is set up, running the install generator and its migrations, mounting the engine, and configuring the sign-in check, the current person method, the current account method, the layout
|
|
3
|
+
description: Use to hook ixport into a project — adding the gem, checking Active Storage is set up, running the install generator and its migrations, running the update generator on an app that installed an earlier ixport, mounting the engine, and configuring the sign-in check, the current person method, the current account method, the layout, the inline row limit and the background runner.
|
|
4
4
|
tools: Bash, Read, Edit
|
|
5
|
-
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports the host app registers, each backed by an importer class the host writes
|
|
5
|
+
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports and the exports the host app registers, each import backed by an importer class and each export by an exporter class the host writes, where an export is either fixed or chosen, and a chosen export lets the account save named templates of picked columns
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
This local hooks ixport into a Rails app by following the steps below exactly. It invents no step, setting or file that is not listed here.
|
|
9
9
|
|
|
10
10
|
## What ixport is
|
|
11
11
|
|
|
12
|
-
ixport is a Rails engine
|
|
12
|
+
ixport is a Rails engine with two pages of work for a signed-in person. On the imports page they upload a CSV file, match its columns to a host app's fields, preview which rows will add, update or be refused, and run the import to save those records into the host app. On the exports page they download a CSV file of the host app's records, either with columns the host app has fixed in advance or from a saved template of columns their account picked. Hook it in when a Rails app needs people to load data from spreadsheets or take its data out as spreadsheets.
|
|
13
13
|
|
|
14
14
|
## Interface
|
|
15
15
|
|
|
16
16
|
- `gem "ixport"` — the Gemfile line that adds ixport to the app. It also brings in Keystone UI, which draws ixport's pages.
|
|
17
|
-
- `bin/rails generate ixport:install` — copies
|
|
18
|
-
- `
|
|
19
|
-
- `Ixport
|
|
20
|
-
- `
|
|
21
|
-
- `config.
|
|
17
|
+
- `bin/rails generate ixport:install` — copies five migrations into `db/migrate/`: one that creates the `ixport_imports` table, one that creates the `ixport_import_refusals` table, one that creates the `ixport_export_templates` table, one that adds a `wallflower_task_id` column to `ixport_imports`, and one that adds an `error_message` column to `ixport_imports`.
|
|
18
|
+
- `bin/rails generate ixport:update` — copies the three migrations added since ixport first shipped, the one that creates the `ixport_export_templates` table, the one that adds the `wallflower_task_id` column and the one that adds the `error_message` column, into an app that installed an earlier ixport.
|
|
19
|
+
- `mount Ixport::Engine` — the line in `config/routes.rb` that serves ixport's pages under a path the app chooses. The imports page is at that path and the exports page is at that path followed by `/exports`.
|
|
20
|
+
- `Ixport.configure` — takes a block that receives the configuration object, where the app sets any of the six settings below.
|
|
21
|
+
- `config.authentication_method` — the name of the controller method run before every ixport page, imports, exports and export templates alike, as a symbol. It defaults to `:authenticate_user!`.
|
|
22
|
+
- `config.current_person_method` — the name of the controller method that returns the signed-in person running imports and exports and saving export templates, as a symbol. It defaults to `:current_user`.
|
|
22
23
|
- `config.current_account_method` — the name of the controller method that returns the account the person is working in, as a symbol. It defaults to `:current_account`.
|
|
23
24
|
- `config.layout` — the name of the host layout ixport's pages are drawn inside, as a string. It defaults to `"application"`.
|
|
24
|
-
- `config.inline_row_limit` — the most data rows an uploaded file may have, not counting its header row, as an integer. A file with more is refused on the upload page with a message giving the limit and the file's row count. It defaults to `1_000`.
|
|
25
|
+
- `config.inline_row_limit` — the most data rows an uploaded import file may have, not counting its header row, as an integer. A file with more is refused on the upload page with a message giving the limit and the file's row count. It applies only while `config.background_runner` is `nil`, and it does not limit exports. It defaults to `1_000`.
|
|
26
|
+
- `config.background_runner` — what runs an import once the person starts it. It defaults to `nil`, which runs the import inside the request. `:wallflower` runs every import as a wallflower task. The name of a class in the app, as a string, hands every import to that class. With any value other than `nil`, every import runs through the runner whatever its size, and the inline row limit does not apply.
|
|
25
27
|
|
|
26
28
|
## How to use it
|
|
27
29
|
|
|
28
|
-
1. Check
|
|
29
|
-
2. Check that the app has
|
|
30
|
+
1. Check whether the app already has ixport: its `Gemfile` has `gem "ixport"` and `db/migrate/` has a `create_ixport_imports` migration. If it does, go to step 13. If it does not, carry on with step 2.
|
|
31
|
+
2. Check that the app has Keystone UI set up: the layout ixport will use has `<%= keystone_theme_attributes %>` in its `<html>` tag. If it does not, stop and hand Keystone UI's setup to the `keystone_ui-install` local before going on, because ixport's pages are drawn with Keystone UI components and its styles.
|
|
32
|
+
3. Check that the app has Active Storage set up, because ixport keeps each uploaded import file as an Active Storage attachment. All three must hold:
|
|
30
33
|
- `config/application.rb` loads Active Storage, either through `require "rails/all"` or through `require "active_storage/engine"`.
|
|
31
34
|
- `db/schema.rb` has the `active_storage_blobs`, `active_storage_attachments` and `active_storage_variant_records` tables.
|
|
32
35
|
- Each environment file in `config/environments/` sets `config.active_storage.service`, and `config/storage.yml` defines that service.
|
|
33
36
|
|
|
34
37
|
If any is missing, tell the developer ixport cannot accept uploads without Active Storage and ask before setting it up. Where the tables are missing, setting it up means running `bin/rails active_storage:install` and then `bin/rails db:migrate`. Ask the developer which storage service each environment should use rather than picking one.
|
|
35
|
-
|
|
38
|
+
4. Add the gem to the app's `Gemfile`:
|
|
36
39
|
```ruby
|
|
37
40
|
gem "ixport"
|
|
38
41
|
```
|
|
39
42
|
Then run `bundle install`.
|
|
40
|
-
|
|
43
|
+
5. Run the install generator:
|
|
41
44
|
```bash
|
|
42
45
|
bin/rails generate ixport:install
|
|
43
46
|
```
|
|
44
|
-
It writes
|
|
47
|
+
It writes five files and touches no other:
|
|
45
48
|
- `db/migrate/<timestamp>_create_ixport_imports.rb` creates the `ixport_imports` table with a required `key` string, a required polymorphic `person` reference, an optional polymorphic `account` reference, a `mapping` JSON column, a required `status` string defaulting to `"pending"`, required `added_count` and `updated_count` integers defaulting to `0`, and timestamps.
|
|
46
49
|
- `db/migrate/<timestamp>_create_ixport_import_refusals.rb` creates the `ixport_import_refusals` table with a required `import` reference with a foreign key to `ixport_imports`, a required `row_number` integer, a required `reason` text, and timestamps.
|
|
47
|
-
|
|
50
|
+
- `db/migrate/<timestamp>_create_ixport_export_templates.rb` creates the `ixport_export_templates` table with a required `key` string, a required `name` string, a required `columns` JSON column, a required polymorphic `person` reference, an optional polymorphic `account` reference, and timestamps.
|
|
51
|
+
- `db/migrate/<timestamp>_add_wallflower_task_to_ixport_imports.rb` adds an optional `wallflower_task_id` bigint column to `ixport_imports`, with an index. It is copied whether or not the app uses wallflower.
|
|
52
|
+
- `db/migrate/<timestamp>_add_error_message_to_ixport_imports.rb` adds an optional `error_message` text column to `ixport_imports`, which holds the error an import stopped on when it fails.
|
|
53
|
+
6. Run the migrations:
|
|
48
54
|
```bash
|
|
49
55
|
bin/rails db:migrate
|
|
50
56
|
```
|
|
51
|
-
|
|
57
|
+
7. Ask the developer which path ixport's pages should live under. There is no default path. Add the mount inside `Rails.application.routes.draw` in `config/routes.rb`, using their answer:
|
|
52
58
|
```ruby
|
|
53
59
|
mount Ixport::Engine => "/data_imports"
|
|
54
60
|
```
|
|
55
|
-
If the app's routes put signed-in pages inside a block such as `authenticate :user do`, ask the developer whether the mount goes inside it.
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
If the app's routes put signed-in pages inside a block such as `authenticate :user do`, ask the developer whether the mount goes inside it. Do not give the mount a name with `as:`. A wallflower task links to the import's result page through the route name `ixport`, so the mount keeps that name. `bin/rails routes` lists the mount with `ixport` in its name column.
|
|
62
|
+
8. Read the app's `ApplicationController` and the controller methods it gets from its sign-in setup. ixport's controllers inherit from the app's `ApplicationController`, so every method named in the next step must be callable from it.
|
|
63
|
+
9. Compare the app's method and layout names with the defaults in the Interface section. Every default matches a Jumpstart app, so an app that uses all of the defaults needs no configuration. Where any name differs, or a method does not exist, ask the developer which method or layout to use rather than picking one:
|
|
58
64
|
- The sign-in check: the method that sends a person who is not signed in to sign in, such as `:authenticate_user!` or `:require_login`.
|
|
59
|
-
- The current person: the method returning the signed-in person, such as `:current_user` or `:current_member`. An import belongs to this person, and another person who opens it gets a not found response.
|
|
60
|
-
- The current account: the method returning the team or company the person works inside, such as `:current_account` or `:current_team`. An import belongs to this account too, and the same person who opens it from another account gets a not found response. Ask whether the app has accounts at all. ixport calls the named method on every page, so an app with no accounts still needs a method of that name callable from `ApplicationController`, and it returns `nil`.
|
|
65
|
+
- The current person: the method returning the signed-in person, such as `:current_user` or `:current_member`. An import belongs to this person, and another person who opens it gets a not found response. The imports page lists only the imports this person uploaded. Each export is built for this person when it is downloaded. An export template records this person as the one who saved it. Any access rule the app gives an import or export is handed this person.
|
|
66
|
+
- The current account: the method returning the team or company the person works inside, such as `:current_account` or `:current_team`. An import belongs to this account too, and the same person who opens it from another account gets a not found response. The imports page lists only the person's imports uploaded in this account. Each export is built for this account when it is downloaded. An export template belongs to this account, and every person in the account can see, download from, edit and delete it, while a person in another account gets a not found response. Any access rule the app gives an import or export is handed this account. Ask whether the app has accounts at all. ixport calls the named method on every ixport page, so an app with no accounts still needs a method of that name callable from `ApplicationController`, and it returns `nil`. In an app whose method returns `nil`, every person shares every export template, and every access rule is handed `nil` as the account. Tell the developer this and ask before adding the method.
|
|
61
67
|
- The layout: the file in `app/views/layouts/` that ixport's pages should be drawn inside.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
10. Ask the developer how imports should run, and do not pick for them. Having wallflower in the `Gemfile` does not choose it. The three answers:
|
|
69
|
+
- Inside the request: leave `config.background_runner` out. Each import file is limited to the inline row limit, and the person waits on the run page until the import finishes.
|
|
70
|
+
- As a wallflower task: set `config.background_runner = :wallflower`. Running an import starts a task and takes the person to wallflower's task page, which shows the import's progress, the rows it changed and refused, and a link to the import's result page when it finishes. This needs wallflower set up in the app first:
|
|
71
|
+
- The app's `Gemfile` has `gem "wallflower", ">= 0.2.0"`. If it does not, ask the developer before adding it.
|
|
72
|
+
- Wallflower is installed and mounted following its own install local, or its own README where the app has no such local. ixport sends the person to wallflower's task page through the route name `wallflower`, so the mount must keep wallflower's own name and not be given a different one with `as:`. `bin/rails routes` lists the mount with `wallflower` in its name column.
|
|
73
|
+
|
|
74
|
+
With `:wallflower` set and wallflower not loaded, the app fails to boot with a `NameError` naming `Wallflower`.
|
|
75
|
+
- Through the app's own runner: set `config.background_runner` to the name of a class in the app, as a string, such as `"ImportRunner"`. Ask the developer for the name. Writing that class is out of scope for this local and belongs to `ixport-develop`, and the app fails on the first run of an import until the class exists.
|
|
76
|
+
|
|
77
|
+
With either background answer, every import runs through the runner whatever its size, and while it runs its page shows the heading "Import running" and the message "This import is running."
|
|
78
|
+
11. Write only the settings that differ from the defaults into a new initializer, `config/initializers/ixport.rb`:
|
|
79
|
+
```ruby
|
|
80
|
+
Ixport.configure do |config|
|
|
81
|
+
config.authentication_method = :require_login
|
|
82
|
+
config.current_person_method = :current_member
|
|
83
|
+
config.current_account_method = :current_team
|
|
84
|
+
config.layout = "back_office"
|
|
85
|
+
config.inline_row_limit = 5_000
|
|
86
|
+
config.background_runner = :wallflower
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
The values above are examples, and each line is written only when the developer's answer differs from the default. Ask about `config.inline_row_limit` only when imports run inside the request and the developer wants a limit other than 1,000 rows per import file, since it has no effect with a background runner.
|
|
90
|
+
12. Start the app and visit the mounted path while signed in. The page shows the heading "Imports" and the message "No imports are set up yet." It shows no "Your imports" list, since that list appears only once the signed-in person has uploaded an import file. Then visit the mounted path followed by `/exports`. The page shows the heading "Exports" and the message "No exports are set up yet." Visiting either while signed out sends the browser through the app's own sign-in check. The install is done, so skip step 13.
|
|
91
|
+
13. For an app that already has ixport, check whether `db/migrate/` has a `create_ixport_export_templates` migration, an `add_wallflower_task_to_ixport_imports` migration and an `add_error_message_to_ixport_imports` migration. If it has all three, the app is up to date and there is nothing to run. If any is missing, run `bundle update ixport`, then run the update generator and the migrations:
|
|
92
|
+
```bash
|
|
93
|
+
bin/rails generate ixport:update
|
|
94
|
+
bin/rails db:migrate
|
|
95
|
+
```
|
|
96
|
+
Until the `error_message` migration has run, an import that fails cannot record its error and the run raises an error naming the missing column. The generator copies whichever of the three migrations step 5 describes are missing, reports each one already present as identical, and touches no other file. Then read the app's `config/initializers/ixport.rb`, or the defaults if it has none. Where the export templates migration was just added, put the current account part of step 9 to the developer, since export templates are shared by that account. Where `config.background_runner` is not set, put step 10 to the developer. Visit the mounted path followed by `/exports` while signed in and check that the page still loads. Then visit the mounted path itself and check that the imports page loads, with a "Your imports" list of the person's earlier imports when they have any. An earlier import of a kind whose access rule refuses the signed-in person is left off that list. Access rules need no migration and no setting.
|
|
75
97
|
|
|
76
98
|
## Conventions
|
|
77
99
|
|
|
78
|
-
- After installing, check that `bin/rails db:migrate:status` lists
|
|
79
|
-
- Run the install generator once. A second run against unchanged copies reports each migration as identical and adds nothing. If a copied migration differs from the one the generator would write, the run stops with an error naming that migration, and copies nothing after it. Do not delete or edit a copied migration once it has been run.
|
|
80
|
-
-
|
|
100
|
+
- After installing or updating, check that `bin/rails db:migrate:status` lists the create-ixport-imports, create-ixport-import-refusals, create-ixport-export-templates, add-wallflower-task-to-ixport-imports and add-error-message-to-ixport-imports migrations as up, that `bin/rails routes` lists the mount path, and that both pages load as described in step 12. With `config.background_runner = :wallflower`, also check that `bin/rails routes` lists ixport's mount under the name `ixport` and wallflower's mount under the name `wallflower`.
|
|
101
|
+
- Run the install generator once, and the update generator only on an app installed with an earlier ixport. A second run of either against unchanged copies reports each migration as identical and adds nothing. If a copied migration differs from the one the generator would write, the run stops with an error naming that migration, and copies nothing after it. Do not delete or edit a copied migration once it has been run.
|
|
102
|
+
- An import that stops on an error is marked failed, keeps the rows it saved before stopping, and reports the error through Rails' error reporter, so it reaches whatever error service the app has subscribed there. ixport needs no setting for this, and an app with no subscriber records the error only on the import's page.
|
|
103
|
+
- When the app renames its sign-in method, its current person or account method, the layout ixport uses, or its own runner class, update the matching line in `config/initializers/ixport.rb` in the same change.
|
|
81
104
|
- A setting given the same value as its default is left out of the initializer. Remove a line when its value goes back to the default.
|
|
82
|
-
-
|
|
105
|
+
- If the app fails to boot after installing, with an error saying an ixport import or export names a class that does not exist, the app registered an import or export whose class is missing. Fixing it is out of scope for this local and belongs to `ixport-develop`.
|
|
106
|
+
- Every signed-in person who passes the sign-in check can use every import and export until the app gives one an access rule. Ask the developer whether some imports or exports should be limited to some people, and hand that work to `ixport-develop`.
|
|
107
|
+
- Setting up the imports and exports that appear on the two pages, choosing which exports are fixed and which are chosen, giving an import or export an access rule, writing the app's own background runner class, and the upload, mapping, preview, run, result, download and template pages they lead to, is out of scope for this local and belongs to `ixport-develop`.
|
data/the_local/interface.yml
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports the host app registers, each backed by an importer class the host writes
|
|
1
|
+
scope: CSV import and export — a mounted engine where a signed-in person sees the mapped imports and the exports the host app registers, each import backed by an importer class and each export by an exporter class the host writes, where an export is either fixed or chosen, and a chosen export lets the account save named templates of picked columns
|
|
2
2
|
|
|
3
3
|
install:
|
|
4
4
|
- gem "ixport"
|
|
5
5
|
- bin/rails generate ixport:install
|
|
6
|
+
- bin/rails generate ixport:update
|
|
6
7
|
- mount Ixport::Engine
|
|
7
8
|
- Ixport.configure
|
|
8
9
|
- config.authentication_method
|
|
@@ -10,6 +11,7 @@ install:
|
|
|
10
11
|
- config.current_account_method
|
|
11
12
|
- config.layout
|
|
12
13
|
- config.inline_row_limit
|
|
14
|
+
- config.background_runner
|
|
13
15
|
|
|
14
16
|
develop:
|
|
15
17
|
- Ixport.register_import
|
|
@@ -20,6 +22,10 @@ develop:
|
|
|
20
22
|
- person
|
|
21
23
|
- account
|
|
22
24
|
- Ixport::MissingImporter
|
|
25
|
+
- Ixport::MissingExporter
|
|
26
|
+
- Ixport::ImportFailed
|
|
27
|
+
- Ixport.run_import
|
|
28
|
+
- enqueue
|
|
23
29
|
- GET /
|
|
24
30
|
- GET /imports/new
|
|
25
31
|
- POST /imports
|
|
@@ -28,6 +34,18 @@ develop:
|
|
|
28
34
|
- GET /imports/:import_id/preview
|
|
29
35
|
- POST /imports/:import_id/run
|
|
30
36
|
- GET /imports/:id
|
|
37
|
+
- Ixport.register_export
|
|
38
|
+
- Ixport::Exporter
|
|
39
|
+
- column
|
|
40
|
+
- value
|
|
41
|
+
- GET /exports
|
|
42
|
+
- GET /exports/:id
|
|
43
|
+
- GET /exports/:export_id/templates/new
|
|
44
|
+
- POST /exports/:export_id/templates
|
|
45
|
+
- GET /export_templates/:id
|
|
46
|
+
- GET /export_templates/:id/edit
|
|
47
|
+
- PATCH /export_templates/:id
|
|
48
|
+
- DELETE /export_templates/:id
|
|
31
49
|
|
|
32
50
|
sources:
|
|
33
51
|
- lib/ixport.rb
|
|
@@ -41,6 +59,7 @@ sources:
|
|
|
41
59
|
- app/controllers/ixport/application_controller.rb
|
|
42
60
|
- app/controllers/ixport/imports_controller.rb
|
|
43
61
|
- app/views/ixport/imports/index.html.erb
|
|
62
|
+
- app/helpers/ixport/imports_helper.rb
|
|
44
63
|
- app/models/ixport/application_record.rb
|
|
45
64
|
- app/models/ixport/import.rb
|
|
46
65
|
- app/controllers/ixport/mappings_controller.rb
|
|
@@ -53,3 +72,19 @@ sources:
|
|
|
53
72
|
- app/controllers/ixport/runs_controller.rb
|
|
54
73
|
- app/views/ixport/imports/show.html.erb
|
|
55
74
|
- lib/generators/ixport/install/templates/create_ixport_import_refusals.rb.erb
|
|
75
|
+
- lib/ixport/exporter.rb
|
|
76
|
+
- app/models/ixport/csv_writer.rb
|
|
77
|
+
- app/controllers/ixport/exports_controller.rb
|
|
78
|
+
- app/views/ixport/exports/index.html.erb
|
|
79
|
+
- lib/generators/ixport/update/update_generator.rb
|
|
80
|
+
- lib/generators/ixport/install/templates/create_ixport_export_templates.rb.erb
|
|
81
|
+
- app/models/ixport/export_template.rb
|
|
82
|
+
- app/controllers/ixport/export_templates_controller.rb
|
|
83
|
+
- app/views/ixport/export_templates/new.html.erb
|
|
84
|
+
- app/views/ixport/export_templates/edit.html.erb
|
|
85
|
+
- lib/ixport/background_runner.rb
|
|
86
|
+
- app/models/ixport/wallflower_runner.rb
|
|
87
|
+
- app/models/ixport/wallflower_task_runner.rb
|
|
88
|
+
- app/models/ixport/null_reporter.rb
|
|
89
|
+
- lib/generators/ixport/install/templates/add_wallflower_task_to_ixport_imports.rb.erb
|
|
90
|
+
- lib/generators/ixport/install/templates/add_error_message_to_ixport_imports.rb.erb
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ixport
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler Schneider
|
|
@@ -91,14 +91,25 @@ files:
|
|
|
91
91
|
- README.md
|
|
92
92
|
- Rakefile
|
|
93
93
|
- app/controllers/ixport/application_controller.rb
|
|
94
|
+
- app/controllers/ixport/export_templates_controller.rb
|
|
95
|
+
- app/controllers/ixport/exports_controller.rb
|
|
94
96
|
- app/controllers/ixport/imports_controller.rb
|
|
95
97
|
- app/controllers/ixport/mappings_controller.rb
|
|
96
98
|
- app/controllers/ixport/previews_controller.rb
|
|
97
99
|
- app/controllers/ixport/runs_controller.rb
|
|
100
|
+
- app/helpers/ixport/imports_helper.rb
|
|
98
101
|
- app/models/ixport/application_record.rb
|
|
102
|
+
- app/models/ixport/csv_writer.rb
|
|
103
|
+
- app/models/ixport/export_template.rb
|
|
99
104
|
- app/models/ixport/import.rb
|
|
100
105
|
- app/models/ixport/import_refusal.rb
|
|
106
|
+
- app/models/ixport/null_reporter.rb
|
|
101
107
|
- app/models/ixport/row_walk.rb
|
|
108
|
+
- app/models/ixport/wallflower_runner.rb
|
|
109
|
+
- app/models/ixport/wallflower_task_runner.rb
|
|
110
|
+
- app/views/ixport/export_templates/edit.html.erb
|
|
111
|
+
- app/views/ixport/export_templates/new.html.erb
|
|
112
|
+
- app/views/ixport/exports/index.html.erb
|
|
102
113
|
- app/views/ixport/imports/index.html.erb
|
|
103
114
|
- app/views/ixport/imports/new.html.erb
|
|
104
115
|
- app/views/ixport/imports/show.html.erb
|
|
@@ -106,11 +117,17 @@ files:
|
|
|
106
117
|
- app/views/ixport/previews/show.html.erb
|
|
107
118
|
- config/routes.rb
|
|
108
119
|
- lib/generators/ixport/install/install_generator.rb
|
|
120
|
+
- lib/generators/ixport/install/templates/add_error_message_to_ixport_imports.rb.erb
|
|
121
|
+
- lib/generators/ixport/install/templates/add_wallflower_task_to_ixport_imports.rb.erb
|
|
122
|
+
- lib/generators/ixport/install/templates/create_ixport_export_templates.rb.erb
|
|
109
123
|
- lib/generators/ixport/install/templates/create_ixport_import_refusals.rb.erb
|
|
110
124
|
- lib/generators/ixport/install/templates/create_ixport_imports.rb.erb
|
|
125
|
+
- lib/generators/ixport/update/update_generator.rb
|
|
111
126
|
- lib/ixport.rb
|
|
127
|
+
- lib/ixport/background_runner.rb
|
|
112
128
|
- lib/ixport/configuration.rb
|
|
113
129
|
- lib/ixport/engine.rb
|
|
130
|
+
- lib/ixport/exporter.rb
|
|
114
131
|
- lib/ixport/importer.rb
|
|
115
132
|
- lib/ixport/registrations.rb
|
|
116
133
|
- lib/ixport/version.rb
|