Hotwire: Rails: バックグラウンドで処理したファイルをダウンロードする時のUI: Broadcastable concern 版

外観

目的
Hotwire: Rails: バックグラウンドで処理したファイルをダウンロードする時のUI では、JavaScript でポーリングして画面の処理状態を更新していました。今回は Broadcastable concern で画面を更新したいと思います。

環境
ruby 3.0.2p107
rails 7.0.0.alpha2

macOS 11.6
foreman 0.87.2 (gem install foreman)
Redis server 6.2.5 (brew install redis)

リポジトリ
https://github.com/usutani/ui_background_process_2

雛形作成

モデルやビューなどを作成します。

rails new ui_background_process_2
cd ui_background_process_2
bundle remove jbuilder

bin/rails active_storage:install
bin/rails db:migrate

bin/rails g model Convert status:integer message:text
t.integer :status, default: 0, null: false
bin/rails db:migrate

bin/rails g controller Converts index

config/routes.rb
resources :converts, only: %i[ index create ]
root to: "converts#index"

処理状態の一覧表示

画像2

app/models/convert.rb
class Convert < ApplicationRecord
  has_one_attached :in_file
  has_one_attached :out_file

  enum status: {
    waiting: 0,
    validating: 1,
    converting: 2,
    succeeded: 3,
    failed: 4
  }

  def status_summary
    "#{id}:#{status}"
  end
end

db/seeds.rb
c = Convert.new(status: 0, message: 'seed data')
c.in_file.attach(io: File.open('public/404.html'), filename: '404.html')
c.save
bin/rails db:seed

app/views/converts/index.html.erb
<h1>Converts</h1>
<ul>
  <div id="converts">
    <%= render @converts %>
  </div>
</ul>

touch app/views/converts/_convert.html.erb
<%= tag.li id: dom_id(convert) do %>
  <%= tag.span convert.status_summary %>
  <%= tag.span do %>
    <% out_file_path = rails_blob_path(convert.out_file) if convert.out_file.attached? %>
    <%= link_to_if(convert.succeeded?, 'Download', out_file_path) { "N/A" } %>
  <% end %>
  <%= tag.span convert.message %>
<% end %>

app/controllers/converts_controller.rb
class ConvertsController < ApplicationController
  def index
    @converts = Convert.all
  end
end

変換処理モデルの生成

画像2

app/views/converts/index.html.erb
<h1>New Convert</h1>
<%= form_with(model: @convert) do |form| %>
  <%= form.label :in_file %>
  <%= form.file_field :in_file %>
  <%= form.submit %>
<% end %>

app/controllers/converts_controller.rb
  def index
    @converts = Convert.all
    @convert = Convert.new
  end

  def create
    @convert = Convert.new(convert_params)
    @convert.save
    redirect_to converts_url
  end

  private
    def convert_params
      params.require(:convert).permit(:in_file)
    rescue ActionController::ParameterMissing
      nil
    end

エラー表示

画像3

app/models/convert.rb
 validates :in_file, presence: true

app/controllers/converts_controller.rb
  def create
    @convert = Convert.new(convert_params)
    if @convert.save
      redirect_to converts_url
    else
      render :create, status: :unprocessable_entity
    end
  end

app/views/converts/index.html.erb
<%= form_with(model: @convert) do |form| %>
  <div id="error_explanation"></div>

touch app/views/converts/create.turbo_stream.erb
<%= render 'shared/turbo_stream_error_explanation', model: @convert %>​

Procfile でサーバーを起動

bin/dev で開始。

Broadcastable concern を使う

app/views/converts/index.html.erb
<%= turbo_stream_from :convert %>

app/models/convert.rb
  after_create_commit -> { broadcast_append_later_to :convert }
  after_update_commit -> { broadcast_replace_later_to :convert }

app/controllers/converts_controller.rb
    if @convert.save
      # redirect_to converts_url

行の更新

別ターミナルから状態を更新すると画面も更新される。
bin/rails c
Convert.first.update!(status: :validating)

画像4

ジョブ

app/controllers/converts_controller.rb
    if @convert.save
      FileConvertJob.perform_later(@convert)

bin/rails g job file_convert

以上です。

この記事が気に入ったらサポートをしてみませんか?