- 投稿日:2020-02-26T22:53:59+09:00
ビューファイルを編集してカラムのデータを表示の仕方
テーブルをposts、カラムをtextとした場合
app/views/posts/index.html.erb<% @posts.each do |post| %> <%= post.text %> <% end %>となる
これをするとtextのカラムの指定をしてtextのデータだけ持って来てくれる
これをやらないと複数のデータを一度に表示しようとしたためにエラーが出る
- 投稿日:2020-02-26T22:24:42+09:00
ActiveRecord クラスとは、データをコントローラーで取得
テーブルから情報を取得するために必要なメソッドを兼ね備えたクラス
| メソッド | 用途 |
|:-----|:-----|
| all | テーブルの全てのデータを取得する |
| find | テーブルのレコードの内、ある1つのデータを取得する |
| new | クラスのインスタンス(レコード)を生成する |
| save | クラスのインスタンス(レコード)を保存する |
などがある
データをコントローラーで取得、ビューファイルでテーブルの単一データ表示の場合、、、テーブル名がposts(モデルはpost)としてapp/controllers/posts_controller.rbdef index @post = Post.find(1) # 1番目のレコードを@postに代入 endとなる
全てのデータを取得できるようにするにはapp/controllers/posts_controller.rbdef index @posts = Post.all # 全てのレコードを@postsに代入 endとなる
module名は、頭文字大文字、単語の境界に「アンダーバー」無しで、ファイル名は、頭文字小文字、単語の境界に「アンダーバー」有りという命名規則があるためこの命名規則に従っていないと、適切に読み込めない
名称 例
コントローラ名 posts
コントローラクラス名 PostsController
ファイル名 posts_controller.rb
- 投稿日:2020-02-26T21:35:09+09:00
【Rails】細かいあれこれ
- 投稿日:2020-02-26T21:25:35+09:00
【Rails devise】Deviseの実装周りのあれこれ
Deviseの実装周りのあれこれ
1.Gemのインストール
commandgem 'devise'commandbundle install2.deviseファイルの生成
commandrails g devise:install3.deviseのビューファイルの生成
commandrails g devise:views4.deviseのモデルの生成
commandrails g devise User(ここは任意です!Adminとかでもおけです)5.deviseのテーブルの生成
commandrails db:migrate参考:
https://github.com/heartcombo/devise以上!
- 投稿日:2020-02-26T21:25:25+09:00
マイグレーションファイル、カラムの「型」とは
マイグレーションファイルは、テーブルの設計図・仕様書。db/migrate/20XXXXXXXXXXXX_create_XXXXX.rbのマイグレーションファイルを編集する。
create_table :posts do |t|
t.timestamps
の間にt.カラムの型 :カラムを必要なものを入れる
t.text :tite
t.string :text
t.text :image
など、、、
カラムの「型」は
|:-----|:-----|:-----|
| 型 | どのような型か | 用途 |
| integer | 数字 | 金額、回数など |
| string | 文字(短文) | ユーザー名、メールアドレスなど |
| text | 文字(長文) | 投稿文など |
| boolean | 真か偽か | はい・いいえの選択肢など |
| datetime | 日付と時刻 | 作成日時、更新日時など |
を参考にして記述する。
このままではデータベースにテーブルを作成はまだできてないので、ターミナルでrails db:migrateを実行する。Sequel Proでテーブルを作成できていればテーブルの完成
- 投稿日:2020-02-26T21:08:48+09:00
モデルとは、モデルの作成
モデルは、Railsの中でデータベースへのアクセスをはじめとする情報のやりとりに関する処理を担当している。簡単な解釈で書くとコントローラー=>モデル=>データベース、データベース=>モデル=>コントローラーと受け渡しになってるが、コントローラーは日本語、モデルは通訳、データベースは英語みたいに考えるとわかりやすいかも
モデルの作成はターミナルでrails g model モデル名で作られる。モデルを作るとVs codeの中にdb/migrate/ディレクトリに20XXXXXXXXXXXX_create_XXXXX.rbが作られる。
- 投稿日:2020-02-26T21:01:51+09:00
【Rails】最短でRailsにFont-awesomeを導入する
- 投稿日:2020-02-26T20:53:59+09:00
【Rails binstubs】Rails sできない時
Rails sできないとき
この記事では
・Rails sできない
・なにやらエラーにbinstubsという単語があるという方のエラー解決に役立てればと思います!
1.binとやらを消す
commandrm -rf ./bin2.binディレクトリを刷新!
commandrake app:update:bin #railsのバージョンが5.2以上の方はこちら rake rails:update:bin #railsのバージョンがそれ未満の方はこちら参考:
https://stackoverflow.com/questions/7546869/problem-running-rails-s
- 投稿日:2020-02-26T20:46:31+09:00
SEしてるけど実はあんまりコード書いたことないんだよねって人に捧ぐ、Rails on Dockerハンズオン vol.9 - Sign in -
はじめに
第9回目です。
前回はサインアップ機能を作ってみましたね。今回はサインインやサインアウト機能を作ってまいります。
今後、サインインしていないと使えない機能や見れないページなども作っていきますので、アプリがユーザーのサインイン状況を知ることができてサインインしているユーザーを特定できるようにしましょう!セッション
RailsはRESTfulなWebアプリケーションフレームワークなのでステートレスなアプリです。
しかし、サインイン機能を実装するとなるとそのユーザーがサインイン状態であることをアプリが知る必要がありますし、アプリはそのユーザーがどのユーザーなのかを特定する必要があります。
このために使われるのがセッション(Session)です。Railsではセッションを管理する便利な
session
メソッドが用意されています。
今回はこのsession
メソッドを使ってサインイン状態を管理し、マイページ機能、つまりサインインしているユーザーのユーザー詳細ページに遷移する機能を作っていきましょう。
サインインしていない場合はユーザーを特定できないので、マイページには遷移させずトップページにリダイレクトをかけるようにもしていきます。このように、セッションでサインイン状態を管理できるようになれば、その状態によるページや動作の出しわけやサインインユーザーに合わせたコンテンツの出しわけが可能になります。
今回はセッションを一つのリソースと捉えて実装していきます。
つまり、サインインしたときにsessions#create
でセッションを作成したり、サインアウトしたときにSessions#destroy
でセッションを削除したりさせます。サインインページを作ろう
セッションはSessionsコントローラーで管理してきます。
アクションとしては、
new
: サインインページに遷移create
: セッション作成(サインイン処理)destroy
: セッション削除(サインアウト処理)が必要になってきます。
今回はrails g
コマンドに頼らずにディレクトリやファイルを作成していきますね。ルーティングを作成する
まずはルーティングを作成しましょう。
config/routes.rbRails.application.routes.draw do ... get '/sign_in', to: 'sessions#new', as: :sign_in post '/sign_in', to: 'sessions#create', as: :create_session delete '/sign_out', to: 'sessions#destroy', as: :sign_out endこの辺りはもう慣れてきましたよね。
コントローラーを作成する
お次はコントローラーを作ります。
# touch app/controllers/sessions_controller.rbapp/controllers/sessions_controller.rbclass SessionsController < ApplicationController def new @user = User.new end def create end def destroy end endとりあえずルーティングと対応したアクションを記述しておきます。
new
アクション、つまりサインインページではまたUserモデルを扱います(ユーザーのサインイン情報を入力してもらうので)。
そのため、new
アクションはUsersコントローラーと同じように@user = User.new
を記述してます。サインインページを作成する
さて、
new
アクションがレンダリングするnew.html.erb
をコーディングしてきましょう。まずは、Sessionsコントローラーのビューファイルを格納するディレクトリを作成し、ビューファイルを作りましょう。
# mkdir app/views/sessions # touch app/views/sessions/new.html.erbさて、ビューファイルをコーディングしてみます。
app/views/sessions/new.html.erb<div class="container"> <h1 class="my-5">Sign in</h1> <%= form_with model: @user, url: create_session_path, local: true do |form| %> <div class="form-group"> <%= form.label :email %> <%= form.text_field :email, class: "form-control" %> </div> <div class="form-group"> <%= form.label :password %> <%= form.password_field :password, class: "form-control" %> </div> <div class="form-check"> <%= check_box_tag :visible_password, :visible, false, class: "form-check-input" %> <%= label_tag :visible_password, "パスワードを表示する" %> </div> <div class="form-group mt-5"> <%= form.submit "Sign in", class: "btn btn-primary form-control" %> </div> <% end %> <p class="text-center">登録がまだの方は<%= link_to "こちら", sign_up_path %></p> </div> <%= javascript_pack_tag 'visible_password' %>基本的には前回のサインアップページと同じですね。今回はサインインなので
password
を入力項目に指定しました。
また、password
はサインアップページと同様、チェックボックスで表示非表示を変更できるようにしています。『Sign in』ボタンの下にまだサインアップが終わっていないユーザー向けにサインアップページへのリンクを追加しています。
<%= link_to "こちら", sign_up_path %>
だけで適切なa
タグを作成してくれるのはやはり便利ですね。一度
http://localhost:3000/sign_in
にアクセスしておきましょう。以下のようなページが表示されたら、ここまでのコーディングは成功です!
サインインページへのリンクを作る
サインインページの形が出来上がってきたので、ここでサインインページへのリンクを以下のページにつけていこうと思います。
- ヘッダーの「Sign in」リンク
- サインアップページの下部に「登録済みの方はこちら」リンクを設置
ヘッダーの「Sign in」リンク
ヘッダーの「Sign in」リンクはまだ遷移先を指定できていませんでしたね。
やっとサインインページができあがってきたので遷移先としてsign_in_path
を指定しておきます。app/views/layouts/application.html.erb... <li class="nav-item"><%= link_to "Sign in", sign_in_path, class: "nav-link" %></li> ...コーディングが終わったら、一度トップページに遷移してヘッダーの「Sign in」リンクを選択してみましょう。
サインインページに遷移できたら成功です!サインアップページの下部に「登録済みの方はこちら」リンクを設置
サインインページで「登録がまだの方はこちら」リンクを設置したように、サインアップページにも「登録済みの方はこちら」リンクを設置してみましょう。
app/views/users/new.html.erb... <div class="form-group mt-5"> <%= form.submit "Sign up!", class: "form-control btn btn-primary" %> </div> <% end %> <p class="text-center">登録済みの方は<%= link_to "こちら", sign_in_path %></p> ...こちらもサインアップページで「こちら」をクリックしてサインインページに遷移できれば成功です!
サインイン処理を作成する
ここまででサインインページの形ができあがりました。
次はサインイン処理(create
)をつくっていきます!サインイン処理ではサインインページで入力された
password
からユーザーを検索します。
ユーザーがヒットすればそのユーザーのユーザー詳細ページ(users#show
)、ユーザーが存在しない、またはパスワードが誤っているような場合はエラーメッセージが表示されるようにしましょう。少しおさらいですが、モデルを検索する場合は
find_by
メソッドを使うことで指定した属性に対して検索をすることができました。
さらに、has_secure_password
をもつモデルはauthenticate
メソッドを使ってパスワード認証ができることも思い出しましょう。
この2つを組み合わせることでユーザーの検索し認証することができそうですね。では、
create
アクションを記述していきます。app/controllers/sessions_controller.rb... def create @user = User.new(email: params[:user][:email]) user = User.find_by(email: @user.email.downcase) if user && user.authenticate(params[:user][:password]) flash[:success] = "サインインしました。" redirect_to user else flash.now[:danger] = "#{User.human_attribute_name(:email)}または#{User.human_attribute_name(:password)}をもう一度確認してください。" render :new end end ...少し複雑に見えるかもしれません。1行ずつ解説していきますね。
@user = User.new(email: params[:user][:email])まず、
@user
に新しくUserモデルを代入しています。属性値はparams[:user][:email]
を持っています。
params
はフォームからのリクエストの値を取得するメソッドで、フォームのinput
タグでname="user[email]"
と定義されている値は[:user][:email]
から取得することができます。
@user
にparams[:user][:email]
の属性値をもつUserモデルを代入しているのは、この処理がエラー(ユーザーが見つからない or パスワード認証が通らない)の場合に今入力した
これがないと入力したuser = User.find_by(email: @user.email.downcase)先ほど
@user
に設定した
Userモデルを作成するときにモデル側で
今DBには必ず全て小文字のメールアドレスが保存されているので、find_by
でdowncase
で検索文字列を小文字化して検索しています。
find_by
は検索対象が存在していた場合はモデルオブジェクトを返却し、存在しない場合はnil
を返却するメソッドであることも改めて意識しましょう。if user && user.authenticate(params[:user][:password]) # trueの処理 else # falseの処理 end次に条件分岐を設けています。
&&
は「アンド条件」、「かつ」を意味していますので、
user
user.authenticate(params[:user][:password])
の両方を満たした場合は
true
、どちらか一方でも満たさない場合はfalse
の処理に分岐します。まず
user
の条件式をみてみます。
これはuser
がnil
やfalse
でないかどうかを検証しています。
先ほどfind_by
は検索結果があればモデルオブジェクト、なければnil
を返却するといいました。
なのでこのuser
の条件はfind_by
の結果そのfalse
の処理を実行するようにするための条件ということになります。次に
user.authenticate(params[:user][:password])
の条件式をみてみます。
これは順番的にuser
の条件式を満たした場合に検証される条件式です。なのでuser
にはUserモデルのモデルオブジェクトが入っています。
authenticate
メソッドはhas_secure_password
の便利機能の一つで、平文のpassword
を与えるとハッシュ化してDBに格納しているpassword_digest
と照会し、同一であればモデルオブジェクトを、そうでなければfalse
を返却してくれるものでした。以上より、このif文の条件式の条件分岐は以下の通りになります。
入力した パスワードが 実行する処理 いない - falseの処理 いる 誤っている falseの処理 いる 正しい trueの処理 やりたいことに合致してますね。
ではtrue
の処理、false
の処理をそれぞれみてみましょう。flash[:success] = "サインインしました。" redirect_to user
true
の処理はとても単純です。
検索し認証したユーザーのユーザー詳細ページにリダイレクトさせているだけです。flashでサインインメッセージも定義してますね。flash.now[:danger] = "#{User.human_attribute_name(:email)}または#{User.human_attribute_name(:password)}をもう一度確認してください。" render :new
false
の処理も最終的にはrender :new
で再度サインインページを表示させていることがわかります。
その前にflash.now
メソッドを使ってエラー文を用意しています。
前回のUsersコントローラーのcreate
アクションでもflash
メソッドを使ってサインアップ成功時のメッセージをユーザー詳細ページに一時的に表示させることをしました。
flash.now
はそれのrender
時に利用する版と思ってください。key
をdanger
にしているのは前回と同じでBootstrapの命名規則に則っています。
flash.now
のメッセージの中身が若干複雑ですね。
まず、Rubyでは文字列の中に変数を入れる場合#{変数}
と記述することができます。
"aaa" + 変数 + "bbb"
のような書き方もできますが、"aaa#{変数}bbb"
の方がよりスマートな気がしますよね。
その変数はUser.human_attribute_name(属性名)
となっています。
human_attribute_name
メソッドは引数の属性のi18nで定義した文字列を返してくれます。ちょうどform.label
と同じような感じですね。
なので今回の場合、User.human_attribute_name(:email)
は『メールアドレス』、User.human_attribute_name(:password)
は『パスワード』が変数の結果として当て込まれます。ここまででサインイン処理(
create
アクション)をみてきました。
最後にflash.now
のメッセージを表示するコードが今のsessions/new.html.erb
にはないので、前回のusers/new.html.erb
に倣って記述します。app/views/sessions/new.html.erb... <h1 class="my-5">Sign in</h1> <% flash.each do |msg_type, msg| %> <div class="alert alert-<%= msg_type %>"><%= msg %></div> <% end %> <%= form_with model: @user, url: create_session_path, local: true do |form| %> ...動作確認
では、サインイン処理を動作確認してみましょう。
前回DBをリセットしてますので、もう一度John Smith
さんをDBに登録しておきます。> User.create(name: "John Smith", email: "john@sample.com", password: "password")
http://localhost:3000/sign_in
にアクセスして色々とチェックしてみましょう!エラー系
password
を入力していない
何も入力していない、またはどちらかだけでも入力していない場合はfind_by
かauthenticate
のいずれかが必ず失敗するのでエラーメッセージが表示されていますね。入力した
test@test.com
みたいな適当な
エラーメッセージが表示されているし、test@test.com
が残っているのも確認できましたね。
password
の組み合わせが異なっている
john@sample.com
と適当なパスワードjohn1234
を入力して確認してみましょう。
こちらもエラーメッセージが表示されていますね。john@sample.com
も残っています。正常系
期待動作は
john@sample.com
&password
でユーザー詳細ページに遷移することです。
やってみましょう!
ちゃんと期待通りの動作になりましたね。セッション管理する
ここまででパスワード認証のロジックができあがりましたね。
ただ今のままでは、「認証」自体はできましたが「認証済み」という状態を管理することはできてません。
「認証済み」という状態を管理するためにセッション管理をする機能を作っていきます。ちなみにRailsの
session
メソッドでは、半永続的で暗号化された一時Cookieが払い出されます。
これは仮にこのCookieが盗まれたとしても、それを使ってサインインを乗っ取ることはできないようになっています。
その代わりと言ってはなんですが、一度ブラウザを閉じると自動的に消える仕組みになっています。SessionsHelperを作成する
セッション管理するために
sign_in
メソッドやsign_out
メソッドなどを自前で実装していきます。
これらはコントローラーの元になるapplication_controller.rb
にコーディングしていきたいところですが、それではapplication_controller.rb
に多くのメソッドを記述する必要がでてくるため可読性が確保できなくなる恐れがあります。今回は、新たにSessionsHelperを作成し、その中で
sign_in
メソッド、sign_out
メソッドを定義します。
このSessionsHelperをApplicationControllerが読み込んでコントローラー内でメソッドを利用できるようにすることで、可読性を損なうことなく機能を実装していきましょう。まずSessionsHelperファイルを作成します。
# touch app/helpers/sessions_helper.rbapp/helpers/sessions_helper.rbmodule SessionsHelper endヘルパーは
module [helper_name]
で定義します。
最後に、ヘルパーをApplicationControllerから読み取るように定義しましょう。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper endSessionsHelperで
sign_in
メソッドを作成する次に、SessionsHelperを編集して、
sign_in
メソッドを作成していきます。
Railsではsession
メソッドが用意されており、簡単にセッションを管理することができます。app/helpers/sessions_helper.rbmodule SessionsHelper def sign_in(user) session[:user_id] = user.id end endこれだけで、
sign_in
メソッドを使ってuser.id
をセッションに記録することができます。サインインページでサインインが成功した場合に
sign_in
メソッドを呼び出してセッションを記録するようにしてみます。app/controllers/sessions_controller.rb... def create ... if user && user.authenticate(params[:user][:password]) flash[:success] = "サインインしました。" sign_in user redirect_to user else ... end ...
redirect_to
の前にsign_in user
を追加しただけです。
SessionsControllerはApplicationControllerを継承しているのでApplicationControllerで読み込んでいるSessionsHelperを利用することができます。
current_user
メソッドを作成するここまででサインインしたときにセッション情報としてサインインユーザーの
id
をCookieに格納できるようになりました。
今度はこれを使って、サインイン中のユーザー情報をアプリが特定するためのcurrent_user
メソッドを作成しましょう。
このcurrent_user
メソッドを使うことで、例えば<%= current_user.name %>
でサインイン中のユーザーの名前を表示できるようにしたりします。今、セッションに
user_id
のキーに対してuser.id
を格納しています。これはsession[:user_id]
で取り出すことができます。
このことからUser.find(session[:user_id])
またはUser.find_by(id: session[:user_id])
でサインイン中のユーザーを特定できることを思いつきますね。
ただ、find
メソッドはNotFoundの場合に例外が発生します。find_by
メソッドの場合はNotFoundでもnil
を返却するだけなのでif文などで簡単にエラー時の処理を実装できるのですが、例外の場合はちょっとクセがありますね。
今回はfind_by
メソッドで実装するようにしましょう。では、SessionsHelperに定義します。
app/helpers/sessions_helper.rbmodule SessionsHelper ... def current_user @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id] end ... end少しつまずきそうなコードですね。ちょっと紐解きます。
まずわかりやすいところから、一番後ろに
if session[:user_id]
と記入しています。
これはこれよりも前の文章が実行される条件式になっています。session[:user_id]
が存在する、つまりサインイン済みの状態であればこれよりも前の文章が実行されるというわけです。
current_user
メソッドではこの1行以外にコードはないので、session[:user_id]
が存在しない場合は何も実行されず、nil
が返却されるようになります。次に見慣れない
||=
をみてみてましょう。
これは見慣れないと思いますが、少しプログラミングをかじったことがある人であれば何かの言語で+=
のような表現を見たことがあるのではないでしょうか?
Rubyでも同じような挙動をとりますが、a += b
はa = a + b
と同義になりますね。
||
はORを表す演算子ですので、a ||= b
はa = a || b
と同義になります。
current_user
は上で少しお話したように、<%= current_user.name %>
、<%= current_user.email %>
のように使われるケースを考えています。
これが同じビューファイルで2回呼び出された場合、User.find_by(id: session[:user_id])
とだけコーディングしていた場合は2回ともDBから情報を取得しなくてはならなくなります。
そこで@current_user ||= User.find_by(id: session[:user_id])
としておくことで、一度目に<%= current_user.name %>
時に@current_user
がセットされるため二度目の<%= current_user.email %>
の場合はDBにアクセスすることなく前回の検索時のモデルオブジェクトを再利用することができるようになります。
DBの負荷軽減やDBアクセスの回数削減によるレスポンス向上を目的として、こういった記述にしてみました。まとめると、
current_user
メソッドは呼び出されたときに以下の動作をします。
サインイン ユーザー 処理 未 - nil
済 NotFound nil
済 Found サインイン済みユーザーのモデルオブジェクト 書き方を変えると以下と同じですね。
def current_user if session[:user_id] if @current_user @current_user else User.find_by(id: session[:user_id]) end end endKStep取れそうですね。笑
上の書き方の方がシンプルで可読性高いですよね。サインイン状態を確認する
signed_in?
メソッドを作るどんどんいきましょう。
次にサインイン状態をtrue
、false
で返却するsigned_in?
メソッドを作ってみます。
このメソッドによってユーザーのサインイン状態に合わせた処理を簡単に実装することができます。先ほどの
current_user
メソッドはユーザーがサインインしている場合はそのユーザーのモデルオブジェクト、サインインしていない場合はnil
を返却するメソッドでした。
そしてRailsにはnil
の場合true
を、そうでない場合false
を返却するnil?
メソッドが用意されています。
さらに!
はtrue
とfalse
が入れ替わる否定演算子です。これらを組み合わせれば
signed_in?
メソッドが作れそうですね。app/helpers/sessions_helper.rbmodule SessionsHelper ... def signed_in? !current_user.nil? end ... endサインアウトする
sign_out
メソッドを作成する最後にサインアウトするメソッドとして
sign_out
メソッドを作ってみましょう!app/helpers/sessions_helper.rbmodule SessionsHelper ... def sign_out session.delete(:user_id) @current_user = nil end ... endなんとなーくわかるとおもいますが、
session.delete(:user_id)
でCookieに保存していたsession[:user_id]
を削除してます。
さらにサインイン中は@current_user
にサインインしているユーザーのモデルオブジェクトが格納されているのでこれもnil
に初期化しています。さてさて、ここまででサインインに関して最低限必要な全てのメソッドを作成できました。
サインインの状態に合わせてヘッダーを出しわけしてみましょう!ヘッダーを更新する
ヘッダーを以下の条件で出しわけしてみます。
- 未サインイン
Home
: トップページに遷移するSign in
: サインインページに遷移する- サインイン済
Profile
: サインインしたユーザーのユーザー詳細ページに遷移するSign out
: サインアウトしてトップページに遷移するまずは、ヘッダーを
signed_in?
メソッドを使って出しわけしてみます。app/views/layouts/application.html.erb... <ul class="navbar-nav"> <% if signed_in? %> <%# サインイン済みの場合のリンク %> <li class="nav-item"><%= link_to "Profile", current_user, class: "nav-link" %></li> <li class="nav-item"><%= link_to "Sign out", sign_out_path, method: :delete, class: "nav-link" %></li> <% else %> <%# 未サインインの場合のリンク %> <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li> <li class="nav-item"><%= link_to "Sign in", sign_in_path, class: "nav-link" %></li> <% end %> </ul> ...今までのヘッダーは未サインインの場合のリンクになっていたので、
signed_in?
がfalse
の場合の方に記述してます。サインイン済みの場合は新たに「Profile」と「Sign out」のリンクを作っています。
<li class="nav-item"><%= link_to "Profile", current_user, class: "nav-link" %></li>「Profile」リンクの方は今までとほぼほぼ変わらない書き方ですね。少し違う点としては
root_path
、sign_in_path
のように遷移先としてpath
を指定するのではなくcurrent_user
というモデルオブジェクトを指定している点です。
link_to
メソッドでは、遷移先としてモデルオブジェクトが指定された場合、そのモデルオブジェクトの参照パスに読み替えてくれます。つまり、user_path(current_path)
と同義になります。<li class="nav-item"><%= link_to "Sign out", sign_out_path, method: :delete, class: "nav-link" %></li>「Sign out」リンクの方は
method: :delete
の箇所が今までとは異なります。
通常link_to
メソッドはGET
メソッドでリンク先にリクエストしますが、method: [method]
で指定することで別のメソッドでリクエストすることができます。
sign_out_path
はroutes.rb
でdelete
メソッドでルーティングするように定義していましたので、この形でlink_to
のHTTPメソッドを指定してあげないとルーティングされなくなってしまいます。そういえば、
sign_out_path
でルーティングされるsessions#destroy
について、コーディングをしていませんでした。
セッションを削除するsign_out
メソッドを実行し、トップページにリダイレクトするようにコーディングしましょう。app/controllers/sessions_controller.rb... def destroy sign_out redirect_to root_path end ...ここまでで、サインイン機能、サインアウト機能、ヘッダーの出しわけについてコーディングが完了しました。
ここで一度動作確認をしていきましょう。サインイン機能、サインアウト機能、ヘッダー出しわけの動作確認
サインインしていない場合
まずサインインしていない状態で
http://localhost:3000
にアクセスしてみましょう。
すでにサインインしてしまっている場合は、ブラウザを閉じるか、「Sign out」リンクを押すかしてサインアウトしましょう。この状態ではヘッダーは「Home」リンクと「Sign in」リンクが表示されています。
「Home」リンクを選択するとトップページへ、「Sign in」リンクを選択するとサインインページに遷移することも確認できますね。
サインインしている場合
では、サインインページから
john@sample.com
でサインインしてみましょう。
すると、john@sample.com
のユーザー詳細ページに遷移し、ヘッダーも「Profile」リンクと「Sign out」リンクに変わっていることが確認できます。
また、「Profile」リンクを選択することで
john@sample.com
のユーザーのユーザー詳細ページに遷移できることも確認できますね。サインアウトを試してみる
最後にサインアウトが正しく動作するか確認してみましょう。
サインインしている状態で、「Sign out」リンクをクリックしてみてください。トップページに遷移して、ヘッダーが未サインイン状態の場合のヘッダーに戻っていることが確認できるはずです。
これで、想定どおりに動作していることが確認できましたね。
サインアップした時もサインイン状態になるようにする
今のままではサインアップした後にサインインをしないといけなくなり面倒です。
サインアップ時(users#create
)もサインイン状態になるように更新します。app/controllers/users_controller.rb... def create @user = User.new(user_params) if @user.save sign_in @user flash[:success] = "サインアップありがとう!" redirect_to @user else render :new end end ...
if @user.save
の下にsign_in @user
を追加しました。
これでDB保存が成功した場合に、そのユーザーでサインイン状態になるようになりました。では、新たにユーザーをサインアップページで作成して、サインイン状態でプロフィールページに遷移することを確認してみましょう。
プロフィールページに遷移してますし、ヘッダーのリンクからサインイン後の状態になっていることがわかりますね。サインイン状態のときに遷移できないページを作る
また、サインインしたあとにサインアップページやサインインページに遷移することは必要ありませんね。(むしろ禁止したい。)
さらに、サインイン後はトップページにもアクセスする必要はなく、ルートパスにアクセスしようとした場合、プロフィールページ(サインインユーザーのユーザー詳細ページ)に遷移させるようにしたいかもしれません。これらを実装してみましょう。
ApplicationControllerにサインインしている場合、強制的にプロフィールページに遷移させるメソッドを作成します。
app/controllers/application_controller.rb... def redirect_to_profile_if_signed_in redirect_to current_user if signed_in? end ...これを、トップページ(
static_pages#home
)、サインアップページ(users#new
,users#create
)、サインインページ(sessions#new
,sessions#create
)にルーティングされた直後(アクションが処理される前)に実行されるようにします。
これはbefore_action
メソッドを使えば容易です。app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController before_action :redirect_to_profile_if_signed_in def home end endapp/controllers/users_controller.rbclass UsersController < ApplicationController before_action :redirect_to_profile_if_signed_in, only: [:new, :create] ... endapp/controllers/sessions_controller.rbclass SessionsController < ApplicationController before_action :redirect_to_profile_if_signed_in, only: [:new, :create] ... endStaticPagesControllerは
home
アクションしかなかったので、コントローラー全体にbefore_action
を適用しました。
UsersControllerとSessionsControllerはonly
オプションをつけて対象のアクションの実行前にのみredirect_to_profile_if_signed_in
メソッドが呼び出されるようにしました。サインイン状態にして、「トップページ」「サインアップページ」「サインインページ」にそれぞれダイレクトアクセス(URL直打ち)してみてください。
全てプロフィールページにリダイレクトされます。また、念のためサインアウトした場合は「トップページ」「サインアップページ」「サインインページ」にそれぞれアクセスできることも確認しておきましょう!
確認できましたね?
では、今日はここまでにしておきましょう!後片付け
いつものように、次回に向けてデータを消します。
$ docker-compose down $ docker-compose run --rm web rails db:migrate:resetDBコンテナが立ち上がった状態だと思うのでdownさせます。
$ docker-compose downまとめ
今回はセッションを利用してユーザーのサインイン機能、サインアウト機能を作ってみました。
さらにヘッダーの出しわけや、特定のページアクセス時にサインインしている状態だとプロフィールページにリダイレクトされる機能を作ってみました。
すでにお気づきとは思いますが、今回作成したredirect_to_profile_if_signed_in
メソッドと逆のことをすればサインインしていないと遷移できないページを作り出すことも可能ですし、条件をsigned_in?
ではないものにすれば、特定の条件のユーザー(例えばadmin
フラグを持っているユーザーとか)しかアクセスできないページを作り出すことも可能です。それにしても、色々と動作確認することも増えてきましたね...
このまま毎回手で目で確認していくのもしんどそうです。次回はこれを解決するためにTDD/BDD、そしてテスト自動化に取り組んでみます!
では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
Reference
- Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
- Ruby on Railsのparamsメソッドの使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン
- Ruby 当たり前のtrue/false - Qiita
- [WIP/初学者]flashとflash.nowの使い分け - Qiita
Links
・ Vol.1 - Introduction -
・ Vol.2 - Hello, Rails on Docker -
・ Vol.3 - Scaffold, RESTful, MVC -
・ Vol.4 - Static pages -
・ Vol.5 - Model and CRUD -
・ Vol.6 - Model validation -
・ Vol.7 - Secure password -
・ Vol.8 - Sign up -
・ Vol.9 - Sign in - ?この記事
- 投稿日:2020-02-26T20:01:34+09:00
【Rails Devise】ログインユーザーのみ、内容を見れるようにするには
ログインユーザーのみ、内容を見れるようにするには
Devise使用時に、ヘッダーやログアウトボタンなどの内容をログインユーザーのみに表示させたい場合などに、
ご参考にして頂ければと思います!めちゃめちゃ簡単です!
html.erb<% if user_signed_in? %> <%# この間に書かれた内容はログインユーザーのみ見れる %> <% else %> <%# この間に書かれた内容はログインしていないユーザーのみ見れる %> <% end %>こんな使い方!
html.erb<% if user_signed_in? %> <li><%= link_to 'LOGOUT', destroy_user_session_path, method: :delete %></li> <% else %> <%= link_to "ログイン", new_user_session_path, class: 'post' %> <%= link_to "新規登録", new_user_registration_path, class: 'post' %> <% end %>これでログインユーザーだけがログアウトできて、ログインしていないユーザーだけが新規登録やログインができます!
- 投稿日:2020-02-26T19:31:20+09:00
画像一覧表示で指定の枠内でスクロールさせる方法
現在スクールにてプログラミングを学習中
目標は4週間の間でフリマアプリを完成させる開発環境
rails 5.2.4.1
ruby 2.5.1初学者向けに記述してます
アウトプット練習の為でもあります。実装したいこと
画像を指定の範囲内でスクロールさせる。完成イメージ
https://i.gyazo.com/55340c88a724b2e46eee6ed3254fbef6.mp4
写真は動物ですがあくまでテストイメージです。
予めご了承ください。実装にあたりhamlの記述は終えているものとします。
書きにはSCSSの記述のみ載せます。まずはスクロールさせたいブロックの枠のクラスに下記を記述(?部分)
&__lists { width: 800px; padding: 26px 0; margin: 0 auto; display: flex; ?overflow-x: scroll;
次にスクロースさせたい子要素の.scssに記述を追記
.item_list { ?min-width: 250px; height: 245px; color: #000000; background-color: #ffffff; display: inline-block; position: relative; margin: 0 10px;widthにmin-を追加する記述で最低の枠の大きさを固定でき
指定した大きさの枠内でクスロールしてくれる。これでスクロール完了です。
- 投稿日:2020-02-26T18:50:23+09:00
Railsでゲームを作成してバックエンドを学ぶ① アクセス(通信)する
はじめに
これは、普段Web系やAndroidなどフロントエンド中心に開発を行っており、びっくりするほどバックエンドのことが分からなかった筆者が、バックエンドの知識を付ける目的で、Railsで◯×ゲーム(三目並べ)を作成した時の記録である。
いくつかの段階に分けて取り組んだので、記事も分けることにする。①アクセス(通信)する
今回はWebサーバにアクセスして画面を表示させられるようにするところまで行う。
実現したいこと
Webサーバにアクセスして、ブラウザに画面を表示させる。
ここではとりあえず、Hello, Worldなどの何かしら簡単なオブジェクトが表示されているようにする。実現するうえでの不明点
- Webサーバへのアクセスの仕方が分からない(忘れた)
→手順としてはrails server
でサーバを立ち上げてからブラウザでアクセスするだけ
Railsチュートリアルの1.3に全部書いてある。開発手順
ほぼ全てRailsチュートリアルの1.3に則って進めた。
Railsサーバの起動
Railsアプリケーションのディレクトリで
rails server
する(rails s
というエイリアスあり)。
自分は別マシンにssh接続して開発しているため、-b
オプションでホストを指定している。
$ rails s -b <ssh先のホスト>
rails server
コマンドを打つと、PumaというWebサーバが起動する( https://railsguides.jp/command_line.html#rails-server )。$ rails s => Booting Puma => Rails 5.0.7.2 application starting in development on http://localhost:3000 => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.1 (ruby 2.6.2-p47), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stop画面の表示
上記でサーバを起動したので、ブラウザからアクセスする。
デフォルトはlocalhost:3000
で、指定したホスト(-b
オプションで指定できる)やポート番号(-p
オプション)に適宜書き換える。
アクセスするとデフォルトのRailsページが表示される。Hello, Worldより遥かに豪華画面に別の物を表示させる
上記で接続した画面に"Hello, World!"を表示して原始的にすることにする。
アクション追加
コントローラに「文字列を描画する」というアクションを追加する。
app/controllers/application_controller.rb
にhello
アクションを追加した。def hello render html: "Hello, World!" endアクションを使う
上記で作成した
hello
アクションを使うようにして、実際に画面に表示されるようにする。
config/routes.rb
にroot 'application#hello'
('<コントローラ名>#<アクション名>'
)を追加する。
これで晴れて"Hello, World!"が画面に表示されるようになった。
エラーや詰まった箇所
Gem::LoadError
rails server
を起動してブラウザでアクセスすると、Gem::LoadError
というエラーが発生した。
sqlite3
というgemの最新バージョン(1.4.x)がActiveRecordに対応していないことによるエラーらしい。
sqlite3
のバージョンを指定してインストールしなおすことで解消した。参考: sqlite3のgemでGem::LoadErrorが出てしまう
ActiveRecord::ConnectionNotEstablished
上記のgemのエラーを解消したらこのエラーが発生した。
アプリがActiveRecordを使わない設定で作られていたのが原因らしい。
config/application.rb
にrequire 'active_record/railtie'
を追加すると解消した。参考: Railsで ActiveRecord::ConnectionNotEstablished: No connection pool with 'primary' found. がでたら確認すること。
補足
Railsアプリケーションの作成
rails new
する。自動的にbundle install
もされる。Gitの導入
git init
する。
git add
とgit commit
をするとローカルにmaster
ブランチができるので、あとはいつもGitを使っている感じでブランチを切ったりできる。まとめ
- Webサーバにアクセスして、自分で指定した文字列を画面に表示させた。
- Railsが頑張っているおかげでWebサーバを容易に利用できる。
- 投稿日:2020-02-26T18:44:13+09:00
RSpecはここから始めよう
Everyday Rails - RSpecによるRailsテスト入門を読みました。
まずはここから始めようと思ったことをまとめます。導入方法はまた別記事で。
用語
spec:テストファイル
example:spec内に書かれているテスト1つ1つspecの種類
モデルスペック、コントローラースペック、フィーチャースペック、システムスペック、レスポンススペックの5種類があります。特に重要なのは以下の3つです。
モデルスペック:バリデーションや、メソッドの単体テスト。
システムスペック:統合テスト。ユーザーの動作を再現する。手動テストを自動でやる感じ。
昔はフィーチャースペックが使われていたけど、今はこっちが推奨されてるみたいです。
実行に時間がかかります。レスポンススペック:主に外部APIとの通信をするテストで使われます。
specファイルの生成
モデルスペック
$ bin/rails g rspec:model userシステムスペックは手動で作成
$ mkdir spec/system $ touch spec/system/users_spec.rbrequest spec
bin/rails g rspec:request user
基本文法
まず
it '<テスト名>' do ~ end
のブロックの中にデータのセットアップや検証したい項目を書きます。it 'can have many notes' do #検証したい内容が入る end次に
expect(<検証したい項目>).to <どうなっていて欲しいか>
を書きます。英語の文法と同じ語順なのでとても読みやすくて気に入っています。it 'can have many notes' do project = ... #データのセットアップなど expect(project).to be_valid end以下のコマンドでテストを実行します。
$ bin/rspec . . Finished in 3.12 seconds (files took 0.93752 seconds to load) 1 examples, 0 failuresテストデータの生成
各テスト内で
User.new
などで生成しても全然オッケーなのですが、めんどくさいので、FactoryBot
というgemを使います。$ bin/rails g factory_bot:model userspec/factories/users.rbFactoryBot.define do factory :user do name { 'Sample Tarou' } email { 'test@example.com' } password { 'mypassword1234' } end endあとはテスト内に
FactoryBot.create(:user)
と書くだけで、上記のユーザーのインスタンスを生成することができます。spec/models/user.rbit 'is valid with a name, email, and password' do user = FactoryBot.create(:user) expect(user).to be_valid end共通のテストデータ
let
を利用することで、1つのファイル内で共通で使うテストデータを作成することができます。spec/factories/users.rblet(:user) { FactoryBot.create(:user) } it 'is valid with a name, email, and password' do expect(user).to be_valid end it 'is Sample Tarou' do expect(user).to eq 'Sample Tarou' end上の例では
let
を使いuser
という変数の中にファクトリに定義したユーザーのインスタンスを格納しました。exampleをグループ分けする
describe
、context
を使って、exampleをグループ分けすることで、可読性をあげることができます。describe 'search message for a term' do context 'when a match is found' do it 'returns notes that match the search term' do . . end end context 'when no match is found' do it 'returns an empty collenction when no result are found' do . . end it 'returns an empty collenction when serach conditions are invalid' do . . end end endシステムスペックとcapybara
システムスペックを書く上で
capybara
というgemが必要です。
インストールすると、capybaraのDSL(独自の言語)が使えるようになります。
以下のvisit
、click_link
、fill_in
、click_button
などがDSLです。非常に直感的で気に入っています。specs/systems/projects_spec.rbit 'signs in' do visit root_path click_link 'Sign In' fill_in 'Email', with: user.email fill_in 'Password', with: user.password click_button 'Log in' expect(page).to have_content 'ようこそ Sample Tarou 様' endshoulda-matchers
shoulda-matchers
というgemを使うと、macher(be_validとか)を拡張することができ、特にモデルスペックのバリデーションのチェックで力を発揮します。it 'is invalid without a first name' do user = FactoryBot.build(:user, first_name: nil) user.valid? expect(user.errors[:first_name]).to include("can't be blank") endこのバリデーションチェックのテストが
it 'is invalid without a first name' do it { is_expected.to validate_presence_of :first_name } endこのように1行で検証できるようになります。
デフォルトで使えるマッチャーについては以下の記事を参照
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」メソッドを切り出す
複数箇所で共通する処理は、メソッドとして切り出すことができる。
ファイルの一番下に書くことが多い。def go_to_project(name) visit root_path click_link name end複数のファイルで共通する処理は
specs/support
ディレクトリにファイルを作る。spec/support/login_support.rbmodule LoginSupport def sign_in_as(user) visit root_path click_link "Sign in" fill_in "Email", with: user.email fill_in "Password", with: user.password click_button "Log in" end end #以下のように記述することで、各ファイルでincludeする必要がなくなる RSpec.configure do |config| config.include LoginSupport end明示的に読み込みたい場合には
include LoginSupport
のように記述する。終わりに
他にもたくさんTIPS的なものがありますが、全部いきなり全部意識するのは無理なので、まずはこの記事にまとめたテクニックでどんどんテストを書いていきます。
参考
- 投稿日:2020-02-26T18:17:59+09:00
Docker Compose でホストとクライアントの両方にて Rails を起動したいそこのあなた!
なにこれ?
あんまりいないとは思うけど、Rails で Docker Compose を使用しているときにホスト側とクライアント側で Rails server を起動したいときはありませんか?自分の場合だと、System Test がどうしても Docker 環境のみで動作させる方法が見つけ出せなかったので、自分の場合 Rails を Ubuntu 環境でシステムテストを走らせて、それ以外は Docker 内のコンテナで動作させたいと思ったのでこういう環境が必要になった。そんなことをしようとしていると困るのが、(Rails のルート)/tmp/ に作られるファイルがホスト側とクライアント側で競合して、同時に起動できないという問題だった。そんなときに読んで解決できる糸口となったのが「DockerでVolumeをマウントするとき一部を除外する方法」だったので、自分もココにその軌跡をのこしておくことにする。
なにするの?
要は Rails 同時に起動できない原因は tmp フォルダが存在するからである。こいつさえなければ、ポートの競合とかはともかく、起動できるのだ。要はお互いが疎な関係になればいい。つまり、クライアントとホストの tmp フォルダが同期しなければいい。以下のコードは tmp フォルダだけはホストと動悸しないようにしてある。
version: '3' volumes: rails_tmp_data: services: as: build: context: ./ ports: - 3333:3000 volumes: - ./:/usr/src/app/ - rails_tmp_data:/usr/src/app/tmp/ command: /bin/sh -c "yarn install --check-files && bundle install && rails db:create && rails db:migrate && rm -f /usr/src/app/tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0' --early-hints"なお上記のコードは、要点だけに絞っている。そのため、コピペでは動かないのは、予めご了承いただきたい。
これでおしまい
ポートの設定を端折ってしまった。そこらへんは、ココでは記事にしない。途中で記事を書くのがめんどくさくなったからね。ググるかなにかしてください。そんじゃねー。
タイトルについて
スタパ斉藤マジリスペクト、というわけでなく、冗談抜きでこんなタイトルしか思いつかなかった。
- 投稿日:2020-02-26T17:48:19+09:00
rails でお問合せフォームを作り、自分のメールアドレスに送信させる方法
railsで簡単にお問合せフォームを作り自分のメールアドレスに送信させる
railsに標準搭載のaction mailerを使用して簡単にお問合せフォームを実装させる。
まずはじめに、お問合せ内容を保存するためのInquiryモデルを作ります。
Inquiryモデルは、nameカラムと、messageカラムを持ちます。
$ rails g model inquiry name:string message:string $ rails db:migrate次に、action mailer を作成していきます
$ rails g mailer inquiryこのinquiryの名前に特に意味はないので、好きな名前でやってもらって大丈夫です。
実行されると、このようにいくつかのファイルが作成されます
Running via Spring preloader in process 32746 create app/mailers/inquiry_mailer.rb invoke erb create app/views/inquiry_mailer invoke test_unit create test/mailers/inquiry_mailer_test.rb create test/mailers/previews/inquiry_mailer_preview.rb次に、メールの送信機能を実装するため、app/mailers/inquiry_mailer.rbに追記をしていきます。
inquiry_mailer.rbclass InquiryMailer < ApplicationMailer # -----追記------ def send_mail(inquiry) @inquiry = inquiry mail( from: 'system@example.com', to: 'manager@example.com', subject: 'お問い合わせ通知' ) end # -----追記ここまで---- endこちらで使用できるメソッドはこちらになります。
オプション できること from 送信元メールアドレス subject メールの件名 to メールの送信先アドレス cc ccのメールアドレス bcc bccのメールアドレス メール本文のレイアウトを作成する
メール本文のレイアウトを作成するためには、命名規則に従ってファイルを作成します。
.app/views/メイラー名_mailer/メイラークラスのメソッド名.text.erb
今回の場合は
app/views/inquiry_mailer/send_mail.text.erb
を作成しましょう。そして中に
send_mail.text.erb<%= @inquiry.name %> 様 から問い合わせがありました。 ・お問い合わせ内容 <%= @inquiry.message %>を追記しましょう。
メールをプレビューで確認する機能
次に、実際にお問合せフォームを送る前に自分のお問合せ内容を確認するプレビュー機能を作成していきます。
action mailerに元から搭載されている機能なので簡単にできます。まず
test/mailers/previews/inquiry_mailer_preview.rb# Preview all emails at http://localhost:3000/rails/mailers/inquiry_mailer class InquiryMailerPreview < ActionMailer::Preview # ----追記----- def inquiry inquiry = Inquiry.new(name: "侍 太郎", message: "問い合わせメッセージ") InquiryMailer.send_mail(inquiry) end #----追記ここまで---- endそしてここでターミナルで
$rails sをし、サーバーを起動させます。
そして
http://ホスト名:3000/rails/mailers/inquiry_mailer
にアクセスしてみてください。大体の方は
http://localhost:3000/rails/mailers/inquiry_mailer
ですね。
すると
Inquiry Mailer
・inquiryと表示されますので
inquiryをクリックしましょう。と言う画面が出てきます。
今回はformatをHTMLに変えているので皆様の画面より少し文字が大きめに書かれていますが問題ないです。Gmailでメールを実際に送信してみましょう
まずメールサーバーを設定します。
config/environments/development.rbconfig.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, domain: 'gmail.com', user_name: '<gmailのメールアドレス>', password: '<gmailのパスワード>', authentication: 'plain', enable_starttls_auto: true }をファイル内のどこでもいいので追記してください。(※一番上のclass名~一番下のendの間でお願いします)
メールを送信するために、自分のメールアドレスとパスワードを書き加えます。
config/environments/development.rbconfig.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, domain: 'gmail.com', #-----変更点--------------------------- user_name: 'aaaaa@gmail.com', password: 'aaaaaaaaaa', #----変更点ここまで----------------------- authentication: 'plain', enable_starttls_auto: true }そしてこちらのファイルにも自分のメールアドレスを書き加えます
inquiry_mailer.rbclass InquiryMailer < ApplicationMailer def send_mail(inquiry) @inquiry = inquiry mail( from: 'system@example.com', #----------変更点-------------- to: 'aaaaa@gmail.com', #----------変更点ここまで--------- subject: 'お問い合わせ通知' ) end endここまできたら、あとは確認のため、rails コンソールからメール送信を実行します。
$ ご自身のファイルディレクトリにいく $ rails c irb(main):001:0> inquiry = Inquiry.new(name: "侍 太郎", message: "問い合わせメッセージ") irb(main):002:0> InquiryMailer.send_mail(inquiry).deliver_nowすると、自分のメールアドレスにメールが送信されたかと思います。
ですが、セキュリティの関係上警告メールが出る場合が多くあります。
これは憶測ですが、コードにそのままアドレスやパスワードを貼っているのが原因なので、この部分を環境変数に変えて、隠す方法を試してみてください。
そちらに関連する記事はこちらです
以上で終了です。
皆様できましたでしょうか?
- 投稿日:2020-02-26T17:17:07+09:00
image画像を一覧表示させる
画像の一覧表示の実装について実装
現在、プログラミングスクールにてフリマアプリを5名で実装中
少し手間取ったホーム画面の一覧表示の実装手順を載せます。
フロント実装は完了済みとします。開発環境
rails 5.2.4.1
ruby 2.5.1マークアップはhamlで行いました。
※アウトプットの為記述
※初学者向けに記述します完成イメージ
※載せている写真は動物ですがあくまでテスト画像として載せているだけです。
予めご了承ください。まずはコントローラの記述から
今回は一覧の表示をしたいので対象のapp/controllers/home_controller.rbを編集home_controller.rb
class HomeController < ApplicationController def index @images = Image.all @items = Item.all end end
今回はフリマのitemテーブルとimageテーブルのデータを取得できるように一旦記述
これでアイテムテーブルとimegeテーブルのデータをホーム画面に持ってきます。
roteはすでにできている為、今回は追記はありませんでしたが、一応載せておりますroutes.rb
Rails.application.routes.draw do devise_for :users root "home#index" resources :items, only: [:new, :create, :show, :edit, :destroy] resources :sendings, only: [:new, :create] resources :users, only: [:edit] resources :cards, only: [:new, :create, :index, :destroy] resources :orders, only: [:index, :new, :create] do collection do get 'index', to: 'orders#index' post 'pay', to: 'orders#pay' get 'done', to: 'orders#done' end end end次にhamlに追記
.main__item__category %h2.main__item__category__title ピックアップカテゴリー .main__item__category__item_box = link_to "#", class: "main__item__category__item_box__title" do 新規投稿商品 %ul.main__item__brand__item_box__lists %li ?- @items.each do |item| ? = link_to item_path(id: item.id) ,class: "main__item__brand__item_box__lists--list, item_list" do - ft_image = item.images.first = image_tag ft_image.photo.url, class: "item_list__picture" .item_list__body %h3.item_list__body__name = item.name %ul %li.item_list__body__price = item.price = "円" %li.item_list__body__likes = icon 'fas', 'star' 0 %p (税込)今回はitemテーブルに紐づいているimageの最初のデータを取得したかった為、上記の?のように追記いたしました。
この記述でデータを取得し表示まではできました。
あとはスクロールで画像がうまくスクロールするようにいたしました。
長くなりますのでスクロールは別の記事に載せたいと思います。
- 投稿日:2020-02-26T16:41:02+09:00
【やってみた】未経験エンジニアのPay.jp導入(Ruby on Rails)
まずはじめに
Pay.jpとは
シンプルなAPIと豊富なライブラリで簡単にクレジットカード決済を導入できます。
ApplePayに対応していたり定期課金を組むことができます。
今回は開発環境での実装をイメージしているので後ほど出てくる各鍵はテストキーを使用しています。導入
Pay.jpの登録
まずはアカウントを取得をします。
登録完了しログインすると下記のようになります。gemのインストール
Gemfilegem 'payjp' ```:fist_tone1::point_up: **インストール**bundle install
```環境変数の設定
まず、プロジェクト直下に.envファイルを新たに作成し、以下のように編集しましょう。
PAYJP_PUBLIC_KEY='自身のアカウントのテスト公開鍵' PAYJP_SECRET_KEY='自身のアカウントのテスト秘密鍵'そしてこの.envファイルをGithubにあげないよう .gitignoreに下記追記をします。
.gitignore/.env
controller
支払い機能を管理するコントローラーを作成しpayアクションを定義します。
私の場合はPurchasesControllerを作成し記載しております。app/controllers/purchases_controller.rbdef index end def pay Payjp.api_key = ENV["PAYJP_SECRET_KEY"] Payjp::Charge.create( amount: params[:amount], card: params['payjp-token'], currency: 'jpy' ) endCharge.createで売り上げ作成処理をしているのかと思います。
amountは売り上げ金額
(ここではviewから取得したparams[:amount]を売上高とします。)、
cardはクレジットカードのトークンを使っての決済
を表し、
currencyは 'jpy'とすることで日本円での決済
としています。また、indexに「カードで支払う」ボタンを設置します。
ルーティング
payアクションのルートを追記します。
route.rbresources :purchases post 'purchases/pay' => 'purchases#pay'view
ここまできたらあとはビューに支払い用のモーダルを設置する処理をするだけですね。
app/views/purchases/index.html.haml= form_with local: true, url: purchases_pay_path do |form| = form.number_field :amount %script.payjp-button{"data-key" => "自身のアカウントのテスト公開鍵", src: "https://checkout.pay.jp", type: "text/javascript"}なんとこれだけでindex.html.hamlに表示される「カードで支払う」ボタンを押すだけで下記のようなモーダルが出てくるんです。
忘れずに支払い完了後のviewも用意します。
app/views/purchases/pay.html.haml支払いが完了しました = link_to 'Topへ', root_pathここまで完了したら実際に試してみましょう!
index.html.hamlに記載した「カードで支払う」ボタンを押して、モーダルを開き必要な情報を入力します。
カード番号はあらかじめPay.jpで用意しているテスト用の共通カード番号'4242 4242 4242 4242'、
有効期限は現在から未来の日付、CVC番号は3桁の適当な数字、名前も適当な名前を入力して「カードで支払う」ボタンをクリック。すると下記のようにpay.html.hamlの画面になれば完了です。
※CSSを当ててないのでシンプルですがご了承ください。実際にPay.jpにアクセスし売り上げの画面を見て、支払い済みとなっていれば問題ないです。
さいごに
今回は開発環境についてですが、本番環境の際はもう少し考慮する点はあるかと思います。
が、皆さんならやってくれると信じてます。ここから発展して定期課金などはまたの機会でご紹介できればなんて思ってます。
ありがとうございました。
- 投稿日:2020-02-26T15:27:16+09:00
Railsチュートリアルメモ - 第8章
第7章からだいぶ日にちが空いてしまった。。。
気を取り直して最後まで完走していく第8章 基本的なログイン機構
8.1 セッション
cookieを利用したセッションの実現方法についての章
ポイント
- ユーザー登録と違い、名前付きルートではなく、個別指定でルーティングを指定する
- flash変数の設定後の画面表示をredirect_toではなく、renderにするとflashメッセージが消えないが、flash.nowにすると次のリクエストでメッセージが消える
8.2 ログイン
ポイント
- Railsで事前定義済みのsessionメソッドが利用できる
session[:user_id] = user.id
User.find(session[:user_id])
を使うと、userが存在しないときに例外が発生するので、User.find_by(id: session[:user_id])
を使用する- helperにログイン用のメソッド、ログイン情報取得用のメソッドを追加し、どこからでも呼び出せるようにする
- 余計なDBアクセスを避けるため、
||=
を使用して、非ログインのときのみDBアクセスが発生するようにするtest/fixtures/
の中のymlに定義することで、都度DBに登録しなくても、DB登録ユーザーのようにユーザー情報のテストを行うことができる- ヘルパーメソッドはテストから呼び出せない
8.3 ログアウト
ポイント
ログアウトでやることは①sessionから:user_idを消す②@current_userインスタンス変数を消す(nilで上書く)の2つ
def log_out session.delete(:user_id) @current_user = nil end8.4 最後に
割愛
- 投稿日:2020-02-26T15:27:12+09:00
ActiveRecord以外で型キャストを使う
ActiveRecordのカラムのように、"0" を false に変換したり、"2020/02/26 12:30" をTimeオブジェクトに変換したりする方法です。フォームから受け取った値をActiveRecord以外のクラスで処理するときなどに使えます。
環境: Rails 5.1, 5.2, 6.0
ActiveModel::Type::Valueのサブクラスをnewしてcastメソッドを呼び出します。true/falseにしたければ、ActiveModel::Type::Boolean です。
[false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]
はfalse、それ以外はtrueになります。ただし、nil と "" は nil になります。また、Rails 5.2.4 と 6.0.0 以降では、
[:"0", :f, :F, :false, :FALSE, :off, :OFF]
も falseになるよう修正が入っています。attr_reader :draft def draft=(val) @draft = ActiveModel::Type::Boolean.new.cast(val) end日付時刻型(Timeクラス)にしたいときは、ActiveModel::Type::DateTime を使います。
Time.parse(val)
とすると書式が不正なときに例外が発生しますが、Railsの型キャストではnilになります。attr_reader :published_at def published_at=(val) @published_at = ActiveModel::Type::DateTime.new.cast(val) endほかにどんな型が使えるのかは、Railsのソースの activemodel/lib/active_model/type 下を参照してください。
https://github.com/rails/rails/tree/master/activemodel/lib/active_model/type
- 投稿日:2020-02-26T14:53:36+09:00
Ruby on rails個人的備忘録
はじめに
私がRuby on Railsについてまとめたいと思い書いた備忘録です。
読む価値はあまりありません。Ruby on Railsでアプリ作ってみたいと思い手を出しました。
が、流れが段々掴めなくなってきたので基礎から私なりに整理して書いていきます。色々端折って書いてます。
Ruby on Rails
そもそもRuby on Railsとはなんやねんってところから始めます。
Ruby on Rails(以下Rails)とはWebアプリケーションを作成する際の骨組みを作ってくれるものです(フレームワークというらしいです)。
RailsではMVCアーキテクチャという設計方法でプログラムを書いていきます。こんな感じです。
Mとはモデル(Model)といいDBのデータを扱うオブジェクトです。Vはビュー(View)といいHTMLなどをテンプレートとしてブラウザに返すものです。Cはコントローラ(Controller)といいモデルからのデータを受け取ってビューに渡すものです。以下に図を示します。ナンバリングは気にしないで下さい。
モデルの必要性として、DBに不正な値が紛れ込まないかなどをチェックすることができアプリケーションのセキュリティ性を高めることが出来ます。
コントローラはデータに対して各処理を施したり、ビューが返す適切なテンプレート(画面の表示方法みたいなもの?)を選択したりします。これらはコントローラ内のアクションというもので行います。
このように、Webアプリケーション内で細かくロールを決めることで作業効率を上げることができ、またプロジェクト全体の可読性も高くなります。まずは基本から
ここから先は、私が学んだRailsに関する知識とかについて綴っていきます。プログラムを書くことは少ないのでご了承下さい。
どんなものを作るか
何作るか考えてないのにものが作れるわけありません。適当に考えてみます。なんか表示させたいだけとかでもいいです。大事なのは仕様を決めることです。どのコントローラとアクション、モデル、ビューで仕様を実現するか考えることです。使わなくてもいいものもありますし、複数使うこともあるかも知れません。ここでは、こう考えます。考え方については私自身手探りで行ってますので正しくないかも知れません。
まず、画面に何を出すか決めます。今回はおなじみの"Hello World!"を表示することにします。トップページに表示するので、コントローラ名はTop
にします。アクションはindex
とします。Hello World!
を表示するだけなので、DBは使わなそうですね。今回はモデルは使いません。
ビューは使います。Hello World!
をどんな文体で表示させようかなとか、中央に配置させようかなとか考えます。ルーティングの設定
ルーティング(routing)とはWebブラウザに送られてきたリクエスト(GETとかPOSTとか)をどのコントローラのどのアクションに対して任せるかという関連付けを行うところです。これをしないとWebのトップページすら作れません(多分)。
ここではroot
というメソッドを使います。root
でどのコントローラのどのアクションで行うかを設定します。
アプリのトップページを表示するようなアクションはこのように書きます。routes.rbRails.application.routes.draw do root "top#index" end
#
の左側でコントローラ名を、右側でアクション名をしています。
コントローラもアクションも定義していないので、次はこれを書きます。コントローラ作成/アクション定義
Ruby on Railsではコントローラを1から書かなくても、コマンドを打つことである程度の部分までコントローラを作ってくれます。モデルも作ってくれます。ターミナルでコマンドを打ち込みコントローラ
top
を作成します。コントローラはtop_controller.rb
というスネークケースで保存されます。クラス名はTopController
のようにキャメルケースで書かれます。
このファイルはapp/controllers/に置かれます。top_controller.rbclass TopController < ApplicationController def index render action: "index" end end
render
メソッド(Railsには様々なメソッド(関数)があります。いきなり出てきて「知らねー」という気持ちになりますが堪えましょう)はHTMLを生成するメソッドです。action: "index"
は「index
アクション用のテンプレートを用いて下さい」という意味になります。
index
というアクションはtop_controller.rb
に書かれたアクションですね。これに対するテンプレートを作成しましょう。ビューの作成
私が読んでいる参考書ではERBというライブラリを用いてテンプレートを作成するERBテンプレートを用いていたのでそちらを使用していきます。ERBテンプレートは適当な名前では使えません。同じコントローラ、同じアクションに対応させるようにテンプレートを配置させる必要があります。app/views/top(コントローラ)/にindex(アクション名).html.erbという名前で配置します。
index.html.erb<% @title = "Hello World!"%> <h1><%= @title %></h1>ここまで書いたらコンソールで
rails s
コマンドを実行しサーバを立ち上げ結果をみます。流れ
①ルーティングでどのコントローラにどのアクションにやってもらうか決めます。
②コントローラとアクションを定義します。(得たデータの加工などはここで行います)
③必要ならばモデルも定義します。(モデルについては分かり次第追記)
④ビューします。最後に
現在の私の記事は他の方がみても何の価値もない備忘録ですが、将来的には誰が読んでも分かるような記事に仕上げていきたいと思っています。精進していきます。
- 投稿日:2020-02-26T14:36:42+09:00
heroku run rails db:migrateをしたときにundefined method `assets'が出る【Did you mean? asset_host】
APIを作成してHerokuでデプロイしようとしていました。そこで以下のコマンドを打つと
$ heroku run rails db:migrate以下のようなエラーが出ました。
NoMethodError: undefined method `assets' for #<Rails::Application::Configuration:0x000055586e3a0090> Did you mean? asset_hostこのエラーを解決するのに結構時間がかかったので、共有したいと思います。
Railsバージョン 6.0.2.1 Rubyバージョン 5.2.1 解決手順①
まずは
config/application.rbrequire "sprockets/railtie"上記がコメントアウトされていると思うので、外してください。もしなければ追加してください。
そして、追加できたら
$ git push origin mastermasterブランチにpushし
$ git push heroku masterデプロイします。
すると以下のようなエラーが。
Sprockets::Railtie::ManifestNeededError: Expected to find a manifest file in `app/assets/config/manifest.js` remote: But did not, please create this file and use it to link any assets that need remote: to be rendered by your app: remote: remote: Example: remote: //= link_tree ../images remote: //= link_directory ../javascripts .js remote: //= link_directory ../stylesheets .css remote: and restart your server remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/sprockets-rails-3.2.1/lib/sprockets/railtie.rb:105:in `block in <class:Railtie>' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/initializable.rb:32:in `instance_exec' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/initializable.rb:32:in `run' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/initializable.rb:61:in `block in run_initializers' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/initializable.rb:60:in `run_initializers' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/application.rb:363:in `initialize!' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/config/environment.rb:5:in `<top (required)>' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/zeitwerk-2.2.2/lib/zeitwerk/kernel.rb:23:in `require' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.2.1/lib/active_support/dependencies.rb:325:in `block in require' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.2.1/lib/active_support/dependencies.rb:291:in `load_dependency' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.2.1/lib/active_support/dependencies.rb:325:in `require' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/application.rb:339:in `require_environment!' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/railties-6.0.2.1/lib/rails/application.rb:515:in `block in run_tasks_blocks' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/sprockets-rails-3.2.1/lib/sprockets/rails/task.rb:62:in `block (2 levels) in define' remote: /tmp/build_e14dcaf905c9154df3f8551de0e5899c/vendor/bundle/ruby/2.5.0/gems/rake-13.0.1/exe/rake:27:in `<top (required)>' remote: Tasks: TOP => environment remote: (See full trace by running task with --trace) remote: remote: ! remote: ! Precompiling assets failed. remote: !このエラーのここ注目してください。
Sprockets::Railtie::ManifestNeededError: Expected to find a manifest file in `app/assets/config/manifest.js`app/assets/config/manifest.jsを期待してたんだけどなあって言われてるんですね。
なので、作っちゃいます。
解決手順②
こんな感じで作ってください。ファイルの中身は空でOKです。
そしてまた
$ git push origin mastermasterブランチにpushし
$ git push heroku masterデプロイします。
https://xxxxxxxxx.herokuapp.com/ deployed to Herokuデプロイ成功です。
そして
$ heroku run rails db:migratemigrateしても
D, [2020-02-26T04:10:06.961840 #4] DEBUG -- : (62.5ms) CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY) D, [2020-02-26T04:10:06.984953 #4] DEBUG -- : (13.8ms) CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp(6) NOT NULL, "updated_at" timestamp(6) NOT NULL) D, [2020-02-26T04:10:06.987824 #4] DEBUG -- : (1.3ms) SELECT pg_try_advisory_lock(1636886831838516615) D, [2020-02-26T04:10:07.016634 #4] DEBUG -- : (2.0ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC I, [2020-02-26T04:10:07.018343 #4] INFO -- : Migrating to CreateUsers (20200225063934) == 20200225063934 CreateUsers: migrating ====================================== -- create_table(:users) D, [2020-02-26T04:10:07.023146 #4] DEBUG -- : (1.1ms) BEGIN D, [2020-02-26T04:10:07.045683 #4] DEBUG -- : (21.9ms) CREATE TABLE "users" ("id" bigserial primary key, "name" character varying, "age" integer, "created_at" timestamp(6) NOT NULL, "updated_at" timestamp(6) NOT NULL) -> 0.0241s == 20200225063934 CreateUsers: migrated (0.0242s) ============================= D, [2020-02-26T04:10:07.058256 #4] DEBUG -- : primary::SchemaMigration Create (2.2ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20200225063934"]] D, [2020-02-26T04:10:07.061377 #4] DEBUG -- : (2.8ms) COMMIT D, [2020-02-26T04:10:07.074206 #4] DEBUG -- : ActiveRecord::InternalMetadata Load (1.4ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]] D, [2020-02-26T04:10:07.089249 #4] DEBUG -- : (1.2ms) BEGIN D, [2020-02-26T04:10:07.091895 #4] DEBUG -- : ActiveRecord::InternalMetadata Create (2.2ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key" [["key", "environment"], ["value", "production"], ["created_at", "2020-02-26 04:10:07.086635"], ["updated_at", "2020-02-26 04:10:07.086635"]] D, [2020-02-26T04:10:07.094787 #4] DEBUG -- : (2.6ms) COMMIT D, [2020-02-26T04:10:07.096357 #4] DEBUG -- : (1.4ms) SELECT pg_advisory_unlock(1636886831838516615)エラーを吐くことなく、無事migrateすることができました。
$ heroku openをしてローカルの内容がきちんと確認できれば大成功です。
今回はAPI特化でRailsアプリを作成していたので、出くわしたことのないエラーが多発して大変でした。
- 投稿日:2020-02-26T13:58:05+09:00
Railsでdeviseを導入する
チラ裏レベルのなぐり書きを世に放って申し訳ない。
方法
$ vim Gemfile => gem "devise" を追加 $ bundle install $ bundle exec rails g devise:install 以下を設定する。 =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. You can copy Devise views (for customization) to your app by running: rails g devise:views =============================================================================== 設定後、 $ bundle exec rails g devise:views $ bundle exec rails g devise Account # 必要ならここでmigrationファイルにフィールドを加える $ bundle exec rails db:migrate
- 投稿日:2020-02-26T13:55:42+09:00
Railsでdeviseを導入する方法
チラ裏レベルのなぐり書きを世に放って申し訳ない。
方法
$ vim Gemfile $ bundle install $ bundle exec rails devise:install 以下を設定する。 =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. You can copy Devise views (for customization) to your app by running: rails g devise:views =============================================================================== 設定後、 $ bundle exec rails g devise:views
- 投稿日:2020-02-26T13:42:25+09:00
each文とrender のcollectionオプション[備忘録]
はじめに
掲示板アプリを作成しているのだが、
いいね機能実装時、https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3
を参考に(ほぼ写して)、コードを書いていた。
機能自体の実装は成功したが、ビューでの表示が思うようにいかない。
各投稿一つ一つに、全ての投稿のイイねボタンがついてしまった。
これに対処するにあたって、
今までおざなりにしていた、renderとそのオプションについての理解がかなり深まったので、備忘録として残す。バージョンなど
ruby 2.5.1
rails 5.2.4.1
bootstrap 4.4.1
haml-rails 2.0.1
jquery-rails 4.3.5コード
posts_controller.rbdef index @posts = Post.all.includes(:user).order('created_at DESC') endindex.html.haml.events__wrapper.row /# each文 - @posts.each do |post| .events__content.col-sm-6.col-md-3.mb-3 .card{id: post.id} %label.m-1 - if post.image %img.card-img-top{src: "#{post.image}"} - else %img.card-img-top{src: "/public/noimage.jpeg"} .card-body.event %h5= link_to "#{post.title}", post_path(post.id), class: "event-title stretched-link text-decoration-none" .event__name #{post.user.name} さん .text-right = l post.created_at, format: :long = render partial: '/posts/posts', collection: @posts, as: :posteach文の中に、renderとオプションでcollectionを使っている形になっている。
この書き方だと、先述の通り、一つのpostにイイねボタンがいくつもついてしまう。
原因を調べていると発見した、とてもrenderとオプションについてまとめてあるqiitaの記事があるので共有しておきます。
https://qiita.com/takeru56/items/299850d0f054ce107e21
この記事を読んで、collectionオプションは変数に自動でeachメソッドを使ってくれていることに気付き、renderの記述を修正。
index.html.haml= render partial: '/posts/posts', locals: {post: post} /# これにより、元々あったeach文の中の変数postを部分テンプレートに渡せる。まとめ
イイね機能の実装を通じて、
collection, locals, object, as, などのオプションの理解が深まった。エラーの数だけ成長がある!!!
- 投稿日:2020-02-26T12:24:23+09:00
Herokuでデプロイしたアプリで投稿画像の保存先をS3に設定してみた。
はじめに
前回記事でHerokuにデプロイしたアプリですが、投稿した画像が時間が経つと次のように表示されなくなります。
原因
git上で管理されている画像は表示されますが、ブラウザ上で投稿した画像については保存されないため、一定時間で表示されなくなってしまいます。
これは、実装した画像アップローダーは、開発環境で動かす分には問題はありませんが、本番環境には適していません。
app/uploaders/image_uploader.rb
のstorage :file
というコードによって、ローカルのファイルシステムに画像を保存するようになるためです。本番環境でも投稿した画像を保存するためには、ファイルシステムではなくクラウドストレージサービスに画像を保存する必要があります。対処法
今回AWS/S3のクラウドストレージサービスを利用します。なにぶん初心者のためエラーにぶつかる事が多く、スマートな処理ではないですがじぶんの備忘録として同じようなエラーに苦労される方の助けになればと思います。
※注意事項として、試行錯誤しながらやっと解決できた感じなので、決してスマートな解決方法ではないと思います。その点はご了承ください。そしてこうしたらスマートに解決できるよ!というご意見がございましたら是非コメントいただければ幸いです!
前提条件
・Railsアプリ作成済
・CarrierWaveとMiniMagickで画像投稿機能は実装済
・AWSアカウント登録済
・AmazonS3FullAccess権限を与えたIAMユーザーを作成済以下実装の流れ
1.fogのインストール
本番環境でクラウドストレージに保存するために、fog gemを使います。
Gemfile.gem 'bootstrap', '~> 4.4.1' gem 'jquery-rails' gem 'devise', '~> 4.6.1' gem 'carrierwave', '~> 1.0' gem "mini_magick" gem 'fog' <= 追加$ bundle install path --vendor/bundle2.S3で保存先を準備
オリジナルのバケット名を入力し、アクセス許可をパブリックに設定します。
3.設定
Herokuでの画像の保存先をAmazon S3に保存できるように設定します。
基本的な話ですが、アクセスキーIDとシークレットアクセスキーは直接入力厳禁です。これをするととんでもないことになります。ですので、必ず環境変数を設定します。私は身をもって体験いたしました。config/initializers/carrierwave.rb〈中略〉 以下を追加 require 'carrierwave/storage/abstract' require 'carrierwave/storage/file' require 'carrierwave/storage/fog' if Rails.env.production? CarrierWave.configure do |config| config.fog_credentials = { :provider => 'AWS', :region => ENV['S3_REGION'], :aws_access_key_id => ENV['S3_ACCESS_KEY'], :aws_secret_access_key => ENV['S3_SECRET_KEY'] } config.fog_directory = ENV['S3_BUCKET'] end endapp/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick # 以下を追加 if Rails.env.production? storage :fog else storage :file end4.herokuへ環境変数登録
herokuに環境変数を登録(IAMでCSVダウンロードした中身通りに値を入力)
$ heroku config:set S3_ACCESS_KEY="Accessキーを入力" Setting S3_ACCESS_KEY and restarting ⬢ [アプリ名]... done, v15 S3_ACCESS_KEY: Accessキー $ heroku config:set S3_SECRET_KEY="Secretキーを入力" Setting S3_SECRET_KEY and restarting ⬢ [アプリ名]... done, v16 S3_SECRET_KEY: Secretキー $ heroku config:set S3_BUCKET="Bucket名を入力" Setting S3_BUCKET and restarting ⬢ [アプリ名]... done, v17 S3_BUCKET: Bucket名 $ heroku config:set S3_REGION="Region名を入力" Setting S3_REGION and restarting ⬢ [アプリ名]... done, v18 S3_REGION: Region名以上でherokuへ再度pushして'heroku open'してみると……
$ heroku logs -t 2020-02-02T05:42:54.905758+00:00 app[web.1]: from /app/vendor/bundle/ruby/ 2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require. rb:22:in `block in require_with_bootsnap_lfi' ・ ・ ・ 2020-02-02T05:43:15.180513+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=photo-ogiri.herokuapp.com request_id=ba2d6298-7c81-40a0-843d-02b9e2a700ef fwd="126.233.30.95" dyno= connect= service= status=503 bytes= protocol=httpsアプリがクラッシュしたとの内容のみで修正が必要な箇所がわからない。
$ heroku run rails c ・ ・ ・ in `block in load_missing_constant': uninitialized constant CarrierWave::Storage::Fog (NameError)見つけました。carrierwaveの部分でエラーが出ているようです。
5.エラー対処
確認すると carrierwave.rb が config/initializers 配下になく、 vendor/bundle 配下にしかなかったため、 config/initializers 配下に新たに carrierwave.rb を作成し、再度、heroku pushすると別のエラーが発生しました。
$ git push heroku master ・ remote: rake aborted! remote: NameError: uninitialized constant CarrierWave::Storage::Fog ・ ・ remote: ! remote: ! Precompiling assets failed. remote: ! remote: ! Push rejected, failed to compile Ruby app. ・まずは開発環境でPrecompileするために、以下を実行し成功することを確認しました。
$ RAILS_ENV=development bundle exec rails assets:precompile ・ ・ yarn install v1.21.1 info No lockfile found. [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... [4/4] ? Building fresh packages... success Saved lockfile. ✨ Done in 0.11s.続いて本番環境でPrecompileできるか試してみました。
$ RAILS_ENV=production bin/rails assets:precompile rails aborted! NameError: uninitialized constant CarrierWave::Uploadercarrierwaveの設定に修正が必要な様子。調べてみるとRailsガイドではRails5.2以降の変更点として次の記載がありましたので、carrierwave.rbを修正しました。
config/credentials.yml.encファイルが追加され、productionアプリケーションの秘密情報(secret)をここに保存できるようになりました。これによって、外部サービスのあらゆる認証credentialを、config/master.keyファイルまたはRAILS_MASTER_KEY環境変数にあるキーで暗号化した形で直接リポジトリに保存できます。
carrierwave.rbrequire 'carrierwave/storage/fog' CarrierWave.configure do |config| if Rails.env.production? config.fog_provider = 'fog/aws' config.fog_credentials = { provider: 'AWS', aws_access_key_id: Rails.application.credentials.aws[:access_key_id], aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key], region: 'ap-northeast-1' } config.fog_directory = 'S3バケット名' config.fog_public = true config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } end end再度、本番環境でPrecompileできるか試してみました。
$ RAILS_ENV=production bundle exec rails assets:precompile rails aborted! ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`失敗しましたがエラーは変わりました。secret_key_baseあたりを確認します。
$ EDITOR=vim rails credentials:edit aws: access_key_id: ~~~~~~~~~~~~~~~~~~~~~~~ secret_access_key: ~~~~~~~~~~~~~~~~~~~ # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: ~~~~~~~~~~~~~~~~~~~~~~~~~値もきちんと入っていますが、rails cで1つずつキーを確認するとsecret_key_baseのみがnillでした。
$ bundle exec rails c >Rails.application.credentials.secret_key_base => nil >Rails.application.credentials.aws[:access_key_id] => ~~~~~~~~~~~~~~~~~ >Rails.application.credentials.aws[:secret_access_key] => ~~~~~~~~~~~~~~~~~ここでかなり苦戦しましたが、結果インデントの位置がおかしかったため生じていたエラーでした。修正後のcredentials.yml.encファイルがこちら。secret_key_base:のインデントを左に寄せただけです。恐らく修正前のコードだとaws:配下のコードとみなされてしまって、secret_key_base:の値が読み取られなかったことが原因です。
$ EDITOR=vim rails credentials:edit aws: access_key_id: ~~~~~~~~~~~~~~~~~~~~~~~ secret_access_key: ~~~~~~~~~~~~~~~~~~~ # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: ~~~~~~~~~~~~~~~~~~~~~~~~~無事プリコンパイル処理もとおりました。
$ RAILS_ENV=production bundle exec rails assets:precompile yarn install v1.21.1 [1/4] ? Resolving packages... success Nothing to install. success Saved lockfile. ✨ Done in 0.15s.あとはherokuにmaster.keyをセットして完了です。master.key は git 管理していないので、heroku にはデプロイされていません。そのため、最初この状態でheroku pushすると
NoMethodError: undefined method '[]' for nil:NilClass
と怒られました。$ heroku config:set RAILS_MASTER_KEY=`cat config/master.key` $ git push heroku master無事heroku上のアプリに画像が表示されるようになり、S3のバケットに画像が保存されていることも確認できました。
参考記事
https://railsguides.jp/asset_pipeline.html
https://github.com/carrierwaveuploader/carrierwave#using-amazon-s3
https://railsguides.jp/5_2_release_notes.html#credential%E7%AE%A1%E7%90%86
https://workabroad.jp/posts/2166
- 投稿日:2020-02-26T12:11:28+09:00
[Rails]個人アプリ開発の際の便利なgem
初めての個人アプリ開発の際に導入すると便利なgemを紹介します。(個人的なメモ用)(どれもとても有名なものです。)
この記事ではmacOS Catalina バージョン 10.15.3にインストールした Rails5.2.4.1を使っています。
Better Errors(Railsのエラーを見やすくしてくれます。)
https://github.com/BetterErrors/better_errors
Bullet(N+1問題を自動で発見してくれます。)
https://github.com/flyerhzm/bullet
robocop(コードが規約に沿って書かれているか指摘してくれます。)
https://github.com/rubocop-hq/rubocop
- 投稿日:2020-02-26T11:00:15+09:00
RailsでjQueryを導入する方法+確認方法
jQuery導入について
・久しぶりにjQueryを導入しようとして苦戦したので調べました。
導入手順
環境
Rails 5.2.3
Ruby 2.5.11.gem 'jquery-rails'をインストール
(Gemfile) # 全ての環境で適用したい為、最下部に記述 gem 'jquery-rails'忘れずにbundle installしましょう!
2.application.jsを編集
require jqueryを追加
application.js//= require jquery
※注意点
記述の順番によってはjQueryが動作しないので要注意!application.js//= require jquery //= require jquery_ujs //= require turbolinks //= require_tree . #require_treeより上にjquery、jquery_ujsを書くこと //= require activestorage基本的にはここまで実装できれば、あとはhtmlファイルでイベントを用意して、jsファイルで
イベントに対して内容を記載すれば動きます。3.turbolinksを使う場合
application.html.erb等htmlファイルのhead内に以下の記述があるか確認。
またdocumentの記載方法についても注意が必要。application.html.erb<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>products.js//初回読み込み、リロード、ページ切り替えで動く。 $(document).on('turbolinks:load', function() { }); //初回読み込み、ページ切り替えで動く。リロードは動かない $(document).on('turbolinks:render', function() { }); //ページ遷移前に行いたい処理用。ページ切り替えでもリロードでも動かない $(document).on('turbolinks:request-start', function() { });4.導入確認方法
①console.logでチェック
ブラウザに表示できているか確認。
application.js//= require jquery //= require jquery_ujs //= require turbolinks //= require_tree . //= require activestorage console.log('hoge') # 追加②仮でコントローラー・ビューを作成
tests_controller.rbclass TestsController < ApplicationController def index end endindex.html<h1>Ajax#index</h1> <p id="hoge">赤色になるよ</p> <script> $("#hoge").css("color","red") </script>5.それでも導入できない場合
coffeeファイルの削除
今回苦戦した原因がこのcoffeeファイルでした。
「rails g controller」で任意のコントローラーを作成した際に、自動で「コントローラー名.coffee」という名前で生成されます。
jsファイルと同様の名前でcoffeeファイルがあると、同じ名前で作ったjsファイルがあっても、coffeeファイルが優先的にRailsから呼び出されるため、jsファイルが呼び出されないという状態になります。
coffeeファイルは使用しないならコントローラー作成時に忘れずに削除しましょう!
今回はturbolinksを初めて使用することもあり、記述誤りばかりを疑っていました。
coffeeファイルについても理解が足りてなかったです
今後学習していく中で追加事項があれば随時更新していきます!参考:
https://qiita.com/s-yank/items/cf7cadbb6c6996d67cf7
https://qiita.com/kumagi/items/289ccadf344f32613304
coffeeファイルについて
- 投稿日:2020-02-26T02:00:22+09:00
Railsの静的コード解析をGitHub Actionsでやる
これはなにか
GitHub に push すると GitHub Actions がブンブン回って、静的コード解析をしてくれるやつ。ついでにキャッシュされるので、毎回 bundle install がまわることがないやつ。はやい、やすい、うまい ... かどうかはわからないが、要はオレによし、オマエによし。
ワークフロー
name: Static Check on: [push] jobs: RuboCop: runs-on: ubuntu-latest steps: - uses: actions/setup-ruby@v1 with: ruby-version: '2.7' - uses: actions/checkout@v2 - uses: actions/cache@preview with: path: ./web/vendor/bundle key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gem- - name: Run RuboCop run: | cd ./web/ bundle install --jobs 4 --retry 3 --path vendor/bundle bundle exec rubocop -a BrakeMan: runs-on: ubuntu-latest steps: - uses: actions/setup-ruby@v1 with: ruby-version: '2.7' - uses: actions/checkout@v2 - uses: actions/cache@preview with: path: ./web/vendor/bundle key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gem- - name: Run BrakeMan run: | cd ./web/ bundle install --jobs 4 --retry 3 --path vendor/bundle bundle exec rubocop -a RubyCritic: runs-on: ubuntu-latest steps: - uses: actions/setup-ruby@v1 with: ruby-version: '2.7' - uses: actions/checkout@v2 - uses: actions/cache@preview with: path: ./web/vendor/bundle key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gem- - name: Run BrakeMan run: | cd ./web/ bundle install --jobs 4 --retry 3 --path vendor/bundle bundle exec rubycriticおわりに
ほんとは Docker 化しているのだから、Docker 上で動かせば良いのだろうけど Docker イメージの置き場所とか考えると、めんどくさくなったのでこうした。次は GitHub Actions で Docker image をロードして CI するやつでもつくろうかな。
- 投稿日:2020-02-26T00:49:02+09:00
ビューファイルの作成
ビューファイルはアプリケーションの見た目を定義するファイル。ビューファイルはapp/views/コントローラー名ディレクトリに、二本指クリックで新しいファイルを選択し、アクション名.html.erbで作成される。
アクション名は7つのアクション
ちなみにhamlでのファイル名はアクション名.hamlでOK
- 投稿日:2020-02-26T00:34:16+09:00
7つのアクションとは
7つのアクションとはindex、new、create、show、edit、update、destroyのこと。
アクションにより処理が分かれる。
indexはトップページを表示
newは新規投稿ページを表示
createはデータの新規投稿
showは個別詳細ページを表示
editは投稿編集ページを表示
updateはデータの編集
destroyは投稿のデータの削除
これらを作ったVS Codeのcontroller.rbに記述していく