carrierwave

carrierwaveとは、画像アップロード用ライブラリ

導入

gem 'carrierwave'

アップローダーの作成

bundle exec rails g uploader BoardImage

生成されたアップローダーに以下記述

class BoardImageUploader < CarrierWave::Uploader::Base

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

#デフォルトの画像ファイル指定
  def default_url
    'board_placeholder.png'
  end

アップロード可能なファイル種別を指定
  def extension_whitelist
    %w(jpg jpeg gif png)
  end

end

アップローダー先のフォルダを.gitignoreに指定することで、アップロードした画像ファイルがGithubにあがらなくなる

/public/uploads

アップローダーをモデル内のカラムに取り付けるためBoardテーブルに画像のカラムを追加

bundle exec rails g migration AddBoardImageToBoards board_image:string  
bundle exec rails db:migrate

Boardモデルに、アップローダーの仕様を宣言
これによりアップローダをカラムに取り付ける
レコード保存時に自動で、画像ファイルをpublic/uploads配下に保存可能に
DBのboard_imageカラムには画像ファイル名のみ保存される

class Board < ApplicationRecord
  mount_uploader :board_image, BoardImageUploader
  belongs_to :user

  validates :title, presence: true, length: { maximum: 255 }
  validates :body, presence: true, length: { maximum: 65_535 }
end

コントローラーで画像ファイルの入力受付

app/controllers/boards_controller.rb
def board_params
     params.require(:board).permit(:title, :body, :board_image, :board_image_cache)
   end
app/views/boards/_form.html.erb
<%= form_with model: board, local: true do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="form-group">
    <%= f.label :title %>
    <%= f.text_field :title, class: 'form-control' %>
  </div>
  <div class="form-group">
    <%= f.label :body %>
    <%= f.text_area :body, class: 'form-control', rows: 10 %>
  </div>
  <div class="form-group">
    <%= f.label :board_image %>
    <%= f.file_field :board_image, class: 'form-control mb-3', accept: 'image/*' %>
    <%= f.hidden_field :board_image_cache %>
  </div>
  <div class='mt-3 mb-3'>
    <%= image_tag board.board_image.url,
                  id: 'preview',
                  size: '300x200' %>
  </div>

  <%= f.submit class: 'btn btn-primary' %>
<% end %>
app/views/boards/_board.html.erb
<div class="col-sm-12 col-lg-4 mb-3">
  <div id="board-id-<%= board.id %>">
    <div class="card">
      <%= image_tag board.board_image_url, class: 'card-img-top', size: '300x200' %>
      <div class="card-body">
        <h4 class="card-title">
          <a href="#">
            <%= board.title %>
          </a>
        </h4>
        <div class='mr10 float-right'>
          <a href="#"><%= icon 'fas', 'trash', class: 'pr-1' %></a>
          <a href="#"><%= icon 'fa', 'pen' %></a>
        </div>
        <ul class="list-inline">
          <li class="list-inline-item">
            <%= icon 'far', 'user' %>
            <%= board.user.decorate.full_name %>
          </li>
          <li class="list-inline-item">
            <%= icon 'far', 'calendar' %>
            <%= l board.created_at, format: :long %>
          </li>
        </ul>
        <p class="card-text"><%= board.body %></p>
      </div>
    </div>
  </div>
</div>

メッセージ追加

config/locales/activerecord/ja.yml
ja:
  activerecord:
    attributes:
      board:
        title: 'タイトル'
        body: '本文'
        board_image: 'サムネイル'
config/locales/carrierwave/ja.yml
ja:
  errors:
    messages:
      carrierwave_processing_error: '処理できませんでした'
      carrierwave_integrity_error: 'は許可されていないファイルタイプです'
      carrierwave_download_error: 'はダウンロードできません'
      extension_whitelist_error: "は %{allowed_types}の形式でアップロードしてください"
      extension_blacklist_error: "%{extension}ファイルのアップロードは許可されていません。アップロードできないファイルタイプ: %{prohibited_types}"
      content_type_whitelist_error: "%{content_type}ファイルのアップロードは許可されていません。アップロードできるファイルタイプ: %{allowed_types}"
      content_type_blacklist_error: "%{content_type}ファイルのアップロードは許可されていません"
      rmagick_processing_error: "rmagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      mini_magick_processing_error: "MiniMagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      min_size_error: "を%{min_size}以上のサイズにしてください"
      max_size_error: "を%{max_size}以下のサイズにしてください"

マイグレーションのロールバック

bin/rails db:rollback
bin/rails db:rollback STEP=3

この場合は最後に行った3つがロールバックされる

bin/rails db:migrate:redo STEP=3

STEPも併用して使用可

データベースのリセット

データベースのリセットには、db:resetとdb:migrate:resetがある
db:migrate:resetのほうがすべてをやり直してくれるのでおすすめ

詳しく書くと、db:resetはスキーマファイル ( db/schema.rb ) をもとにDB作成するので、マイグレーションファイルを編集しても、その内容は反映されない。

db:migrate:reset はDBを削除した後に、db/migrate/**.rb を古い順から実行するのでマイグレーションファイルの変更が反映される。

ストロングパラメーター

ストロングパラメーターとは

コントローラーでパラメータを送る際に主に利用される仕組み
受け付けたパラメータが、安全かどうか検証して安全ではないデータの登録、更新を防いでくれる

params.require(:user).permit(:nickname, :email, :password)

上記のコードがあったとするとrequireでPOSTで受け取る値のキーを設定、permitで許可するバリューを設定ができる
これによりparams[:user][:nickname] params[:user][:email]は受け取ることできるがparams[:nickname]やparams[:email]は受け取ることができない
なぜなら、requireでuserを指定しているからで、カラムもnickname・email・passwordをpermitで指定しているため、それ以外は受け取れない

formヘルパー

formヘルパーとは

Railsの便利なメソッドのうちの一つがformヘルパーで、form_for, form_tag, form_with などがある

ポイント

  • action属性
    入力したものをどのサーバーに送るか指定したもの

  • input要素
    サーバーに送信する値。selectやtextareaがある

  • input要素のname属性
    各項目の識別子
    emailは@example.comというように、名前を振り分ける

params[:email]のように書かれているとする
これはformのname属性と紐づいている

渡されてきたモデル(例えばform_with model: @userの@user部分)が空かどうaction="/users"なのかaction="/users/1"を判断して設定してくれる

日時のフォーマットについて

問題

世界標準で日時が表示される

解決法

  1. タイムゾーンを日本時間に設定
config/application.rb
module TimeFormatSandbox
  class Application < Rails::Application
    # ...

    # タイムゾーンを日本時間に設定
    config.time_zone = 'Asia/Tokyo'
  end
end

上記の記述で日時が日本時間になる

  1. フォーマットを変更する

strftimeメソッドで変更可能だが、DRYではなコードになってしまうので、lメソッドを使って実装する

# app/views/boards/_board.html.erb

<%= l board.created_at, format: :long %>

ロケールを設定

config/application.rb
module TimeFormatSandbox
  class Application < Rails::Application
    # ...

    # デフォルトのロケールを日本(ja)に設定
    config.i18n.default_locale = :ja
  end
end
ja.yml
time:
    formats:
        long: "%Y年%m月%d日 %H時%M分"

すると以下のように表示されるようになる f:id:uuuuuDi_3712:20220116125810p:plain

railsの基準時刻

DB側の時刻設定と、アプリ側の時刻設定が異なる場合、別々の時間表示で保存されてしまうのでアプリケーション作成の段階でRails側の標準時刻と、DB側の標準時刻を合わせておく

renderメソッド オプション

コレクションオプション

一覧表示をする際などに、eachを使用するところを、コレクションオプションを使うことでパフォーマンスを上げることができる

eachを使った場合

コントローラー
def index
  @users = User.all
end
ビュー
<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= user.email %></td>
  </tr>
<% end %>

コレクションを使った場合

ビュー
<%= render partial: "user", collection: @users  %>
部分パーシャル
<tr>
    <td><%= user.name %></td>
    <td><%= user.email %></td>
  </tr>

ローカルオプション

<%= render partial: 'product_body', locals: { product: @product } %>

省略形
<%= render 'product_body', product: @product %>

localオプションを使うことで、インスタンス変数をローカル変数にしてパーシャルに渡すことができる

パーシャルにインスタンス変数を渡さないほうがいい理由

パーシャルは呼び出し元のテンプレートとだけ紐付けしたほうがいいから

インスタンス変数を使うと、コントローラーとも結びつき再利用性が低くなってしまう

controller側でインスタンス変数の名前や挙動を変更したとき、partial側も変更しなければならなくなる。

特定のモデルのデータと関連づけられてしまうので、フレキシブルに使うことができない。