20210123のRubyに関する記事は25件です。

投稿3日目

投稿3日目
Ruby超入門 220ページまで
ロジカルシンキングが大切という意味が少しわかった!!

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

findメソッドとfind_byメソッドはどう違うのか

プログラミングスクールの課題アプリ開発の過程で、findメソッドとfind_byメソッドを勘違いしてまる1日エラーに悩まされた経験から、findメソッドとfind_byメソッドについて整理します。

findメソッド

IDを指定してレコードを取得します。

Fruit.find(1)
# 複数のIDを指定して、該当するレコードを取得できる
Fruit.find(1,4,6)
# 配列でも指定できる
Fruit.find([1,4,6])
# 同一ファイル内にidの情報を含むインスタンスがあれば、以下のような書き方ができる
Fruit.find(@fruit.id)

find_byメソッド

条件を指定してヒットした最初の1件を取得します。IDも条件に指定できます。

Fruit.find_by(name: "strawberry")

Fruitsテーブルに以下のようにデータが入っているとします。

Fruitsテーブル
| id | name       | color  |
| 1  | apple      | red    |
| 2  | strawberry | red    |
| 3  | orange     | orange |
| 4  | banana     | yellow |

この場合、次のように記述して該当するデータを取得できます。

Fruit.find_by(name: "apple")
Fruit.find_by(name: "orange")
Fruit.find_by(color: "yellow")

個人的なミス経験で恐縮ですが、ぼくはfindメソッドとfind_byメソッドを混同して以下のように記述してエラーに悩まされていました。

Fruit.find(id: @fruit.id)

findメソッドを使っているのに、カッコ()の中はfind_byメソッドの文法で記述していました。

どう違うのか

Railsドキュメントを読んでみると、findメソッドはRailsのバージョン1.0.0から、find_byメソッドはRailsのバージョン4.0.2から適用されているので、歴史的にはfind_byメソッドの方が後から登場したメソッドのようです。

機能的には次のような違いがあります。

  • findメソッドもfind_byメソッドもIDを指定してレコードを取得することができる
  • findメソッドはIDを複数指定していれば複数の値を取得できるが、find_byメソッドは1つのレコードしか取得できない
  • find_byメソッドはID以外の条件を指定してレコードを取得できる

結論としては、こんな感じでしょうか。

取得したいレコード 使用するメソッド
1つ findメソッド・find_byメソッド
1つ&IDを指定 findメソッド・find_byメソッド
複数&IDを指定 findメソッドのみ
1つ&ID以外を指定 find_byメソッドのみ
複数&ID以外を指定 ???

最後のパターンで???が出ました。
このパターンは、findメソッドでもfind_byメソッドでも取得できません。
このときはどうすればよいでしょうか?

Whereメソッド

取得したいレコードが「複数&ID以外を指定」の場合はWhereメソッドを使用することで実行可能です。

whereメソッドは条件に当てはまる値を全て取得します。

@fruit = Fruit.where(name: "strawberry")

ここまでみるとwhereメソッドがなんでもできて最強じゃないかとも思いますが、findとfind_byメソッドにはできて、whereメソッドにはできないことがあります。

# findとfind_byメソッドで取得した値は、特定のカラムを取得できる
@fruit = Fruit.find(1) # もしくは、@fruit = Fruit.find_by(name: "strawberry")
@fruit.name
# 実行結果 => "strawberry"

# whereメソッドで取得した値は特定のカラムを取得できない
@fruit = Fruit.find(name: "strawberry")
@fruit.name
# 実行結果 => NoMethodErrorになる

細かい理由は参考させていただいたQiita【Rails】find・find_by・whereについてまとめてみたをご参照ください。

参考資料

Railsドキュメント モデルについて
Qiita【Rails】findとfind_byの違い
Qiita【Rails】find・find_by・whereについてまとめてみた

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

Formオブジェクトを使用したテーブルの編集・更新のやり方

概要

この記事で使用している言語のバージョンは以下のとおりです。

  • Ruby 2.6.5
  • Rails 6.0.3.4

Formオブジェクトで編集・更新を行うにはどうするか調べたのですが、バージョンが古いものが多かったので自分なりに考えて出来たやり方をここで共有します。

もっと良い書き方出来るところが絶対あると思いますので修正すべき点は指摘して頂けると大変うれしいです。

今回作ったのはサンプルのアプリなので簡単な作りとなっています。ご了承をよろしくお願いいたします。

作成したコード

stations_controller.rb
class StationsController < ApplicationController
  def index
    @stations = Station.all
  end

  def new
    @station = StationForm.new
  end

  def create
    @station = StationForm.new(station_params)
    if @station.valid?
      @station.save
      redirect_to root_path
    else
      render :new
    end
  end

  def edit 
    @station = StationForm.new(id: params[:id])
  end

  def update
    @station = StationForm.new(station_params.merge(id: params[:id]))
    if @station.valid?
      @station.update
      redirect_to root_path
    else
      render :edit
    end
  end

  private
  def station_params
    params.require(:station_form).permit(:name, :address_url)
  end
end
station_form.rb
class StationForm 
  include ActiveModel::Model

  attr_accessor :name, :address_url 

  with_options presence: true do
    validates :name
    validates :address_url
  end

  def initialize(attribute = {})
    if !(attribute[:id] == nil)
      @station = Station.find(attribute[:id])
      @address = @station.address
      if !(self.name = attribute[:name])
        self.name = @station.name 
      else
        self.name = attribute[:name]
      end
      if !(self.address_url = attribute[:address_url])
        self.address_url = @address.address_url 
      else
        self.address_url = attribute[:address_url]
      end
    else
      super(attribute)
    end
  end

  def persisted?
    if @station.nil? 
      return false 
    else
       return @station.persisted?
    end
  end

  def save
    station = Station.create(name: name)
    Address.create(address_url: address_url, station_id: station.id) 
  end

  def update
    @station.update(name: name)
    @address.update(address_url: address_url)
  end
end

new.html.erb
<%= form_with model:@station, url:stations_path, local: true do |f|%>
  <%= render 'error_messages', model: @station %>
  <div class="field">
    <%= f.label :name, "駅名" %>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :address_url, "駅住所" %>
    <%= f.text_field :address_url %>
  </div>
  <div class="actions">
    <%= f.submit "登録する" %>
  </div>
<% end %>

edit.html.erb
<%= form_with model:@station, url:station_path, local: true do |f|%>
  <%= render 'error_messages', model: @station %>
  <div class="field">
    <%= f.label :name, "駅名" %>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :address_url, "駅住所" %>
    <%= f.text_field :address_url %>
  </div>
  <div class="actions">
    <%= f.submit "登録する" %>
  </div>
<% end %>

コード解説

おそらくFormオブジェクトで一番重要なのは以下です

station_form.rb
def initialize(attribute = {})
    if !(attribute[:id] == nil)
      @station = Station.find(attribute[:id])
      @address = @station.address
      if !(self.name = attribute[:name])
        self.name = @station.name 
      else
        self.name = attribute[:name]
      end
      if !(self.address_url = attribute[:address_url])
        self.address_url = @address.address_url 
      else
        self.address_url = attribute[:address_url]
      end
    else
      super(attribute)
    end
  end

改善の余地だらけだとは思いますが、ここではStationFormインスタンスの初期化を行っています。
引数として設定している attibute = {}はインスタンスが存在するかどうか判断するためにつかいます。

Formオブジェクトのインスタンスが生成される時はnew&createアクション時とedit&updateアクション時です。(下記は他の記述を省略しています)

station_controller.rb
  def new
    @station = StationForm.new
  end

  def create
    @station = StationForm.new(station_params)
    if @station.valid?
      @station.save
      redirect_to root_path
    else
      render :new
    end
  end

  def edit 
    @station = StationForm.new(id: params[:id])
  end

  def update
    @station = StationForm.new(station_params.merge(id: params[:id]))
    if @station.valid?
      @station.update
      redirect_to root_path
    else
      render :edit
    end
  end

新規投稿と編集のインスタンス生成時の違いは保存されたデータが既に存在しているかどうかなのでedit&updateを行う時は編集を行う対象データのidを引数として送りinitializeメソッド内で処理をする必要があります。

station_form.rb
 if !(attribute[:id] == nil)
      @station = Station.find(attribute[:id])
      @address = @station.address
      if !(self.name = attribute[:name])
        self.name = @station.name 
      else
        self.name = attribute[:name]
      end
      if !(self.address_url = attribute[:address_url])
        self.address_url = @address.address_url 
      else
        self.address_url = attribute[:address_url]
      end
    else
      super(attribute)
    end

引数のidが存在しない場合はsuper(attribute)としてインスタンスを生成します。
(このsuperが何かよく分からない方は参考文献をどうぞ!簡単に言うと何もないinitializeメソッドを行っている認識でいいかと思います。)

ここでidが渡された場合は保存されたデータを取得する動きになります。
このタイミングで僕がつまいづいたのはただ情報を取得するだけだとeditのフォームに情報が表示されない点です。

原因は簡単でStationFormインスタンス自体に情報が入っていないからです。

staion_form.rb
if !(self.name = attribute[:name])
   self.name = @station.name 
else
   self.name = attribute[:name]
end
if !(self.address_url = attribute[:address_url])
   self.address_url = @address.address_url 
else
   self.address_url = attribute[:address_url]
end

ここでStationFromインスタンス自体に初期値を設定しています。
selfがインスタンスを表していて、formで表示しておきたい値を渡している形になります。
条件分岐をしているのはupdateアクションの時用です。
updateアクションが行われる時は引数としてパラメーターを渡しています。

stations_controller.rb
  def update
    @station = StationForm.new(station_params.merge(id: params[:id]))
    if @station.valid?
      @station.update
      redirect_to root_path
    else
      render :edit
    end
  end

上記のように記述することでid,name,address_urlが渡されるのでattributeの中に3つの値が入ります。
update時は保存する値をStationFormインスタンスに代入する必要があるので代入をします。

ここまでの動きをinitializeメソッドで行っています。
この初期値の設定さえうまく行けばFormオブジェクトをつかった編集と更新はうまくいくはずです。

まとめ

以上が個人的にFormオブジェクトをつかった編集と更新のポイントかなと思います。
ただ、先程も述べたようにもっときれいな書き方が出来るところがあるのと、人によって書き方も違うと思うので教えて頂けると大変嬉しいです。

この記事がすこしでも参考になれば嬉しいです。

最後までご覧頂きありがとうございました!

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

rails 検索機能の実装

1.ルーティングの指定

ルーティングの中に検索したいテーブルの中にserchを記述します。以下を例とします。collectionを使うことによって生成されるルーティングのURLと実行されるコントローラーを任意でカスタムできます。

config/routes.rb
resources :posts do
      collection do
        get 'search'
      end
  end

2.モデルの指定

モデルを指定します。以下のようにwhereメソッドとLIKE句を使い、検索をかけたいカラムを記述します。

post.rb
def self.search(search)
        if search != ""
          Post.where('text LIKE(?)', "%#{search}%" )
        else
          Post.all
        end
      end

3.この他に検索のカラムを追加したいとき

もしいくつかのカラムを検索につかしたいときは、orを使い"%#{search}%"以下のように記述しましょう。

post.rb
def self.search(search)
        if search != ""
          Post.where('text LIKE(?) or title LIKE(?)', "%#{search}%", "%#{search}%")
        else
          Post.all
        end
      end

4 コントローラーにserchアクションを記述

コントローラーにsearchアクション記述をします。この記述でバックエンドの記述は終了です。あとはビューに反映させるだけなので、お好みの形でデザインしましょう。

controllers/posts_controller.rb
def search
 @post = Post.search(params[:keyword])
end

5.最後に

検索機能自体は他のアクションと同じように設定できますので、モデルの指定だけ特徴をつかめば簡単に実装できます。是非実装してみましょう。

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

$ rails db:seedを用いて初期データを格納

この記事/メモについて(はじめに)

Twitterの呟きやそれに対するリプライ・コメントは、「ユーザーが送信した情報」をデータとしてデータベースに保存する、と言った仕組みのものです。
しかし、データベースへ格納するデータは必ずしもユーザーや第三者が投稿・送信してきたものだけとは限りません。

例えば、自分が行ったことのあるおすすめの国について紹介するWebサイトを制作しようとした時。
このサイトでは、ユーザーが「ここの国はおすすめだよ!」「この地域の料理、とても美味しかった!」と呟いたものではなく、開発者のみなさんが現地で感じた思いやおすすめのポイントなどを掲載したいですよね。

本記事では、そんな方々のために、開発者が裏側からデータを格納する方法についてご紹介しようと思います。

手順 (2Stepで簡単にできます!)

裏側からのデータの格納は、

①seedファイルに保存したい初期データを記述
②「$ rails db:seed」というコマンドを入力する

という実に簡単な方法で行うことができます。
①番の記述の仕方を抑えてしまえば、特に難しい内容はありません。以下で詳しく説明していきます。

Step1 seedファイルに保存したい初期データを記述

例として、Countries(プロダクトに合わせて変更)テーブルに開発者側からデータを入れる場合を考えます。
Countriesテーブルには、nameカラムと、説明のためのbodyカラム、さらに滞在日数を表すdayカラムとその予算(万円)を示すbudgetカラムが存在しているとします。

ここでは、表のような情報を開発者が入れる初期データとして保存していこうと思います。

ID name   body day budget
1 韓国   低予算で初海外旅行におすすめ! 3日 5
2 スペイン   自然より建物好きならヨーロッパ! 1週間 15
3 シンガポール チキンライスは外せない! 4日半 10

保存形式は以下の通りです。文字列と整数が混在している場合を考えます。
・name → 文字列(string型)
・body → 長い文字列(text型)
・day → 文字列(string型)
・budget → 整数(integer型)

このようなデータをCountriesテーブルに保存したい時、まずdb/seeds.rb内に以下のように記述します。

db/seeds.rb
Country.create!(name: '韓国', body: '低予算で初海外旅行におすすめ!', day: '3日', budget: 5)
Country.create!(name: 'スペイン', body: '自然より建物好きならヨーロッパ!', day: '1週間', budget: 15)
Country.create!(name: 'シンガポール', body: 'チキンライスは外せない!', day: '4日半', budget: 10)

レコード1つにつき1行分必要となるイメージです。(たくさんのレコードを保存させたい場合に合わせて、より効率の良い書き方もあります。他にも様々な書き方があるので、是非調べてみてください!)

?1point豆知識?
整数(integer型)で保存するbudgetについて、値を「""」や「''」で囲っていないことに注意してください。
→seedsファイルに限らず、プログラミングの世界ではダブル/シングルクオーテーションを用いて囲ったものは基本的に文字列として認識されてしまうので、エラーが生じてしまいます。

Step2 seedファイルに記述した内容をデータベースに反映させる

ターミナル、またはコマンドプロンプトで以下のコマンドを実行することで、db/seeds.rbに記した内容をデータベースに反映することができます。

$ rails db:seed

以上です!簡単でしたね!

Step3 データベース内のデータを確認/削除する

データベースに保存されているデータについて確認/削除したいとき、$ rails cというコマンドを実行します。($ rails cを終わりにする際はexitと入力してEnterを押してください。)

こちらの内容はseeds.rbを用いて初期データを格納する場合以外でも頻出のコマンドとなるので、別記事に軽くまとめるつもりです。(気が向いたら。。)

======

$ rails cは頻出のコマンドとなるので、データの確認/削除は「$ rails c」!!と自分に三回言い聞かせましょう。

データの確認

以下のようにコマンドを入力してください。Countriesテーブルに保存されているデータが出力されるはずです。

$ rails c
irb(main):001:0> Country.all

# => 保存されているデータの出力(確認)

データの削除

以下のようにコマンドを入力すると、今度はCountriesテーブルに保存されているデータを全て削除することができます。

$ rails c
irb(main):001:0> Country.delete_all

# => 保存されているデータの削除

参考

railsのseedの書き方いろいろ
https://qiita.com/takehanKosuke/items/79a66751fe95010ea5ee

【Rails】CarrierWave + seedで初期データに画像ファイルを投入する
(CarrierWaveをすでに実装している方のうち、初期データに画像を入れたい方はこちらの記事が大変わかりやすいのでぜひ見てください!)
https://remonote.jp/rails-carrierwave-seed

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

【Heroku×Ruby2.6.5】error: failed to push some refs to 'https://git.heroku.com/●●.git'→Herokuのバージョンを下げて対応。

エラーが発生した状況

  • Heroku createでデプロイ用のアプリを作成。
  • データベースの設定や、環境変数の設定を行った。
  • 最後にmasterの内容を、Heokuへ反映しようとgit push heroku masterのコマンド実行したところ、下記の通りエラーが発生。

エラー画面(ターミナル)

エラー内容(heroku).png

バージョン等

  • ruby 2.6.5
  • Rails 6.0.3.4
  • MySQL Ver 14.14 Distrib 5.6.50

エラーが起きた理由

  • エラー文の中に書いてあるHerokuのURLを確認したところ、Ruby2.6.5のサポートが終了したとのことだった。

https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
11a5c02082a7ae17edc7e5b63705e781.png

解決策

(注意!)
ここに書いてある解決策は、アプリのRubyをバージョンアップせずにデプロイする方法。
デプロイ時に、環境構築できるなら、Rubyのバージョンアップを行い、サポートしてもらえるバージョンでデプロイ出来ると良い。

エラーの本文内にある、

heroku stack:set heroku-18 -a [ここにはcreateで作成したアプリ名を記載]

を実行したところ、git push heroku masterのコマンドが実行でき、デプロイすることが出来た。
done2.png

今回学んだこと

  • 実は今回、記事伊藤さんの動画を見ながらバージョンアップを試みた。
  • 伊藤さんの記事から、アップデートする前にGemfile内をアップデートして一つずつ行うのか?とめっちゃ勉強になった。

  • 無事にバージョンアップが出来て、アプリのGemfileを修正し、bundle install

  • いざ、rails sしようとしたところ、を以下のエラーになってしまった?

f.〇〇@〇〇noMacBook-Air breadcrumb % rails s
rbenv: rails: command not found

The `rails' command exists in these Ruby versions:
  2.6.5
  • なぜそうなってしまったのか調べていたところ、Rubyのバージョンアップを行うにはrbenvの初期化から行う必要があり、Rubyだけでなく今までrbenvに設定していたmysqlyarnbundler等の設定し直しであることを学んだ。?

  • コマンドに言われた通りのバージョンにアップデートする以外にも、Heroku側のバージョンを下げる方法があることを知った。

  • 他のデプロイの選択肢(例えばAWS等)において、Ruby2.6.5でも対応しているものもあるため、Heroku以外の選択肢を選ぶこともありと感じた。

  • 今回は取り急ぎHerokuのバージョンを下げる方法での対応をしたが、今度macのバージョンアップとともにRubyとRailsのアップデートも行いたい。

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

【if !status else end】否定の否定を考えさせるプログラミングはやめよう

Rubyを例にしますがどの言語でも同じことがいえます。

否定の否定を考えさせるってなに?

たとえば、以下の要件があったとき。

変数statusfalseのときにはいろんな処理を実施し、trueのときにはちょっとした処理を実施する。

要件に書かれている通りにプログラミングしました。(Ruby)

status = false

# bad
if !status
    p 'いろんな処理'
else
    p 'ちょっとした処理'
end

次に、ちょっとした処理が実施される条件は何かを調べる必要が出てきたとき。
!statusの否定だから、status == trueだな。」
と考えますね。
これが否定の否定を考える、です。プチストレスになりますので、こういうプログラミングは避けましょう。

unlessを使った場合も同様です。
unlessを使うときはelseブロックを書いてはいけません。

status = false

# bad
unless status
    p 'いろんな処理'
else
    p 'ちょっとした処理'
end

以下のようにリファクタリングします。

status = false

# good
if status
    p 'ちょっとした処理'
else
    p 'いろんな処理'
end

まとめ

  • if~else~endでは、条件式に否定!を使わず、trueの処理とfalseの処理を入れ替えましょう。
  • unlessではelseブロックを書かないようにしましょう。if~else~endに置き換えましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像付き】RailsでDeviseを利用してLINEログイン機能を実装

はじめに

自作アプリでLINEBotを作成する際にLINEログイン機能を実装しました。

実装するに当たって様々な記事を参考にさせて頂きましたが, 記事によって記述が異なったり, ここはもう少し簡潔に書けるのでは?と思ったり, 記事通りに書いてもエラーが出たり, そもそも記事が古かったりで苦労しました。。

今後また実装する時に自分の記事さえ見れば導入できるように画像付きでまとめていきたいと思います!

基本的にRailsを想定していますが, LINE Developersの登録などはRails以外の言語でも参考になる部分があるかと思いますので, 少しでも参考になれば幸いです?

0.導入方法

導入方法はおおまかに以下の4つです。順を追って説明します。

1.Railsアプリを作成
2.Deviseを導入
3.LINE Developersに登録
4.LINEログイン機能を導入
5.LINEログイン詳細設定

1. Railsアプリを作成

  • 以下の前提で進めていきます。(アプリやコントローラー名は任意の名前にして下さい)
  • postgresqlに設定していますが, データベースはお好みで。
ターミナル
rails new linelogin_sample -d postgresql
rails db:create
rails g controller homes index
config/routes.rb
Rails.application.routes.draw do
  root 'homes#index' 
end

ここまで出来たら rails s でサーバーを起動して http://localhost:3000 にアクセスし, 動作確認を行ってみて下さい。エラーが表示されていなければOKです!

2.Deviseを導入

LINEログイン機能の前に, まずはDeviseでログイン機能を実装していきます。

2-1.Gemfileの編集とインストール

Gemfile
# ログイン機能
gem 'devise'

Gemfileを編集したらターミナルで bundle install を実行

2-2.Deviseをインストール

【公式】 plataformatec/devise

ターミナル
rails g devise:install
rails g devise User
rails db:migrate

(※ userの箇所は,任意のモデル名でOKです)

問題がなければ rails s でサーバーを立ち上げ,http://localhost:3000/users/sign_in にアクセスすると,ログイン画面が表示されます。
(すでにサーバーを起動している場合は再起動して下さい。)

スクリーンショット 2020-10-21 16.55.41.png

3.LINE Developersに登録

LINEログイン機能を実装するには LINE Developersに登録する必要があります。
まだ登録されてない方はこちらのURL ( https://developers.line.biz/ja/ ) からログイン画面に遷移し, LINEの登録時に設定したメールアドレスとパスワードでログインします。

登録が完了するとこのような画面が表示されるので, LINEログインを選択し, 今すぐはじめよう というボタンをクリックしてください。
スクリーンショット 2020-10-21 17.04.16.png

今すぐはじめよう をクリックすると新規プロバイダーを作成する画面が出てきますので, 必要な項目を入力し, プロバイダーを作成してください。

例えば, アンケートBotというアプリにLINEログイン機能を導入する場合は画像のように入力していきます。

スクリーンショット 2020-10-21 17.30.26.png

スクリーンショット 2020-10-21 17.30.35.png

スクリーンショット 2020-10-21 17.30.44.png

上記はあくまで一例です。

プロバイダー名, チャネルアイコン, チャネル名, アプリタイプなどは任意のものにしてください。

4.LINEログイン機能を導入

LINE Developersで新規プロバイダーを作成したら, いよいよLINEログイン機能を実装していきます!

4-1.Omniauthのインストール

Gemfileに以下を追加し, bundle install を実行。

Gemfile
gem 'omniauth-line'

4-2.環境変数を設定

次にChannel IDとChannel Secretの設定を行います。
今度はGemfileに以下のgemを追加し, bundle install を実行してください。

Gemfile
gem 'dotenv-rails'

gemを追加したら, アプリのルート配下に .env ファイルを作成し, 以下の2つを追加してください。

.env
LINE_KEY='自身のChannel ID'
LINE_SECRET='自身のChannel Secret'

▼環境変数の確認
スクリーンショット 2020-10-21 22.13.55.png

スクリーンショット 2020-10-21 22.14.10.png

【注意!】
このファイルを公開してしまうと悪用されてしまう可能性があるので, 必ずGitの管理下から外してください。
.gitignore に以下を追加

.gitignore
/.env

4-3.DeviseとLINEログインの設定

次にDeviseの設定ファイルにLINEログインの設定を行います。

  • 以下を追加
devise.rb
config.omniauth :line, ENV['LINE_KEY'], ENV['LINE_SECRET']
  • :omniauthableomniauth_providers: %i[line] を追加 (TwitterやFacebookログインなども導入する場合は %i[line] の配列に追加してください)
user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: %i[line]
end

4-4.コントローラーの設定

  • omniauth_callbacks_controllerを作成します。
ターミナル
rails g controller omniauth_callbacks

【注】OmniAuthのバージョンが原因でエラーが出ることがあります。

▼バージョンエラーが出た場合の対処

Gemfile
gem 'omniauth', '~> 1.9.1'

Gemfileに上記のコードを追加し, ターミナルで bundle update

bundle update でomniauthのバージョンが変わったのを確認したら, bundle install を実行。
再度 rails g コマンドでコントローラーを作成して下さい。

  • 以下を修正
omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def line; ; end
end

4-5.ルーティングの設定

  • 以下のようにルーティングを設定します。
routes.rb
devise_for :users, controllers: {
  omniauth_callbacks: "omniauth_callbacks"
}

ルーティングを rails routes で確認し、

user_line_omniauth_authorize GET|POST /users/auth/line(.:format)               omniauth_callbacks#passthru
user_line_omniauth_callback GET|POST /users/auth/line/callback(.:format)       omniauth_callbacks#line

このような表示がされていたらルーティングの設定は完了です!

4-6.モデルの変更

DeviseでUserモデルを作成しているのでそのままUserモデルに変更を加えてLINEログインができるようにしたいと思います。

  • 以下のコマンドでマイグレーションファイルを作成
ターミナル
rails g migration add_column_to_users
  • 作成されたマイグレーションファイルに以下を追加
db/migrate/20201022073739_change_column_to_user.rb
class ChangeColumnToUser < ActiveRecord::Migration[6.0]
  def change
  # 以下を追加
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :name, :string
  end
end

マイグレーションファイルを編集したら rails db:migrate でマイグレーションを実行してください。

4-7.Userモデルの編集

  • 以下を追加
user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable,
         omniauth_providers: %i[line]

  # 以下を追加
  def social_profile(provider)
    social_profiles.select { |sp| sp.provider == provider.to_s }.first
  end

  def set_values(omniauth)
    return if provider.to_s != omniauth["provider"].to_s || uid != omniauth["uid"]
    credentials = omniauth["credentials"]
    info = omniauth["info"]

    access_token = credentials["refresh_token"]
    access_secret = credentials["secret"]
    credentials = credentials.to_json
    name = info["name"]
    # self.set_values_by_raw_info(omniauth['extra']['raw_info'])
  end

  def set_values_by_raw_info(raw_info)
    self.raw_info = raw_info.to_json
    self.save!
  end
  # 以上を追加
end

4-8.コントローラーを編集

  • classの ApplicationControllerDevise::OmniauthCallbacksController に変更するのを忘れないように!
omniauth_callbacks_controller.rb
# ApplicationController を Devise::OmniauthCallbacksController に変更
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  # 以下を追加
  def line; basic_action end

  private
  def basic_action
    @omniauth = request.env["omniauth.auth"]
    if @omniauth.present?
      @profile = User.find_or_initialize_by(provider: @omniauth["provider"], uid: @omniauth["uid"])
      if @profile.email.blank?
        email = @omniauth["info"]["email"] ? @omniauth["info"]["email"] : "#{@omniauth["uid"]}-#{@omniauth["provider"]}@example.com"
        @profile = current_user || User.create!(provider: @omniauth["provider"], uid: @omniauth["uid"], email: email, name: @omniauth["info"]["name"], password: Devise.friendly_token[0, 20])
      end
      @profile.set_values(@omniauth)
      sign_in(:user, @profile)
    end
    flash[:notice] = "ログインしました"
    redirect_to root_path
  end

  def fake_email(uid, provider)
    "#{auth.uid}-#{auth.provider}@example.com"
  end
  # 以上を追加
end

これで設定は完了です!
http://localhost:3000/users/sign_in にアクセスすると Sign in with Line が追加されていると思います。

スクリーンショット 2020-10-24 18.01.47.png

ただし, 今の状態ではクリックしてもログインできません。LINE Developers側の詳細設定をしていきましょう!

5.LINEログイン詳細設定

5-1. コールバックURL

ログイン時のコールバックURLを設定していきます。

LINE Developersの任意のプロバイダーからLINEログイン設定を開いてください。
コールバックURLを設定するフォームがありますので, そこに入力していきます。

スクリーンショット 2020-10-26 16.43.38.png

基本は https://アプリ名/users/auth/line/callback で大丈夫です!
念のため rails routes で URI Pattern を確認してみてください。
スクリーンショット 2020-10-26 17.10.47.png

ここで注意して欲しいのが, LINEログインを使用する際のURLは https でないといけないということです!
http://localhost3000/users/auth/line/callback のようには設定できません。

なので開発環境で動作確認を行う際は一工夫しなければなりません。
開発環境でも動作確認を行いたい場合は次の ngrok を参考にしてください。
(ngrokはあくまで一例ですので, 他にも色々やり方はあります。必要のない方は読み飛ばして下さい!)

5-2.ngrok

ngrok はlocalhostに外部から接続できるようにするというサービスです。
簡単に導入できますので説明していきます。

  • 以下のコマンドで ngrok をインストールしてください
ターミナル
brew cask install ngrok

インストールが終了したらバージョン確認をしてみてください。
以下のようにバージョンが表示されればOKです!

❯ ngrok -v
ngrok version 2.3.35

ngrokのインストールが終わったら、外部アクセス用 https のURLを発行しましょう。
以下のコマンドを実行してください。

ターミナル
ngrok http 3000

( 3000 はポート番号なので適宜変更してください。)

コマンドを実行するとこのような感じで表示されると思います。

スクリーンショット 2020-10-26 17.37.25.png

こちらで表示された http://xxxxxxxx.ngrok.io で外部からlocalhostのWebサーバーにアクセスすることが可能になります。

今回は https で接続したいので下の方の https://xxxxxxxx.ngrok.io をコールバックURLに指定してください。

スクリーンショット 2020-10-26 17.48.10.png

【注】ngrokで作成したURLには使用制限があり, 発行して8時間が経過すると, 設定したURLではアクセスできなくなってしまいます。その際は再度 ngrok http 3000 を実行し, 発行されたURLでコールバック先を指定し直してください。

→ 以前は8時間だったのですが, 2時間に短縮されているかも。。LINEログインの動作確認だけだと2時間で十分かと思いますが, 他に便利なサービスがあれば追記します?

【Rails6の場合】

Rails6から保護機能が強化され, ngrokで発行したホスト名ではアクセスできないので, 許可したいホスト名をRailsアプリ側に登録しなければなりません。
(Rails5を使用されている方はこちらは読み飛ばして頂いて大丈夫です。)

それではngrokで作成したホストをRailsのアプリに登録していきます。

config/environments/development.rb に下記を記載し、ホワイトリストに許可したいhostを追加すればOKです!
許可したいホスト名には xxxxxxxx.ngrok.io の部分を追加してください。

config/environments/development.rb
Rails.application.configure do
  # 以下を追加
  config.hosts << "許可したいホスト名"
end

ただ先ほど説明したように, ngrokは期限が切れるたびに違うURLを発行し直さなければなりません。

そうすると許可したいホスト名も毎回書きかえないといけなくなりますので, 以下の方法も紹介しておきます。

config/environments/development.rb
Rails.application.configure do
  # 以下を追加
  config.hosts.clear
end

これにより, すべてのホスト名に対するリクエストを通過させることができます。
(ただしせっかくRails6で追加された保護機能を無効化してしまうため、どちらを選択するかはお任せします。)

ここまでの設定が完了したらngrokで発行したURLにアクセスできるようになります!
すでにサーバーを起動している場合は再起動し, 実際にURLにアクセスして確認してみてください。

スクリーンショット 2020-10-26 23.01.06.png

【注】ngrokとlocalhostのサーバーは同時に起動させてください。終了させる時はどちらも Ctrl + C で終了できます。

5-3.チャネルの公開

最後に, 作成したチャネルを公開していきます!

上の方に表示されている 非公開 ボタンをクリックすると「チャネルを公開しますか?」という表示が出てきますので, 公開をクリックしてください。
スクリーンショット 2020-10-26 17.52.55.png

きちんと公開できていれば 公開済み に切り替わります。
スクリーンショット 2020-10-26 17.53.04.png

ここまで設定したらLINEログインの設定は終了です!!!
ログイン画面の Sign in with Line をクリックするとログイン画面が表示されますので, そちらからログインできるようになります!
(初回ログインの際は, LINE登録時のメールアドレスとパスワードを求められる場合があります)

スクリーンショット 2020-10-26 22.03.12.png

Messaging API を利用してLINEBotなどを作る場合, LINEログイン機能は大変便利だと思いますので, 少しでも導入する際の参考になれば幸いです。

仕様の変更や補足があれば随時更新していきたいと思います!
お疲れさまでした〜!✨

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

【Mysqlエラー】Mysql2::Error: Specified key was too long; max key length is 767 bytes

はじめに

アプリ開発中に起こったMySQLのエラーについて忘備録も兼ねて記録しておこうと思います。

エラー文

rails g devise userでuserモデルを作成し、rails db:migrateを行うと下記のエラー文が表示。

Mysql2::Error: Specified key was too long; max key length is 767 bytes

直訳すると「指定したキーは長すぎます。キーの最大範囲は767バイトまでです」

意味は「Mysql2に格納できる最大文字データ量(767バイト)に対してオーバーしてしまったためエラーになった」ということのようです。

エラー解決に向けての予備知識

utf8

文字コードの中で世界で最も普及している文字コード。
通常は1~4バイトで文字を表現するが、MySQLでは3バイトの文字までしか扱えない。
1文字当たり3バイト。

utf8mb4

データベースMySQLで扱うための文字コード。
4バイトの文字を扱えるため、絵文字を扱いたい場合はこちらを使用する
→絵文字は4バイトなので、3バイトまでしか扱えないutf8では不可能なため。
1文字当たり4バイト。

VARCHAR型

・可変長(長さを変えられる)文字列のこと

・varchar(m)という形で指定する。(mはバイト数。0~65535まで。)

例) varchar(255) varchar(191)

・char型と異なり、末尾に空白は付かない。
・末尾に空白が付いた文字列はそのまま格納される。
↑の場合、取得時も空白が付いたままだが、WHERE句での比較時には削除された状態で比較が行われる。

エラー考察

前提条件として

MySQLのインデックスは最大767バイト

Railsのstringにおけるデフォルト値が255文字(MySQLで保存できるデータ量)。

  • 3バイトのutf8では

 3バイト × 255文字 = 765バイト < 767バイト

  • 4バイトのutf8mb4では

 4バイト × 255文字 = 1020バイト > 767バイト

つまり、上記のように文字コードutf8mb4では、
4 × 255 = 1020バイトとなって、MySQLのインデックスの最大数である767バイトを上回ってしまいエラーが発生してしまったということです。

解決方法

解決方法は大きく2つあるようで、

①767バイトを超えないように最大文字数を削る
②767バイトを超えてもOKにする

今回は簡単そうな①でエラー解決していきます。



結論 : 文字制限をかけるファイルを新規作成する

現在の入力できる最大文字数、255文字『varchar(255)』191文字『varchar(191)』変更できるファイルを新規作成し、767バイトを超えないようにします。



191文字『varchar(191)』の理由は

MySQLのインデックスは最大767バイト
utf8mb4は1文字当たり4バイト必要なので

767バイト ÷ 4バイト = 191.75文字 

実際のコード

mysql.rbconfig/initializer配下に新規作成しvarcharlimit191にしましょう。

このmysql.rbというファイルによってActiveRecord内のNATIVE_DATABASE_TYPESという設定を上書きすることができます。

ソースはこちら

config/initializer/mysql.rb
require 'active_record/connection_adapters/abstract_mysql_adapter'

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter
      NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 } #注目
    end
  end
end

mysql.rb作成前

rails consoleにて確認することができます。

[1] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>255}, #limitが255になっている
#...以下略

mysql.rb作成後

:string=>{:name=>"varchar", :limit=>191}と最大文字数が191になっているのがわかりますね。

[2] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>191}, #ここがmysql.rbで設定した内容に変わる
#...以下略

これで文字のデータ量を767バイト以内に納めることができました。

この後rails db:migrateを入力すると上手く作動しエラーが解消されました

ちなみに

今回のように、最大文字数を255文字から191文字にする方法ではなく、
最初にconfig/database.ymlに記載した文字コードを途中から変更(今回であればutf8mb4からutf8に変更してマイグレーション)する方法を取ると、もっと面倒臭いエラーになるようです。

rails db:migrateをすると消えない謎のファイルが現れ、モデルの作り直しになってしまうとか。

database.ymlの記載された情報は、迂闊に手を出せないので慎重に取り扱う必要がありますね。

参考記事

Qiita記事【MySQL】Mysql2::Error: Specified key was too long; max key length is 767 bytes

Qiita記事 utf8mb4で巻き起こったトラブル

Qiita記事 【SQL】char型とvarchar型の違いについてまとめてみた

MySQL 5.5 / Redmine 3.4.3 / Ruby23-x64 --- Mysql2::Error: Specified key was too long; max key length is 767 bytes

MySQLでutf8とutfmb4の混在で起きること

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

RubyonJetsをGithubActionsで自動デプロイする

概要

Ruby on Jetsを用いたアプリケーションをmergeやpushと同時に自動デプロイできるようにします。
GithubActionsが提供するランナーにはAWS CLIやrsync、zipなどが元からインストールされているみたいで、簡単にデプロイすることができました。

手順としては2ステップでできます!
1. AWS CredentialsをGithub Secretsに登録
2. .github/workflows/にymlファイルを記述する

環境

Ruby: 2.5.7
Ruby on Jet: 2.3.18

手順

AWSのCredentialsをGithub Secretsに登録

暗号化されたシークレットを参考にAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYを設定してください。
結構簡単に設定できます。

GithubActionsの設定ymlを記述

今回の例ではmainブランチにmerge(or push)した際にproductionに自動デプロイできるような設定を記述しました。

/.github/workflows/deploy_to_production.yml
name: Deploy to production

on:
  push:
    branches: [ main ]

jobs:
  deploy:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        ruby-version: [ '2.5.7' ]

    steps:
    - uses: actions/checkout@v2

    - name: Set up Ruby
      uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
      with:
        ruby-version: ${{ matrix.ruby-version }}
        bundler-cache: false # runs 'bundle install' and caches installed gems automatically

    - name: Bundle install
      run: bundle install --path vendor/gems

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1

    - name: Jets deploy
      env:
        JETS_ENV: production
      run: echo y | bundle exec jets deploy

以下の箇所はjets deploy時にbundle installが走るpathと揃える目的があります。
こうしないとjets deploy時にgemの容量がオーバーしてうまくいきませんでした。

- name: Bundle install
  run: bundle install --path vendor/gems

まとめ

stagingのデプロイは/.github/workflows/deploy_to_staging.ymlなど作成して、JETS_ENV: stagingにするだけでOKです!
ちなみにこの記述をしたブランチをmainに反映した時点から自動デプロイが始まるので気をつけてください!

参考

GitHubの新機能「GitHub Actions」で試すCI/CD

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

【Railsチュートリアル】第2章 Toyアプリケーション 演習と回答

演習と回答

2.2.1 ユーザーページを探検する

2.2.1 - 1

CSSを知っている読者へ: 新しいユーザーを作成し、ブラウザのHTMLインスペクター機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?

<p id="notice">User was successfully created.</p>

リロード前は上のようになっていて

<p id="notice"></p>

リロードしたあとはUser was successfully created.が消えて空白になっていた。

2.2.1 - 2

emailを入力せず、名前だけを入力しようとした場合、どうなるでしょうか?

名前だけでも登録ができる。

2.2.1 - 3

「@example.com」のような間違ったメールアドレスを入力して更新しようとした場合、どうなるでしょうか?

更新できる。

2.2.1 - 4

上記の演習で作成したユーザーを削除してみてください。ユーザーを削除したとき、Railsはどんなメッセージを表示するでしょうか?

User Destroy (1.1ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 3]]

などと表示される。

2.2.2 MVCの挙動

2.2.2 - 1

図 2.11を参考にしながら、/users/1/edit というURLにアクセスしたときの振る舞いについて図を書いてみてください。

割愛。

2.2.2 - 2

図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。Hint: set_userという特殊な場所の中にあります。

private
  # Use callbacks to share common setup or constraints between actions.
  def set_user
    @user = User.find(params[:id])
  end

2.2.2 - 3

ユーザーの情報を編集するページのファイル名は何でしょうか?

edit.html.erb

2.3.1 マイクロポストを探検する

2.3.1 - 1

CSSを知っている読者へ: 新しいマイクロポストを作成し、ブラウザのHTMLインスペクター機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?

<p id="notice">Micropost was successfully created.</p>

リロード前は上のようになっていて

<p id="notice"></p>

リロードしたあとはMicropost was successfully created.が消えて空白になっていた。

2.3.1 - 2

マイクロポストの作成画面で、ContentもUserも空にして作成しようとするどうなるでしょうか?

作成できる。

2.3.1 - 3

141文字以上の文字列をContentに入力した状態で、マイクロポストを作成しようとするとどうなるでしょうか?(ヒント: WikipediaのRubyの記事にある設計思想の引用文が140文字を超えているので、これをコピペしてみましょう)

作成できる。

2.3.1 - 4

上記の演習で作成したマイクロポストを削除してみましょう。

それぞれ削除しました。

2.3.2 マイクロポストをマイクロにする

2.3.2 - 1

先ほど2.3.1.1の演習でやったように、もう一度Contentに141文字以上を入力してみましょう。どのように振る舞いが変わったでしょうか?

作成できず、エラーメッセージが出るようになった。

2.3.2 - 2

CSSを知っている読者へ: ブラウザのHTMLインスペクター機能を使って、表示されたエラーメッセージを調べてみてください。

<textarea name="micropost[content]" id="micropost_content">Ruby には Perl や Python とは決定的に違う点があり、それこそが Ruby の存在価値なのです。それは「楽しさ」です。私の知る限り、Ruby ほど「楽しさ」について焦点を当てている言語は他にありません。Ruby は純粋に楽しみのために設計され、言語を作る人、使う人、学ぶ人すべてが楽しめることを目的としています。しかし、ただ単に楽しいだけではありません。Ruby は実用性も十分です。実用性がなければ楽しめないではありませんか。
— まつもとゆきひろ、Ruby プログラミング入門 まえがき 監修者よりのページ</textarea>

2.3.3 ユーザーはたくさんマイクロポストを持っている

2.3.3 - 1

ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください(コラム 1.2で紹介した技術の出番です)。うまく表示できたかどうか、/users/1 にアクセスして確認してみましょう。

first_user.microposts
  Micropost Load (0.1ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 7, content: "hi!", user_id: 1, created_at: "2021-01-23 06:05:27", updated_at: "2021-01-23 06:05:27">]> 
2.6.3 :006 > 

2.3.3 - 2

リスト 2.18は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう(図 2.17のようになっていると成功です)。

できたので割愛。

2.3.3 - 3

リスト 2.19のFILL_INとなっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください(図 2.18)。

user.rb
class User < ApplicationRecord
  has_many :microposts
  validates :name, presence: true
  validates :email, presence: true
end

2.3.4 継承の階層

2.3.4 - 1

Applicationコントローラのファイルを開き、ApplicationControllerがActionController::Baseを継承している部分のコードを探してみてください。

application_controller.rb
class ApplicationController < ActionController::Base #この行
  def hello
    render html: "hello, world"
  end
end

2.3.4 - 2

ApplicationRecordがActiveRecord::Baseを継承しているコードはどこにあるでしょうか? 先ほどの演習を参考に、探してみてください。ヒント: コントローラと本質的には同じ仕組みなので、app/modelsディレクトリ内にあるファイルを調べてみると...?)

app/models/application_record
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

2.3.5 アプリケーションをデプロイする

2.3.5 - 1

本番環境で2〜3人のユーザーを作成してみましょう。
作成できました。

2.3.5 - 2

本番環境で最初のユーザーのマイクロポストを作ってみましょう
作れました。

2.3.5 - 3

マイクロポストのContentに141文字以上を入力した状態で、マイクロポストを作成してみましょう。リスト 2.14で加えたバリデーションが本番環境でもうまく動くかどうか、確認してみてください。

うまく動きました。

さいごに

Railsチュートリアル第2章をクリアしました!
エラーに遭遇しましたが、きちんと文章を読む、検索する、めげない、で解決へたどり着けました。これからもっと難しくおもしろくなってくるようで、楽しみです。

  • MVCモデルを知った
  • RESTアーキテクチャを知った
  • データモデルを作成した
  • Twitterっぽい何かを作ることができた
  • エラーに遭遇しても検索して解決へたどり着けた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsでbootstrapを使う

bootstrapとは

洗練されたWebデザインとユーザーインターフェイス要素を簡単に導入できるCSSのフレームワーク。
最大の特徴はアプリケーションをレスポンシブデザインにできること。

公式サイト

導入

①gemファイルを記入

Gemfile
source 'https://rubygems.org'

gem 'bootstrap', '~> 4.5.0'
.
.
.

スクリーンショット 2021-01-23 17.22.39.png

②ターミナルでbundleをインストールする

$ bundle install

③scssファイル作成

/app/assets/stylesheets/application.scss のファイルを作成。

/app/assets/stylesheets/application.scss
@import "bootstrap-sprockets";
@import "bootstrap";
.
.
.

④コードの記入

bootstrapはclassを特定のコードに書き変えると自動で色や大きさを変えてくれます。

今回はこのボタンにbootstrapを当ててみます。

スクリーンショット 2021-01-23 17.34.29.png

公式サイトのCompornents → buttons の中のこのコードを使います。

スクリーンショット 2021-01-23 17.34.48.png

classの部分をコピーしてコードに貼り付ける。

スクリーンショット 2021-01-23 17.44.48.png

スクリーンショット 2021-01-23 17.44.56.png

サーバーを再起動すると、、、

スクリーンショット 2021-01-23 17.47.02.png

指定したボタンに早変わり!!

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

Ruby on Railsで簡単に新着マークを作る方法

はじめに

独学で学習中の学生がこの記事を書いています。
間違っているところやこんな書き方はしないなど思われるかもしれませんが、優しく指摘してもらえるととても嬉しいです。

作りたいもの

677B1C4C-90FF-451F-9F2E-8A60A12F20AD_4_5005_c.jpeg

ここで作りたいものは新着マークです。
新しい投稿があると、その投稿から7日間の間だけ表示される新着マークを作りたいと思います。(
7日経つを自動で消える)
※日数は変更可能

前提条件

投稿機能と投稿一覧は既に実装済みということで進めていきます。

必要になる情報

1 現在の時間
2 新規投稿された時間
3 新規投稿された時間から7日後の時間

作成の流れ

1 現在の時間を取得(可変)
2 投稿された時間の取得(不変)
3 投稿された時間から7日後の時間を取得(不変)
4 3が1よりも先である(未来である)場合に新着マークを表示される

1 現在の時間を取得

app/controllers/posts_controller.rb
def index
 @posts = Post.page(params[:page]).per(10)
 @current_time = Time.current
end

indexアクションの1行目は、kaminariを使って既に投稿されている10記事の情報を取得しています。
2行目は、現在の時間を取得しています。Railsでは、2種類のタイムゾーンがあり、様々な現在時間の取得方法がありますが、TimeWithZoneクラスを使う方法が一番良いとされているようなので、Time.currentを使っています。(Time.zone.nowでも同じ)
詳しくは、こちらの記事が参考になると思います。

2 投稿された時間の取得と3 投稿された時間から7日後の時間を取得

app/views/posts/index.html.erb
<% @posts.each do |post| %>
  省略〜
 <% post.created_at.since(7.day) %>         
<% end %>

@postsから1記事ずつ取り出し、その記事が作成された時間(2番)をcreated_atで取得、そしてそこにsinceメソッドを使い、投稿された時間から7日後の時間(3番)を取得しました。
※sinceメソッドの引数を変更すれば、新着マークの表示期間を変更可能

4 新着マークを表示させる

app/views/posts/index.html.erb
<% @posts.each do |post| %>
  省略〜
 <% if @current_time < post.created_at.since(7.day) %> 
    <div class = "new-mark">新着</div>
  <% end %>        
<% end %>

投稿された時間から7日後の時間が現在の時間よりも先であれば(未来であれば)新着マークを表示するようにしたいので、if文を使い、その中に、表示したい内容を入れます。
これで、完成です。
※デザインの部分は省略しています。

まとめ

今回はRuby on Railsで期間限定の新着マークを作ってみました。
おそらく、実際は、cookieを使って実装する方が、新着情報を使って他に機能を作る時など、色々な面で使いやすいと思います。
なので、次回はcookieでの実装にもチャレンジしてみようと思います。

参考文献

RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い

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

ruby silverに合格したのでまとめました(2021年1月)

この記事ってなに?

ruby silverに合格しました。勉強法について誰かのためになるかもしれないのでまとめておきます。

前提

  • 2020年新卒です
    • この時までプログラミング経験ありません
  • rubyの開発は4ヶ月ぐらいです
  • たのしいRuvy一周やったぐらい

勉強したこと

  • Ruby技術者認定試験合格教本
    • ざっと一周した後、巻末のテストを3周しました
      • 間違えてわからないことは本で調べて覚えていく感じ
      • 一回目は5割とかでした。最終的にはほぼ満点でした。
  • RubyExamination
    • 5回か6回ぐらいテストしました
      • 確か最初は7割あるかないかぐらいだったかな
      • 最終的には9割ぐらいでした。

勉強法でのポイント

  • 対策を立てる
    • 他の合格記事をみてみる
  • 知らないメソッド覚える
    • 似たような問題は出るので、よく間違える問題は覚えるぐらいでいいと思います。
    • 破壊的メソッドなのかどうかを確認する
    • 同意の違う名前のメソッドを覚える
  • 回答の選択肢になっているもので、挙動がわからないものはすぐ確認する

ぐらいを意識しながら勉強していました。

1日大体2時間から3時間ぐらいで休みの日は5時間か6時間ぐらいやっていたと思います。
ruby silverに向けての勉強期間は二週間弱だったと思います

試験当日

  • 見直しめっちゃ大事(二問ミスしているのに気が付きました)
  • 知らない問題多かった(このメソッドこの使い方あってる?みたいな問題も多かった)
  • 一方で、やった問題もでた

まとめ

試験に向けて、という明確な目標があってやりやすかったです
誰かの参考になれば幸いです。

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

[rails]繰り返した処理の回数をclassにインデックスとして付与する方法

初めに

eachメソッドでテーブルから取得してきた値に対してそれぞれ違った処理をしたい!ときってありませんか?

例えば、、、

vegtables = [キャベツ,トマト,なす]

がデータベース上のテーブルに保存されていて、これをeachメソッドを使って値を取得し、"トマト"だけ赤色にしたい、boxの形を変更したい時など。

実装中にハマったので共有します。

結 論

each.with_indexメソッドを使う。

詳 細

コントローラーの記述は以下の通り

vegtables_controller.rb
class VegtablesController < ApplicationController
  def index
    @vegtables = Vegtable.all
  end

なお、vegtablesテーブルのveg_nameカラムには
veg_name=[キャベツ,レタス,きゅうり,トマト,なす,ホウレンソウ,タマネギ,ジャガイモ]
が入っています。

これにそれぞれ異なるclassを与えたいとき

index.html.erb
  <% i=0 %>
  <% @vegtables.each.with_index do |v, i| %>
    <div class="wrapper_<%= ++i %>">
      <%= v.veg_name %>
    </div>
  <% end %>

とすることで実装できました!

因みに、scssに書いたclassのnameは
.wrapper_0{}
.wrapper_1{}
.wrapper_2{}
・・・
などです。

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

Railsでgem 'devise'を練習した話

1.何をしたの?

Railsを勉強している最中、ログイン機能に便利なdevise gemに出会ったので、練習がてら2段階認証が必要な簡単なログインアプリを作成することにしました。
作成したアプリはこちらです
https://github.com/yosnak13/Login_APP

devise gemの使い方はこちらの方の記事がとても参考になりました!
https://qiita.com/cigalecigales/items/f4274088f20832252374

2.なぜそうしようと思ったのか。

自作アプリで動くメイラーを実装できないものか悩んでいたためです。

著者はrailsチュートリアルからrailsを学び、ログイン機能とメイラーの実装方法は学習しました。当時(2020年6月ごろ)チュートリアルでは、SendGridと呼ばれるherokuのアドオンを使用してメイラーを実装するようテキストに書かれていました。しかし、実際にアドオンを実行しようと思ってもメイラーが機能せず、アカウント有効化できないかSendGrid社に問い合わせたところ、「お客様が当社のプラットフォームに適していないと判断したため、アカウントを有効化することができません。」と返事がありました。SendGridアドオンは明確な商用目的がないと使用できないようです。

仕方ありません!アカウントを有効化するにあたり5つ質問に答えて欲しいと指示があったのですが、質問内容かなりガチでした(笑)。英語でのやりとりでしたがいい経験になりました。
現在RailsチュートリアルはMailgunというアドオンを使用するようにテキストが変わっていますが、そちらはどうなのかはまだチェックできておりません!

3.やったことを羅列

備忘録を兼ねて実行したことを羅列します

1.rails new LOGIN_APPでアプリ立ち上げ
2.devise gemのインストール
3.rails g devise UserでUserモデル作成
4.db/migrate/XXXXXXXXXXXXXX_devise_create_users.rbに'name'カラムを追加(emailカラムはモデル作成時すでに存在している仕様らしく、今回は'name'のみです)してmigrate
5.rails g devise:views usersでdeviseが管理するビューを作成・編集
6.rails g devise:controllers usersでdeviseが管理するコントローラーを作成・編集
7.route設定
8.ログインページ、パスワード再設定ページ、アカウント作成ページを編集
スクリーンショット 2021-01-23 16.42.25.png
今回CSSなしなのでデザインはもうシンプルにこんな感じです。

9.gmailを使ってメイラーを設定
10.アカウント作成後のリダイレクト先の作成、設定(route, controller, view)
11.ログイン後のリダイレクト先を作成、設定(route, conttoller, view)
スクリーンショット 2021-01-23 16.43.21.png
これで完成です!!ログインしたらログインおめでとうの文字が現れます(笑)

大まかに分けるとこの11工程になりました。

4.メイラーについて

今回はアドオンではなく習ったばかりのgmailのサービスを使用しました。
やり方は1で紹介した記事ほぼ同じやり方してます。
ただそのままgithubにプッシュすると個人情報ばればれなので、dotenv-gemを使って定義した環境変数から呼び出す形式にし、環境変数はgitignoreに記載することで回避しています。
スクリーンショット 2021-01-23 16.47.55.png

開発環境では無事メールが飛んでくれました。以前のチュートリアルの時は飛ばなかったので少し進歩です(笑)
これでrailsチュートリアルの時に抱えていたモヤモヤがすっきりしました!
次回からログイン機構を搭載するなどメールが必要なときはこの方法を使っていこうと思います。

5.deviseの他の機能

deviseには10個のモジュールが搭載されていて、
今回は、ユーザーの作成、編集、削除ができる'Registerable',メールで2段階認証が搭載できる'Confirmable'を使いました。
他の機能は作るアプリによって使うかどうかというところですが、機会があれば使ってみたいですね。

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

Githubを使ってHerokuへデプロイする流れ

メモとして簡易的な流れを記入しておきます。(mac OS)

前提

Herokuへの登録は完了しているものとします。
データベースにはMySQLを使用します。
Ruby on Railsを使用します。
Rubyはバージョン2.6.5

簡易的流れ

下準備

①Heroku CLIのインストール

ターミナル
% brew tap heroku/brew && brew install heroku

確認のためバージョンを確認

ターミナル
% heroku --version

②Herokuへのログイン

ターミナル
% heroku login --interactive 

登録したメールアドレスとパスワードを入力してログインします。

デプロイ方法

①Heroku上にアプリケーションを作成

ターミナル上でデプロイしたいアプリケーションのタスクへ移動し、下記コマンドを実行します。
※既に登録されているアプリケーション名は使用できないので注意!

ターミナル
% heroku create アプリケーション名

下記のコマンドで設定できているか確認できます。

ターミナル
% git config --list | grep heroku

※ fatal: not in a git directoryであれば設定できていません。

②ClearDBアドオンの追加

ターミナル
% heroku addons:add cleardb

③MySQLに対応するGemについて設定を変更する

下記コマンドでClearDBのURLを変数heroku_cleardbに格納します。

ターミナル
% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` 
ターミナル
% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5} 

④Heroku上で環境変数を設定

ターミナル
% heroku config:set RAILS_MASTER_KEY=`cat config/master.key`

下記で確認ができます。

ターミナル
% heroku config

⑤Stackの指定

ターミナル
% heroku stack:set heroku-18 -a アプリケーション名

⑥Githubのmaster情報をHerokuへプッシュ

ターミナル
% git push heroku master

⑦Heroku上でのマイグレーション実行

ターミナル
% heroku run rails db:migrate

公開の確認

ターミナル
% heroku apps:info

上記コマンドで確認したWeb URL: のURLへアクセスして確認ができる。

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

Heroku Profcfileを使わずに使いたい時

Procfileを消すとどうなるか?

基本、herokuを動かす時はProcfileに動かしたいファイルを書くでしょう。
しかし状況によってはHerokuのAdd-onsにある機能を使い、そこで動かしたいファイルを指定すればできる場合もあります。
例えば、Add-onsにあるHerokuSchedulerを使いそこで動かしたいファイルを指定すれば、Procfileを使わなくても、定期的に実行できます。
(https://devcenter.heroku.com/articles/scheduler)

ご参考までに。

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

rails generate scaffoldをしたときに打ち間違いをしてしまったらどうするか

はじめに

Railsチュートリアル第2章で/usersが開けなかったので、原因を調べたら、scaffoldを作成したときに打ち間違いをしていたためでした。

エラーの原因

rails generate scaffold Usser name:string email:string

Userと打ちたかったところをUsserと打ち間違いをしていますね。

エラー解決までの道のり

打ち間違えに気づいたときには、すでにrails db:migrateを実行していたため、まずは変更を取り消しからはじめました。

rails db:rollback

上記のコマンドを実行すると、以下の表示がターミナルに表示されます。

== 20210123005915 CreateUssers: reverting =====================================
-- drop_table(:ussers)
   -> 0.0047s
== 20210123005915 CreateUssers: reverted (0.0122s) ============================

そして、scaffoldを削除します。

rails destroy scaffold Usser

これでrails generate scaffold Usser name:string email:stringを実行する前まで戻ったはずなので、もう一度rails generate scaffoldをします。今度はうち間違えないように慎重に。

rails generate scaffold User name:string email:string

忘れずにmigrateします。

rails db:migrate
== 20210123041232 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0116s
== 20210123041232 CreateUsers: migrated (0.0129s) =============================

無事に表示されました!

さいごに

打ち間違えって怖いですね。一文字でも違っていればアウトですもんね。
でもまぁ確かに、私の名前は「え"ま"」でも「え"も」でもなく「え"む"」ですもんね。
さて、Usserってなんと読むのでしょう。「ゆーずずずぁー」って感じでしょうか。どうでもええがな。

参考にした記事

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

Rails × Ajaxでクリックする度booleanが反転するボタンを実装する

はじめに

Railsで非同期通信をする際、リロードせずにbooleanを反転させたいケースがあり、やり方がわからず時間を費やしてしまったので自戒の意味を込めて記事に残させていただきます。

やりたいこと

↓画像のようなボタンを作り、非同期でbooleanが反転するようにしたい。
スクリーンショット 2021-01-22 9.13.23.png

完成版パラメーター

【ボタンクリック1回目】

Parameters: {"user_groups"=>{"subscribed"=>"false"}}

【ボタンクリック2回目(非同期なのでリロードはしない)】

Parameters: {"user_groups"=>{"subscribed"=>"true"}}

ルーティング・コントローラー・ビュー

前提

以下の画像のようにuserとuser_groupは1対多の関係になっています。今回boolean反転させるのはuser_groupsテーブルのsubscribedカラムです。
スクリーンショット 2021-01-23 8.27.43.png

ルーティング

今回はユーザーが複数所属するグループが『グループ単位でメルマガを購読するかどうか』を変更するオプション(そんなのあまりないかもしれませんが、例としてです・・)をユーザー側で変更できる実装をする前提で、update_optionsというアクション名にします。

update_subscribed等の名前の方が良いかもしれませんが、今後同じような操作をするボタンを他にも作ることを想定して共通化された名前としています。
また今回使用するのはPATCHアクションのみなので、ルーティングもPATCHの部分のみ記載しています。

routes.rb
resources :user_groups do
  member { patch 'update_options' => 'user_groups#update_options' }
end

コントローラー

user_groups_controller.rb
class UserGroupsController < ApplicationController
  before_action :set_group

  def index #set_groupで定義した@groupをviewファイルで使用
  end

  def update_options
    @group.update_attributes(user_group_params)
  end

  private

  def user_params
    params.require(:user_group).permit(:subscribed)
  end

  def set_group
    @group = current_user.user_group.find_by(id: params[:id])
  end
end

ログイン中のユーザーは以下でcurrent_userとして定義しています。

shared.rb
def current_user
  @current_user ||= User.find(session[:user_id]) if session[:user_id]
end

ビュー

スタイリングは本題とは外れるので大変恐縮ですが今回は触れずに行きます。

index.slim
= link_to update_options_user_path(user_group: {subscribed: !@group.subscribed}), remote: true, method: :patch do
 .btn
  | 変更

以上で大体実装ができましたが、このままだとボタンを複数回押した際にパラメーターが切り替わりません。
実際に押してみると、

【ボタンクリック1回目】

Parameters: {"user_groups"=>{"subscribed"=>"false"}}

【ボタンクリック2回目】

Parameters: {"user_groups"=>{"subscribed"=>"false"}}

このように非同期でbooleanが反転されません。もちろんリロードすれば成功します。

解決法①

原因は、index.slimの= link_to部分がボタンを押した際に書き換えられていないので、パラメーターに変化がありませんでした。
ですので、update_optionsアクション側でDBの値を得て変更させれば『非同期で複数回クリックしてもboolean値が切り替わる』ことは実現できました。

user_groups_controller.rb
class UserGroupsController < ApplicationController
  def update_options
    @group.update_attributes(subscribed: !@group.subscribed)
  end
end
index.slim
= link_to update_options_user_path, remote: true, method: :patch do
 .btn
  | 変更

このようにすれば、クリックを押すたびにDBのboolean値が切り替わります。
しかし、ユーザーが同時に複数人ログインしていてこのボタンを一斉に押されると、DBの値がユーザーの意図しない値に書き換わってしまうことも考えられるので、やはり『パラメーターの値によってDBの値が変更される』方が良さそうです。

解決法②(私はこちらを採用しました)

こちらの方法ではJavaScriptを使って= link_toのvalue(今回はURL部分のみ)部分を、クリックするたびに書き換える実装をします。

index.slim
= link_to update_options_user_path(user_group: {subscribed: !@group.subscribed}), id: "change-subscribed-link", remote: true, method: :patch do
 .btn
  | 変更

まずは先程のslimファイルの= link_to部分にidを付与します。

index.slim
id: "change-subscribed-link"

としました。
次にupdate_optionsアクションが走った時にJavaScriptが走るようにしたいので、update_options.js.erbファイルをindex.slimと同ディレクトリに作成し、以下をファイルに記載します。

update_options.js.erb
$("#change-subscribed-link").attr("href", "<%= update_options_user_path(subscribed: {has_filed: !@group.subscribed}) %>")

このようにすることで、複数回クリックしても非同期でbooleanのパラメーターが反転するようになります。
こちらのコードではjQueryのセレクターで該当する= link_toを持ってきて、中身のURL部分だけを上書きしています。

少し見にくいですが、検証ツールでどういう挙動なのか確認できます。
スクリーンショット 2021-01-23 8.44.18.png
このaタグのhref部分をupdate_options.js.erbファイルで書き換えたということです。

【ボタンクリック1回目】

Parameters: {"user_groups"=>{"subscribed"=>"false"}}

【ボタンクリック2回目(非同期なのでリロードはしない)】

Parameters: {"user_groups"=>{"subscribed"=>"true"}}

パラメーターも無事反転しました!
また今後別のボタンを作り、booleanのみを切り替える動作をさせたい場合は、update_optionsアクションを使用すれば良いので、一からルーティング、コントローラーを追加する必要もなくなりました!

さいごに

誤っている箇所がありましたらご指摘いただきたく存じます。
また、他にも何か良いやり方等ありましたらご教示いただければ幸いです!

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

【Rails】ルーティングヘルパー

はじめに

ポートフォリオの作成をし、メンターさんにフィードバックをいただいた点があったので今後の為に書き留めておきます。
改善目標として
・Railsっぽいコード(レールに乗ったコード)を目指す。つまり、Railsが用意している便利な機能を活用する。

・ルーティングは以下のように設定しています。

config/routes.rb
resources :blogs

Before

app/views/blogs/edit.html.erb
<h1>ブログ編集</h1>
    <%= form_with model:@blog, url:"/blogs/#{@blog.id}", local: true do |f| %>
      <h4>タイトル</h4>
      <%= f.text_field :blog_title %>
      <h4>画像</h4>
      <%= f.attachment_field :image %>
      <h4>本文</h4>
      <%= f.text_area :blog_text, size: "40x10" %>
      <%= f.submit "編集を保存", class: "btn btn-primary" %>
    <% end %>

改善すべきは2行目の「url:"/blogs/#{@blog.id}"」
このようにurlをベタがきしていると、urlを変更したくなった時に修正箇所がとても多くなってしまう。
そこで、ルーティングヘルパーという機能でこのコードを改善します。

やったこと

Terminal
$ rails routes
                    blogs GET    /blogs(.:format)                                                               blogs#index
                          POST   /blogs(.:format)                                                               blogs#create
                 new_blog GET    /blogs/new(.:format)                                                           blogs#new
                edit_blog GET    /blogs/:id/edit(.:format)                                                      blogs#edit
                     blog GET    /blogs/:id(.:format)                                                           blogs#show
                          PATCH  /blogs/:id(.:format)                                                           blogs#update
                          PUT    /blogs/:id(.:format)                                                           blogs#update
                          DELETE /blogs/:id(.:format)                                                           blogs#destroy

パスとヘルパーメソッドの関係はターミナルから「rails routes」と打って確認できます。
左がヘルパーメソッドに渡すコードで右がそれによって返されるパスという認識です。

After

app/views/blogs/edit.html.erb
<h1>ブログ編集</h1>
    <%= form_with model:@blog, url: edit_blog_path(@blog.id), local: true do |f| %>
      <h4>タイトル</h4>
      <%= f.text_field :blog_title %>
      <h4>画像</h4>
      <%= f.attachment_field :image %>
      <h4>本文</h4>
      <%= f.text_area :blog_text, size: "40x10" %>
      <%= f.submit "編集を保存", class: "btn btn-primary" %>
    <% end %>

最後に

Railsが提供している機能を知り、レールに乗ったコードが書けるように精進します:fist::fire:

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

Couldn`t find Item(Model) without an IDの解決

はじめに

個人でアプリを開発していてわかったことを今回記事にしました。
状況によっては異なる場合がございます。

開発環境

pc: macOS Catalina
shell: zsh
rails: 6.0.0
ruby: 2.7.2

エラー内容

スクリーンショット 2021-01-23 11.10.16.png

controller処理でfindメソッドがIDがないと警告を出しています。
itemテーブルの情報を取得する記述をしています。

仮説

①ルーティングの影響により記述が間違えていることによりパラメーターのIDを取得することができない
②ビューのURLに引数として指定されたIDが引き渡しができていないことでIDがないと言っている。

結論

今回のエラーの解決方法にはルーティングのネストの影響が関係していました。
ルーティングをネストすることで、IDが変わってしまうと言うことです。

routes.rb
Rails.application.routes.draw do
  devise_for :users

  root to: "items#index"

  resources :items do
    resources :orders , only: [:index, :create]
  end
end

% rail routes

        root GET    /                                            items#index
 item_orders GET    /items/:item_id/orders(.:format)             orders#index
             POST   /items/:item_id/orders(.:format)             orders#create
       items GET    /items(.:format)                             items#index
             POST   /items(.:format)                             items#create
    new_item GET    /items/new(.:format)                         items#new
   edit_item GET    /items/:id/edit(.:format)                    items#edit
        item GET    /items/:id(.:format)                         items#show
             PATCH  /items/:id(.:format)                         items#update
             PUT    /items/:id(.:format)                         items#update
             DELETE /items/:id(.:format)                         items#destroy

解説

items controllerでURIは/items/:id(.:format)となっています。
そのため、@item = Item.find(params[:id])とすることでIDが取得することができます。
しかし、Prefixのitem_ordersをみてみるとURIには/items/:item_id/orders(.:format)となっており
IDが変化してしまっています。ですので上記のように記述をしてしまうと、エラーを引き起こしてしまうのです。
@item = Item.find(params[:item_id])とすることでルーティングのURIのIDが一致し取得することができます。

ここから、ネストすることの意味も把握する必要があるのだなと実感しました。

参考文献

・ Ruby on Rails       著:小餅 良介
・ 達人に学ぶDB設計徹底指南書  著:ミック

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

form_withを使って、ユーザーが新規作成される仕組みを理解する

webサービスで新規登録やユーザログインのときに使われる、form_withというヘルパーメソッド。

Railsチュートリアルなどで突然登場して、なんとなく使い方が分かってるけど説明はできない「曖昧状態」になっていませんか。

ぼくもまさにそのひとり。

今回は、form_withを使って、ユーザーが新規作成される仕組みを理解する記事を書いていこうと思います!

そもそも「フォーム」とは?

  • nameとvalueがペアになってサーバに送られる

  • nameとvalueはinputやselectやradioなどに付与される

  • action属性に指定されたURLに対して入力値が送られる

引用記事:
[https://qiita.com/DaichiSaito/items/cd66115569b0a75f1bfa#11-%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%A8%E3%81%AF%E3%81%8C%E6%9B%96%E6%98%A7]

この3つをはじめてみたとき???という感じでした。

例を挙げて説明してみます。

railsチュートリアルから以下のコードを引っ張ってきました。

<%= form_with(model: @user, local: true do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.submit "Create" %>
    <% end %>

これがHTMLソースに変換されると

<form accept-charset="UTF-8" action="/users" class="new_user"
      id="new_user" method="post">

  <label for="user_name">Name</label>
  <input id="user_name" name="user[name]" type="text" />

  <input  name="commit" type="submit"
         value="Create" />
</form>

※local: trueとは?
local: trueがない場合、Rails5ではAjaxによる送信という意味になる。
HTMLとしてフォームを送信する場合にlocal: trueが必要になります。

例えば、フォームの名前に「たろう」と入力されると

<input id="user_name" name="user[name]" type="text" value="たろう" />

このようになります。

そしてformタグのaction属性に指定されたURL("/users" )に対して、nameとvalueが送られます。

コントローラ

@user = User.new(params[:user])

params[:user]というのは、送られてきたnameに対応しています。今回であればname="user[name]" value="たろう"が送られてきているので、@userというインスタンスにname:"たろう"が格納されます。

form_withのしていること

<%= form_with(model: @user, local: true do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

整理すると、

  • model: @user・・・入力値を格納するインスタンスとaction属性を指定("/users")

  • local: true・・・HTMLとしてフォームを送信する場合にlocal: true必要

  • <%= f.label :name %>・・・フォームのラベルを決めている

  • <%= f.text_field :name %>・・・HTMLに変換されるとname="user[name]" type="text"になる

何が起きているかわからないとなってしまうform_withですが、
HTMLコードに分解して考えると、理解しやすいですね。
ブラウザの開発者ツールからHTMLソースを見る癖をつけていきましょう。

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

ruby のLoadErrorでつい忘れがちなこと

よく出るこういうエラー

Traceback (most recent call last):
2: from /Users/****/.**/**.rb:5:in `'
1: from /Users/
/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/Users/
***/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- parseconfig (LoadError)

今回はparseconfigというgemファイルをそもそもインストールしていなかったのが原因でした

解決策

gem install parseconfig -v <バージョン名>

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

[Ruby]文字列と整列の変数展開に関して(+英語)

こんばんは、たにーです。

今回は、Twitterでもつぶやいたことがある
Rubyにおけるメソッド(文字列と整数の変数展開)に関してです。

これはややこしいものかなと思うけども、個人的には
英語として分解して理解すればよりすっと理解できるのではと思います。

いつもですが、なにか略をみた際に、
なぜその英語表記なのか(略されているのか)と私は気になってしまいます。笑

文字列と整数の変数展開

学習した変数展開は、二つあります。
①.to_s
②.to_i

それらの意味(使い方)と英語で分解してみると、下記の通りになります。

メソッド 機能
.to_s  整列 → 文字列へ to string (意味:文字)
.to_i 文字列 → 整列へ to integer (意味:整数)

参考例

.to_s に関して

string.rb
age = gets.to_s
puts "私の年齢は、#{age}歳です。"

to_iに関して

※こちらに関して例が思いつきませんでした。
 もし何かあればコメントいただけると嬉しいです。
 今後勉強していく中で、あった際にはまだ記事を書こうかと思います。

個人的には、、

英語として分解してみると、だからそういった表記になるのか!と納得できると思いますし、
変な文字列だったものが、理解できるものへと変わっていくと思います。
そうなれば、きっとプログラミング言語も少しは苦じゃなくなると思います。

以上、たにーでした。

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