
自宅のWindowsでRuby on Rails ~ 非同期処理
今回は、Railsでの非同期処理について学習していきます。
自宅のWindowsでRuby on Rails ~データベース操作2で途中まで実装していた、仕入帳アプリの続きを実装しながら学習します。
1. 非同期処理とは?
複数の処理を同時に実行する方法の一つで、非同期処理を使うと、複数の処理が同時に進行するため、待ち時間を減らすことができます。
通常、プログラムは処理を実行するときに、1つの処理が終了するまで次の処理に進まない同期処理を行います。
しかし、非同期処理を使用すると、複数の処理を同時に実行し、処理が完了したら、必要に応じてコールバックを実行することができます。
非同期処理は、Webアプリケーションやモバイルアプリケーションなどでよく使用されます。
たとえば、ユーザーがWebページをリクエストした場合、Webサーバーは複数の処理を同時に実行することができます。
データベースからデータを取得する処理や、外部APIとの通信などは、通常、非同期処理で実行されます。
これにより、Webページのレスポンス時間を短縮し、ユーザーの体験を向上させることができます。
また、非同期処理は、I/O処理において有効です。たとえば、ファイルの読み取りや書き込み、ネットワーク通信などは、I/O処理のため、通常、待ち時間が発生します。
非同期処理を使用することで、I/O処理の待ち時間を減らし、プログラムの実行時間を短縮することができます。
2. Railsの非同期処理
Railsには非同期処理を行うためのいくつかの方法があります。
以下にいくつかの方法を紹介します。.jpg)
ここで紹介する方法の中で、Active JobはRailsに組み込まれているため、簡単に導入できます。
しかし、バックグラウンド処理の規模が大きくなる場合は、SidekiqやDelayed Jobなどの外部ジェムを使用することをお勧めします。
また、簡単な非同期処理を実行したい場合は、Ajaxリクエストであれば、サーバーサイドでの非同期処理は必要ありません。
今回はAjaxリクエストを使用したいと思います。
2-1. Ajaxリクエスト
Ajaxリクエストを使用してJavaScriptを介してサーバーにリクエストを送信し、JavaScriptでレスポンスを受信してビューを更新することで非同期処理を実現します。
この方法は、サーバーサイドでバックグラウンド処理を実行する必要がないため、シンプルで軽量であるという利点があります。
2-2. Active Job
Active JobはRailsに組み込まれた非同期処理ライブラリです。
Active Jobを使用することで、バックグラウンドでジョブを実行することができます。
Active Jobは、多数のバックエンドに対応しており、非同期処理を実行するためには選択したバックエンドに対応するジェムをインストールする必要があります。
2-3. Sidekiq
Sidekiqは、Redisをバックエンドとした非同期処理のためのジェムです。
Sidekiqを使用することで、バックグラウンドでジョブを実行することができます。
Sidekiqは、Active Jobと連携して使用することができ、Active Jobのバックエンドとしても使用することができます。
2-4. Delayed Job
Delayed Jobは、データベースをバックエンドとした非同期処理のためのジェムです。
Delayed Jobを使用することで、バックグラウンドでジョブを実行することができます。
Delayed Jobは、Active Jobと連携して使用することができます。
2-5. ネイティブスレッド
Railsは、MRI以外のRubyインタプリタでは、ネイティブスレッドをサポートしています。
ネイティブスレッドを使用することで、マルチスレッドでの非同期処理が可能になります。
ただし、ネイティブスレッドはMRIではサポートされていないため、MRIを使用している場合は使用できません。
また、ネイティブスレッドを使用する場合は、スレッドセーフなコードを書く必要があります。
3. 実装(続き)

3-1. コントローラーの実装
前回実装していなかった、PurchaseRecordsControllerを実装します。
class PurchaseRecordsController < ApplicationController
  before_action :set_purchase_record, only: [:destroy, :update, :edit, :show]
  def index
    @purchase_records = PurchaseRecord.includes(:product, :supplier).all
  end
  def show
  end
  def new
    @purchase_record = PurchaseRecord.new
  end
  def edit
  end
  def create
    @purchase_record = PurchaseRecord.new(purchase_record_params)
    if @purchase_record.save
      redirect_to @purchase_record, notice: '作成しました。'
    else
      render :new
    end
  end
  def update
    if @purchase_record.update(purchase_record_params)
      redirect_to @purchase_record, notice: '更新しました。'
    else
      render :edit
    end
  end
  def destroy
    @purchase_record.destroy
    redirect_to purchase_records_url, notice: '削除しました。'
  end
  def search
    conditions = ["purchase_records.purchase_date = ? OR products.name = ? OR suppliers.name = ? OR products.stock = ?", params[:purchase_date], params[:product_name], params[:supplier_name], params[:product_stock]]
    @purchase_records = PurchaseRecord.joins(:product, :supplier).select('purchase_records.*, products.name as product_name, suppliers.name as supplier_name').where(conditions)
    render :index
  end
  def get_suppliers
    product = Product.find_by(id: params[:product_id])
    if product.nil?
      render json: []
    else
&3-2. ルーティングの追加
routes.rbにルーティングを以下のように追加します。
Rails.application.routes.draw do
  root 'purchase_records#index'
  resources :products
  resources :suppliers
  resources :purchase_records do
    collection do
      get 'get_suppliers'
      get 'search'
    end
  end
end3-3. ビューとスクリプトの実装
今回Ajaxを使用した非同期処理を行うのは、新規登録画面と編集画面です。
商品のドロップダウンリストから商品を選択すると、その商品を扱う業者だけが業者のドロップダウンリストに現れる仕組みです。
以下、新規登録画面のビューと、スクリプトです。
new.html.erb
<%= javascript_pack_tag 'get_suppliers' %>
<h1>仕入登録</h1>
<%= form_with(model: @purchase_record, local: true) do |form| %>
  <% if @purchase_record.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@purchase_record.errors.count, "error") %> prohibited this purchase record from being saved:</h2>
      <ul>
      <% @purchase_record.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <div class="form-group">
    <%= form.label :purchase_date, "仕入日" %>
    <%= form.date_field :purchase_date, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= form.label :quantity, "個数" %>
    <%= form.number_field :quantity, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= form.label :product_id, "商品名" %>
    <%= form.collection_select :product_id, Product.all, :id, :name, {}, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= form.label :supplier_id, "業者名" %>
    <%= form.collection_select :supplier_id, [], :id, :name, {}, class: 'form-control' %>
  </div>
  <div class="actions">
    <%= form.submit '仕入登録', class: 'bt4. 解説

4-1. ビューの解説
new.html.erbの最初の行には、<%= javascript_pack_tag 'get_suppliers' %>を記述しています。
この記述では、RailsのWebpacker を使用してjavascriptを呼び出しています。
4-2. Webpacker
Webpackerは、RailsアプリケーションでWebpackを使用するための公式の方法です。
Webpackは、JavaScript、CSS、画像などのアセットをバンドルし、コードを最適化するためのツールであり、Webpackerは、WebpackをRailsアプリケーションに簡単に統合するためのフレームワークです。
Webpackを使ってJavaScriptファイルをバンドルする手順は、以下のようになります。
1.JavaScriptファイルを作成する
app/javascript/packsディレクトリにJavaScriptファイルを作成し、必要な機能を実装します。
今回はget_suppliers.jsを作成しました。
2.JavaScriptファイルをWebpackでバンドルする
ルートディレクトリのwebpack.config.jsを以下のように編集(または作成)します。
module.exports = {
  entry: {
    get_suppliers: './app/javascript/get_suppliers.js'
  },
  output: {
    filename: '[name]-bundle.js',
    path: __dirname + '/public/javascripts'
  }
}3.ビューにバンドルしたJavaScriptを読み込む
ビューファイルに、バンドルしたJavaScriptを読み込むためのタグを追加します。
<%= javascript_pack_tag 'get_suppliers' %>というタグを追加すると、上記で作成したhelloという名前のJavaScriptファイルが読み込まれます。
4-3. javascriptファイルの解説
get_suppliers.jsはJavaScriptのライブラリであるjQueryを使用して書かれたものです。
スクリプトが読み込まれたらまず最初にupdateSuppliers()が実行されます。
また、商品のドロップダウンリストが変更された時もupdateSuppliers()は実行されます。
つまり、updateSuppliers()では、商品に関連付けられている取引業者のリストを、ドロップダウンリストとして作成しています。
ajaxで/purchase_records/get_suppliersにGETで値を渡していますが、このURLはルーティングで設定したPurchaseRecordsコントローラーのget_suppliersアクションに当たります。
処理が成功したときのレスポンスは、idとnameを持つsupplierのリストをjson形式にしたものです。
select.append($('<option></option>').val(supplier.id).html(supplier.name));では、ドロップダウンリストにoptionを追加しています。
4-4. アクションの解説
ajaxで渡された値は、paramsで受け取ります。
ここでは渡されたproduct_idによって関連付けられたsupplierをとりだし、さらにidとnameのみを含むハッシュを作成してjson形式で返しています。
ハッシュを作成する理由は、そのままのデータを返すよりも、ハッシュを作成してから必要な情報だけを含むようにしてjsonで返す方が、余分な情報を含めずに効率的にデータをやり取りできるからです。
また、ハッシュを作成することで、jsonで返す情報を柔軟にカスタマイズすることもできます。
5. まとめ

今回はRailsにおいてのajaxを使った非同期処理を学習しました。
とても簡単でしたね。
Railsにおいてのajaxを使った非同期処理は、JavaScriptのjQueryを使用して実現されます。
画面遷移をせずにサーバーと通信し、データを取得することができ、さらに非同期通信をすることで、サイトのパフォーマンスを向上させることができます。
ajaxを使うには、まずajaxリクエストを送信するためのJavaScriptを書きます。
Railsでは、ajaxリクエストを受け取るためのコントローラーアクションを定義する必要があります。
コントローラーアクションで、必要なデータを取得し、JSON形式でレスポンスを返します。
JavaScriptでレスポンスを受け取り、HTMLを動的に書き換えることで、非同期にデータを表示することができます。
しかしながら、Railsでは、jQueryでajaxを使うよりも、UJSを使って非同期通信を行うことが推奨されます。
UJS(Unobtrusive JavaScript)はRailsの標準機能であり、Railsのコントローラーアクションを呼び出すことや、Railsが提供するエラーメッセージの表示、自動的なCSRFトークンの付与など便利な機能を利用することができます。
今回は触れることはできませんでしたが、今後、学習していきたいと思います。
今回は、ここまでとなります。
ありがとうございました。

