Railsのif文で荒れたViewを整理する強力かつ柔軟なテクニック

こんにちは、TECH DRIVEのTedです。

今回は複雑になりがちなViewをスッキリさせることができる強力なテクニックをご紹介します。

ページ単位で部分的に表示を変えたい時や、特定の条件の場合だけ表示したいというようなViewでの複雑な要件に出会うことがあると思います。

そんな時、Viewにif文のような条件式を実装していくと、プロジェクトが進行していく毎にコードの保守性や可読性が落ちやすくなってきます。

今回ご紹介する方法は複雑なViewに対して柔軟な対応ができるようになります。

Viewを整理するテクニックはたくさんあれど、「こういう方法もある」という道具の使い方のご紹介に焦点を置いてご紹介しようと思います。

今回の内容は説明が難しいこともありますので、サンプルコードを用意しました。この記事の最後に掲載させていただきましたので、一通り等記事を読んでからサンプルコードを読んでいただければより理解が深まると思います。

何を使うのか?

Railsが提供しているcontent_forlayoutを使用し、Viewを整理するテクニックをご紹介します。

この2つは柔軟なViewを作るうえで強力な組み合わせになります。

まずはcontent_forlayoutの簡単な説明をしておきます。

content_for

content_forに渡した名前付きブロックは、名前を指定してyieldで呼び出すことができます。

例えばこんな感じでユーザー一覧の画面を出すためのViewがあるとします。 RailsサーバーのUsersController#indexにアクセスしているイメージです。

views/users/index
  
<% content_for :add_content do %>
  # 埋め込みたいコンテンツ
<% end %>
# ユーザー一覧表示の処理が続く
layouts/application.html.erb
# 別の処理

<%= yield %> # users/indexを呼び出す

<% if content_for? :add_content %>
  <%= yield :add_content %> # content_for :add_content ブロックがある場合その中身を呼び出す
<% end %>

# 別の処理

上のコードの場合、<% yield %>で呼び出したusers/index.html.erbを呼び出し、さらに呼び出したusers/index.html.erbの中にcontent_for :add_contentがあるので<% yield :add_content %>でその中身を呼び出す流れです。

yieldに続けてシンボル名を記述すると、そのシンボル名に対応する content_forのブロックの中身をそこへ埋め込んでくれます。

content_forのみではそこまでの大きな利点がないかもしれません。一定の単位で、表示するコンテンツを切り替えたい時なんかに真価を発揮するようになります。

次にご紹介するlayoutを組み合わせると、ここまでの説明の意味がわかるようになるはずです。

layout

こちらはコントローラー内で指定するlayoutのことを指します。

これは、どのファイルをViewのレイアウトファイルとして使用するかを定義することができます。

Rails newしたときにデフォルトで使用されるレイアウトファイルはapp/views/layouts/appication.html.erbです。

Railsは使用するViewのレイアウトファイルを検索するときに、呼び出されているコントローラーの基本名をもつレイアウトファイルがapp/views/layouts/以下にあることを探すというルールがあります。

例えばUsersControllerの場合はViews/layouts/users.html.erbを探しにいきます。

該当ファイルがなければ、デフォルトで用意されているapp/views/layouts/application.html.erbを使用してくれます。

ここで、コントローラー内でlayoutを指定するとレイアウトファイルを検索するルールを上書きすることになります。

例えば以下の感じで指定するとUsersControllerのアクションにアクセスする場合、app/views/layouts/main.html.erbがあるかどうかを探しに行き、ある場合はレイアウトファイルとして使用します。

class UsersController < ApplicationController
  layout "main"

  def index
  end

  # 別の処理
end

こうすればコントローラー単位で使用するレイアウトを切り替えることができそうです。

実は、レイアウトファイルを検索するルールには続きがあります。

それは、呼び出されているコントローラーの基本名をもつレイアウトファイルがない場合は、継承元のコントローラーの基本名、もしくはlayoutで指定したレイアウトファイルがあるかどうかを次に検索します。

通常のRailsの場合、コントローラーはApplicationControllerを継承しているはずなので、最終的な継承元であるApplicationControllerの基本名のapplication.html.erbに行き着くので、先ほどご紹介したapplication.thml.erbがデフォルトのレイアウトであるということは、これに倣っているということになります。

呼び出すコントローラーとApplicationControllerの間に別のコントローラーを継承するとそのコントローラーの階層でもレイアウトファイルの検索をしてくれます。

class DashboadController < ApplicationController
end

class UsersController < DashboadController
  # いくつかのアクションが定義されている
end

この場合、users -> dashboard -> application の順でレイアウトファイルを検索してくれます。

DashboardControllerを継承するかどうかで使用するレイアウトファイルを切り替えられそうです。

何をどうするか

Users < Dashboard < Applicationでコントローラーのクラスが継承されている前提で進めます。

  1. dashboard.html.erbでメインのコンテンツをcontent_forブロック内に構築する

  2. dashboard.html.erbにはcontent_forのブロックの後でrender template: 'layouts/application'をテンプレートファイルとして呼び出す

  3. application.html.erbではdashboard.html.erbで構築したコンテンツを<%= yield %>で埋め込む

こうすることで最終のレイアウトはapplication.html.erbとして共通化することができ、レイアウトファイルが増えてもheadタグ等をなんども書く必要が無くなります。

すごくややこしいことをしているように見えるかもしれませんが以下に図を示します。

コントローラーの継承関係とレイアウトファイルの有無 f:id:travy:20190607174521p:plain

コンテンツ生成 f:id:travy:20190607180516p:plain

ファイル数は増えてしまいますが、Viewがグッと読みやすくなりかつ、柔軟性も高まるはずです。

ぼんやりとこんなイメージ感を持って読み進めていただけると良いかもしれません。

f:id:travy:20190617192434p:plain

それでは、順を追って説明します。

先ほどご紹介したコントローラーの継承関係の場合で、UserControllerのindexアクションにアクセスするとします。

class DashboadController < ApplicationController
end

class UsersController < DashboadController
  # いくつかのアクションが定義されている

  def index
    # 処理
  end
end

DashboadControllerの基本名であるdashboard.html.erbを、app/views/layouts/以下に作っておいてレイアウトファイルとして使います。

ここで一捻り加えます。

dashboard.html.erb内ではhtmlのbodyタグに配置するべき内容を全ての内容を content_for :content ブロック内に配置します。

<% content_for :content do %>

  <header>
    # ヘッダーに表示したい情報
  </header>

  <main>
    # サイドバーとか
    <%= render 'layouts/sidebar' %>
    # flashメッセージとか
    <%= render 'layouts/flash' %>
    # ↓メインコンテンツ
    <%= yield %>
  </main>

<% end %>

しかし、これだけでは画面に何も表示されません。content_forで登録したブロックをyieldで呼び出す必要があります。

メインコンテンツ直下の<%= yield %>にアクセス中のアクションで呼ばれているusers/index.html.erbの内容が埋め込まれます。

何も表示されないと困るので、上記のコードの最後に次の1行を加えます。

<%= render template: "layouts/application" %>

デフォルトで用意されているapplication.html.erbtemplateオプションで呼び出します。

templateオプションをつけることで対象のファイルをレイアウトとして呼び出すことができます。

これでapplication.html.erbが最終的なレイアウトファイルになり、このファイルの中でdashboard.html.erb内で構築したコンテンツを<%= yield :content %>を使って埋め込みます。

# application.html.erb

<% if content_for? :content %>
  <%= yield :content %>
<% else %>
  <%= yield %>
<% end %>

content_for :contentの有無で呼び出すコンテンツを切り替えておくと、今回場合でDashboardControllerを継承しない場合でも対応できるようになります。

これで、application.html.erbでは表示するコンテンツの中身を意識することなく、呼び出すコンテンツ名だけを指定するだけで良いのでif文の中身がスッキリします。

次に、ユーザー詳細画面ではサイドバーを表示したくなったとします。

ここでdashboard.html.erbの中にアクションがshowだった場合のif文かいてその中に、サイドバーのコンテンツを用意して、、、

# dashboard.html.erb

<% if controller.controller_name = "Users" && controller.action_name == "show" %>
  # サイドバーのコンテンツ
<% end %>

としなくても良いのです。dashboard.html.erbにはサイドバーのコンテンツを実際に書く必要はなく、先ほどと同じ要領で

<% if content_for? :side_bar %>
  <%= yield :side_bar %>
<% end %>

と書いておけば良いのです。dashboard.html.erb

:side_barのコンテンツがあったら埋め込んでやるよ」くらいの気構えです。

そしてサイドバーを出したいusers/show.html.erbでサイドバーの内容をcontent_for :side_barのブロック内に用意します。

<%= content_for :side_bar  do %>
  # サイドバーのコンテンツ
<% end %>

# userの詳細の表示が続く

これでユーザーの詳細画面でサイドバーのコンテンツを埋め込むことができます。

他のページ、例えばユーザーの編集画面でも同様にサイドバーが表示したければ

<%= content_for :side_bar do %>
  # サイドバーのコンテンツ
<% end %>

# userの編集画面

とすれば実装可能です。

ページ単位で表示するコンテンツを1つのhtmlファイルにまとめることができるので可読性がグッと上がります。サイドバーのコンテンツが同じものならパーシャル化してしまうのも良いでしょう。

さらにはこんなこともできます。

application.html.erbで書いたようなコンテンツの呼び出し方をdashboard.html.erbでも書いてみます。

<% if content_for? :main_content %>
  <%= yield :main_content %>
<% else %>
  <%= yield %>
<% end %>

そして、ユーザーのリソースでのみ特別なコンテンツを表示したいとします。

# layouts/user.html.erb
<% content_for :main_content do %>
  # ユーザーコンテンツのみの特別なコンテンツ
    
  # ↓それぞれのアクションでのhtmlのコンテンツが埋め込まれる
  <%= yield %>
<% end %>

<%= render template: "layouts/dashboard" %>

図で表すとこんな感じ

f:id:travy:20190617190320p:plain

UsersControllerのアクションにアクセスする場合、検索ルールにしたがってlayouts/user.html.erbがレイアウトファイルとして選択されることになります。

このレイアウトファイルにはcontent_for :main_contentが用意されていて、各アクション(show.html.erbとかindex.html.erbとか)で生成されるコンテンツが<%= yield%>に埋め込まれています。

そして今度は、<%= render template: "layouts/dashboard" %>を呼び出しています。

こうすることでlayouts/user.html.erbdashboard.html.erbを、dashboard.html.erbapplication.html.erbをレイアウトとして呼び出してる状態になります。

結局、先ほどご紹介したように常にapplication.html.erbが最終的なレイアウトファイルになります。今回ご紹介した内容は最終のレイアウトファイルにどんなコンテンツを埋め込むかと考えるとわかりやすいかもしれません。

と、このようにcontent_foryieldを上手に組み合わせるとcontent_forの呼び出し元のコードを汚さなくてよくなりますしコンテンツを柔軟に埋め込むこともできます。

最後に、今回の内容に焦点を当てたサンプルコードを掲載しておきますので手元にcloneしてお試しください。

github.com

付録

今回ご紹介した内容は、柔軟かつ読みやすくhtml要素を埋め込むことができることを前提に書いていますが、同様にやればcssjavascriptを特手のページだけに埋め込むこともできます。

その際は、stylesheet_link_tagjavascript_include_tagを使用して同様に書きます。

  • CSS
# dashboard用のcssファイルをデフォルトのファイルの代わりに使用する
<%= content_for :styles do %>
  <%= stylesheet_link_tag 'dashboard', media: 'all' %>
<% end %>
<% if content_for? :styles %>
  <%=  yield :styles %>
<% else %>
  <%= stylesheet_link_tag [デフォルトのcssファイル] , media: 'all' %>
<% end %>
  • JavaScript
# dashboard用のjsファイルをデフォルトのファイルの代わりに使用する
<%= content_for :load_scripts do %>
  <%= javascript_include_tag 'dashboard' %>
<% end %>
<% if content_for? :load_scripts %>
  <%=  yield :load_scripts %>
<% else %>
  <%= javascript_include_tag  [デフォルトのjavascriptファイル] %>
<% end %>

まとめ

もう一度簡単におさらいしておきます。

  • content_for
  • layout

この2つ+render template:を組み合わせることで柔軟なViewの表現ができます。

あとはコンテンツ内容が同じ(サイドバーとか)だけども呼び出す条件だけ違う場合は、そこだけパーシャル化してしまうと可動性、保守性がよくなります。

初めてこれを使ったり、読んだりする人にはすぐには理解が追いつかない内容だと思います。

私がまさにそうでしたw

しかし、今回の内容を自分で作っているサービスで使い倒してみたところ、非常に使いやすくて重宝しています。今回のような少し難しめの内容の場合、頭だけで理解しようとせずに小規模で良いので自分で書いてみると理解が早いかも知れません。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

【Rails】enumの使い方 - 便利なenumの何をどう使えば良いか

こんにちは!TECH DRIVEのTedです。

今回はenumについて、enumをどのように使えば良いかについてのご紹介をしたいと思います。

はじめに

enumとは列挙型のことを指し1つの識別子が複数の定数をもつことができる型のことである・・・と、enumについて調べると色々知ることができます。 しかし、初めてenumに触れる人にとってはイメージが掴みにくいかもしれません。

今回も、Railsを題材に使ってenumをどのように使えば効果的に使えるのかをみていきたいと思います。 少なくとも現場でコードを書かせていただいている私も同様の使い方をするので1つの例になると思います。

それから今回は、よくenumと合わせて使われるgem enum_helpも合わせて使っていきます。

enumを宣言してみる

enumはよくレコードの状態を表現するために使われます。

例えば、WordPressのようなCMSにおける記事を模したPostというモデルがあった場合に、投稿には下書きというプロセスがあるはずです。

このような場合にenumが力を発揮してくれたりします。

そして今回は次にあげるような仕様で進めたいと思います。

UserとPostは1対多の関係にあり。PostモデルはUserの投稿になります。

他のユーザーに公開するかどうかの状態をpostsテーブルのstatusというカラム持っていてこれをenumで管理して使い分ける場面です。

  • 下書き
  • 公開
  • 非公開

まずは、postsテーブルにstatusカラムを加えます。

マイグレートファイル

class AddStatusToPost < ActiveRecord::Migration
  def change
    add_column :posts, :status, :integer, default: 0
  end
end

モデルはこんな感じ

class User < ApplicationRecord
  # nameとかemailとかカラムを持っている

  has_many :posts
end

class Post < ApplicationRecord
  # bodyとstatusのカラムを持っている

  belongs_to :user
end

ここでPostのstatusというカラムに対してenumを宣言します

class Post < ApplicationRecord
  # bodyとstatusのカラムを持っている

  belongs_to :user
  enum status: { draft: 0, published: 1, unpublished: 2 } # ここでenumを宣言する
end

statusはinteger型で定義されたカラムなのでデータベース上に保存されるのは0か1か2のどれかになります。

上のマイグレートファイルで、statusカラムはdefault: 0として定義されているので、以下のようになるはずです。

  • draft:下書き(デフォルト)
  • publish:公開済み
  • unpublish:非公開

enumで使用するカラムはこのようにデフォルト値を設定しておくと扱いやすくなるでしょう。

statusをenumで宣言したので、この後プログラム上でstatusを扱う場合、それぞれの数字に割り当てられた状態(ここでいうdraft,published,unpublished)として扱うことができます。

enumで使えるようになるメソッド

enumを宣言することで次のようなメソッドが使用できるようになります。

これが本当に使い勝手が良いのです。

post = Post.create(user_id: 1, status: :draft, body: "投稿の中身")
# statusは status: 1 とかのintegerでも可能

# postの現在のstatusを出力
post.status # 出力 => draft
# postのstatusがdraftかどうかをbooleanで返す
post.draft? # 出力 => true
# postのstatusがpublishedかどうかをbooleanで返す
post.published? # 出力 => false
# statusがunpublishedに更新される
post.unpublished! 
# カラム名の複数形で状態のハッシュを取得
Post.statuses # 出力 => { draft: 0, published: 1, unpublished: 2 }

# スコープとしても使える
Post.published 
# publishedなpostレコードを検索する
# Post.where(status: :published)

現場で使われるenumでは上のようなメソッドを使用しているケースが多いのでこの辺りを押さえておくことをオススメします。

メソッドを応用した様々なユースケース

ここまでで、enumを宣言すると起こることがつかめるようになると思います。

しかし、enumの恩恵はわかりましたが、「何をどうやって使えば良いか」というのがピンとこないと思います。

ここでenumを実際に使ったケースを3点ご紹介します。

セレクトボックス

入力フォームで状態をセレクトボックスで選ばせたいとかあると思います。

以下のような(題材とは項目が違いますがイメージです)

f:id:travy:20190605161047p:plain

これはこんな風にやると実装できたりします。

<%= form_with(設定がある) do |form %>
  <%= form.select :post_config, Post.statuses%>
<% end %>

先ほどご紹介したメソッドの中にstatasesというのがありましたね。

今回はそれを使っています。

状態ごとのフィルタリング

欲しい状態のレコードだけを検索したい、みたいな状況でenumが活躍します。

enumで提供されているスコープをそのまま活用するだけなのですが、実際現場でよく使われる方法なので「こう使われる」というところだけ抑えていると良いでしょう。

例えば公開済みの記事だけを取得したいとしましょう。

posts = Post.published

これでpostsには公開済みの投稿が格納されます。

では、あるユーザーの公開済みの投稿を取得の場合

user = User.find(1)
user.posts.published

簡単に書くとこのようになります。

このようにenumのスコープは使い勝手が良い分、至る所で出会うことがあります。

更新処理

状態だけをサクッと変えたいとき、今回の場合だと下書きの記事をボタン1つで更新!としたい場合です。

statusカラムだけを更新したいけども、わざわざform_forとかでフォームを作って送信!みたいにviewに書くとちょっと仰々しいです。

なので、viewではaタグ(link_toで生成されるhtmlタグ)とかで更新用に作ったアクションにアクセスしてアクションの中でpublished!を読んでしまうと楽に実装できたりします。

# view
# PostsController#update_statusへのリンク
<%= link_to "公開", [publishアクションへのパス], method: :post %>
class PostsController < ApplicationController
  def update_status
    post = Post.find(params[:id])
    post.published! # ここでサクッと更新してしまう
    redirect_to xxx_path
  end
end

日本語化(i18n)

最後に、enumの日本語化についてここで少し書いておこうと思います。

先ほどご紹介した、セレクトボックスをそのまま実装すると、enumで宣言したdraftpublishedunpublishedが英語のまま表示されてしまいます。

またviewのページで post.statusみたいに書くと同様に英語で表示されることでしょう。

ここは次のように、i18n対応(日本語)にしておきたいところではあります。

  • draft => 下書き
  • publish => 公開
  • unpublish => 非公開

ここでgem enum_helpの出番です。このgemを使うことでenumで列挙した項目のi18n(日本語)対応ができるようになります。

次のようなステップで日本語化の準備をしていきます。

  1. app/config/locales以下に日本語化ファイルに設定を書き込む
# app/config/locales/ja.yml ← ここは読み込めるのであれば名前はなんでも良い
ja:
  enums:
    post:
      status:
        draft: 下書き
        published: 公開
        unpublished: 非公開
  1. 実際に使うとき enumを宣言したカラムを呼び出すときにカラム名の後に_i18nと書けばi18n対応ができます。

例えばPostモデルのstatusカラムの場合

post.status_i18n

となります。

さらに、先ほどのセレクトボックスも以下のように書き直すことができます。

form.select :status, Post.statuses_i18n.invert

と書くといい感じに日本語化されるでしょう。

Post.statuses_i18n
=> { "draft" => "下書き", "publish" => "公開", "unpublish" => "非公開" } 

invertについてはrubyのhashオブジェクトのメソッドになるので今回ご紹介はしませんが調べておくと良いでしょう。

gem enum_helpはenumを使用するときにはほぼ必須のgemになりますので覚えておくと良いでしょう。

まとめ

enumを宣言するだけで便利なメソッドやスコープが簡単に使えるようになるのは、enumの素晴らしい利点だと思います。

今回のような状態を扱う実装を行う際は「enumは使えないだろうか?」と検討してみるのは良いことだと思います。

今回は投稿の下書き、公開についてでしたが、例えばユーザーが管理者であるか一般のユーザーであるかを分けるため、のような使い方もよく見かけます。

enumを使いこなすことでコードがスッキリして良いコードを書く手助けになることだと思いますので是非使ってみてください。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

【初心者向け】コントローラーで使っている値をモデルに渡す感覚を掴んでみよう!

こんにちは、TECH DRIVEのTedです。

今回はコントローラーで使っている値をモデルに渡すときに、初心者の方がつまずくポイントを抑えつつレクチャしたいと思います。

初心者や未経験の方が一歩先に踏み出す、そして同じレベルの人達と1味違うことをアピールできる内容になっているはずなのでお楽しみ下さいー

はじめに

今回もRuby on Railsを題材に使ってご紹介してきますが、Railsのgem devise を利用されている方で、次のようなことを思った方もいることでしょう。

「なんでモデルの中で current_user が使えないんだよ!」

筆者もはじめは同じような悲痛な叫びを心の中でやったものです(笑)

Ruby on Railsは色々よしなにやってくれる便利なフレームワークですが、それでもモデルの中ではcurrent_userは使えないんです。

先に答えを書いておくと、引数を使用することでこの問題は解決できます。

でも、引数を使うと言われてもピンとこない。。。という方もいらっしゃるかもしれません。

なので今回は、deviseのcurrent_userメソッド(厳密にはcurrent_〇〇)を題材に「メソッドの実行結果を上手にモデルに渡す方法」を一緒に学んでいきましょう!

*今回もRuby on Railsを題材にしてのご紹介になりますのでご了承ください。

本題

モデルに処理を書くのを怠ってコントローラに処理を書き続けると、可読性の低いかつ保守性の低いコードになっていきます。

そこでコントローラーの処理をモデルに引っ越しさせる場面です。

Userモデルのオブジェクトが親、Postモデルのオブジェクトが子の関係があるとします。

ここではよく使われるgem devise を採用していることとします。

class Post < ApplicationRecord

  belongs_to :user

end

そしてユーザーの投稿の更新するためのコントローラーのアクションがあります。

ここで、自分の投稿(ここでは@post)以外は更新できないようにしたいとします。

deviseが提供してくれるログイン中のユーザーを取得するメソッド(currenrt_〇〇、ここではcurrent_userとします。)を使って投稿主がログイン中のユーザーであるかどうかをチェックしてこれを実現しています。

class PostController < ApplicationController
  def update
    @post = Post.find(params[:id])
    
    if  @post.user_id == current_user.id #この1行の処理をモデルに移したい
      return redirect_to posts_path
    end

    if @post.update(post_parameter)
      # 更新成功時の処理
    else
      # 更新失敗時の処理
    end
  end
end

今回はこの「ログインユーザーか否か」を判定する処理をコントローラーからモデルに移したいと思います。

この場合、Postモデルのインスタンスメソッドに自分のユーザー自身の投稿かどうかを確認するメソッドがあると嬉しいはずなので実装してみます。

class Post < ApplicationRecord
  def owner?
    self.user_id == current_user.id
  end
end

こうすると

@post.owner?

と使えそうです。

ですが、これでは冒頭で書いたように動かないんです。

current_userはログイン中のユーザーを呼び出すおまじないではなく、deviseで提供されているヘルパーメソッドという1つのメソッドなのです。

なのでモデルの中で呼び出すことができないのです。

しかし、コントローラーで使っていたcurrent_user、つまりログイン中のユーザーの情報をモデル内で使えないと困ります。

なので、こういう場合はowner?メソッドにcurrent_userの実行結果を引数として渡して呼んであげましょう。

では書いてみましょう。

user = current_user
@post.owner?(user)

モデルでは以下のように修正します。

class Post < ApplicationRecord
  def owner?(user) # 引数をつけた
    self.user_id == user.id # current_userは引数名のuserに変わった
  end
end

userという名前で引数を受け取ることを定義しました。 (selfは省略できますが、説明のため敢えて書いてます。)

ここで呼び出し側(コントローラー側)でのowner?メソッドの呼び方に混乱するポイントがあります。

モデルでは

owner?(user)

と定義しているので呼び出し側でも引数はモデルと同じようにuserと書いて渡さなければいけないと思いがちになってしまうことです。

なので引数がuserであることにこだわって

user= current_user
@post.owner?(user)

とか

@post.owner?(user = current_user)

のように描きたくなってしまいます。

実はこれはどちらも問題なく動きます。ですが、ここで大事なことは引数userの中身なのです。

今回の場合、current_userの実行結果である、ログイン中のユーザーが引数として入っていることが大事なのです。

例えばcurrent_user == User.find(1)の場合、以下の引数の渡し方でも問題はないでしょう。

@post.owner?(User.find(1))

このように、引数の渡し方が違っていても中身が同じであれば良いのです。

そしてメソッドを呼ぶときに呼び出し側(今回はコントローラー内)は、モデルに定義したメソッドの引数名と同じである必要はないということです。

ここの理解ができていると「自分はちゃんとわかってるよ!」とアピールできるでしょう。

さらには、意味のある引数をつけることができるようになり、可読性を意識できるコードが書けるでしょう。

実は、モデル内にメソッドを定義するときの引数名もなんでも良いのです。

例えば

  def owner?(aaaa)
    self.user_id == aaaa.id
  end

でも問題ありません。(可読性が悪いという問題はありますが(笑))

大事なことなので再度になりますが、重要なことは引数で渡す、渡ってくる中身です。

まとめ

混乱する内容になってしまったので整理します。

  • コントローラーからモデルに引数という形で値を渡すことができる。
  • モデルに定義したメソッドの引数名と呼び出し側の引数名は同じでなくても良い
  • 引数で渡す、渡ってくる値が重要

この3つを順に抑えていると実装するときや、誰かのコードを読むときの混乱が軽くなるはずです。

慣れてくると違和感なくコードの読み書きができるようになるのでぜひ挑戦してみてください!

今回ご紹介した内容は、「オブジェクト思考」について学んでみるとより理解が深まると思います。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

影響範囲は何のことなのか?ミスのない仕事をするための重要なこと

こんにちは! TECH DRIVEのTedです。

今回は影響範囲という言葉についてご紹介したいと思います。

未経験で現場に入られた方や現場に入って日が浅い方に多いと思いますが、業界で使われる語彙の意味をとらえきれずに苦しい思いをされている方も少なくはないと思います。

ちなみに筆者(Ted)は現場に入って7〜8ヶ月ほどになりますが、現場に入った瞬間から様々な語彙に囲まれて四苦八苦しました(今もですがw)

日常的に使う語彙の意味を知っておくことはチームでお仕事する時に役立ちますので、ぜひ抑えていただければと思います!

*チームごとに使用される語彙の差はあると思いますが、弊社では当記事で紹介する内容を「影響範囲」と呼んでおりますので下記も同様に進めさせていただくことをご了承ください。

[解説編] 影響範囲って何ですか?

ここでいう「影響範囲」とは、何かしらのコードを書いたとき、そのコードが影響を及ぼす範囲のことを差します。

言葉だけでは説明がつきにくいので、例をあげてみましょう。

ここではRuby on Railsを題材に取り上げます。

例えば、Userモデルが姓: last_name名: first_nameという情報(カラム)を持っていたとします。

そしてユーザーのフルネームを表示するために下記のようなメソッドが定義されているとします。

class User < ApplicationRecord

  def name
    last_name + ' ' + first_name
  end

end

何度も使われそうな処理はメソッドにしてしまうと便利なのです。

そしてユーザーのフルネームが呼びたくなっと時はこんな風に使えそうですね。

user.name

このような便利なメソッドがあるといろんな場所で使われそうです。

しかし、複数の場所で使われるということは、このnameに変更が発生した場合、このメソッドを呼び出している箇所全てが「変更の影響」をうけるようになります。

[まだまだ解説] 影響範囲って何が重要なの?

さらに重要な話になってきます。

影響範囲を意識せずにコードに変更を加えると、予期せぬ場所でバグを埋め込んでしまったりサービスの動きが変わってしまったり最悪の場合、本番環境でサービスが望む動きをしなくなる可能性も十分にありえます。

先ほど挙げたUserモデルとnameというメソッドで例を挙げていきましょう。

nameというメソッド名を変更しなければいけないことになったとしましょう。

下記のように修正するはずです

class User < ApplicationRecord

  def name # name → full_nameへ
    last_name + ' ' + first_name
  end

end

nameメソッドがfull_nameに変更されたということは、プロジェクトでnameメソッドを使っている箇所を全てfull_nameへ修正しなければエラーが発生してしまいます。

なので、nameを変更することで影響を受ける範囲を洗い出し、同様に修正をかける必要があります。

もしも修正漏れがあった場合、今回の例だとエラーによってユーザーが画面を開くことができなくなってしまいます。

そしてこの変更が誰のレビューの目にも止まらずに本番にデプロイされてしまったときは致命的なミスになります。

というように今回は表示するメソッドを例えに挙げましたが、大きな例でいうとお金の支払いに関する処理で同様のことが起きると本当に大変であることがわかります。

なので影響範囲を確認することは重要で、確実な仕事をするためには必要不可欠な要素です。

次からは、じゃあどうやって影響範囲を調べるの?に迫ります!

[実践編] 影響範囲の見極め方

では、実際に影響範囲をどうやって調べて見極めるのかが大事になってくると思います。

実際に私が現場に入りたてのころ使っていたやり方をご紹介しようと思います。

ご紹介する方法は現在では古典的なやり方なので、お使いの開発環境によってはより良い方法があると思いますが、知っておいて損は無いので眺めてみてください。

今回の場合ならば、nameというメソッドがプロジェクト内のどこで使われているかを知る必要があります。

nameはUserモデルのメソッドでuserオブジェクト.nameのように使われるはずです。

ここでLinuxコマンドのgrepを使います。

grepはファイル内の文字列に対して指定した文字列(正規表現)に該当する文字列を検索するコマンドです。

grepには-rというオプションがあるのでファイル単体だけでなくディレクトリごと指定することができます。

なので

grep -r name app/*

みたいに検索するとappディレクトリ以下でnameの文字列が書かれている場所を一気に検索してくれます。

これでプロジェクトでnameが使われている場所の全てがわかります!

あとはヒットした箇所が自分の変更によって変わるべきなのかを吟味すると良いでしょう。

ちなみに私の場合は検索結果をリスト化して残し、動作チェックする時に使用したりもしますし、チームのメンバーに相談する時の良い材料にもなります。

Rubyを用いた開発をしていてRubyMineをお使いの方の場合は、RubyMineの便利機能がありますのでそちらを使うとなお強力な検索ができます。

下記の記事の検索系やコードジャンプを使うとgrep相当のことが手軽にできます。

RubyMine以外の多くのエディタの場合、一括検索もしくは全文検索という名前でgrep相当の機能がありますのでそちらを活用するのも良いでしょう。

dev.techdrive.top

コラム

今回検索した文字列はnameでしたが、表記方法には数種類あります。

  • スネークケース&キャメルケース →クラス名はキャメルケース、テーブル名はスネークケースだったりします。
  • シンボル表記

これらを全て網羅した検索ができると、影響範囲の見積もり漏れをよりしっかり防ぐことができるでしょう。

終わりに

影響範囲は開発作業をする上で非常に重要な要素であり、必要不可欠と言えるでしょう。

逆に検索する能力をしっかり高めて、これを抑えた仕事をきっちりできる人は信頼されるはずです。

一度癖つけると自然と体がついてくるので是非実行してみましょう!

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

RubyMine便利機能のご紹介

こんにちは、TECH DRIVEのTedです。

今回はRubyMineが持っている便利な機能についてご紹介したいと思います。

弊社ではメンバー全員がRubyMineを使って開発を行なっています。

先日、RubyMineの機能をどこまで引き出せているかを会話したのですが、意外とメンバー間で持ち合わせている知識にバラツキがあることに気がつきました。

会話の中ではお互い「なるほど」「それクールだw」等、良い知識の交換会になりました。

RubyMineは有料のエディタなので、お使いになられていない方は「ほうほう、こんな感じなのか」と若干でもRubyMineの感じを知って頂ければと思います。

*弊社ではソースコードの管理にGitを使用しており、その前提でのご紹介となりますのでご了承ください。

ご紹介機能一覧

どれも強力ですが、★は取り分け強力で超便利機能

  • 機能検索機能(Find Action)★
  • コミット(Commit)
  • プッシュ(Push)
  • コードジャンプ1(Forward, Back)
  • コードジャンプ2(Declaration) ★
  • 全体検索(Find in Path) ★
  • ファイル内検索(Find)
  • 全体ファイル検索 ★
  • ファイル内置換(Replace)
  • 全体置換(Replace in Path)
  • Railways ★
  • Database ★

アクション検索機能

Command + Shift + A

RubyMineで使用できる機能を検索することができます。この機能でご紹介機能一覧の()の文字で検索すると各機能にヒットするはずです。

キーバインドを設定したけど、「何を押せば・・・」みたいにバインドを忘れた時なんかや、直感的に検索すると使いたい機能がヒットすることもあります。

例えば、RubyMineからGit操作を行う場合、branch newとかで検索するとブランチを作ってくれる機能がヒットしたりします。

これがあればどんな機能でも探して実行できるので超便利です。

コミット&プッシュ

コミット: Command + K

Gitのコミットメッセージや差分を1つのウインドウで操作することができます。コマンドラインでやらなくて良いので結構楽になります。

右下の Commit を押せばコミット完了!

f:id:travy:20190412111530p:plain

プッシュ: Command + Shift + K

コミット一覧をコミットメッセージと一緒に確認することができます。

右下の Push でプッシュできます。Push ボタンの右のプルダウンで Force Pushも選べます。

f:id:travy:20190412111855p:plain

コードジャンプ(Forward, Back)

ツールバー NavigateのForward,Back, Declarationを呼び出す方法です。

使うと自分が作業していた前後のファイルへジャンプできます。

Forward: Option + Command + →

Back : Option + Command + ←

もっと使えるコードジャンプ(Declaration)

Command + B

こいつは使えます!これを使いこなせると開発やデバックやコードリーディングのスピードが段違いにUpすること間違いなしです。

  • 自分で定義した変数やメソッドにジャンプ
  • ViewからControllerやModelにジャンプ
  • Gemが提供しているメソッドにジャンプ
  • レンダリングでしてしているファイルへジャンプ
  • メソッドを使用している箇所へジャンプ

とにかくファイルを超えて定義元や対象のファイルへジャンプすることができます。

困ったときには Command + B でジャンプしまくると良いでしょう。

最後の、メソッドを使用している箇所へジャンプを利用した応用テクニックがあります。

メソッドの定義元で Command + B をすると使用している箇所の一覧が表示されます。

こうすることで、メソッド自体を修正するときに影響範囲を明確にすることができます。

デバッグ作業をしている時なんかに、メソッドのロジック自体に問題があるので迂闊に修正をかけると思わぬところでバグが出たり挙動が変わってしまった、みたいなことが発生することがあります。

しかし、このコードジャンプをマスターしていれば修正する前に影響範囲を探ることができます。

修正したいメソッドの影響範囲が大きすぎる場合、対象のメソッドを修正した後の大量の動作確認がもれなくついて来て時間が無駄に過ぎてしまいます。

なので別のメソッドを作ってしまう等、別の方法で開発を進めることができるかもしれません。

コードジャンプはファイル移動を楽にするだけでなくて上等な検索手段であり、プロジェクト内の情報収拾の強力な道具になります!

検索機能

ファイル検索: Command + Shift + O

f:id:travy:20190412121553p:plain

プロジェクト以下のファイル検索をやってくれます。

検索ヒットしたファイルにジャンプすることができます。

「あのファイル開きたいからあのディレクトリ開いて、開いて、開いて、、、」

面倒だと思います。

ファイル名を記憶しているのであれば検索して一気にジャンプすると楽ですね。

僕はschema.rbをよく開くのですがプロジェクトのルート→db→schema.rbと移動がとにかく面倒なので1発検索で開いてますw

ファイル内検索: Command + F

ファイル内の指定した文字列を検索することができます。

全体検索

Command + Shift + F

f:id:travy:20190412123242p:plain

こいつも強力です。もっと使えるコードジャンプでも紹介しましたが、影響範囲の情報を知る上で欠かせないツールです。

やってる内容は指定した文字列がプロジェクト内で使われていたら一覧に表示するだけなのですが検索の仕方によっては強力な武器になります。

例えば、定義したインスタンス変数やメソッドを全体検索すると自分が修正する箇所がどこに影響するのかを知ることができます。先ほどと重複するので割愛しますが、影響範囲を特定することはデバッグに置いて重要なことなのでコードジャンプと合わせて使うとより正確なデバッグができます。

さらに全体検索には他と違う機能があります。

検索結果(画像)右下のOpen in Find Windowを開くことで検索結果をファイルツリーにして表示してくれます。

f:id:travy:20190412124321p:plain

さらに!画像左下の赤枠のボタンを押すと検索結果一覧をテキストにして表示してくれます。

f:id:travy:20190412124450p:plain

こいつを丸ごとコピーしてエクセルやGoogleスプレッドシートに貼り付けると影響範囲兼、動作確認チェックシートができたりするので確認の抜け漏れを防いだりするのにすごく便利です。

僕はよくこの要領でチェックシートを作ってたりするのでおすすめです。

ファイル内置換&全体置換

ファイル内置換: Command + R

全体置換: Command + Shift + R

検索方法はCommand + FCommand + Shift + Fと同じです。

あとはReplaceを押すだけで置換してくれます。

検索結果を全部一気に置換したいときはReplace Allを使ってみるのもありです。(ただし影響範囲にご注意ください)

こいつは正規表現で検索することができるので一意の文字列ではなくともまとめて検索をヒットさせることができるのである特定の文字列を一気に置換したいときに正規表現で検索をかけると便利だったりします。

僕がseedファイルを修正する時なんかは正規表現でまとめてヒットさせて一気に置換したりしてますw

Railways

これは超便利なプラグインです。RubyMineをご使用されている場合はぜひインストールをお勧めします。

Railwaysは何ができるようになるかですが、bin/rails routesで表示されるルーティング一覧をさらに見やすく表示してくれるプラグインです。

インストールすると右上にRoutesというタブが出現してプロジェクトが提供するURLとそれにひもづくControllerのアクションと名前付きパスが表示されます。

さらに上の検索窓でパスを検索することができるので必要なルーティングだけを表示することができます。

f:id:travy:20190412143651p:plain

プラグインのインストールはPreferences(Command + ,)Pluginsでできます。

Database

プロジェクトで使用するデータベースにアクセスすることができます。

アクセスすることでRubyMine上で主に以下の操作をすることができます。

  • レコードの作成、更新、削除
  • SQL文の実行

何が嬉しいかというと、開発中にあるレコードだけを更新させたいけどブラウザ上でできない or 面倒であるときなんかにコマンドラインで

User.find(1).update(name: 'hoge')

のようにレコードを更新する処理を叩くと思いますがRubyMineでデータベースに接続しておくとエディタ上で全て完結させることができます!

こんな感じ

f:id:travy:20190412145235p:plain

接続方法が少し難しいので簡単にご紹介します。

エディタの右上にDatabaseというタブが出ていると思うのでクリックして'+ → Data Source → + 今使っているDBの種類を選択します。

f:id:travy:20190412145651p:plain

すると次の画像のようなウインドウが表示されますが、初めて設定する場合は訳がわからないかも知れないです。

ですが、複雑な方法でDBにアクセスしているプロジェクトでない限り必要なことは実はそこまで多くありません。

Ruby on Railsで開発されている方の場合はconfig/database.ymlにそこに入力する内容は揃っているはずです。

ではconfig/database.ymlの情報を使ってDBに接続してみましょう。

以下のウインドウで入力します。

f:id:travy:20190419124002p:plain

ウインドウのDatabaseの入力蘭に使用しているデータベース名、Userにはusernameの内容、Passwordにはpasswordの内容を入力して

Test ConnectionをクリックしてDBにテストアクセスをします。上手くいけば緑色でSuccessfulと出るはずで、あとは右下のApplyをクリックして接続完了です!

再度右上のDatabaseタブを開くと接続完了したデータベースが表示されるので▶︎をクリックして開いていくと自分が作った見覚えのあるテーブル名が並んでいるはずです。

それをクリックすることでデータベースを開くことができます。

f:id:travy:20190412151430p:plain

レコードの更新はカラムに変更内容を入力してDBと書いてあるボタンをクリックするだけです

あとは好きな時にデータを作ったり更新したり削除したりできるのでコマンドラインで一回一回メソッドを叩くよりも素早くレコードの変更ができてしまいます。

f:id:travy:20190412151845p:plain

まとめ

今回ご紹介したものはよく使う機能のみなので是非試してみてください。

RubyMineならではの機能もあるので、せっかくのエディタを最大限活かせるようになると開発スピードも上がるしもっと楽に開発できるようになるはずです。

筆者である僕自身もまだまだ知らない機能もあるのでいろいろ試したいと思います。面白い機能を見つけた時はまた記事にてお会いしましょうw

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

新しく加わったRuby on Railsプロジェクトを素早く把握するコツと便利なツールのご紹介

こんにちは、TECH DRIVEのTedです。

今回は新しく加わったRuby on Railsプロジェクトを素早く把握するための便利なツールをご紹介します。 本記事はこれから開発現場に入っていく方や、新しく別の現場に入られる方のお役に立てる記事になっていると思います。

本題

新しく入ったプロジェクトを手元のマシンにとってきた後に環境構築を始めると思います。
そして、例えばgithubを使っている現場であれば「さて、振られたissueを対応していきますか!」と開発を始めることになると思いますが、自分が加わったプロジェクトについての情報がないとスムーズに開発の流れに乗れないと思います。 逆に、情報が多いと効率よく開発作業を進めることができるはずです。

ここで情報が手元にない時に情報を集める方法がいくつかあるかと思いますが、 私の場合は下記の情報があると開発を始めやすいのではないかと思います。

  1. データベースの詳細、テーブル数やその相関関係
  2. ルーティングの規模感
  3. ディレクトリ構造

2~3についてはプロジェクトのコードを読むことで解決できます。例えば

2. ルーティングの規模感が調べたければroutes.rbを読んでみたり、bin/rails routesを実行してみると良いでしょう。

3. ディレクトリ構造rails newで自動生成されるディレクトリ以外についてで、よく使われているディレクトリではapp/decoratorsapp/services(このあたりはプロジェクトによって多少の違いはあります)のようなディレクトリです。見慣れないディレクトリ名を見つけた時は気をつけましょう。

今回は下記に焦点をあてたいと思います。

データベースの詳細を知りたい

先ほど、挙げた3項目の中で最も大事だと思います。

この1. データベースの詳細、テーブル数やその相関関係についてはER図があると把握しやすくなります。

ER図とは下記のようなテーブル同士の関係を示す図のこと

f:id:travy:20190327183149p:plain

schema.rbを参照することで同様にテーブル数や相関を読むこともできますが、ER図ではこのあたりを図で見ることができるので理解しやすく、schema.rbを見るよりもよりテーブル構造を把握することができるでしょう。

ER図をプロジェクトのチームが持っているならば共有してもらえば良いのですが、持っていない場合もあります。

そんな時に自動でER図をPDF形式で生成してくれるrails-erdというgemが存在します。

rails-erdの導入

Gemfileに以下のような記述をします。本番環境で使うことはないのでdevelopment環境で使えるようになっていればOKです。

*Gitを使用して開発をしている場合、既存のコードを汚さないために新しく作成したブランチで作業することをお勧めします。

group :development do
  gem 'rails-erd'
end

続いてgemをインストールします

bundle install

これで準備完了です!あとはER図を出力するだけです

bundle exec erd

下記のような図が出力されます。

f:id:travy:20190327154800p:plain

ER図をコマンド1発で手に入れることができるのは非常に強力だと思います。

ER図があると何が良いのか

ここで、ER図があると何が良いのかおさらいしておきましょう。

ER図があると親子関係や中間テーブルのようなテーブル同士の相関を一目で見ることができ、各テーブルがどのようなカラム、つまりどのような情報を持っているのかも把握することができますし、開発する時にもテーブル同士の関係性を確かめながら進めることができます。

さらに、図で見ることでサービスにおいて重要なテーブルや機能を推察するためのヒントが得られます。

ER図から得た情報は、プロジェクトの中で目を通しておくべきコードの把握や、その意味を理解するための助けになります。

終わりに

ER図が手元にある状態でコードリーディングや開発作業をすると凄く心強い気持ちになれます。

余計な脳のリソースを使うことなくテーブル構造をインプットできるER図はとても強力です。

私もよく自分で作ったサービスだったり関係するプロジェクトのER図を今回ご紹介したrails-erdで描画しようとします。開発中も常にER図の画面を開きながら開発しています。

本記事を通して少しでも新規プロジェクトへの加入が楽になるのであれば幸いです。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

脱・Rails初心者!子モデルのインスタンス生成と同時に親モデルに紐付ける方法

はじめに

こんにちは、TECH DRIVEのTedです。

今回はRuby、Ruby on Railsを学習されている初学者へ向けた、初学者から1歩レベルアップするためのコードの書き方をご紹介します。開発現場でよく使われる書き方の1つでもあるのでぜひ習得してみてください!

本題

親子関係のテーブルで親テーブルに紐づいた子テーブルのインスタンスを作るときに親のテーブルのidを簡単に持たせることができます。結果的にコントローラーやモデルに書く処理を少なくすることができます。

例えばユーザーの情報を持つusersテーブルとユーザーの投稿の内容を持つpostsテーブルがあったとします。下記のような感じ

f:id:travy:20190320151608p:plain

class User < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :user
end

userspostsの親になります。そして、サービスの中でユーザーが入力した内容をpostsテーブルのデータとして保存する処理があるとします。

ツイッターをイメージすればわかりやすいと思います。ユーザー(users)とツイート(posts)のような関係です。

ここで子テーブルと親テーブルを紐づけるときにいくつか方法がありますが普通に書くと下記のようになると思います。

class PostsController < ApplicationController
  before_action :set_user
  # 他のアクション
  def new
    @post = Post.new
  end
  
  def create
    @post = Post.new(post_params)
    @post.user_id = @user.id
    if @post.save
    # 保存が成功した時の処理
    else
    # 失敗したときの処理
    end
  end
  
  private
  def set_user
    @user = User.find(params[:user_id])
  end
  
  def post_params
    params.require(:post).permit(:title, :body)
  end
end

上記は特に問題はないのですが、さらに下記のように書くこともできます。

class PostsController < ApplicationController
  before_action :set_user
  # 他のアクション
  def new
    # ここがポイント!↓
    @post = @user.posts.new
  end
  
  def create
    # ここがポイント!↓
    @post = @user.posts.new(post_params)
    if @post.save
    # 保存が成功した時の処理
    else
    # 失敗したときの処理
    end
  end
  
  private
  def set_user
    @user = User.find(params[:user_id])
  end
  
  def post_params
    params.require(:post).permit(:title, :body)
  end
end

2つのコードの違いはnewcreateアクション の中にあります。
Postモデルのインスタンスをnewするときに親であるUserモデルに紐づけてnewしているかどうかです。
こうすることで新しく作られたPostモデルのインスタンスは親であるUserモデルのid、つまりuser_idを最初から持っている状態になります。
逆にPost.newで作るとuser_idはあとでセットしなくてはなりません。

# これが
@post = Post.new(post_params)
@post.user_id = @user.id
# こうなる
@post = @user.posts.new(post_params)

これで少ないコードで実装できて投稿される内容はユーザーに紐づいていることがすぐにわかりそうです。

おしまいに

今回はコントローラーに処理を書いてご紹介しましたが、親子関係の子にあたるモデルのインスタンスを作るときは他の場所でも同じことができます。(例:モデルのメソッド内とか)

こういったコードが書けるだけで他の初学者と1歩差をつけることができますし、現場のエンジニアが見ても「こいつ、わかってるな」とアピールすることができます。

私もよく自作のサービスで前者のようなコードをよく書いていました。 しかし、同じ動作が期待できるコードでも書き方によってあとで見る人やあとで自分で見たときに「これは何か意味があるのだろうか?」と迷ってしまうこともあります。

ご自分でサービスやコードを書かれている方は、本記事の内容を踏まえてもう1度自分のコードを眺めて見るのも良いかもしれません。
このような細かい配慮の積み重ねが読みやすいコードに繋がります!

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントエンド/バックエンド問わず各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

TECH DRIVEについて

TECH DRIVEは「技術者の成長を加速させる」をキーワードに都内で活動をしているコミュニティです。
TwitterやFacebookにて技術ネタやイベント情報の発信を行っていますので、ご興味があれば、いいねやフォローをお願いいたします。