Resources

Download Source Code

Summary

# Terminal
rails active_storage:install
rails g scaffold countries name flag:attachment
rails g controller countries/imports
rails g job countries/import
rails g job Countries::CreateRecord
bundle add solid_queue
rails g solid_queue:install
bundle exec rake solid_queue:start # should be added to the Procfile.dev

# Gemfile
gem "image_processing", "~> 1.2"
gem "solid_queue", "~> 0.3.4"

# config/routes.rb
resources :countries
namespace :countries do
  resource :imports, only: [:new, :create]
end

# app/controllers/countries/imports_controller.rb
class Countries::ImportsController < ApplicationController
  def new
  end

  def create
    file = params[:file]

    blob = ActiveStorage::Blob.create_and_upload!(
      io: file,
      filename: file.original_filename,
      content_type: file.content_type
    )
    Countries::ImportJob.perform_later(blob.signed_id)
    redirect_to countries_path, notice: "Countries are being imported..."
  end
end

# app/views/countries/imports/new.html.erb
<%= form_with url: countries_imports_path do |f| %>
  <div class="mb-3">
    <%= f.label :file, class: "form-label" %>
    <%= f.file_field :file, class: "form-control" %>
  </div>

  <%= f.submit "Import", class: "btn btn-primary" %>
<% end %>

# app/views/welcome/index.html.erb
<%= link_to "Import Countries", new_countries_imports_path %>

# app/jobs/countries/import_job.rb
require "csv"

class Countries::ImportJob < ApplicationJob
  queue_as :default

  def perform(blob_signed_id)
    blob = ActiveStorage::Blob.find_signed(blob_signed_id)

    blob.download do |data|
      encoded_data = data.force_encoding("ASCII-8BIT").encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")
      CSV.parse(encoded_data, headers: true) do |row|
        Countries::CreateRecordJob.perform_later(
          name: row["Country"],
          flag_url: row["Flag URL"]
        )
      end
    end
  ensure
    blob.purge_later
  end
end

# app/jobs/countries/create_record_job.rb
require "open-uri"

class Countries::CreateRecordJob < ApplicationJob
  queue_as :default

  def perform(name:, flag_url:)
    country = Country.find_or_initialize_by(name: name)
    country.flag.attach(
      io: URI.open(flag_url),
      filename: File.basename(URI.parse(flag_url).path)
    )
    country.save!
  end
end

# config/application.rb
config.active_job.queue_adapter = :solid_queue

# config/solid_queue.yml
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 1
      processes: 1
      polling_interval: 0.1

development:
 <<: *default

test:
 <<: *default

production:
 <<: *default

# app/views/countries/index.html.erb
<%= image_tag country.flag.variant(resize_to_limit: [30, 30]) if country.flag.attached? && country.flag.image? %>