Importers
CM Admin importers process files uploaded through an importable action. Use CmAdmin::BaseImporter for standard CSV and Excel row processing, or provide a standalone importer when you need complete control over parsing and execution.
Configure the import actionâ
Add importable inside the model's cm_admin configuration:
cm_admin do
importable class_name: 'UserImporter',
importer_type: :default_importer,
sample_file_path: '/user_import.csv'
end
| Option | Required | Description |
|---|---|---|
class_name | Yes | Importer class initialized by CM Admin. |
importer_type | Yes | Use :default_importer for a CmAdmin::BaseImporter subclass or :custom_importer for a standalone importer. |
sample_file_path | No | Public path to a sample file shown on the import page. |
route_type | No | :collection by default; use :member to import against one record. |
display_if | No | Lambda controlling whether the import action is visible. |
page_configuration | No | Import page settings such as title, description, and submit_button_text. |
The uploaded file is stored as a FileImport and processed asynchronously by FileImportProcessorJob. The importer class must be autoloadable and accept file_import: in its initializer.
Use CmAdmin::BaseImporterâ
The base importer provides file download, CSV/TSV/XLS/XLSX parsing, header validation, blank-row skipping, value trimming, per-row error capture, counters, and import status calculation.
Create an importer and implement process_row:
class UserImporter < CmAdmin::BaseImporter
def self.required_headers
%w[email name]
end
private
def process_row(row, _line_number)
User.create!(email: row[:email], name: row[:name])
end
def identifier_for(row)
row[:email]
end
end
required_headersâ Optional. Lists headers that must exist in the first row. Header matching is exact.process_row(row, line_number)â Runs once for each non-blank row.rowsupports string and symbol keys. Return a truthy value for success; raise an error or returnnil/falsefor failure.identifier_for(row)â Optional. Returns the value shown beside a failed row; the default is-.
Without transactions, the importer continues after a failed row. Successful rows remain committed and the final status is :partial_success when at least one other row succeeds.
Run the whole import in a transactionâ
Transactions are disabled by default. Opt in by overriding use_transaction?:
class UserImporter < CmAdmin::BaseImporter
def self.required_headers
%w[email name]
end
def self.use_transaction?
true
end
private
def process_row(row, _line_number)
User.create!(email: row[:email], name: row[:name])
end
def identifier_for(row)
row[:email]
end
end
When enabled, all rows run inside one ActiveRecord::Base.transaction. The first failed row stops processing and rolls back database changes made earlier in that transaction. The failed row is still included in the import error report.
Use a transaction only when the import must be atomic. Keep it disabled for partial imports or when processing large files where a long-running transaction is undesirable. Transactions only cover database work participating in the same Active Record transaction; files, API calls, jobs, and other external side effects are not rolled back.
Keep a fully custom importerâ
Use :custom_importer to retain the previous standalone approach. CM Admin does not require inheritance and does not control parsing or transactions:
cm_admin do
importable class_name: 'UserImporter', importer_type: :custom_importer, sample_file_path: '/user_import.csv'
end
The custom class must satisfy this interface:
class UserImporter
InvalidRow = Struct.new(:line_number, :identifier, :errors)
attr_reader :invalid_rows, :success_count, :row_count
def initialize(file_import:)
@file_import = file_import
Current.user = file_import.added_by
@invalid_rows = []
@success_count = 0
@row_count = 0
end
def run!
# Download and process @file_import.import_file here.
rescue StandardError => e
@invalid_rows << InvalidRow.new(1, '-', e.message)
end
def import_status
return :success if invalid_rows.empty?
# use partial_success with success_count and row_count if required.
:failed
end
end
The processor expects:
initialize(file_import:)â Receives the persistedFileImportcontaining the uploadedimport_fileattachment.run!â Performs the import.invalid_rowsâ Returns objects exposingline_number,identifier, anderrors.import_statusâ Optional. Return:success,:partial_success, or:failed. Without it, CM Admin returns:successwheninvalid_rowsis empty and:failedotherwise.
A custom importer is responsible for file parsing, header validation, error handling, cleanup, transactions, and status semantics. Use it when the base importer's row-oriented workflow does not fit the import.