20201122のRubyに関する記事は22件です。

letter_openerでエラーが出た時の話

表示されていたエラー

Launchy::CommandNotFoundError Exception: Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug

経緯

vagrantがどうこうっていう記事ばかりで参考になるものが決め打ちでなかなか見当たらなかった為、次回同じ原因でハマった時用の備忘録。

環境

Rails 4.2
Ruby 2.3
EC2に内にソースコードを置いて、SSH接続での開発

対応方法

letter_openerだと、リモート開発時(EC2にSSH接続している等)には、上記エラーを吐いてしまう。
リモート開発でメール送信機能の確認をしたいのであれば、letter_opener_webの追加が必要。
導入手順は以下。

1 gemfileにletter_opener_webの追加

gemfileで、

gem 'letter_opener_web', group:  :development

を追加する。

2 config/environments/development.rbの設定追加

config.action_mailer.delivery_method = :letter_opener_web

を追加する。

※※※既にletter_openerをインストールしているのであれば、

config.action_mailer.delivery_method = :letter_opener

という行があるはずなので、
単純に末尾に_webを足してやればOK。

3 config/routes.rbに設定追加

最後に、以下3行をroutes内に追加してやればOK。

  if Rails.env.development?
    mount LetterOpenerWeb::Engine, at: '/letter_opener'
  end

letter_opener_web導入後

localhost:3000/letter_opener/を別タブでブラウザ上に開いておけば、
メールの送信記録が確認できる。

参考記事

https://qiita.com/k-shogo/items/d85905535a64e82a3b2b
http://rubyandrails.hatenablog.com/entry/letter_opener_web
https://www.geek.sc/archives/2227
https://qiita.com/7sgg_m/items/178e0d90fc55c5716474

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionMailer実行時にletter_openrが原因で落ちてしまった件について

表示されていたエラー

Launchy::CommandNotFoundError Exception: Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug

経緯と概要

vagrantがどうこうっていう記事ばかりで参考になるものが決め打ちでなかなか見当たらなかった為、次回同じ原因でハマった時用の備忘録。

EC2でリモート開発するなら、letter_openerではだめで、letter_opnener_webを使わないといけない。

環境

Rails 4.2
Ruby 2.3
letter_opener 1.4.1
EC2に内にソースコードを置いて、SSH接続での開発

対応方法

letter_openerだと、リモート開発時(EC2にSSH接続している等)には、上記エラーを吐いてしまう。
リモート開発でメール送信機能の確認をしたいのであれば、letter_opener_webの追加が必要。
導入手順は以下。

1 gemfileにletter_opener_webの追加

gemfileで、

gem 'letter_opener_web', group:  :development

を追加する。

2 config/environments/development.rbの設定追加

config.action_mailer.delivery_method = :letter_opener_web

を追加する。

※※※既にletter_openerをインストールしているのであれば、

config.action_mailer.delivery_method = :letter_opener

という行があるはずなので、
単純に末尾に_webを足してやればOK。

3 config/routes.rbに設定追加

最後に、以下3行をroutes内に追加してやればOK。

  if Rails.env.development?
    mount LetterOpenerWeb::Engine, at: '/letter_opener'
  end

letter_opener_web導入後

localhost:3000/letter_opener/を別タブでブラウザ上に開いておけば、
メールの送信記録が確認できる。

参考記事

https://qiita.com/k-shogo/items/d85905535a64e82a3b2b
http://rubyandrails.hatenablog.com/entry/letter_opener_web
https://www.geek.sc/archives/2227
https://qiita.com/7sgg_m/items/178e0d90fc55c5716474

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionMailer実行時にletter_openrが原因で落ちた

表示されていたエラー

Launchy::CommandNotFoundError Exception: Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug

経緯と概要

vagrantがどうこうという記事ばかりで参考になるものが決め打ちでなかなか見当たらなかった為、次回同じ原因でハマった時用の備忘録。

結論、EC2でリモート開発するなら、letter_openerではだめで、letter_opnener_webを使わないといけない。

HogeMailer.hoge(huga).deliver

みたいな感じのパターンで落ちてて、上記エラー出てるなら疑うべき。

環境

Rails 4.2
Ruby 2.3
letter_opener 1.4.1
EC2に内にソースコードを置いて、SSH接続での開発

対応方法

letter_openerだと、リモート開発時(EC2にSSH接続している等)には、上記エラーを吐いてしまう。
リモート開発でメール送信機能の確認をしたいのであれば、letter_opener_webの追加が必要。
導入手順は以下。

1 gemfileにletter_opener_webの追加

gemfileで、

gem 'letter_opener_web', group:  :development

を追加する。

2 config/environments/development.rbの設定追加

config.action_mailer.delivery_method = :letter_opener_web

を追加する。

※※※既にletter_openerをインストールしているのであれば、

config.action_mailer.delivery_method = :letter_opener

という行があるはずなので、
単純に末尾に_webを足してやればOK。

3 config/routes.rbに設定追加

最後に、以下3行をroutes内に追加してやればOK。

  if Rails.env.development?
    mount LetterOpenerWeb::Engine, at: '/letter_opener'
  end

letter_opener_web導入後

localhost:3000/letter_opener/を別タブでブラウザ上に開いておけば、
メールの送信記録が確認できる。

参考記事

https://qiita.com/k-shogo/items/d85905535a64e82a3b2b
http://rubyandrails.hatenablog.com/entry/letter_opener_web
https://www.geek.sc/archives/2227
https://qiita.com/7sgg_m/items/178e0d90fc55c5716474

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsの基本理念を僕なりに解釈

はじめに

脱初心者にむけてアウトプットをしていこうと思って記事を書いております。
間違ったことがありましたら、ぜひコメントいただけると幸いです。

Railsとは何か

railsはRubyのフレームワークです。
フレームワークとはアプリケーション開発やシステム開発を楽に行えるように必要な機能を予め用意してあるプログラムの雛形のこと。

Railsには基本理念がある

この基本理念を遵守することでこのフレームワークの能力を最大限に生かすことができる。
つまりこの基本理念を守ってプログラムを書こうということです!

DRY

DRYとはDon't Repeat Yourselfの略で、同じ情報を繰り返し定義しない、という考え方です。メリっどとしてコードの量を減らすことで、可読性が向上することや、アプリケーション自体の動作が早くなるというメリットがある

例えば

class EventsController < ApplicationController

  def index
    @events = Event.all
  end

  def new
    @event = Event.new
  end

  def show
    @event = Event.find(params[:id])
  end

  def create
    Event.create(event_params)
    redirect_to events_path notice:"作成しました"
  end

  def destroy
    @event = Event.find(params[:id])
    @event.destroy
    redirect_to events_path, notice:"削除しました"
  end

  def edit
    @event = Event.find(params[:id])
  end

  def update
    @event = Event.find(params[:id])
    if @event.update(event_params)
      redirect_to events_path, notice: "編集しました"
    else
      render 'edit'
    end
  end

  private

  def event_params
    params.require(:event).permit(:title, :start_time, :content)
  end

end

このような記述があったとして、繰り返し記述してる部分は
@event = Event.find(params[:id])  です。

これを構文をDRYの理念に遵守すると
class EventsController < ApplicationController
  before_action :set_event, only: [:edit, :show, :update, :destroy]
  def index
    @events = Event.all
  end

  def new
    @event = Event.new
  end

  def show
  end

  def create
    Event.create(event_params)
    redirect_to events_path notice:"作成しました"
  end

  def destroy
    @event.destroy
    redirect_to events_path, notice:"削除しました"
  end

  def edit
  end

  def update
    if @event.update(event_params)
      redirect_to events_path, notice: "編集しました"
    else
      render 'edit'
    end
  end

  private

  def set_event
    @event = Event.find(params[:id])
  end

  def event_params
    params.require(:event).permit(:title, :start_time, :content)
  end

end

このようにコードが少なくなり可読性を向上させることができます。
(わかりずらかったら申し訳ないです。)
この場合before_actionというそれぞれのアクションが実行される前に処理を行うことでそれぞれのアクションの記述量を減らして結果的に可読性も上がるということで、いいことしかないのです。

CoC

CoCとはConvention Over Configurationの略で、設定よりも規約を優先するという考え方です。
Railsを使ったことがある方ならわかるかと思いますが、コマンドだけでファイルを生成してくれたりrailsが勝手に処理を用意してくれたりなど、『全て設定よりも規約を優先』することにより開発スピードが上がったり結果的に質も担保してくれるなど、いいことしかないのです。

例えば

規約を無視しているコード
class TestsController < ApplicationController
 def render_top_page
  render template: "tests/index"
 end
end
規約にそったコード
class TestsController < ApplicationController
 def index
 end
end

このように記述量も減り可読性が上がり、結果的に開発スピードをあげることになるのです。

まとめ

私は初心者なのですが、この基本理念を忘れて強引にコードを書いたりしてしまい、エラーが起きてしまうことが多々ありました。
なので、今回は初心に戻るという意味で、基本理念を記事にしてみました。

ここまで読んでいただきありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails初心者がRSpecに入門してみた 2

前回

大分間が空いてしまいましたが、前回からの続きでテストの記述方に触れていきます。

RSpec記述の基本形

'rails_helper'

RSpec.describe "テストする対象", type: :Specの種類 do

  describe "どのような機能" do

  before do (以下のテストを確認するために必要な用意)
    (事前準備)
  end

    context "どのような状況/条件で" do
      (具体的な条件)
      it "どのような結果を期待する" do
      (具体的な結果/処理)
      end
    end
  end
end

基本的には上記のような形になると思います。

describeはテストしたい機能、contextはif文のような条件分け、itはそれに対して期待する結果を記述します。before doは必要であればテストをする以前に必要な処理、例えばユーザーをcreateしてログインさせておくといった内容を書いておきます。これをすることで多くのitを扱う際に一つ一つにログイン処理を書く必要がなくなり、記述量を大幅に削減できる訳ですね。また、itに到達するまでにcontextはdescribeは幾つでもネストして使用することも可能です。複雑なテストを書く場合には有効でしょう。

まとめると以下の通りです。

具体的にテストを実行する→before, it
テストを分類してまとめる→describe, context

それでは一例としてmodel_specを書いてみます。
今回テストしたいのはUser modelの登録時のvalidationについてです。
validatesは以下の通りです。

model/user.rb
validates :name,  presence: true, #名前は必須
                  length: {maximum: 30} #30文字以下
validates :email, presence: true, #アドレスも必須
                  length: {maximum: 255}, #255文字以下
                  format: {with: VALID_EMAIL_REGEX}, #アドレスのフォーマットであるか
                  uniqueness: { case_sensitive: false } #重複がないか
validates :password,  presence: true, #パスワードは必須
                      length: {minimum: 6} #6文字以上

まず何を記述していけばいいか考えていきます。

・有効なユーザー情報
・そこから各項目をnilにした情報(presenceの検証)
・文字数が多すぎる/少なすぎる情報(lengthの検証)
・項目固有の情報(emailのフォーマットや重複の検証)
spec/models/user_spec.rb
require "rails_helper"

RSpec.describe User, type: :model do

  let!(:user) { create(:user) } #全てのvalidationを通る情報をFactoryBotで作成

  context "submit valid values" do #事前用意したuserが通るか確認
    it "should be valid user" do
      expect(user).to be_valid
    end
  end

  describe "User validation" do #ここからvalidation
    describe "Name" do
      context "name is valid" do #nameが有効な場合
        it "be valid" do
          user.name = "foobar"
          expect(user).to be_valid
        end
      end

      context "name is nil" do #nameがない場合
        it "be invalid" do
          user.name = nil
          expect(user).to be_invalid
        end
      end

      context "name is too long" do #nameが長すぎる場合
        it "be invalid" do
          user.name = "a" * 31
          expect(user).to be_invalid
        end
      end
    end
  end

  describe "Email" do
    context "email format is valid " do #emailのフォーマットが正しい場合
      it "be valid" do
        user.email = "foobar@valid.com"
        expect(user).to be_valid
        user.email = "test@user.com"
        expect(user).to be_valid
        user.email = "user@example.com"
        expect(user).to be_valid
      end
    end

    context "email format is invalid" do #emailのフォーマットが正しくない場合
      it "be invalid" do
        user.email = "foobar@invalid"
        expect(user).to be_invalid
        user.email = "__test@user.bar+org"
        expect(user).to be_invalid
        user.email = "foobar@example.com.."
        expect(user).to be_invalid
      end
    end

    context "email is nil" do #emailがない場合
      it "be invalid" do
        user.email = nil
        expect(user).to be_invalid
      end
    end

    context "email is too long" do #emailが長すぎる場合
      it "be invalid" do
        user.email = "a" * 256 + "@example.com"
        expect(user).to be_invalid
      end
    end

    context "email is already exists" do #emailが既存のものと重複している場合
      it "be invalid" do
        second_user = user.dup
        second_user.email = user.email.upcase
        user.save!
        expect(second_user).to be_invalid
      end
    end
  end

  describe "Password validation" do 
    context "submit valid values" do  #passwordが有効である場合
      it "be valid" do
        user.password = "password"
        expect(user).to be_valid
      end
    end

    context "password is nil" do #passwordがない場合
      it "be invalid" do
        user.password = nil
        expect(user).to be_invalid
      end
    end

    context "password is too short" do #passwordが短すぎる場合
      it "be invalid" do
        user.password = "test"
        expect(user).to be_invalid
      end
    end

    context "doesn't match with confimation" do #passwordと確認が合わない場合
      it "be invalid" do
        user.password = "testuser"
        user.password_confirmation = "foobar"
        expect(user).to be_invalid
      end
    end
  end
spec/factories/user.rb
FactoryBot.define do

  factory :user, class: User do
    name { "TestUser" }
    email { "testuser@example.com" }
    password { "password" }
    password_confirmation { "password" }
  end
end

最初はminitestより書きにくいかもしれませんが、慣れるとより自然言語に近い形でテストが確認できます。

こう記述した方がより簡潔であるとか、自分だったらこのようにネストして記述するといったアドバイスがあればぜひよろしくお願いします。ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyで絶対値を返したい時はabsメソッド

ruby 2.7.0の記事です

-12345.abs   # => 12345
12345.abs    # => 12345
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの入力と出力メソッド

ruby 2.7.0についての記事です。

Rubyの入力メソッドのまとめ

1.gets
getsメソッドを使って入力した値は全て文字列になる
公式ドキュメント(IOクラスgets)

【関連するメソッド】

num = gets.split # "10 100"と入力します
print num #=> ["10", "100"]
print num[0] #=> 10
print num[1] #=> 100

answer = num[0].to_i * num[1].to_i # 整数に変換します
print answer #=> 1000

Rubyの出力メソッドのまとめ

num = gets.split # "10 100"と入力します
puts num #=>
10
100
puts num[0] #=> 10
puts num[1] #=> 100

answer = num[0].to_i * num[1].to_i # 整数に変換します
puts answer #=> 1000

puts "hello"
puts "world" #=>
hello
world

print "hello"
print "world"
#=> helloworld

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TECH CAMP (エンジニア転職)7週目の学習内容の振り返り

みなさんおつかれさまです。今週やっとテックキャンプの最終課題が終わったので、明日から自分のポートフォリオの作成に入ろうと思います。ではでは、振り返りをやっていこうかと思います。

11/16~11/22までの学習進捗
・商品詳細表示機能の導入
・商品情報編集機能の導入
・商品削除機能の導入
・商品購入機能の導入

正直いって詳細表示、編集、削除機能に関しては、そんなに難しくなかったですが、購入機能は結構な工数が必要になり、時間は結構かかりました。いつもならそれぞれの項目でどんなことをやったのかを書いているのですが、フリマアプリの商品情報に関する機能はそこまで難しくない上、文章として残しておけるほどの情報量を思い出せないので割愛しておきます(笑)。

・購入機能の導入
まず始めに、大まかに手順が必要だったのかを箇条書きで書くと、
・テーブルの作成(購入履歴と配送先の情報をDBに記録)
・フォームオブジェクトパターンの実装
・決済機能の導入
・テストコードの実装
・購入機能実装によるビューファイルの編集
という感じですね

最初にやるテーブル作成では、2つのテーブル(購入履歴と配送先)が必要で、それらについての実装をしなければいけませんでした。これに関しては、先週に書いたER図やread.meの記述を参照しながらやったのでそこまでの時間はかからなかったですね。ただ次にやるフォームオブジェクトパターンの実装、自分の中ではこれが一番難しかったですね。

フォームオブジェクトパターンとは一つのフォームから複数テーブルへ情報に送信・保存を行うための手法で、一つのフォームから一つのテーブルに情報を保存する場合とは異なる記述をしなければいけません。やはり最大の違いは、MVCのやりとりの流れが違う点です。通常、一つのフォームから一つのテーブルに情報を保存する場合、コントローラーにストロングパラメーターを設定するモデルやDBに通す前に制限をかけるとおもうのですが、フォームオブジェクトパターンでは、取得する情報を新しく作成したモデル内に列挙し、どの情報をどのテーブルに割り振るのか振り分けを(モデル内で)行うという流れでデータベースに情報が送信・保存されます。その考え方があまり理解できなくて実装に時間をかけてしまいました。

決済機能の導入では、Pay.jpというカード決済代行サービス利用し、セキュリティ面に配慮しながら実装を進めなければいけませんでした。カード情報をそのまま送信しては第三者の介入でカード情報が抜き取られる可能性があるため、トークン化というものを行い、情報が漏洩しても悪用できない仕様にします。概念はわかるのですが、javascriptをいじる必要があるので面倒でした(苦手意識)。

テストコードの実装は今までの内容とやるべきことは変わらないものだったので時間はかかりましたが、難しくはありませんでした。

最後のビューファイルの編集は、商品の購入記録の有無で売り切れと表示したり、購入ボタンをなくしたりするものです。データの有無を判定するメソッド(nil?メソッド)を知らなかったのでこれも時間をかけてしまう作業でした。

土日のうちにカリキュラム外でやったこともいろいろあるのですが、それは一段落がついてから記事にしていこうと思います。来週は自作ポートフォリオについて進捗報告をしていこうと思います。
それでは!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rubyによるデザインパターン】オブザーバーパターンのメモ

プログラムの設計力向上のため『Rubyによるデザインパターン』を読んでおり、気になるデザインパターンを、1つずつまとめています。

今回は、オブザーバーパターンについてまとめました。

デザインパターン記事一覧

【Rubyによるデザインパターン】テンプレートメソッドパターンのメモ - Qiita
【Rubyによるデザインパターン】ファクトリーメソッドパターンのメモ - Qiita
【Rubyによるデザインパターン】ストラテジーパターンのメモ - Qiita
【Rubyによるデザインパターン】コマンドパターンのメモ - Qiita
【Rubyによるデザインパターン】オブザーバーパターンのメモ - Qiita <- 本記事
【Rubyによるデザインパターン】シングルトンパターンのメモ - Qiita

オブザーバーパターンについて

何らかのオブジェクトの状態が変化したとき(イベントが発生した時)に、依存関係にあるすべてのオブジェクトに自動的にその変化をその変化を通知するパターンです。

状態が変化するオブジェクトを持つクラスを Subject、状態変化を検知するクラスを Observer として定義します。

サンプルコード

どのようなWebサイトでも「会員」がいるケースが多いと思います。今回はこの会員の登録、退会時のメール送信処理想定します。

とはいえ、実際ににメール送信新処理を実装するのは大変なので、 puts で代用しています。

まずはオブザーバーパターン適用前からです。

require 'date'

class User
  def initialize(name, start_at = Date.today, retire_at = Date.today)
    @name = name
    @start_at = start_at
    @retire_at = retire_at
  end

  def send_sign_up_mail
    puts "何かしらの会員登録処理"
    puts "メール送信処理(#{@name}#{@start_at} に登録しました。)"
  end

  def send_retire_mail
    puts "何かしらの会員退会処理"
    puts "メール送信処理(#{@name}#{@retire_at} に退会しました。)"
  end
end

user = User.new('ユーザー')
user.send_sign_up_mail
user.send_retire_mail

実行時はこのとおりです。

user = User.new('ユーザー')
user.send_sign_up_mail
user.send_retire_mail
# 何かしらの会員登録処理
# メール送信処理(ユーザーは 2020-11-22 に登録しました。)
# 何かしらの会員退会処理
# メール送信処理(ユーザーは 2020-11-22 に退会しました。)

登録時、退会時のみにメール送信をしていますので、そこまで複雑ではないですが、他にも、会員情報が更新されたときなどメール送信が必要な処理が増えると、たちまち User クラスが肥大化してしまいます。

ユーザーの登録/退会処理とメール送信処理が同じクラス内に含まれており、複雑になるのが問題となりやすいです。

次は、オブザーバーパターンを適用して書き換えた場合です。

require 'date'

class User
  attr_reader :name, :start_at, :retire_at

  def initialize(name, start_at = Date.today, retire_at = Date.today)
    @name = name
    @start_at = start_at
    @retire_at = retire_at
  end

  def sign_up
    puts "何かしらの会員登録処理"
    UserMailer.sign_up(self)
  end

  def retire
    puts "何かしらの会員退会処理"
    UserMailer.retire(self)
  end
end

class UserMailer
  def self.sign_up(user)
    puts "メール送信処理(#{user.name}#{user.start_at} に登録しました。)"
  end

  def self.retire(user)
    puts "メール送信処理(#{user.name}#{user.retire_at} に退会しました。)"
  end
end

実行結果はこのとおりです。

user = User.new('ユーザー')
user.sign_up
user.retire
# 何かしらの会員登録処理
# メール送信処理(ユーザーは 2020-11-22 に登録しました。)
# 何かしらの会員退会処理
# メール送信処理(ユーザーは 2020-11-22 に退会しました。)

メール送信の責務を担う UserMailer を定義し User から受け取ったメール送信イベントはすべて UserMailer が担います。

言い換えると UserMailer は Observer として Subject である User から送られているメール送信イベントを監視しています。

メール送信のイベントが増えても、 User の処理には影響を与えないという点で、会員登録処理とメール送信処理を疎結合になりました。

オブザーバーパターンの適用例

サンプルコードの今回のメール送信処理は、Rails においては ActionMailer が組み込まれており、おのずとと メール信処理を分離できるような設計となっています。

他にも、 Active Record は after_create や after_update 便利なメソッドを備えています。(オブジェクトのライフサイクルにフックを掛ける仕組みをコールバックと読んでいますね。)

このようにRails には オブザーバーパターンを反映した設計がいくつもあることがわかります。

おわりに

オブザーバーパターンはイベント処理を分離するというパターンです。もともとRails でも取り入れられていることを知り、Railsの有用性を再認識しました。

普段何気なく使っているフレームワークの設計意図を知れるのも、デザインパターンを学ぶ意義の1つですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]respond_toについて勉強してみた!![初心者]

respond_toメソッドとは?

URLにアクセスする際に、リクエストされるフォーマット(HTMLやJSONなど)ごとに、処理を分けることができるメソッドです。
リクエストのフォーマットは、URLの最後に拡張子(htmlやjsonやjs等)をつけることで、指定することが出来るものです。

普段、インターネットを利用する時に、わざわざ拡張子まで意識して利用している人は少ないと思いますが、特にフォーマットの指定がなかった場合は、デフォルトのHTML形式で送信されます。

近年に入り、Web APIでJSON形式のデータを扱うことも増えたので、このメソッドを目にする機会も多くなりました。

つまり、通常はHTML形式で結果を取得できているのですが、明示的に他のフォーマットを指定された場合に備えて、このrespond_toメソッドを使用しましょう、ということです。

使い方

まずは基本構文から確認しましょう。

respond_to do |format|
  format.形式 {行いたい処理}
end

↑のように記述します。

具体的な使用例

books_controller.rb
class BooksController < ApplicationController
  def index
    @books = Book.all
    respond_to do |format|
      format.html {redirect_to root_path}
      format.json {render json: @books}
      format.js {render 'shared/index.js.erb'}
    end
  end
end

また、format.htmlに処理の指定を記載しない場合もあります。

books_controller.rb
class BooksController < ApplicationController
  def index
    @books = Book.all
    respond_to do |format|
      format.html #何も書かないパターン
      format.json {render json: @books}
      format.js {render 'shared/index.js.erb'}
    end
  end
end

このように、format.htmlに具体的な指定を書かない場合は、デフォルトのテンプレートが指定されたフォーマットで表示されます。

format.anyとは?

通常は、上記のような記述をするパターンがほとんどです。
まれに、それ以外のフォーマットを指定されるパターンもあり、
format_any
を使用することもあります。

おわりに

メソッドの意味自体は、比較的簡単だと思うのですが、使いどころが難しいですね。
アプリを開発するときは、あらゆる可能性を考え抜いた上で、ユーザーに快適に使ってもらえるように、試行錯誤しないといけないということでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

順列の全探索をするプログラム(競技プログラミング向け)

順列をすべて列挙するプログラムを各言語で書きました。10, 20, 30, 40 の4つの要素があったとして次のような出力をします。要素が $ n $ 個あれば、 $ n! $ 行の出力をします。出力するところになにか処理を書けば順列の全探索ができます。

 10 20 30 40
 10 20 40 30
 10 30 20 40
 10 30 40 20
 10 40 20 30
 10 40 30 20
 20 10 30 40
 20 10 40 30
...

書いた言語:

  • ライブラリ使用: C++, Scala, Ruby, Python
  • 独自実装: C言語, Java, JavaScript, Elixir

経緯:

AtCoderのABC183 C - Travelでは順列の列挙が必要でした。順列列挙のアルゴリズムがわからず、競技中は無理やり書いてACは取れたのですが、解説を読むともっとスマートなアルゴリズムがありました。しかもいくつかの言語では順列を列挙するライブラリも標準装備されていました。

悔しかったので各言語で順列を全部列挙するプログラムを書いたものです。

参考:

C++

C++には next_permutation という関数があります。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
  int len = 4;
  vector<int> vec(len);
  for (int i = 0; i < len; i++) vec[i] = 10 * (i+1);
  do {
    for (int i = 0; i < len; i++) cout << " " << vec[i];
    cout << endl;
  } while (next_permutation(vec.begin(), vec.end()));
}

Scala

Scalaには permutations というメソッドがあります。

object Main extends App {
  val len = 4;
  val seq = (1 to len).map(10 * _);
  seq.permutations.foreach { seq =>
    println(" " + seq.mkString(" "));
  }
}

Ruby

Rubyには permutation というメソッドがあります。

len = 4
arr = (1 .. len).map do |i|
  10 * i
end
arr.permutation do |arr|
  puts(" " + arr.join(" "))
end

Python

Pythonには itertools.permutations という関数があります。

import itertools

len = 4
lst = [10 * (i+1) for i in range(len)]
for lst2 in itertools.permutations(lst):
    print(" " + " ".join(map(str, lst2)))

C言語

C++の next_permutation を自分で実装しています。(ほとんどAtCoderの解説そのままです)

#include <stdio.h>

int next_permutation(int *arr, int len) {
  int left = len - 2;
  while (left >= 0 && arr[left] >= arr[left+1]) left--;
  if (left < 0) return 0;
  int right = len - 1;
  while (arr[left] >= arr[right]) right--;
  { int t = arr[left]; arr[left] = arr[right]; arr[right] = t; }
  left++;
  right = len - 1;
  while (left < right) {
    { int t = arr[left]; arr[left] = arr[right]; arr[right] = t; }
    left++;
    right--;
  }
  return 1;
}

int main() {
  int len = 4;
  int arr[len];
  for (int i = 0; i < len; i++) arr[i] = 10 * (i+1);
  do {
    for (int i = 0; i < len; i++) printf(" %d", arr[i]);
    printf("\n");
  } while (next_permutation(arr, len));
}

Java

C言語で実装した next_permutation と同じ実装です。

class Main {
  public static boolean nextPermutation(int[] arr) {
    int len = arr.length;
    int left = len - 2;
    while (left >= 0 && arr[left] >= arr[left+1]) left--;
    if (left < 0) return false;
    int right = len - 1;
    while (arr[left] >= arr[right]) right--;
    { int t = arr[left]; arr[left] = arr[right];  arr[right] = t; }
    left++;
    right = len - 1;
    while (left < right) {
      { int t = arr[left]; arr[left] = arr[right]; arr[right] = t; }
      left++;
      right--;
    }
    return true;
  }

  public static void main(String[] args) {
    int len = 4;
    var arr = new int[len];
    for (int i = 0; i < len; i++) {
      arr[i] = 10 * (i+1);
    }
    do {
      var sb = new StringBuilder();
      for (int i = 0; i < len; i++) {
        sb.append(String.format(" %d", arr[i]));
      }
      System.out.println(sb);
    } while (nextPermutation(arr));
  }
}

JavaScript

C言語やJavaで実装した next_permutation と同じ実装です。

function next_permutation(arr) {
    const len = arr.length;
    let left = len - 2;
    while (left >= 0 && arr[left] >= arr[left+1]) left--;
    if (left < 0) return false;
    let right = len - 1;
    while (arr[left] >= arr[right]) right--;
    { const t = arr[left]; arr[left] = arr[right]; arr[right] = t; }
    left++;
    right = len - 1;
    while (left < right) {
        { const t = arr[left]; arr[left] = arr[right]; arr[right] = t; }
        left++;
        right--;
    }
    return true;
}

const len = 4;
const arr = [];
for (let i = 0; i < len; i++) arr.push(10 * (i+1));
do {
    let str = "";
    for (let i = 0; i < len; i++) str += " " + arr[i];
    console.log(str);
} while (next_permutation(arr));

Elixir

C言語やJavaでの実装とは異なるアルゴリズムです。 next_permutation と違って、順列の全パターンを始めに作成しています。

defmodule Main do
  def permutations([]), do: [[]]
  def permutations(list) do
    for elem <- list, p <- permutations(list -- [elem]), do: [elem | p]
  end

  def main do
    len = 4
    list = (1 .. len) |> Enum.map(fn x -> 10 * x end)
    permutations(list) |> Enum.each(fn l ->
      IO.puts(" " <> Enum.join(l, " "))
    end)
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 子レコードの作成・更新時に親レコードのupdated_atも更新する

こんにちは。
初めて記事を投稿してみます。

Railsで子レコードを作成・更新時、親レコードのupdated_atも更新したい時
ActiveRecordの関連付けで、touchオプションを使えば簡単にできること知ったので
備忘のためにまとめてみます。

touchオプション

Railsガイドより

:touchオプションをtrueに設定すると、そのオブジェクトがsaveまたはdestroyされたときに、関連付けられたオブジェクトのupdated_atタイムスタンプやupdated_onタイムスタンプが常に現在の時刻に設定されます。

class Shop < ApplicationRecord
  has_many :reviews
end

class Review < ApplicationRecord
  belongs_to :shop, touch: true
end

review = Review.last
review.update( title: "いいお店")
=> # review.shop.updated_atも更新する

利用シーン

キャッシュの無効化

このためにtouchオプションを使うのが一番多いようです。
Russian Doll Cachingという、ネストされたフラグメントキャッシュの内側のキャッシュ(review)が更新されると、外側のキャッシュ(shop)も更新してくれる機能で、子のmodelにtouchオプションをつければ有効になります。
キャッシュを自動で更新してくれるので便利ですね。

検知処理の高速化

例えばこんな機能を追加しようとして

  • お店(shop)の評点を集計する
  • 評点は全レビュー(review)の平均点を使用する

これをバッチ処理で開発する場合

  • 差分更新だけにしたい
  • 更新対象は、前回バッチ実行時から今まででreview.updated_atが更新されたかで検知したい

touchオプションを付けておくと、review作成・更新時にshopのupdated_atも自動更新してくれるので、バッチ処理ではshopのupdated_atを参照するだけで集計対象を検知できるようになります。
(もしreviewのupdated_atを参照しようとすると、shopとReviewは1:Nなので参照するレコードが多くなり処理が遅くなります)

touchしたくない場合

touchオプションを付けたものの、なんらかの理由(子レコードを大量作成や、全て更新)でtouchしたくない場合、ActiveRecord.no_touching で一時的に無効にできます。

shop = Shop.last

ActiveRecord::Base.transaction do
  ActiveRecord::Base.no_touching do
    shop.reviews.find_each do |review|
      # shopのupdated_atは更新されない
      review.update!(some_attributes)
    end
  end
end

参考URL

https://railsguides.jp/association_basics.html#belongs-to%E3%81%AE%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3-touch
https://postd.cc/the-complete-guide-to-rails-caching/
https://techracho.bpsinc.jp/hachi8833/2018_04_18/55475

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

lメソッドで時刻の表示が指示通りできない

起きたこと

https://qiita.com/jnchito/items/831654253fb8a958ec25
に従い設定をしました。
しかし、ja.ymlのdefaultをいくらいじってもブラウザに指示通りの表記がされませんでした。

ja.yml
ja:
 time:
   formats:
     default: "%Y/%m/%d"

結論

ja.ymlファイルの中に2つja:の塊を作っていたことが原因でした。
もう一つのja:の塊とは別のことも記述していました。具体的にはエラー時に表示されるカラム名を日本語に変換するためのデータです。

ja.yml改善前
ja:
 time:
   formats:
     default: "%Y/%m/%d"
ja:     
 activerecord:
   attributes:
     user:
      nickname: 名前
      grade_id: 段位
      email: Email
      password: パスワード
      password_confirmation: 確認用パスワード

これを一つにまとめて、

ja.yml改善後
ja:
 time:
   formats:
     default: "%Y/%m/%d"
 activerecord:
   attributes:
     user:
      nickname: 名前
      grade_id: 段位
      email: Email
      password: パスワード
      password_confirmation: 確認用パスワード

とすることで年/月/日の表示が無事できました。
インデントの幅がスペース一つずれていても認識されなかったので注意が必要です。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

たびれぽシリーズ2021

こんだけ制度と数字が変わるとうちらもわけわからんな
ってことで手分けできそうなところは上手に分担、委託 役割分担(Role)

インフラ(このはちゃん)GMO
開発環境2021
 OS:Ubuntuの新型
 Rails:aptで入るやり方
     なんかPumaがいるね 
るびきちさんとMatzさん来年もよろしく

お仕事内容
 お金をもってお店に行って福を分けるお仕事
 http://www.tabirepo.online/archives/772

by.png
これの成田版とか仙台版も見てみたいなぁ
ちょっとまえのLaravel案件では仙台の人にお世話になりました。ありがとうございます。

この辺は、こんな感じのおいしい店があって、お金に余裕がある人行ってあげてよみたいな情報システム
みんなで優良店に通ってあげることで、地域が活性化するはずだ きりっ

http://www.tabirepo.online/archives/886

熊本で鯉コク食べるならここ みたいな感じで

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初学者向け】単体テスト・機能テスト・統合テストを書こう【Rails Tutorial】

経緯

先日RailsTutorialを修了し、自分でコードを書き始めた途端にテストを書かなくなってしまいました。
これは非常に良くないと思ったので、勉強した内容について簡単にまとめておきます。

この記事で(多分)わかること

  • テストを書くことのメリット
  • RailsTutorialに登場する単体テスト・機能テスト・統合テストの違い
  • それぞれのテストが何を対象としているか、どんなテストを書けばいいのか

テストを書くことのメリット

まずは勉強のモチベーションを上げるために、テスト自動化ができると何が嬉しいのかを調べました。

1. テスト作業の効率化と工数の削減

テストを手動で行っていた工数を大幅に削減し、開発に時間を割けるようになる。
リアルタイムでテストを実行できるため、誤ってコードを改変したときにどこが悪かったのか即座に検知できる。

2. テスト作業におけるヒューマンエラーの排除

機械が何度でも正確にテストを繰り返してくれるため、人的ミスを排除することができる。

3. 手動では実現できなかったテスト作業を実現

「数万件の同時アクセス」などの負荷テストや、イレギュラーなテストパターンを容易に実行できる。

参考:webrage-テスト自動化とは?テスト自動化のメリットについて

ポートフォリオ作成に取り掛かる初学者にとっては、1.が感じやすいメリットだと思います。

「上手くいっていたものを動作しないコードに書き換えてしまう」ことが頻発してしまいそうなので、それをリアルタイムで検知してくれるのは非常に頼もしく、安心してリファクタリングできそうです。

先にまとめ

テストの種類 テスト対象 テスト方法 テストを記載するファイル
単体テスト モデル モデルに記載された機能に対して、テストデータを投入して結果を確認する test/models
機能テスト コントローラ コントローラに対してHTTPリクエストを発行し、コントローラの振る舞いを確認する test/controllers
統合テスト モデル・コントローラ・ビュー 想定されるユーザの操作フローを模倣して、モデル・コントローラ・ビューの一連の振る舞いを確認する test/integration

参考:Rails チュートリアル 【初心者向け】 テストを10分でおさらいしよう!

以下でそれぞれのテストについて記載していきます。

単体テスト

単体テストはモデル(/app/models/xxx.rb)単位に記述します。
モデルに記載したバリデーションチェック等の機能一つに対して、対応するテストを一つ記述するイメージです。

例)

models/user.rb
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
end
test/models/user_test.rb
  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
            password: "foobar", password_confirmation: "foobar")
  end

  #user.nameのバリデーションに関するテスト

  test "name should be present" do
  @user.name = "     "
  assert_not @user.valid?
  end

  test "name should not be too long" do
  @user.name = "a" * 51
  assert_not @user.valid?
  end

上記の例ではuserモデルのnameというカラムに以下のバリデーションチェックを設けています。
1. 名前が空白でないこと
2. 50字以内であること

それに対して以下のテスト実行しています。
1. 名前が空白の@userを作成し@userが無効になること
2. 名前が51文字の@userを作成し@userが無効になること

上記は非常に単純な例ですが、複雑な機能でもやっていることは一緒のような気がします。
モデルに一つ機能を追加するたびに一つテストも追加するようなイメージですかね。

機能テスト

機能テストはコントローラ(/app/controllers/xxx_controller.rb)単位に記述します。
「def new」「def create」などのアクション一つ一つに対して記述するイメージです。

コントローラにHTTPリクエスト(GET,POST,PATCH,DELETE)を送り、
それに対するコントローラの振る舞いが想定通りであるかどうかを確認します。

例)

controllers/users_controller.rb
  def create
    @user = User.new(user_params)
    #ユーザの保存に成功した場合は、①flashにメッセージを格納し②ログインした後③ユーザ詳細画面にリダイレクトする
    if @user.save
      flash[:success] = "Welcome to App!"
      login @user
      redirect_to @user
    #ユーザの保存に失敗した場合は、再びユーザ登録画面にリダイレクトする
    else
    render 'new'
    end
  end
test/controllers/users_controller_test.rb
  #有効なユーザ情報を登録した場合のcreateアクションの振る舞いをテストする
  test "should get create with valid user" do
    #ユーザ登録画面にアクセスし、正常応答を得る
    get new_user_path
    assert_response :success
    #有効なユーザ登録のPOSTリクエストを送り、ユーザの数が1増えることを確認する
    assert_difference 'User.count', +1 do
    post users_path, params: { user: { name: "Example User",
                                       email: "user@example.com",
                                       password: "foobar",
                                       password_comfirmation: "foobar"} }
    end
    #flashにメッセージが格納されている(ユーザ登録成功のメッセージ)ことを確認する
    assert_not flash.empty?
    #ユーザ登録後ログインできていることを確認する
    assert is_logged_in?
  end

  #無効なユーザ情報を登録した場合のcreateアクションの振る舞いをテストする
  test "should get create with invalid user" do
    #ユーザ登録画面にアクセスし、正常応答を得る
    get new_user_path
    assert_response :success
    #無効なユーザ登録のPOSTリクエストを送り、ユーザの数が増減しないことを確認する
    assert_no_difference 'User.count' do
    post users_path, params: { user: { name: " ",
                                       email: " ",
                                       password: " ",
                                       password_comfirmation: " "} }
    end
    #flashにメッセージが格納されている(ユーザ登録失敗のメッセージ)ことを確認する
    assert flash.empty?
    #ユーザ登録後ログインできていないことを確認する
    assert_not is_logged_in?
    #ユーザ登録画面にリダイレクトされていることを確認する
    assert_template 'users/new'
  end

上記の例では、userコントローラのcreateアクションの想定される振る舞いは以下の2通りです。
1. 登録時のユーザ情報が有効である場合、ユーザを登録しユーザ詳細画面に遷移する
2. 登録時のユーザ情報が無効である場合、再度ユーザ情報入力画面に遷移する

それに対して以下のテストを実行しています。
1. 有効なユーザ情報のPOSTリクエストを送信し、ユーザ詳細画面に遷移すること
2. 無効なユーザ情報のPOSTリクエストを送信し、ユーザ登録画面に戻されること

「アクション一つにつきテスト一つ」というわけではなく、
「リクエストに対して想定される振る舞いの数のテストが必要」という点がポイントかなと思いました。

統合テスト(UIテスト)

統合テストはユーザーの実際の操作を想定し、操作のフローに伴うアプリの挙動を確認します。
テストを書くにあたってアプリケーションでユーザがどのような操作をできるのかを洗い出す必要がありそうです。

例)

test/integration/site_layout_test.rb
require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest

  test "layout links" do
    #ホーム画面に遷移する
    get root_path
    #遷移先のURLが想定されたものであることを確認する
    assert_template 'static_pages/home'
    #遷移先画面で表示されているリンクの数が想定された通りであることを確認する
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
  end
end

上記の例では、「ユーザがホーム画面に遷移する」という操作を想定ししています。

  1. ルートパスにgetリクエストを送り、ホーム画面に遷移することを確認する
  2. 遷移したホーム画面にて、画面上に表示しているリンクが想定通りであることを確認する

前述までの単体テスト・機能テストでは死んでいるリンクなどを検知できません。
統合テストを書くことでビューのバグも検知できるため、自分でブラウザを開いて一個一個クリックする必要がなくなりそうです。

本当にあっている?

ここまで記載しましたが、自分で怪しいと思っている点が2点あります。

  1. 機能テストの例で出したものは本当に機能テストになっているか?
    機能テストの例としたcreateコントローラのテストは「ユーザのログイン」操作を模倣した統合テストとも捉えられます。
    統合テストを書こうとしても上述した機能テストの繰り返しのようになってしまう気がしました。
    ①userコントローラにgetリクエストを送り、アクセスできることのテスト
    ②userコントローラに有効なpostリクエストを送り、ユーザ登録できることのテスト
    ③userコントローラに無効なpostリクエストを送り、ユーザ登録できないことのテスト
    とリクエスト一発に対して何が帰ってくるかを細かく区切った一つ一つが機能テストになるでしょうか?

  2. 「テストを記載するファイル」について別にルールはないのでは?
    上記の「test/controllers」に記載しているテストは「test/integration」の中に記載しても動きます。
    極端に言えば全てのテストを同じファイルに記載しても、テストは実行可能なのではないでしょうか。
    Tutorialで分かりやすいフォルダ構成を決めているだけで、テストを書くファイルは実際には自由?

  ・・・有識者の方、ご指摘いただけると助かります。

最後に

RailsTutorialは出てきたテストをそのままコピーしていたので、
それぞれのテストの違いを理解しないまま進めてしまっていたので、今回整理できてよかったです。

誤っている記述などあれば、コメントにてご教示いただけると助かります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者向け]Ruby on Railsについて徹底解説してみた

はじめに

今回はRuby on railsの解説記事です。

初心者向けに、わかりやすく解説していきます。

頑張っていきましょう。

今回は、記事を書く上でたかし君に協力してもらいます。

Ruby on rails とは

Ruby on rails とは、Webフレームワークの一種です。

最初に、そもそもフレームワークとは一体何なのかについて解説します

フレームワークとは

フレームとは、簡単に言えば骨組みのことです。

フレームワークの意味をwikipedia先生に聞いてみましょう。

プログラミングにおいて、一般的な機能をもつ共通コードをユーザーが選択的に上書きしたり特化させたりすることで、ある特定の機能をもたせようとする抽象概念のことである。

相変わらず堅い言葉なので、噛み砕いて解説します。

フレームワークとは、様々な機能を提供してくれる骨組みのことであると考えて問題ないと思います。

今度は、フレームワークの具体例をみて考えていきましょう。

Bootstrap

何かWebサイトを作成しようとしたときに、コツコツHTMLとCSSを書いていくのは大変ですよね。

当然、何とか楽をしたいと考えます。どうすれば楽にできるでしょうか。

そうです、誰かが作ってくれたものをパクれば楽ができるのです。

しかし、Webサイトをそのままパクれば怒られてしまうし、作れるバリエーションも限られてしまいます。

ここでたかし君は考えました。

たかし君「そういえば、Webサイトに使われる部品って限られてるよな。ヘッダー部分とかボタン部分とかを個別に楽にかける枠組みがあれば楽に書けるんじゃね?」

こうして生まれたのがBootstrapです。

BootstrapはCSSのフレームワークで、Webサイトの各々の部品ごとの枠組みを提供してくれています。

Webフレームワークとは

ここまでの説明でなんとなく分かりましたね。

Webフレームワークとは、Webアプリを作るための枠組みのことです。

もう一度たかし君に聞いてみましょう。

たかし君「Webアプリ作るのめっちゃだるいな。でも、Webアプリで使う機能って以外に限られてるよな。ユーザーが打ち込んだURLに応じてデータベースを操作したり、HTMLファイルを作成して送ったり、ログイン機能が必要だったりするだけじゃん。この枠組みを誰かが作ってくれて、それをパクれば楽に書けるんじゃね?」

このようにして生まれたのが、Webフレームワークだったり、Ruby on railsだったりするわけです。

それでは、実際にRuby on railsでアプリを作ってみましょう。

骨組みの作成 (rails new)

最初にローカルのパソコンにRubyとRuby on railsをインストールしてください。

環境構築については、色々な人が記事を書いているので割愛します。

環境構築はうまく行かないことがほとんどですが、できるまで粘り強く調べ続けてください。応援しています。

最初に、railsアプリを作りたいディレクトリに移動して、以下のコマンドを打ちましょう。

rails new qiita_project

newの後ろは何でも好きな名前にしてください。

以下のようファイルが作成されます。

image.png

これで、Webアプリを作る際の枠組みを作成することができました。

サーバーの起動(rails server)

作成したqiita_projectに移動してください。

cd qiita_project

これで作成したqiita_projectに移動することができました。

次はサーバーを起動してみましょう。次のコマンドです。

rails server

次に、chromeなどのブラウザのURL欄に以下のURLを入力してください。

http://localhost:3000/

image.png

ここまでで、サーバーを起動することができました。

コントローラーの作成 (rails generate controller コントローラー名)

以下のコードでコントローラーを作成することができます。

コントローラーが何を行うかは後に解説します。

rails generate controller home

上のコードを実行すると、app>>controllers配下にhome_controller.rbという名前のファイルが作成されます。

image.png

home_controllerの中身を見てみましょう。

home_controller.rb
class HomeController < ApplicationController

end

このようにして、コントローラーを作成することができました。

それでは、コントローラーの解説行います。

コントローラーとは

コントローラーを理解するためには、まず以下の図を理解する必要があります。railが動作する仕組みについてです。

image.png

順番に見ていきましょう。

①ユーザーがサーバーにリクエストを送る

image.png

まず、左のユーザーがサーバーにリクエストを送ります。右の様々な動作は、全てサーバー側の話です。

リクエスト送るとは、サーバーに何かを要求することです。例えば、URLを入力すると、「このURLに対応するファイルを送ってこい!」と要求(getリクエスト)することになりますし、IDとパスワードを入れてログインしようとすれば、「送ったデータを使って何かしらの処理をしろ!」と要求(postリクエスト)することになります。

②ルーティングによりどのコントローラーを使うのかを決定

image.png

ルーティングにより、ユーザーから受け取ったURLを処理します。

例えば、ユーザーからhome/topというURLを受け取ったとします。

そうすると、ルーティングはhome#topというルーティングに変換します。

これは、「homeコントローラーのtopアクションを利用します」という意味です。アクションというのは、コントローラーごとに設定されている「何かを行うコード」だと考えてください。

今回は、簡単のためにユーザーからは「このURLに対応するファイルを送ってこい!」と要求(getリクエスト)されたと考えます。

ユーザーから"home/top"というURLが送られてきて、それをhomeコントローラーのtopアクションを実行するという命令にルーティングしましょう。

configディレクトリ配下のroutes.rbファイルに以下のように追記してください。

routes.rb
Rails.application.routes.draw do
  get "home/top" => "home#top"
end

コントローラーによりviewファイルを探索

image.png

home/topというURLがユーザーから送られてきて、ルーティングによりhome#topに変換されたとします。

この場合、homeコントローラーのtopアクションを行うことになります。

以下のように、homeコントローラーにtopアクションを追加しましょう。

home_controller.rb
class HomeController < ApplicationController
    def top
    end
end

このようにコードを書くと、homeコントローラーはhome/top.html.erbファイルを探して、それをユーザーに返してくれます。

viewファイルの作成

app >> views 配下のhomeディレクトリにtop.html.erbファイルを追加してください。

image.png

top.html.erbファイルを以下のように編集してください。

top.html.erb
<h1>Hello World</h1>

このようにして、viewファイルを作成することができました。

実際にレスポンスを見てみましょう。

ユーザーがURLを打ち込んだ場合

再び、以下のコードでサーバーを起動しましょう。

rails server

webブラウザで以下のURLを打ち込んでください。

http://localhost:3000/home/top

そうすると、以下の画面が送られてきます。

image.png

何が起きているのかを、もう一度ざっくり解説します。

http://localhost:3000/home/topというURLが送られると、それがルーティングによりhome#topという形に変換されます。

それにより、homeコントローラーのtopアクションが行われます。

そうすると、app >> viewsディレクトリ配下のhomeディレクトリの中のtop.html.erbファイルがrailsにより発見され、それがユーザーに送られます。

ルーティングの変更

ルーティングは自分の好きなように設定することができます。

例えば、/pocomaruと書いたときに、home#top(homeコントローラーのtopアクション)が行われるようにしましょう。

ルーティングを以下のように変更します。

routes.rb
Rails.application.routes.draw do
  get "pocomaru" => "home#top"
end

この状態で、以下のようにURLを打ちます。

http://localhost:3000/pocomaru

このようにすると、http://localhost:3000/pocomaruというURLはhome#top(homeコントローラーのtopアクション)という命令に置き換えられます。

ルーティングは、localhost:3000以降のURLに対して行います。

実行すると、以下の画面になりました。

image.png

リンクの作成

それでは、リンクを作成していきましょう。

top.html.erbというファイルに、test.html.erbファイルへのリンクを張ります。

以下のようなファイル構造になっています。
image.png

test.html.erbファイルの中身は次のようにします。

test.html.erb
<h1>テスト</h1>

このファイルへのリンクを作成していきましょう。

リンクをクリックすると別のファイルになぜ飛ぶか

しかし、リンクをクリックして、それがユーザーに送られるとはどういうことでしょうか。

一度、何が起きているのかを考えてみましょう。以下の図をみてください。

image.png

リンクをクリックして別のファイルに飛ぶということは、上の図の赤く囲った部分の話になります。

ユーザーがURLをサーバーに送り、そのURLに応じてコントローラーがviewファイルを探し出し、アクションに書いてある処理をした後、viewファイルをユーザーに送ります。

このように、ユーザーがサーバーにviewファイルを要求することをgetリクエストを送ると呼びます。

ユーザーがURLをサーバーに送る動作は様々です。リンクが貼られている文字をクリックしたり、IDを打ち込んだ後に送信などと書かれたボタンを押したりする、などがあります。

そのようなURLが送られてきた後、サーバーはルーティングにより翻訳を行うのでしたね。

image.png

ルーティングにより、どのコントローラーのどのアクションを行うかを指定します。

また、これらのアクションは、viewファイルに対応しています。

例えば、ルーティングによりhomeコントローラーのtopアクションが呼ばれると、app >> views 配下のhomeディレクトリのtop.html.erbファイルが呼ばれます。

つまり、何故リンクをクリックすると別ファイルに飛ぶのかを考えると以下のように答えることができます。

リンクをクリックすると、URLがルーティングにより変換され、どのコントローラーのどのアクションを行うかが指定され、そのアクションに応じたviewsファイルがユーザーに送られてくるから

実際にリンクを作成

Ruby on railsのリンクの作成方法は大きく分けて以下の2つがあります。

  • htmlのaタグのhrefで指定する
  • Railsのlink_toメソッドを使う

せっかくなので、2つの方法で実装してみましょう。

top.html.erbファイルを以下のように書き換えました。

top.html.erb
<h1>Hello World</h1>
<a href="test">test by href</a>
<%= link_to("test by link_to", "test") %>

image.png

二行目のリンクの書き方は、典型的なhtmlのリンクの書き方ですね。

三行目の書き方に注目してください。

<%= %>という形で囲むと、これはRubyのコードだよ!と伝えることになります。link_toを使ったコードは、htmlのコードではなくRubyのコードなので、このように囲む必要があります。

ちなみに、<% %>で囲っても、Railsのコードであると伝えることになります。

<%%>で囲むのか、<%=%>で囲むのかについて解説と、link_toの使い方の解説を以下で行います。

<%= %>と<% %>の違い

実は.html.erbのerbとはEmbedded RuByの略称になります。つまり、Rubyを埋め込んだhtmlファイルであるという意味です。

つまり、このhtml.erbファイルの内部にはRubyのスクリプトを埋め込むことができます。

<%= %>と<% %>の2つは、html.erbファイルにおいてrubyのコードを書くときに使うことになります。

しかし、htmlファイルにrubyスクリプトを埋め込むときに、別にブラウザに表示する必要がない処理を行う場合があります。

そのようなときに、この2つを使い分ける必要があります。ざっくりと以下のように覚えてください。

  • ブラウザに表示させたいときは<%= %>を使う
  • ブラウザに表示させたくないときは<% %>を使う

link_toの使い方

link_toはRubyのコードであるので、<% %><%= %>で囲む必要があります。

今回はクリックしたらどこかに飛ぶリンクを作成したいので、当然ブラウザに表示する必要があります。

<%= %>で囲みましょう。

link_toは、第一引数にブラウザに表示する文字を取り、第二引数にURLを取ります。

今回は、testというURLを取ることにしました。

ここからは、ユーザーからリンクがクリックされた、つまりURLが送られてきたときに、test.html.erbファイルを送る方法について解説します。

まず、最初にルーティングを変更しましょう。

ルーティングを変更

以下のようにルーティングを変更してください。

routes.rb
Rails.application.routes.draw do
  get "pocomaru" => "home#top"
  get "test" => "home#test"
end

新しく、testとうURLが送られてきた場合にhome#topというルーティングに変換するコードを追記しました。

つまり、ユーザーからtestというURLがクリックされると、homeコントローラーのtopアクションが実行されるようになります。

次は、homeコントローラーにtopアクションを追記しましょう。

コントローラーにアクションを追記

homeコントローラにtestアクションを追記しましょう。

hoem_controller.rb
class HomeController < ApplicationController
    def top
    end

    def test
    end
end

このようなコードが追記されることで、ユーザーからhome#testというルーティングが呼ばれたときに、homeコントローラーのtestアクションを行い、view >> home >> test.html.erbファイルをユーザに送り返すことができるようになります。

image.png

それでは次に、データベースについて見ていきましょう。

データベースとは

データベースとは、データを保存しておく場所のことです。

Ruby on Railsにおいては、コントローラーからモデルを作成することによりデータベースを操作することができます。
image.png

コントローラーの作成

今回は、ユーザーのIDとパスワードを保存するデータベースを作成することを考えます。

データベースを作成する前に、一度コントローラーとアクションを作成してみましょう。

ターミナルで以下のコードを実行してください。

rails g controller users index

これでuserコントローラーを作成し、その中にindexアクションを追加することができました。rails g コマンドにおいて、コントローラー名とアクション名の2つを設定することができます。

以下のようにルーティングしてください。

routes.rb
get 'users/index' => "users#index"

これで、ユーザーからuser/indexというURLが送られてきたときに、userコントローラーのindexアクションを行うことができるようになりました。

userコントローラーを確認してみましょう。今回は、最初からアクションが追加されています。

users_controller.rb
class UsersController < ApplicationController
  def index
  end
end

実はviewファイルも自動で生成されています。

image.png

以下のようにindex.html.erbファイルを書きましょう。

index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

以下のURLでこのファイルにアクセスしましょう。

http://localhost:3000/users/index

以下の画面がでてきます。

image.png

アクションから変数をviewファイルに渡す

image.png

コントローラーがviewファイルを探してきて、それをユーザーに返します。

その時、コントローラーの中のアクションに変数を定義することで、その変数をviewファイル内で用いることができます。

以下のように変数を定義しましょう。

users_controller.rb
class UsersController < ApplicationController
  def index
    @users = ["maru", "poco"]
  end
end

@userのように、変数の前に@を用いることでその変数はインスタンス変数になります。こちらに解説がありました。

このように、コントローラーからviewファイルに変数を渡すときは、ローカル変数ではなくインスタンス変数を用います。

Railsのコントローラでインスタンス変数を使用するのは、以下の場合です。
・メソッド間でのデータの受け渡し(典型的には、before_actionでデータをロードしておくとか)
・ビューへのデータの受け渡し

このように、@変数で作成したインスタンス変数はviewファイル内で利用することができます。

index.html.erb
<% @users.each do |user| %>
  <div>
    <%= user %>
  </div>
<% end %>

image.png

@usersは配列が格納されているので、.each do ~ end で取り出すことができます。この部分は全てRubyのコードなので、<%%>ではさみましょう。ブラウザに表示する必要がないので、<%=%> ではなく <%%>ではさむところがポイントです。

<%= user %>の部分で、ブラウザに表示させます。

今回は、コントローラーのアクション内で配列を定義して、それをviewファイルに渡すことで利用しました。

今度は、データベースからデータをアクション内に持ってきて、それをviewファイルに渡すことを考えてみましょう。

モデルの作成

image.png

データベースを操作するためには、モデルを作成する必要があります。

モデルとは、データベースの情報を操作する仕組みのことです。または、データベースとのやり取りを行うクラスとも言うことができます。

以下のコードでモデルと。マイグレーションファイルを作成します。

rails g model user name:string password:string

Userがモデル名で、id、passwordがカラムになります。カラムとは、データベースの縦のデータ、つまり列のことです。

このコードで作成したデータベースは、以下のような表になっています。

image.png

この表全体をテーブルと呼び、縦のデータをカラム、横のデータをレコードと呼びます。モデルとは、このデータベースに対応するRubyのクラスであり、モデルクラスのインスタンスは一つの行(レコード)を表すオブジェクトであり、テーブルの列(カラム)に対応する属性をもちます。

上記のコードを実行すると、データベースを作成するためのmigrationファイルと、モデルが作成されます。

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :name
      t.string :password

      t.timestamps
    end
  end
end
user.rb
class User < ApplicationRecord
end

上記の作成したマイグレーションファイル以下のコードで取り込むことで、データベースを作成することができます。

rails db:migrate

ここまでで、データベースを作成することができました。

それでは、マイグレーションファイルとは何なのかについてまとめていきましょう。

マイグレーションファイルとは

マイグレーションファイルとは、データベースを生成する際の設計図のようなものです。

マイグレーションファイルを実行することで、その内容に基づいたデータテーブルを作成することができます。

railsでは、rails g modelコマンドを実行すると、マイグレーションファイルとモデルの両方が生成されます。

マイグレーションファイルはデータベースの枠組みを作成するための道具で、モデルはデータベースとコントローラーを橋渡しする道具のようなものだと覚えておいてください。

データをデータベースに保存

以下のコードでRailsのコンソールを起動しましょう。

rails console

コンソールを起動したら、モデルを使ってデータベースにデータを保存してみましょう。

user = User.new(name: "poco", password: "maru")
user.save

これでデータベースにデータを保存することができました。

データベースの確認

それでは作成したデータベースを確認していきましょう。

データベースクライアントの起動

以下のコードでデータベースクライアントを起動できます。

rails dbconsole

テーブルの一覧表示

データベースクライアントを起動した後は、以下のコードでテーブル一覧を表示することができます。

sqlite>.table

ar_internal_metadata schema_migrations users

スキーマの表示

以下のコードでスキーマを表示することができます。スキーマとは、構造という意味です。指定したデータベースの構造を確認することができます。

sqlite>.schema users

CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "password" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);

データベースクライアントの停止

以下のコードでデータベースクライアントを停止することができます。

sqlite>.quit

ここまでで、データベースを作成することができました。

それでは、実際にデータベースを利用してみましょう。

データベースの利用

データベースには、モデルを用いてアクセスします。

image.png

データベースを利用する際は、コントローラーからモデルを用いてアクセスします。

users_controller.rb
class UsersController < ApplicationController
  def index
    @user = User.first
  end
end

これで、index.html.erbファイル内でインスタンス変数@userを使用することができるようになりました。

@userには、usersテーブルの一番最初の列のレコードが格納されています。

以下のように、index.html.erbファイル内で使用します。

index.html.erb
<p><%= @user.name %></p>
<p><%= @user.password %></p>

image.png

それでは次に、命名規則について見ていきましょう。

Railsの命名規則について

ここまで、コントローラーやモデルを作成してきました。

それらには、命名規則があります。学んでいきましょう。

Modelの命名規則

モデルとは、データベースを作成するための設計図です。

設計図は一つであるため、モデルクラス名は単数形で作成します。

テーブルは、そのモデルのデータが複数あるため、自動的に複数形で表されます。

前回の記事でuserという名前でモデルを作成しました。

そうすると、usersという名前でテーブルが作成されます。

また、クラス名はUserというふうに、最初が大文字になります。

Viewの命名規則

Viewフォルダは配下に複数のファイルを持つため、複数形になります。

Controllerの命名規則

Controllerは複数のアクションを持つため、複数形で作成します。

データベースにデータを登録

以前の記事では、railsのコンソールを使用してデータベースにデータを登録しました。

今回は、ブラウザをユーザーに操作してもらうことでデータベースにデータを登録しましょう。

その前に、一度データベースの確認を行います。

ターミナルに以下のコマンドを打ち込みましょう。

rails dbconsole

次のコードでカラム名の表示をONにできます。

sqlite> .headers on

この状態で、次のSQLの文を書くと中身を確認することができます。

sqlite> select * from users;
id|name|password|created_at|updated_at
1|poco|maru|2020-05-20 10:50:13.177731|2020-05-20 10:50:13.177731

現在は、usersテーブルにnameカラムとpaswordカラムが格納されていることが分かりますね。

それでは、入力フォームを作成してユーザーから送られてくるデータをデータベースに格納してみましょう。

/users/new.html.erbファイルに以下のコードを書いてください。

new.html.erb
<%= form_tag("/users/create") do %>
  <p>ユーザー名</p>
  <input name="name">
  <p>password</p>
  <input name="password">
  <input type="submit" value="送信">
<% end %>

form_tagは、viewファイルからコントローラーに何か値を送ったり、削除したりするときに使います。いわゆるPOSTリクエストと呼ばれるものです。getリクエストとpostリクエストの違いについては、こちらの記事を参考にしてください。
type="submit"となっているボタンがユーザーから送られると、users/createに対応するpostリクエストが実行されます。

今回は、以下のようにルーティングしています。

routes.rb
post "users/create" => "users#create"

つまり、上記のデータはusersコントローラーのcreateアクションに送信されます。createアクションでは以下のコードを書きます。

users_controller.rb
def create
  user = User.new(name: params[:name], password: params[:password])
  user.save
end

このコードで、userモデルのUserクラスを用いて、データベースのusersテーブルにデータを格納しています。

form_tagで送られてきたデータにおいて、name属性が指定されているタグが存在した場合、コントローラー内においてparams[name属性]として値を扱うことができます。

この場合、<input name="name">内の値がparams[:name]に、<input name="password">の値がparams[:password]に格納されます。

その送られてきたデータに対してUser.new(name: params[:name], password: params[:password])を用いることでモデルを作成し、データベースに格納しています。

ユーザーからは以下のようになっています。

image.png

この状態で送信を押してみましょう。すると、usersコントローラーのcreateアクション内において、thisという文字列がparams[:name]に格納され、testという文字列がparams[:password]に格納されます。

以下のようにデータを格納することができました。

3|this|test|2020-05-21 05:30:36.718523|2020-05-21 05:30:36.718523

しかし、このままだと送信を押しても特に変化はありません。退屈なので、他のファイルにリダイレクトさせましょう。

送信を押した場合にリダイレクト

送信を押すとusersコントローラーのcreateアクションが実行されるので、このcreateアクション内にリダイレクトのコードを書けば、他のファイルにリダイレクトされるようになります。

usersコントローラーに以下のようにコードを追記しましょう。

users_controller.rb
def create
  user = User.new(name: params[:name], password: params[:password])
  user.save
  redirect_to("/users/index")
end

このように書くと、送信を押した場合に/users/indexに対応するファイルにリダイレクトされます。

ちなみに、リダイレクトを書く場合は、パスの書き始めに/を入れることを忘れないようにしてください。

それでは次に、データベースの編集を行っていきましょう。

データベースのデータ一覧の表示

以下のコードでusersテーブルのデータを取得して表示しましょう。

index.html.erb
 <% @users.each do |user| %>
   <p>name</p>
   <div class="ruby-name"><%= user.name %></div>
 <% end %>

インスタンス変数@usersを使用するために、コントローラ内で渡しています。

users_controller.rb
def index
  @users = User.all
end

以下のような画面が表示されます。

image.png

また、以下のようにcssを指定しています。

users.scss
.ruby-name {
    color: purple;
    display: inline-block;
    font-weight: bold;
}

データベースの削除

それではデータベースの削除を実装してみましょう。

以下のようにコードを書き換えてください。

index.html.erb
 <% @users.each do |user| %>
   <p>name</p>
   <div class="ruby-name">
     <%= user.name %>
     <%= link_to("削除", "/users/#{user.id}/destroy", {method: "post"})%>
   </div>
 <% end %>

image.png

削除ボタンをクリックすると、要素を削除することができました。

image.png

具体的な処理を見ていきましょう。index.html.erbに、以下のコードが追記されています。

<%= link_to("削除", "/users/#{user.id}/destroy", {method: "post"})%>

これにより、削除をクリックすると、第二引数の処理が実行されます。

第二引数の"/users/#{user.id}/destroy"は、usersコントローラーのdestroyアクションへのルーティングに、データベースのidを含めたものになっています。このようにURLを指定することで、コントローラーは削除したいデータベースのidを受け取ることができます。

第三引数は、これがgetリクエストではなく、postリクエストであることを指定しています。

以下のようにルーティングしてください。

routes.rb
post "users/:id/destroy" => "users#destroy"

:id の部分は、任意の数字を受け取ることができます。受け取った数字は、usersコントローラーにおいて、params[:id]の中に格納されます。

以下のようにコントローラーをコーディングしてください。

users_controller.rb
def destroy
  user = User.find_by(id: params[:id])
  user.destroy
  redirect_to("/users/index")
end

user = User.find_by(id: params[:id])の部分で、データベースからモデルを使ってデータを抜き出します。index.html.erbから送られてきたidと同じidのデータをデータベースから抜き出し、userに格納します。

user.destroyの部分でそのデータを削除しています。

redirect_to("/users/index")の部分で、index.html.erbにリダイレクトしています。今回は、index.html.erbから削除リンクをクリックしたときの動作であるので、リロードになります。

ここまでで、データベースからデータを削除することができました。

データベースの編集

次はデータベースの編集を行っていきましょう。

次のようにindex.html.erbを編集してください。

index.html.erb
 <% @users.each do |user| %>
   <p>name</p>
   <div class="ruby-name">
     <%= user.name %>
     <%= link_to("削除", "/users/#{user.id}/destroy", {method: "post"})%>
     <%= link_to("編集", "/users/#{user.id}/edit") %>
   </div>
 <% end %>

今回新たに追加されたのは<%= link_to("編集", "/users/#{user.id}/edit") %>の部分です。

これにより、新たにusers/editというviewファイルへ移動します。

そのときに、そのviewファイルに編集したいデータベースのidを渡すことになります。

次のようにルーティングしてください。

routes.rb
get "users/:id/edit" => "users#edit"

次のパスに以下のviewファイルを準備してください。
image.png

edit.html.erb
<%= form_tag("/users/#{@user.id}/update") do  %>
  <input type="text" value="<%=@user.name%>" name="new_name">
  <input type="submit" value="送信">
<% end %>

試しに以下のindex.html.erbファイルの編集と書いてある部分をクリックしましょう。
image.png

以下のようになります。
image.png

ここで、edit.html.erbファイルについての解説です。

form_tag("/users/#{@user.id}/update")の部分で、どのコントローラーのどのアクションを使うかを指定しています。

今回は、usersコントローラーのupdateアクションを使用します。また、データベースの編集を行うため、編集したいデータベースのidも送ります。form_tagはpostリクエストになるのでしたね。

<input type="text" value="<%=@user.name%>" name="new_name">の部分で、inputタグの初期値と名前を設定しています。@userはusersコントローラーのeditアクションから送られてきたものです。

users_controller.rb
def edit
  @user = User.find_by(id: params[:id])
end

@userには、データベースからidに応じて探してきた値が格納されています。

それでは、下のように値を変更して送信してみましょう。

image.png

そうすると、以下のルーティングにより、usersコントローラーのupdateアクションが実行されます。

routes.rb
post "users/:id/update" => "users#update"
users_controller.rb
def update
  user = User.find_by(id: params[:id])
  user.name = params[:new_name]
  user.save
  redirect_to("/users/index")
end

updateアクション内では、送られてきたidに応じてデータベースからデータを探索し、ローカル変数userに格納した後、nameカラムのデータを送られてきた新しい名前に書き換えて、user.saveでセーブした後に、/users/indexにリダイレクトしています。

そのため、以下のように変化します。

image.png

それでは次に、バリデーションについて学んでいきましょう。そのために、新しいコントローラーを作成します。

新しいコントローラーの作成

新しいコントローラーを作成していきましょう。

今回は、投稿を管理するpostsコントローラーを作成します。ターミナルで以下のコードを実行してください。

rails g controller posts all

all.html.erbファイルで、全ての投稿を表示するようにしましょう。

そのために、新しい投稿を作成するファイルを作成します。new.html.erbファイルをpostsファイルの中に作成しましょう。

image.png

このnew.html.erbファイルで、新しい投稿を作成していきます。

新しいモデルの作成

では、投稿を管理するデータベースを作成しましょう。今回は、まだデータベースを全く作成していないので、モデルから作成していきます。

モデルとは、データベースの情報を操作する仕組みであり、データベースとのやり取りを行うクラスとも言うことができるものでしたね。

モデルは通常、小文字から始まる単数形で命名します。なぜなら、モデルとはtableに対して一つしか存在しないからです。

 rails g model post content: string

このようにモデルを作成すると、同時にデータベースの設計図であるmigrationファイルが作成されるのでしたね。

以下のコードでマイグレーションファイルを実行しましょう。

rails db:migrate

データベースコンソールから、作成したtableを確認してみましょう。

rails dbconsole
.table

ar_internal_metadata schema_migrations

posts users

postsテーブルが作成されていますね。以下のコードで中身を見てみましょう。

.schema posts

CREATE TABLE IF NOT EXISTS "posts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "content" varchar, "string" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);

投稿を作成

new.html.erbファイルに、投稿を作成するコードを追加しましょう。

new.html.erb
<%= form_tag("/posts/create") do  %>  
  <textarea name="content" cols="30" rows="10"></textarea>
  <input type="submit" value="送信">
<% end %>

image.png

/posts/createというURLにpostリクエストを送っています。params[:content]に、textarea内部のデータが格納されて、送られます。

以下のようにルーティングしましょう。

routes.rb
post "posts/create" => "posts#create"

postsコントローラーのcreateアクションにルーティングされています。textareaのデータをpostsテーブルに格納するために、postsコントローラーを以下のようにコーディングします。

posts_controller.rb
def create
  post = Post.new(content: params[:content])
  post.save
  redirect_to("/posts/new")
end

これで、postsテーブルにデータを保存することができるようになりました。

以下のようにtextareaに記入して送信ボタンを押しましょう。

image.png

データベースを確認してみましょう。ターミナルで以下のコードを打ってください。

rails dbconsole
.header on
select * from posts;

id|content|string|created_at|updated_at
1|pocomaru||2020-05-23 10:37:51.509719|2020-05-23 10:37:51.509719

このように、投稿をデータベースに格納することができました。

バリデーションの設定

バリデーションとは、データベースにデータを保存する際にかける制約のことです。

例えば、何かのサイトにログインする際にメールアドレスやパスワードが空っぽでもログイン可能だとやばいですよね。

そんなことを防ぐために存在します。

バリデーションは、モデルに設定します。

実際に設定してみましょう。

空っぽの投稿を防ぐ

空っぽの投稿を防ぐバリデーションを考えます。

投稿を保存するのはpostsテーブルなので、このバリデーションはpostモデルのPostクラス内部に記入します。

postモデルはデフォルトでは以下のようになっています。

post.rb
class Post < ApplicationRecord
end

バリデーションは、以下の書式で書きます

validates :カラム名, {検証内容}

空っぽの投稿を防ぐバリデーションは以下のようになります。

post.rb
class Post < ApplicationRecord
end

バリデーションは、以下の書式で書きます

validates :カラム名, {検証内容}

空っぽの投稿を防ぐバリデーションは以下のようになります。

post.rb
class Post < ApplicationRecord
    validates :content, {presence: true}
end

これで、空っぽの投稿を防ぐことができました。

バリデーションに引っかかった場合、saveすることができなくなります。また、saveに成功スっればTrueが戻り値として帰ってきて、saveに失敗すればFalseが戻り値として帰ってきます。

postsコントローラーを以下のように書き換えましょう。

posts_controller.rb
def create
  post = Post.new(content: params[:content])
  if post.save
    redirect_to("/posts/all")
  else
    redirect_to("/posts/new")
  end

このようにコードを書き換えることで、saveが成功した場合には/posts/allにリダイレクトし、saveが失敗した場合には/posts/newにリダイレクトします。

これで、空っぽの投稿を排除するバリデーションを作成することができました。

次は、一定以上の文字数を削除するバリデーションを作成してみましょう。

一定以上の文字数を防ぐ

今回は20文字以上の投稿を防ぐバリデーションを設定してみましょう。

以下のようになります。

post.rb
class Post < ApplicationRecord
    validates :content, {presence: true}
    validates :content, {length: {maximum: 20}}
end

このvalidates :content, {length: {maximum: 20}}の部分により、20文字以上の投稿を制限することができるようになりました。

しかし、このようにバリデーションを設定するだけだと、問題があります。20文字以上の投稿をしたときに、textareaが空っぽになってしまうのです。

理由はpostsコントローラーにあります。バリデーションに引っかかり、redirect_to("/posts/new")が呼ばれたとき、ルーティングによりpostsコントローラーのnewアクションが実行されます。そうすると、インスタンス変数をnew.html.erbファイルに送ることができないため、頑張って書いたtextareaが空っぽになってしまうのです。

これを防ぐために、renderというメソッドを使います。

renderとはレンダリングすることができるメソッドであり、レンダリングとはブラウザにviewファイルを読み込ませて描画させることです。

renderメソッドをviewファイル内で使うと、複数のviewファイルの共通の部分を描画する部分テンプレートを使うことができます。

今回はコントローラー内部で使うことにより、アクションを介することなくレンダリングを行うことで、作成したインスタンス変数をviewファイルに渡すことができます。

postsコントローラーを次のように書き換えましょう。

def create
  post = Post.new(content: params[:content])
  @content = params[:content]
  if post.save
    redirect_to("/posts/all")
  else
    render("posts/new")
  end
end

@contentに、投稿の内容を格納した後、redirect_to の代わりにrenderを使ってnew.html.erbファイルを描画しています。

このようにすることで、インスタンス変数であ@contentをnew.html.erbに渡すことができます。

new.html.erbファイル内でこのインスタンス変数を表示するために、以下のように書き換えましょう。

new.html.erb
<%= form_tag("/posts/create") do  %>  
  <textarea name="content" cols="30" rows="10"><%= @content%></textarea>
  <input type="submit" value="送信">
<% end %>

textareaの初期値に@contentを渡しています。これで、バリデーションに引っかかったときに、その値を表示することができます。

実際に試してみましょう。

image.png

以下のように20文字以上入力して、送信を押してみましょう。

image.png

特に何も変わりません。

image.png

それでは次に、フラッシュについて学んでいきましょう。

フラッシュとは

具体例を探しに、Twitterにで試してみました。

image.png

この入力されたユーザー名が~の部分がフラッシュです。

フラッシュとはページ上に一度だけ表示されるものであり、ページを更新したり別のページに移動したりするとフラッシュは表示されなくなります。

image.png

このフラッシュを実装してみましょう。

フラッシュの実装

railsでフラッシュを表示するためには、特殊な変数flashが用意されています。

アクションで変数flash[:notice]に文字列を代入すると、viewファイルで使用することができます。flashは一度使用された後は自動的に削除されることになっています。

また、フラッシュは色々な場所で共通して使用するので、application.html.erbファイル内で使用すると便利です。

application.html.erbファイルに書かれたものは、全てのviewファイルに共通して表示されるようになります。

views >> layoutsフォルダの配下にあるapplication.html.erbファイルに以下のように記入してフラッシュが表示できるようにしましょう。

application.html.erb
<% if flash[:notice]%>
  <div class="flash">
    <%= flash[:notice]%>
  </div>
<% end %>

これで、フラッシュが存在するなら表示されるようになりました。

どうせなんで、cssも設定しておきましょう。

application.css
.flash {
    background-color: brown;
    color: white;
}

postsコントローラーを次のように書き直して、flash[:notice]にエラーメッセージを代入してみましょう。

posts_controller.rb
def create
  post = Post.new(content: params[:content])
  @content = params[:content]
  if post.save
    flash[:notice] = "投稿に成功しました"
    redirect_to("/posts/all")
  else
    flash[:notice] = post.errors.full_messages[0]
    render("posts/new")
  end
end

post.saveの部分で失敗した場合に、post.errors.full_messagesにエラーメッセージがリストとして格納されるため、その1つ目の値をflash[:notice]に格納しています。投稿に成功した場合は、その旨をflash[:notice]に格納します。

これでフラッシュを実装することができました。

実際に試してみましょう。

以下のnew.html.erbファイルを開いてみましょう。また、バリデーションは以下のようになっています。

new.html.erb
<%= form_tag("/posts/create") do  %>  
  <textarea name="content" cols="30" rows="10"><%= @content%></textarea>
  <input type="submit" value="送信">
<% end %>
post.rb
class Post < ApplicationRecord
    validates :content, {presence: true}
    validates :content, {length: {maximum: 20}}
end

image.png

試しに、値を何も入れずに送信を押してみましょう。バリデーションに弾かれ、エラーメッセージがflash[:notice]に格納されます。

image.png

次は、20文字以上入力して送信を押してみましょう。次のエラーメッセージが表示されます。

image.png

今度は、投稿に成功した場合を試してみます。次のようになります。

image.png

上手くいきましたね。

終わりに

今回の記事はここまでになります。

とても長い記事になってしまいました。

ここまでお付き合い頂きありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー】Postgres PG::ConnectionBad: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432"?【Rails】

brew install imagemagickした後にサーバーを立ち上げると下記のエラーが。スクリーンショット 2020-11-22 午前8.59.59.png

したこと

とりあえずhttps://qiita.com/great084/items/98c83364f246473249c4 を参照。
しかしエラーは解決せず。

postgres -D /usr/local/var/postgres 
2020-11-21 23:37:47.476 AEDT [84006] FATAL:  database files are incompatible with server
2020-11-21 23:37:47.476 AEDT [84006] DETAIL:  The data directory was initialized by PostgreSQL version 12, which is not compatible with this version 13.1.

postgresqlのバージョンをあげたらいけるっぽいのであげてみる

brew postgresql-upgrade-database
Error: Upgrading postgresql data from 12 to 13 failed!
==> Removing empty postgresql initdb database...
==> Moving postgresql data back from /usr/local/var/postgres.old to /usr/local/v
Error: Failure while executing; `/usr/local/opt/postgresql/bin/pg_upgrade -r -b /usr/local/Cellar/postgresql@12/12.5/bin -B /usr/local/opt/postgresql/bin -d /usr/local/var/postgres.old -D /usr/local/var/postgres -j 4` exited with 1.

何故かバージョンがあげれないというエラーが。
そこで、以前postgresql関連のエラーが出たときにした事を思い出してbrew doctorを実行

Warning: Git could not be found in your PATH.
Homebrew uses Git for several internal functions, and some formulae use Git
checkouts instead of stable tarballs. You may want to install Git:
  brew install git

Warning: No developer tools installed.
Install the Command Line Tools:
  xcode-select --install


Warning: Your Xcode does not support macOS 10.15.
It is either outdated or was modified.
Please update your Xcode or delete it if no updates are available.

Warning: Xcode alone is not sufficient on Catalina.
Install the Command Line Tools:
  xcode-select --install

検知された問題を解消していく。
そして再度postgresqlのバージョンをあげてみる

brew postgresql-upgrade-database
~~(省略)~~
==> Upgraded postgresql data from 12 to 13!
==> Your postgresql 12 data remains at /usr/local/var/postgres.old

無事にバージョンをあげることができ、サーバーもエラーが出ずに立ち上げれました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で既存カラムにdefault オプションを追加したい

はじめに

Rails で既存カラムのdefault オプションを追加したい場面があったので備忘録です。

環境

  • macOS 10.15.6
  • Ruby 2.5.7
  • Rails 5.2.3

参考URL

https://railsguides.jp/active_record_migrations.html
https://qiita.com/toda-axiaworks/items/dcc24c8e4b23318e37f4
http://tanihiro.hatenablog.com/entry/2014/01/10/182122

目標

  • 既存カラムにdefault オプションのみ変更したい
  • きちんとロールバックできるmigration ファイルを作成したい

ロールバック用のメソッドについて

  • up/down メソッド
  • change メソッド

上記の2つのパターンでmigration ファイルを記述できますが、オプションの変更のみであればchange メソッドの方がシンプルでロールバックも可能ということなので今回はchange メソッドで進めます。
こちらの記事を参考にしました。ありがとうございました。

実装

Users テーブルのid カラムにdefault: 0 オプションを追加する想定です。

  1. migration ファイル作成
  2. migration ファイルにオプションを追加する処理を追加
  3. migrate して設定を反映させる
# 既存カラムのオプション変更用migratiton ファイル作成
$ rails g migration change_column_default_to_users
# migration ファイルでdefault値に0 を追加
class ChangeColumnDefaultToUsers < ActiveRecord::Migration[5.2]
  def change
    change_column_default :users, :id, from: nil, to: "0"
  end
end
# migration ファイルの各項目の詳細
  def change
    change_column_default :テーブル名複数形, :カラム名, from: 変更前のdefault オプションの状態, to: 変更したいdefault オプション
  end

default: true/false, dafault: デフォルト値 などのオプション変更も可能です。

# 作成したmigration ファイルでmigrate
$ rails db:migrate

# migrate の履歴を確認
$ rails db:migrate:status

学び

  • option を追加するだけだったのに以外にハマってしまった
  • migration ファイルはロールバックできるのかを意識して書く事が重要
  • オフィシャルのドキュメントはちゃんと読む事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】でデータを保存・更新後に直前のページに戻りたい!

はじめに

たとえばECサイトの購入ページからユーザー情報を更新した後、トップページに飛ばされたらどうでしょう?
ユーザーはまた購入画面にアクセスし直さなければならず、かなりUXが悪い状態になってしまいます。


そこで、元の購入ページへ戻す方法を調べてみました。

直接指定すれば良いのでは?

もちろんページ同士が1対1の関係にある場合はこれでOKです。

しかし、ユーザー情報の更新などは他のページからも行えるようにしたいですよね。
ページの関係が多対1の場合、リンク元によって戻るページを変える必要があります。

1つ前のページに戻す方法

1つ前に戻る方法はRailsに標準メソッドにあるので簡単です。

redirect_back(fallback_location: root_path)

しかし、今回はこれを使うことができません。

実は「2つ前」に戻さないといけない

データの保存・更新では元のページnewcreateと遷移するので元のページに戻るには2つ前に戻らないといけません。

そこで、戻りたいページのURLをセッションに保存し、create後にそのページに遷移する設定にします。

controller
  def new
    session[:previous_url] = request.referer  # ここで前ページセッションを保存
  end

  def create
    redirect_to session[:previous_url]  # create後に遷移させる
  end

request.refererで遷移元のURLを取得することができるので、それをセッションに保存し、createメソッド後に保存しておいたセッションを利用してnew直前のページに遷移させています。
これで2つ前のページに戻ることができます。

参考

Referer - HTTP | MDN
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referer

Action Controller の概要 - Railsガイド
https://railsguides.jp/action_controller_overview.html#request%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A8response%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88

Railsで遷移元のURLを取得する方法
https://qiita.com/taka_571/items/9b1c82d8fcc602df8a1a![C4CEA757-38D3-4557-A690-5176C5FA5FED_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672627/169675de-3a9a-6c9b-fc99-8bdffd5be323.jpeg)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの第2歩(変数とmethod編)

Mac OS X-10.15.7 ruby-2.6.1p33

はじめに

本記事ではマルチスケールシミュレーション第6回授業Rubyの変数とmethodについて扱います。

目次

  1. 前回の振り返り

  2. 授業内容

    1. Rubyの変数
    2. Rubyのmethod
  3. まとめ

  4. 参考資料

前回の振り返り

前回の記事(Rubyの入出力編)では、Rubyにおける入出力の方法について書きました。

入力:puts, p, pp, print …

出力:gets, ARGV[0]

出力の違いは様々あり、使い分けが必要でした。

入力に関しては、今回の授業目的の一つであるコマンド操作を覚えるために、コマンド引数のような感じで使用できる ARGV[0] を用いることにしました。

Rubyの変数とmethod

変数

Rubyの変数に関して、実は前回の記事(Rubyの入出力編)の中で使用しているのですが、説明を何もしていなかったので、改めてこちらの記事で紹介します

まず、Rubyの変数の大きな特徴として、 Rubyは変数の型宣言を必要としません

Pythonなどのスクリプト言語では型宣言が不必要な言語が多いですが、Rubyもその中の一つです。

変数の型宣言が

name_variable.rb
name = ARGV[0]
age = 19
puts "Hello #{name} ,type = #{name.class}"
puts "age = #{age} ,type = #{age.class}"

↓ 結果 ↓

$ ruby name_variable.rb Rooter
Hello Rooter ,type = String
age = 19 ,type = Integer

変数の型がしっかり決まっていることがわかる。

型宣言不要のメリット・デメリット

メリット

  • 記述量が少なくて済む
  • ソースコードの変更が容易
  • 型の制約にとらわれずに記述可能

デメリット

  • 演算が遅くなる
  • バグの発見が遅くなる場合がある

Method

Methodとはなんですか

Rubyでは他言語のfunctionやprocedureと同様にオブジェクトの動きを定義することができます。

これをRubyでは Method と呼びます。(もちろん他言語でもmethodはあります。。。)

Method

クラス/モジュール定義中ならばそのクラス/モジュールのメソッドを定義します。トップレベルならばどこからでも呼べるメソッドを定義します。このようなメソッドは結果として他の言語における「関数」のように使えます。

Ruby2.7.0リファレンスマニュアルより引用

つまり、、、他言語で「関数」に親しみのある人(私とか)は、多分関数と同じように扱って大丈夫です。。。

というわけで、とりあえず試してみる。

hello_method.rb
def hello(name)
    puts 'Hello '+name+'.'
end
name = ARGV[0]
hello(name)

↓ 結果 ↓

$ ruby hello_method.rb Rooter 
Hello Rooter.

記述の方法を覚えればこれまでのプログラミングと同じ感覚で使えそう。

よかったぁぁ。。。

まとめ

ここまでの2回のQiitaで基本的なrubyの記述文法については述べたと思う。そこまでRuby特有の文法などは見受けられなかったのが助かった。

参考

以下のサイトを参考にしました。


  • source ~/grad_members_20f/members/Rooter-edi/qiita/week6.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの第1歩(入出力編)

Mac OS X-10.15.7 ruby-2.6.1p33

はじめに

本記事ではマルチスケールシミュレーション第5回授業Rubyの入出力について扱います。

目次

  1. Rubyとは

  2. 授業内容

    1. Rubyの出力
    2. Rubyの入力
    3. 出力先指定
  3. まとめ

  4. 参考資料

そもそもRubyとは

Rubyを実際に触れる前に、Rubyという言語がどういった経緯で生まれ、どんな特徴を持った言語なのかを調べてみた(Wikipedia)。

Ruby誕生の経緯

Ruby(ルビー)は、まつもとゆきひろ(通称: Matz)により開発されたオブジェクト指向スクリプト言語。名称の Ruby は、プログラミング言語 Perl が6月の誕生石である Pearl(真珠)と同じ発音をし、「Perlに続く」という意味で、6月の次の誕生石(7月)のルビーから名付けられた。

Wikipediaより引用

Rubyの入出力

様々な出力方法

Rubyには様々な出力方法がある(らしい)。

Rubyを授業で扱い出してから、まだ挨拶(HelloWorld)ができていなかったので、

手始めにいくつかの出力方法を挨拶がわりに試してみます。

(出力結果は、コメントアウト部分)

hello_world.rb
puts 'hello world by.puts'
# hello world by.puts

p 'hello world by.p'
# "hello world by.p"

pp 'hello world by.pp'
# "hello world by.pp"

print 'hello world by.print'
# hello world by.print%

↓ まとめ ↓

出力  改行 説明・予想                
puts 通常?の出力。個人的にお気に入り。             
p ""で囲まれて出力。今後確認していきたいが、デバッグ用っぽい。
pp ""で囲まれて出力。pとの差異がわからないが、pの進化系らしい。
print × 他言語でもよく見るやつ。putsとの差異は改行の有無だと思われる。

様々な出力方法があって使い分けが必要になりそう。。。

様々な入力方法

出力については色々と試してみたので、次は入力を見てみましょう

まずはよくある入力、getsを使ってみます

gets_name.rb
print '名前入力してね >>>>> '

name = gets
puts "Hello " + name

↓ 出力結果 ↓

$ ruby get_name.rb
名前入力してね >>>>> Rooter
Hello Rooter

こんな感じ。。。

しかし、今回はコマンドライン操作も授業目的の一つであるため、

gets は使わず、コマンドライン引数的な入力方法を用います。

というわけで、今回は ARGV[0] について勉強していきます。

ARGV[0]

ARGV[0]とは、引数(argument)配列の 0番目 を表しています。

hello_name.rb
puts "Hello #{ARGV[0]}. by.puts"

print "Hello #{ARGV[0]}. by.print \n"

print "Hello " + ARGV[0] + ". by.print-ver2 \n"

↓ 出力結果 ↓

$ ruby hello_name.rb Rooter
Hello Rooter. by.puts
Hello Rooter. by.print
Hello Rooter. by.print-ver2

出力先の指定

ruby hello_name.rb Rooter > hello_name.txt

↓ 確認 ↓

$ cat hello_name.txt
Hello Rooter. by.puts
Hello Rooter. by.print
Hello Rooter. by.print-ver2

まとめ

今までなぜか触ってこなかったRubyに授業内だが触れることができた。所々他言語と違う部分があって、難しい。

参考

以下のサイトを参考にしました。


  • source ~/grad_members_20f/members/Rooter-edi/qiita/week5.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「Ruby on Rails」データベースに修正を加える方法

はじめに

Railsで学んだことをアウトプットします。
初投稿で、Markdown記法すらわからず、いろいろ調べながら書いてみました!
お手柔らかにお願いします。

カラムに後からオプションを加える流れ

これは、データベースの設計にミスったので、
新しくマイグレーションファイルを作って、上書きしたところです。

追加したマイグレーションファイル
class ChangeColumnToOrderDetail < ActiveRecord::Migration[5.2]

  def up
    change_column :order_details, :making_status, :integer, default: 0
  end

  def down
    change_column :order_details, :making_status, :integer
  end
end

もともとのテーブル名はorder_details,カラム名はmaking_status,データ型はintegerとなっています。

下のdownで定義されたものが、もともとデータベースに保存されていたものです。
上のupで定義したのは、カラムに新しく修正を加えたものです。

みての通り、optionにdefault: 0という値が追加されていますね。

さいごに

忘れてはいけないものがありますね!!

ターミナル
rails db:migrate

お決まりのこれです!
これをすることで、データベースに新しいカラムのデータが反映されます!

読んでいただきありがとうございました!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む