ぺんぎんのRails日記

ぺんぎん。エンジニア経験ゼロ。Rails勉強中。

【備忘録】三項演算子を使ってif文を省略

今回は三項演算子を使って、if文を省略して書く方法についてです٩( ᐛ )و

簡潔にまとめると、このif文が、

if xxx

  yyy

else

  zzz

end

三項演算子を使って短く書くと、こうなります。

xxx ? yyy : zzz

具体的に例を書いてみます。

bird = 'penguin'
bird == 'penguin' ? (puts 'You can swim!') : (puts 'You can fly!')

=> You can swim!

birdがペンギンだったら'You can swim!'を、ペンギン以外だったら'You can fly!'が表示されるようにしました。

ちなみに下のように?が2つあっても驚かないでください。

price = ''
default_price = 100

price.empty? ? (puts default_price) : (puts price)

=> 100

empty?という真偽判定を行うメソッドを使っています。 つまり、「priceは空ですか?」という判定をしていて、trueの場合はdefault_priceを表示し、falseの場合はpriceを表示します。

【備忘録】パスのあとのインスタンス変数

link_toやredirect_toなどで次のようなパスを見ることがあります。

redirect_to user_path(@user)
redirect_to @user

この二つは同じ動きをしますが、なぜパスのあとにインスタンスを入れているのでしょうか?

次のコードについて考えてみましょう。このコードから生成されるURLはなんでしょうか?

redirect_to user_path(10)

実はこの10はidを指しているんです。
そのため生成されるURLは「users/10」のようになります。

では、最初に取り上げたコードは何を指すのかというと、 呼び出されたインスタンスのidを代入していることになります。

@user = User.new(user_params)
if @user.save
  redirect_to @user
end

User.newで生成されたユーザーのidがURLに代入されてビューが表示されます。

参考

Rails のルーティング - Railsガイド

第7章 ユーザー登録 - Railsチュートリアル

【備忘録】アソシエーションで関連付けしたオブジェクトの初期化

モデルで関連付けしたオブジェクトをコントローラで初期化します。
初期化とは、最初の状態を整えること。ここではオブジェクトの作成のことです。

まず、Userモデルを親、Postモデルを子として関連付けします。
user.rb

has_many :posts

post.rb

belongs_to :user

posts_controller.rb

@post = current_user.posts.build(post_params)

・current_userはsorceryのメソッドでログイン中のユーザーの値が入っています
・postsは、モデルで設定されているhas_many :postsによって使えるようになったメソッド。userオブジェクトで使えます
・アソシエーションでオブジェクトを初期化するときはbuildを使います。挙動はnewと同じですが、関連付けされていることが視覚的にわかるようになります。

ログイン中ユーザーの投稿に編集用アイコンを表示する

ログインしているユーザーが自分の投稿を編集できるように、編集用のえんぴつマークのアイコンをFontAwesomeを使って表示させます。  

自分以外のユーザーの投稿やコメントには編集アイコンを表示させないようにします。

 

ログインしているユーザーが投稿を作成したユーザーかどうか判定する

sorceryのcurrent_userメソッドを使って、ログイン中ユーザーと投稿を作成したユーザーが一致するかどうかを判定します。  

 

掲示板 _post.html.erb

<% if current_user.id == post.user_id %>
  <a href="#">
    <i class="fa fa-pen"></i>
  </a>
<% end %>

 

コメント _comment.html.erb

<% if current_user.id == comment.user_id %>
  <a href="#">
    <i class="fa fa-pen"></i>
  </a>
<% end %>

これは一見良さそうに見えますが、よく見ると掲示板とコメントで同じようなコードを書いています。

postとcommentで違うモデルを扱っているから仕方ないじゃん!と思いますが、ユーザーのidを取って来ている点で同じなので、まとめられるんです。

 

Userモデルで共通化する

Userモデルでまとめてしまいましょう!

user.rb

def own?(object)

  object.user_id == self.id  #selfは省略可

end

objectで共通化しています。これでpostでもcommentでも、引数を指定して使えるようになります。
ちなみにインスタンスメソッドの中でselfを使うと、そのメソッドを利用したレシーバを参照することができます。
以下のビューでcurrent_user.own?のように使うので、この場合のselfの中身はuserが入るようになりますことになります。
(current_userはsorceryによって作られたログイン中ユーザーを特定するメソッドです)

 

 

投稿 _post.html.erb

<% if current_user.own?(post) %>
  <a href="#">
    <i class="fa fa-pen"></i>
  </a>
<% end %>

コメント _comment.html.erb

<% if current_user.own?(comment) %>
  <a href="#">
    <i class="fa fa-pen"></i>
  </a>
<% end %>

モデルで定義したown?メソッドを使って、ログイン中ユーザーのidとpostやcommentのuser_idを比較しています。  

 

完成です! Image from Gyazo

【Rails】Carrierwaveで画像アップロード

こんにちは。ぺんぎんです。

今回はユーザー登録や投稿の時に使う、画像アップロード機能を実装していきたいと思います。
railsの便利なライブラリ、Carrierwaveを使えば簡単に画像アップロード機能を実装することができます。

Carrierwaveとは

Railsにおける画像アップロード用のgem。
Carrierwaveでは、アップロード機能用のアップローダークラスを作って、細かい設定を書いていきます。
そのため個別のモデルに依存しないで、柔軟にいろいろな設定をすることができます。

手順

1. gemをインストール

Gemfile

gem 'carrierwave'
fem 'minimagick'

今回は画像をリサイズできるようにminimagickもインストールします。 minimagickにはImageMagickが必要になります。 インストール方法はこちら

ターミナル

bundle install

bundle installしたらサーバーを再起動させます。

2. アップローダーを生成する

$ bundle exec rails g uploader アップローダー名
$ bundle exec rails g uploader Image

→app/uploaders/image_uploader.rbというファイルが生成されます。

3. 生成されたアップローダーに設定を記述する

4. ローカル環境でアップロードした画像をアップロードしないように設定する

保存した画像はgithubなどにあげて共有する必要はないですよね。
そこで、.gitignoreファイルに画像の保存先を指定して、プッシュされないように設定しましょう。
.gitignore

/public/uploads

この設定をしないでプッシュてしまった人はこちら

5. boardsテーブルに画像のカラムを追加する

progateで学んだ人もいると思いますが、画像はDBに直接保存するわけではありません。
DBの容量がパンクしないように、画像名のカラムを作って保存するようにします。

$ bundle exec rails g migration AddImageToBoards image:string
$ bundle exec rails db:migrate

これでboardsテーブルに画像用のimageカラムができました。

6. モデルでアップローダークラスとカラムの紐付け

先ほど作ったアップローダークラスは、今のままではBoardクラスとバラバラになっているので、Boardモデルでアップローダークラスを紐づけておきましょう。

class モデル名 < ActiveRecord::Base
  mount_uploader [:カラム名], [アップローダークラス]
end
class Board < ApplicationRecord
  mount_uploader :image, ImageUploader
end

ちなみにmountはよく耳にするあの「マウンティング」と同じ言葉です。
もともと「乗る」という意味で「据え付ける」という意味もあります。

7. コントローラでparamsに画像アップロード用カラムの追加

BoardコントローラではBoard.newで新しい掲示板を作っています。
paramsで受け取れるように、画像用カラムを追加します。

def create
    @board = Board.new(board_params)
end

def board_params
  params.require(:board).permit(:title, :body, :image)
end

8. Viewに画像ファイルのフィールドを追加する

  • 投稿フォーム 画像ファイルを送信する際のファイル選択ボックスを生成します。
    ラベル部分はあとでi18nで日本語を定義します。
<%= f.label :カラム名 %>
<%= f.file_field :保存されるカラム %>

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 :image %>
    <%= f.file_field :image, class: 'form-control mb-3', accept: 'image/*' %>
    <%= f.hidden_field :board_image_cache %>
  </div>

  <%= f.submit class: 'btn btn-primary' %>
<% end %>

accept属性で選択できるファイルの種類を指定します。
これを指定しないと選択画面でどんなファイルも選択できてしまいます。
「image/*」は画像ファイル全般を指します。

accept属性

また<%= f.hidden_field :board_image_cache %>を入れておくと、バリデーションエラーになった後でも、データが引き継がれ情報を再入力する手間が省けます。

【Rails】CarrierwaveのCache機能を使用し、バリデーション後の画像データを保持する方法

  • 画像表示 アップロードした画像を表示させます。
    image_tagの使い方
<%= image_tag 'ファイル名', 'オプション' %>

ここではファイル名の代わりに、アップローダークラスのurlメソッドを使って、ファイルのURLを取得するようにします。

app/views/boards/_board.html.erb

<%= image_tag board.image_url, class: 'card-img-top', size: '300x200' %>

画像がない場合にデフォルト画像を表示させたい人はこちらを見てください。

【Rails】image_tagを使って簡単に画像を表示させよう!

9. i18nで翻訳を定義する

・imageカラムの日本語を定義します。
config/locales/activerecord/ja.yml

ja:
  activerecord:
    attributes:
      board:
        title: 'タイトル'
        body: '本文'
        image: 'サムネイル'

・画像に関するメッセージの日本語を定義する
Carrierwaveですでに用意されているバリデーションのエラーメッセージを日本語にします。
Carrierwave用の定義ファイルを用意しましょう。

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}以下のサイズにしてください"

いろいろな設定

アップローダークラスでいろいろな設定をしましょう。

* アップロードしたファイルの保存場所

アップロードされた画像の保存場所は、デフォルトで「public/uploads/モデル名/画像カラム名/id」に設定されています。

class ImageUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

今回の場合は「public/uploads/board/image/id」に保存されます。
この「id」には掲示板のidが入るので、board.idが1の掲示板で画像をアップロードしたら、「public/uploads/board/image/1」というフォルダに保存されます。

* 画像がない場合のデフォルト画像の指定

画像がないときにデフォルト画像を表示するように設定します。

class ImageUploader < CarrierWave::Uploader::Base
  def default_url
    'sample.png'
  end
end

これを設定しておけば、画像がない時とある時でView側で条件分岐をしなくてすみます。

* アップロードできる拡張子の指定

def extension_whitelist
    %w(jpg jpeg gif png)
  end

gitignoreを設定をしないでgitでコミットしてしまった場合

コミット後にこれを追記しても、アップロード済みのファイルは変更されません。 コミット後はファイル名を指定してgitの管理対象外に変更します。

git rm --chached ファイル名

また不要なファイルをpushしてしまった場合は、リモートリポジトリから削除します。

git rm ファイル名

必ずpush前に不要なファイルがないか確認しましょう( ^ω^ )

参照

CarrierWave github.com

Rails】CarrierWaveチュートリアル pikawaka.com

はじめまして

どうも、はじめまして。ぺんぎんです。

読んでくださっている方ありがとうございます。

 

エンジニア・IT業界の経験ゼロで、エンジニア目指して1からプログラミングの勉強をしてます。

 

ここではRuby on Railsを中心に、自分のハマったポイントなどを、備忘録の意味もこめて書いていきます。

 

まだまだ初心者のぺんぎんですので、間違っていることがあればたくさんツッコんでいただけたらうれしいです。

 

よろしくお願いします🐧