Rails: 保存期間設定画面の習作

外観

Basecampの新機能 Limit chat history のような画面を作ったことがなかったのでRailsで習作アプリを作りました。モデルは想像になります。本物は設定画面にラジオボタングループが2つありCSSで角丸ボタンにしていますが、今回の習作ではスタイリングは対象外にします。

環境

ruby 3.1.2p20
rails 7.0.4

リポジトリ

https://github.com/usutani/try_retention

画面

設定画面

仕様

ラベル: ['Forever', '30 days', '90 days', '1 year', '2 years']
ラジオボタンの値: ['', '30', '90', '365', '730']
モデル: [nil, 30, 90, 365, 730]
Retentionモデルのcycleとprojectに日数を保存する。
ビューのForever(空文字列)はモデルではnilにします。

画面遷移: ホーム画面←→設定画面←→確認画面→ホーム画面
前の画面に戻れる。
保存時に確認画面でCONFIRMと入力させる。
フォームオブジェクトは使わない。
設定画面と確認画面のURLは同じ。

モデル: Retention
データは1件だけ用意する。Retention.first!で取得する。

コントローラー: HomesController, RetentionsController
HomesController
show: ホーム画面を表示する。

RetentionsController
show: 設定画面を表示する。
update: 確認画面を表示する。

実装

準備

モデルとコントローラーを作成します。
ホーム画面から設定画面までのリンクを用意する。

rails new try_retention && cd $_
rails g model Retention cycle:integer project:integer
rails db:migrate
test/fixtures/retentions.yml
one:
  cycle: nil
  project: nil
rails db:fixtures:load
rails g controller Homes show
rails g controller Retentions show
config/routes.rb
Rails.application.routes.draw do
  resource :home, only: :show
  resource :retention, only: %i[ show update ]
  root "retentions#show"
end
app/views/homes/show.html.erb
<h1>Home#show</h1>
<p>Find me in app/views/home/show.html.erb</p>

<p style="color: green"><%= notice %></p>
<%= link_to 'Retentions', retention_path %>
rails s
open http://localhost:3000/home

設定画面

編集対象のファイル
app/controllers/retentions_controller.rb
app/views/retentions/show.html.erb
app/views/retentions/_radio_buttons.html.erb
app/views/retentions/update.html.erb
app/helpers/retentions_helper.rb

touch app/views/retentions/_radio_buttons.html.erb
touch app/views/retentions/update.html.erb

app/controllers/retentions_controller.rb
show: 設定画面を表示する。
update: 確認画面を表示する。

class RetentionsController < ApplicationController
  before_action :set_retention, only: %i[ show update ]

  def show
    @items = [nil, 30, 90, 365, 730]
  end

  def update
    if confirmed? && @retention.update(retention_params)
      redirect_to home_url, notice: 'Retentions successfully saved.'
    else
      @retention = Retention.new(retention_params)
      render status: :unprocessable_entity
    end
  end

  private
    def set_retention
      @retention = Retention.first!
    end

    def retention_params
      # params.permit(:cycle, :project).to_h { |k, v| [k, v.presence] }
      {
        cycle: params[:cycle].presence,
        project: params[:project].presence,
      }
    end

    def confirmed?
      params[:confirmation] == 'CONFIRM'
    end
end

app/views/retentions/show.html.erb

<h1>Retentions#show</h1>
<p>Find me in app/views/retentions/show.html.erb</p>

<%= form_with url: retention_path, method: :put do %>
  <h2>Cycle</h2>
  <%= render 'radio_buttons', items: @items, name: 'cycle', value: @retention.cycle %>
  <h2>Project</h2>
  <%= render 'radio_buttons', items: @items, name: 'project', value: @retention.project %>
  <%= submit_tag 'Review chnges...' %>
  <%= link_to 'Never mind', home_path %>
<% end %>

app/views/retentions/_radio_buttons.html.erb
itemがnilなら空文字列。
item == valueならチェックを付ける。

<% items.each do |item| %>
  <%= label_tag do %>
    <%= radio_button_tag name, item || '', item == value %>
    <%= tag.span days_to_human(item) %>
  <% end %>
  <br>
<% end %>

app/views/retentions/update.html.erb

<h2>Cycle</h2>
<%= days_to_human(@retention.cycle) %>
<h2>Project</h2>
<%= days_to_human(@retention.project) %>
<%= form_with url: retention_path, method: :put do %>
  <%= hidden_field_tag 'cycle', @retention.cycle %>
  <%= hidden_field_tag 'project', @retention.project %>
  <%= text_field_tag 'confirmation', nil, placeholder: 'Type CONFIRM', required: true %>
  <br>
  <%= submit_tag 'Yes, confirm and apply changes' %>
  <%= link_to 'Never mind, go back', retention_path %>
<% end %>

app/helpers/retentions_helper.rb

module RetentionsHelper
  def days_to_human(days)
    return 'Forever' if days.blank?
    years = days.to_i / 365
    return "#{days} days" if years == 0
    return "#{years} year" if years == 1
    "#{years} years"
  end
end

以上です。

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