dev/mom0tomo

技術メモ

【メモ】herokuにRailsアプリケーションをデプロイするときのコマンド

うっかり忘れがちなので自分用にメモにまとめておく。

// ログイン
$ heroku login

// アプリケーション作成
$ heroku create my-rails-app

// 作成されたか確認
$ heroku apps
my-rails-app

// herokuにpush
$ git push heroku master

// pushされたか確認
$ git remote -v
heroku  https://git.heroku.com/my-rails-app.git (fetch)
heroku  https://git.heroku.com/my-rails-app.git (push)
origin  https://github.com/mom0tomo/my-rails-app.git (fetch)
origin  https://github.com/mom0tomo/my-rails-app.git (push)

// herokuでdb:migrate
$ heroku run rails db:migrate

heroku run rails db:migrate忘れがち。

Rails kaminariでページネーションを実装する

読んだもの

github.com

学んだこと

kaminariの使い方はすごく簡単。

tasks_controller.rb

def index
  @tasks = Task.all.page(params[:page])
  # @tasks = Task.all <- kaminari利用前のコード
end

index.html.erb

<h1>タスク一覧</h1>
<ul>
  <% @tasks.each do |task| %>
    <li>
      <%= link_to task.id, task_path(task) %> : <%= task.content %>
    </li>
  <% end %>
</ul>

# ページネーションしたいところに記述する
<%= paginate @tasks %>

<%= link_to '新しいタスクを登録', new_task_path %>

controllerのTask.all部分に .page(params[:page])を追加して、viewのページネーションしたいところに <%= paginate @tasks %>をつけるだけ。

表示数を変更する

1ページの表示数はデフォルトで26に設定されており、変更したいときは .per(number)をつける。

  def index
    @tasks = Task.all.page(params[:page]).per(10)
  end

表示順番を降順にする

表示順番を降順にしたい場合は、Controller側で orderを指定する。

tasks_controller.rb

def index
  @tasks = Task.order(created_at: :desc).page(params[:page]).per(10)
end

こんな感じ。

Railsでパーシャルを切り出すときはインスタンス変数をローカル変数にする

new.html.erb

<h1>新規タスク作成</h1>

<%= form_for(@task) do |f| %>
  <% f.label :content, 'タスク' %>
  <% f.text_field :content %>
  <% f.submit '登録' %>
<% end %>

<%= link_to '一覧に戻る', tasks_path %> 

edit.html.erb

<h1>タスク編集画面</h1>
<p><%= @task.content %></p>

<%= form_for(@task) do |f| %>
  <% f.label :content, 'タスク' %>
  <% f.text_field :content %>
  <% f.submit '登録' %>
<% end %>

<%= link_to '一覧に戻る', tasks_path %> 

こういうのがあったときに、共通部分(フォーム周りのところ)をパーシャルにして切り出したい。

学んだこと

以下のようなパーシャルをつくる。

_form.html.erb

<%= form_for(task) do |f| %>
  <%= f.label :content, 'タスク' %>
  <%= f.text_field :content %>
  <%= f.submit '登録'%>
<% end %>

new.html.erb

<h1>新規タスク作成</h1>

<%= render 'form', task: @task %>

<%= link_to '一覧に戻る', tasks_path %> 

edit.html.erb

<h1>タスク編集画面</h1>
<p><%= @task.content %></p>

<%= render 'form', task: @task %>

<%= link_to '一覧に戻る', tasks_path %> 


ポイントは以下の通り。

  • パーシャルでは元のインスタンス変数 @taskをローカル変数 taskに変更している
  • new/editでは task: @taskとすることでパーシャルに対して変数を渡している
    • taskという変数名で @task の値を渡している
    • パーシャルにインスタンス変数を使わないことでより汎用的になる

RailsでviewのフォームからcontrollerにPOSTでデータを送信する

RailsCRUDの基本のところで、newアクションからcreateアクションにデータを送るあたりがよくわからなくなったのでまとめる。

学んだ事

formタグのPOSTメソッドとパラメータの関係

まずはRailsではなく素のHTMLで考える。

<form action="/" method="POST">
  <label>名前: <input type="text" name="target_name"></label>
  <input type="submit" value="送信">
</form>

viewにこのようなフォームがある場合、

  • ユーザからのアクションは全て、HTTPリクエストのGETメソッドやPOSTメソッドとしてWebサーバに送信される。
  • HTTPリクエストのPOSTメソッドで送ったデータは、params(パラメータ)に格納される。
  • Webサーバはリクエストをもとに、paramsに入っているユーザからのデータを処理し、レスポンスを返す。

これが基本。


Railsのnewアクション

app/controller/task_controller

class TasksController < ApplicationController
  before_action :set_task, only: [:show, :edit, :update, :destroy]

  def index
    @tasks = Task.all
  end

  def show
  end

  def new
    @task = Task.new
  end
end

newアクションでは、対応するviewをPOST メソッドを送信する新規作成用の入力フォーム置き場として使う。

app/views/messages/new.html.erb

<h1>タスク新規作成ページ</h1>

<%= form_for(@task) do |f| %>
  <%= f.label :content, 'タスク' %>
  <%= f.text_field :content %>

  <%= f.submit '登録' %>
<% end %>

<%= link_to '一覧に戻る', messages_path %>

上のようなviewを書く事で、POSTメソッドを使ったフォームが生成される。

<body>
  <div class="container">
    <h1>新規タスク作成</h1>
    <form class="new_task" id="new_task" action="/tasks" accept-charset="UTF-8" method="post"> // POSTメソッドを使う
      <input name="utf8" type="hidden" value="&#x2713;" />
      <input type="hidden" name="authenticity_token" value="1XF/tmSKY6ak0LovbntLa/AqaxPSPAG6Ak5YyBdA4W3ebzM8KilBV+Vxw5adGwhqPE7pEJ9mn3ZbgJdGKQsJ1w==" />
      <label for="task_content">タスク</label>
      <input type="text" name="task[content]" id="task_content" />
      <input type="submit" name="commit" value="登録" data-disable-with="登録" />
    </form>
    <a href="/tasks">一覧に戻る</a>
  </div>
</body>

viewから実際に生成されるHTMLはこんな感じになる。

Railsのcreate アクション

app/controller/task_controller

class TasksController < ApplicationController

....

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
  
    respond_to do |wants|
      if @task.save
        flash[:success] = 'Taskが登録されました'
        redirect_to @message
      else
        flash.now[:danger] = 'Taskが登録されませんでした'
        render :new
      end
    end
  end

  private

    def set_task
      @task = Task.find(params[:id])
    end

    # ストロングパラメータ
    def task_params
      params.require(:task).permit(:content)
    end
end

create アクションは、newのviewからPOSTで送信されたフォームのデータを処理する。

new からcreateへ送られてきたフォームの内容は params[:task] に入る。
params[:task] をそのまま使用するのはセキュリティ上よくないので、ストロングパラメータを使ってフィルタする。

パラメータがネストしているときのストロングパラメータの書き方

前回の応用編。

読んだもの

qiita.com

学んだこと

ネストしたパラメータのサンプル

{
  "name": "momo",
  "address": {
    "prefecture": "Toyo",
    "city": "Shinagawa"
  }
}

上記のようなネストした構造のパラメータについて、ストロングパラメータの書き方は以下の通り。

user_controller.rb

params.permit(:name, address: [:prefecture, :city])

ストロングパラメータの簡単なサンプル

読んだもの

qiita.com

学んだこと

  • Railsではストロングパラメータを必ず使う
  • フォームの受け渡しなどで使わないとエラーになる(警告)

ストロングパラメータの基本的な書き方

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

やっていること

以下の二つ。 1. requireでPOSTで受け取る値のキーを設定している 2. permitで許可するカラムを設定している


上の例ではname, email, password属性の値だけDBに入れるのを許可している。

サンプルコード

実際に書くときはこんな感じ。

task_controller.rb

class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def show
    @task = Task.find(params[:id])
  end

  def update
    @task = Task.find(params[:id])
    if @task.update(task_params)
      redirect_to @task
    else
      flash.now[:danger] = 'Message は更新されませんでした'
      render :edit
    end
  end

  private

    # ストロングパラメータ
    def task_params
      params.require(:task).permit(:content)
    end
end

Sinatra(Ruby)とGoの標準パッケージで立てたサーバを見比べる

たまたま同じ時期に0からサーバーを立てる機会に恵まれて、似てるなと思ったので比較してみる。

Sinatra

app.rb

require 'sinatra'

get '/' do
  erb :index
end

index.erb

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>タイトル</title>
  </head>
  <body>
    <p>Sinatraだよ</p>
  </body>
</html>

Go

init.go

package main

import "net/http"

func init() {
    http.HandleFunc("/", index)
}

index.go

package main

import (
    "html/template"
    "net/http"
)

var indexTmpl = template.Must(template.New("index").Parse(`<!DOCTYPE html>
<html>
  <head>
      <meta charset="UTF-8">
      <title>タイトル</title>
  </head>
  <body>
      {{.}}
  </body>
</html>`))

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Println(w, "Goだよ")
}

改めてGoの標準パッケージすごい。そして html/templateつらい。

selfを理解する

mom0tomo.hateblo.jp 前回の続き。

selfについて、実務であまり使うこともなくちゃんと理解しているのが怪しかったので確認する。


self は、クラス内部の変数やメソッドを明示して使うときに利用する。

class Person
  attr_accessor :name, :age

  def initialize
    self.name = 'もも'
    self.age = 22
  end

def cording(programing_language)
    puts "#{self.name}#{programing_language}を利用しています。"
  end
end

mom0tomo = Person.new
mom0tomo.cording('Go言語ʕ ◔ϖ◔ʔ') # ももはGo言語ʕ ◔ϖ◔ʔを利用しています。
p mom0tomo # #<Person:0x00007fa1b684d640 @name="もも", @age=22>

selfは、呼び出されたときのインスタンス (ここではmom0tomo)を指す。

つまり、mom0tomoインスタンスから cording('Go言語ʕ ◔ϖ◔ʔ')メソッドが呼び出されたとき、

mom0tomo.cording('Go言語ʕ ◔ϖ◔ʔ')

mom0tomo. が、 self. に代入される。

attr_accessorメソッド

attr_accessor はクラス内で変数を定義するためのメソッド。

下記のように、引数として与えられたシンボルをクラス内の変数として定義する。

class Person
  attr_accessor :name, :age

  def initialize
    self.name = 'もも'
    self.age = 22
  end
end

mom0tomo = Person.new

p mom0tomo

selfについては次回。

リテラルとはなにか

簡単にいうと、 リテラルは値 である。

  • a = 'hoge' としたとき、'hoge' のことを「文字列リテラル」と呼ぶ。
  • a = 1 としたとき、1 のことを「数値リテラル」と呼ぶ。
  • 配列・ハッシュ・論理値・nilリテラルである。

リテラルは値なので、変数に代入できる。

つまり変数に何かを代入するときは、 変数 = リテラル という形を取っていることになる。

Rubyのattr_readerについて

読んだもの

ref.xaio.jp

学んだこと

attr_readerメソッドはアクセサメソッドを定義する。 アクセサメソッドとは、クラスやモジュールにインスタンス変数を読み出すためのメソッドである。 引数には、インスタンス変数名をシンボルか文字列で指定する。

下記はBookクラスにtitleメソッドとpriceメソッドを定義する例。beforeはattr_readerを使わない場合の書き方。

before

class Book
  def title
    @title
  end

  def price
    @price
  end
end

上の例をattr_readerを書き直すと、下記のようになる。

after

class Book
  attr_reader :title, :price
  
  def initialize(title, price)
    @title = title; @price = price
  end
end

Rubyのmapとeachの違い

読んだもの

manji602.hatenablog.com

すごくわかりやすかった。

学んだこと

each

eachはレシーバ自身が返る。

def each_sample
  (1..10).to_a.each do |i|
    i + 10
  end
end

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

レシーバ i が返っており、インクリメントはされていない。

map

mapはブロックの戻り値を集めて返す。

def map_sample
  (1..10).to_a.map do |i|
    i + 10
  end
end

=> [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Rubyのelsifとガード節について

コードレビューで、if elseは条件が対称な場合にだけ使った方が良いという指摘を受けた。

# if elseを使うのに適しているものの例

if number.odd?
  # 奇数の場合の処理
else
  # 偶数の場合の処理
end

上のように分岐の条件に対称性がある場合はif elseを使う。 そうでない場合は、早期リターンを使ったほうが意図が伝わりやすいという指摘だった。

読んだもの

blog.livedoor.jp techracho.bpsinc.jp

学んだこと

  def display_kanri_fee # 管理費(画面表示用に整形する)
    if kanri_fee.blank? # 管理費がない場合
      '- 円' => 
    elsif kanri_fee_type.blank? # 月額/年額表示がない場合
      number_to_currency(kanri_fee, unit: '')
    else
      "#{number_to_currency(kanri_fee, unit: '')} / #{kanri_type_i18n}"
    end
  end

ガード節で書き直す

  def display_kanri_fee
    return '- 円' if kanri_feeblank?
    return number_to_currency(kanri_fee, unit: '') if kanri_fee_type.blank?
    "#{number_to_currency(kanri_fee, unit: '')} / #{kanri_type_i18n}"
  end

なるほど、わかりやすい気がする。

Railsのja.ymlでEnumの内容が被ったら

Enumの内容がかぶると、重複エラーになる。

You tried to define an enum named "monthly" on the model "Room", but this will generate a instance method "monthly?", which is already defined by another enum.

こういうときはprefixとsuffixをつけて区別してあげればいい。

読んだもの

api.rubyonrails.org

学んだこと

修正前

model/Room.rb

  enum yachin_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }

  enum kanrihi_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }

# => 重複エラーになる

修正(prefixをつける場合)

model/Room.rb

  enum yachin_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }, _prefix: :kanrihi_type

  enum kanrihi_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }

修正(suffixをつける場合)

model/Room.rb

  enum yachin_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }, _suffix: true

  enum kanrihi_type: {
    monthly: 1, # 月額
    yearly: 2 # 年額
  }