20210607のRailsに関する記事は23件です。

【Rails】Failed to destroy the recordというエラー(メモ)

エラー文詳細 (以下)コンソールでの表示 Completed 500 Internal Server Error in 29ms (ActiveRecord: 11.2ms | Allocations: 10309) ActiveRecord::RecordNotDestroyed (Failed to destroy the record): Failed to destroy the recordというエラーが出力され、指定したものが削除できない。 task.destroy!までは動いているので、他のところに原因があるか? 解決方法 原因 task.rbの以下のコードが原因。 belongs_to :board, dependent: :destroy BoardとTaskは1対多で関連づけているため、 task.rb側においては、dependent: :destroyの記述は必要ない (Board側においては必要) 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアル(第6版) 第13章 アカウントの有効化

第13章 短いメッセージを投稿できるようにするためのリソース「マイクロポスト」機能を追加していく。 手順として ・Micropostデータモデル作成 ・Userモデルとhas_manyおよびbelong_toメソッドで関連付け ・結果を処理し表示するための必要なフォームを作る Micropostモデル 今回のマイクロポストモデルはテストされて、デフォルトの順序を持つ。また、親であるユーザーが破棄されたら自動的に破棄されるものになる。 トピックブランチを作成しておく。 $ git checkout -b user-microposts 基本的なモデル Micropostモデルは、マイクロポストの内容を保存するcontent属性と、特定のユーザーとマイクロポストを関連付けるためのuser_id属性を持つ。 参照:railsチュートリアル String型とText型 ・String型は、255文字まで格納できる ・Text型は、それ以上。また、Text用のテキストエリアを使うので、より自然な投稿フォームになる。将来のことを考えるとこっち。さらに、パフォーマンスでは差は出ないとのこと。 Micropostモデルを生成する $ rails generate model Micropost content:text user:references これでApplicationRecordを継承したモデルが作られた。 app/models/micropost.rb class Micropost < ApplicationRecord belongs_to :user end user:referencesという引数を含めていたため、belongs_toというコードが追加されている。 このreferences型を利用している点が、Userモデルとの最大の違いになる。 これを利用すると、自動的にインデックスと外部キー参照付きのuser_idカラムが追加されて、UserとMicropostを関連付ける下準備をしてくれる。 Userモデルと同様にcreated_atとupdated_atというカラムが追加されてる。 インデックスが付与されたMicropostのマイグレーション db/migrate/[timestamp]_create_microposts.rb class CreateMicroposts < ActiveRecord::Migration[6.0] def change create_table :microposts do |t| t.text :content t.references :user, foreign_key: true t.timestamps end add_index :microposts, [:user_id, :created_at] end end 上のコードでは、user_idとcreated_atカラムにインデックスが付与されている。これで、user_idに関連付けられたすべてのマイクロポストを作成時刻の逆順で取り出しやすくなる。 また、user_idとcreated_atの両方を1つの配列に含めている。これで、Active Recordは両方のキーを同時に扱う複合キーインデックス(Multiple Key Index)を作成する。 マイグレートする $ rails db:migrate Micropostのバリデーション Micropostモデル単体を動くようにする。 Micropostの初期テストは手順 ・setupでfixtureのサンプルユーザーと紐だ付けた新しいマイクロポストを作成 ・次に、作成したマイクロポストが有効かどうかのチェック ・最後に、あらゆるマイクロポストはユーザーのidを持つべきなので、user_idの存在性のバリデーションに対するテストを追加 test/models/micropost_test.rb require 'test_helper' class MicropostTest < ActiveSupport::TestCase def setup @user = users(:michael) # このコードは慣習的に正しくない @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id) end test "should be valid" do assert @micropost.valid? end test "user id should be present" do @micropost.user_id = nil assert_not @micropost.valid? end end 1つ目のテストは、現実に即しているかどうかをテスト(reality check) 2つ目のテストは、user_idが存在しているかどうか(nilではないか) user_idに対する存在性のバリデーション app/models/micropost.rb class Micropost < ApplicationRecord belongs_to :user validates :user_id, presence: true end 次にcontent属性に対するバリデーションの追加。 test/models/micropost_test.rb test "content should be present" do @micropost.content = " " assert_not @micropost.valid? end test "content should be at most 140 characters" do @micropost.content = "a" * 141 assert_not @micropost.valid? end content属性も存在性と、140文字より長くならないという制限を加える。 マイクロポストにバリデーションを追記する。 app/models/micropost.rb class Micropost < ApplicationRecord belongs_to :user validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } end User/Micropostの関連付け 個々のモデル間で関連付けをしておく。 今回は、それぞれのマイクロポストは一人のユーザーと関連付けられ、それぞれのユーザーは潜在的に複数のマイクロポストと関連付けられている。 MicropostとそのUserはbelongs_toの関係性 参照:railsチュートリアル UserとそのMicropostはhas_manyの関係性 参照:railsチュートリアル このような関係を行うことで メソッド  用途 micropost.user  Micropostに紐付いたUserオブジェクトを返す  user.microposts  Userのマイクロポストの集合をかえす  user.microposts.create(arg) userに紐付いたマイクロポストを作成する  user.microposts.create!(arg) userに紐付いたマイクロポストを作成する(失敗時に例外を発生) user.microposts.build(arg) userに紐付いた新しいMicropostオブジェクトを返す user.microposts.find_by(id: 1) userに紐付いていて、idが1であるマイクロポストを検索する 上のメソッドが使えるようになる。 注意点として Micropost.create Micropost.create! Micropost.new ではなく user.microposts.create user.microposts.create! user.microposts.build となっている。 紐づいているユーザーを通してマイクロポストを作成することができる。新規のマイクロポストがこの方法で作成される場合、user_idは自動的に正しい値に設定される。 最初の書き方↓ @user = users(:michael) # このコードは慣習的に正しくない @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id) 書き換えると↓ @user = users(:michael) @micropost = @user.microposts.build(content: "Lorem ipsum") buildメソッドはオブジェクトを返すがデータベースには反映されない。 一度関連付けを定義すれば、@micropost変数のuser_idには、関連するユーザーidが自動的に設定される。 UserモデルとMicropostモデルを紐づける。 Micropostは、belongs_to :user Userモデルは、has_many :micropostsと追加する app/models/user.rb class User < ApplicationRecord has_many :microposts . . . end setupメソッドを修正し、正しいテストにする。 test/models/micropost_test.rb def setup @user = users(:michael) @micropost = @user.microposts.build(content: "Lorem ipsum") end マイクロポストを改良する UserとMicropostを関連付けを改良する。 ユーザーのマイクロポストを特定の順序で取得できるようにしたり、マイクロポストをユーザーに依存させ、ユーザーの削除と同時にマイクロポストも自動的に削除されるようにする。 デフォルトスコープ user.micropostメソッドはデフォルト状態では読み出し順序に何も保証がない。 ブログやTwitterの慣習に従い、作成時間の逆順、最も新しいマイクロポストを最初に表示する。 これを実装するには、default scopeを使う。 このテストは、一見成功しているように思えるが、「アプリケーション側の実装が本当は間違っているのにテストが成功してしまう」トラップがある。 なので、テスト駆動開発で進める。 テスト内容 データベース上のマイクロポストが、fixture内のマイクロポスト(most_recet)と同じであるか検証する。 test/models/micropost_test.rb require 'test_helper' class MicropostTest < ActiveSupport::TestCase . . . test "order should be most recent first" do assert_equal microposts(:most_recent), Micropost.first end end fixtureでコメント、作成日、ユーザーへの関連付けを行う。 test/fixtures/microposts.yml orange: content: "I just ate an orange!" created_at: <%= 10.minutes.ago %> user: michael tau_manifesto: content: "Check out the @tauday site by @mhartl: https://tauday.com" created_at: <%= 3.years.ago %> user: michael cat_video: content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk" created_at: <%= 2.hours.ago %> user: michael most_recent: content: "Writing a short test" created_at: <%= Time.zone.now %> user: michael fixtureファイル内では日付を更新可能となっている。 続いて、Railsのdefault_scopeメソッドを使う。 このメソッドは、データベースから要素を取得した時のデフォルトの順序を指定するメソッドになる。 特定の順序にしたい場合、default_scopeの引数にorderを与える。 例えば、created_atカラムの順にしたい場合は以下の通り。 order(:created_at) デフォルトの順序が昇順の為、小さい値から大きい値にソートされる。つまり最も古い投稿が最初に来る。 順序を逆にするには、生のSQLを引数に与える。 order('created_at DESC') DESCは、SQLの降順(descending)を指す。これで新しい投稿から古い投稿の順に並ぶ。 なんとRails4.0以降は、Rubyの文法でも書けるようになった。 order(created_at: :desc) default_scopeでマイクロポストを順序付ける app/models/micropost.rb class Micropost < ApplicationRecord belongs_to :user default_scope -> { order(created_at: :desc) } validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } end 上のコードでは、ラムダ式という文法を使っている。 これは、Procやlambda(無名関数)と呼ばれるオブジェクトを作成する文法。 ->というラムダ式は、ブロックを引数に取り、Procオブジェクトを返す。 このオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価する。 Dependent: destroy マイクロポストに第二の要素を追加する。 サイト管理者はユーザーを破棄する権限を持つため、ユーザーが破棄されたらユーザーのマイクロポストも同時に破棄されるべき。 has_manyメソッドにオプションを渡してあげればOK app/models/user.rb class User < ApplicationRecord has_many :microposts, dependent: :destroy . . . end dependent: :destroyというオプションを使うことで、ユーザーが削除されたら、そのユーザーに紐づいたマイクロポストも一緒に削除される。 持ち主の存在しないマイクロポストがデータベースに取り残される問題を防ぐ。 Userモデルを検証する。 テスト内容 ・ユーザーを作成する ・そのユーザーに紐づいたマイクロポストを作成する ・その後、ユーザーを削除してみて、マイクロポストが1つ減っているかどうか確認する test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end . . . test "associated microposts should be destroyed" do @user.save @user.microposts.create!(content: "Lorem ipsum") assert_difference 'Micropost.count', -1 do @user.destroy end end end マイクロポストを表示する ユーザープロフィールにマイクロポストを表示させるため、最初に極めて神父rなERbテンプレートを作成する。 次に、サンプルデータ生成タスクにマイクロポストのサンプルを追加し、画面にサンプルデータが表示されるようにする。 マイクロポストの描画 ユーザーのプロフィール画面(show.html.erb)で、そのユーザーのマイクロポストを表示させたり、これまでに投稿した総数も表示させる。 Micropostのコントローラーとビューを作成するために、コントローラを生成する。 $ rails generate controller Microposts _micropost.html.erbパーシャルを使って、マイクrポストのコレクションを表示しようとすると以下のようになる。 <ol class="microposts"> <%= render @microposts %> </ol> ulタグではなく、順序付きリストのolタグを使っている。 マイクロポストが特定の順序に依存しているため。 対応するパーシャのコード↓ app/views/microposts/_micropost.html.erb <li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"><%= micropost.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. </span> </li> time_ago_in_wordsというヘルパーメソッドを使っている。これは、「〇分前に投稿」といった文字列を出力するもの。 <li id="micropost-<%= micropost.id %>"> これは、各マイクロポストにCSSのidを割り振っている。将来的に、各マイクロポストを操作したくなった時に役立つ。 ページ分割するため、will_paginateメソッドを使う。 <%= will_paginate @microposts %> 第10章のユーザー一覧画面のコードでは、<%= will_paginate %>という単純なコードだった。 実は、上のコードは引数なしで動作していた。will_paginateがUsersコントーラのコンテキストにおいて、@userインスタンス変数が存在していることを前提としてたから。このインスタンス変数は、ActiveRecord::Relationクラスのインスタンス。 今回の場合は、Usersコントローラのコンテキストからマイクロポストをページネーションしたいため(コンテキストが異なる)、明示的に@microposts変数をwill_paginateに渡す必要がある。 従って、そのようなインスタンス変数をUsersコントローラのshowアクションで定義しなければならない。 @micropostsインスタンス変数をshowアクションに追加する。 app/controllers/users_controller.rb class UsersController < ApplicationController . . . def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page]) end . . . end マイクロポストの関連付けを経由し、micropostテーブルに到達して、必要なマイクロポストのページを引き出している。 最後に、マイクロポストの投稿数を表示するが、これはcountメソッドでOK user.microposts.count 関連付けを通して、countメソッドを呼び出している。 なんとcountメソッドでは、データベース上のマイクロポストを全部読みだしてから結果の配列にlengthを呼ぶような、無駄な処理はしていない。 (そりゃぁそうでなければね) データーベースに代わりに計算してもらい、特定のuser_idに紐づいたマイクロポストの数をデータベースに問い合わせている。countメソッドよりもさらに高速なcounter cacheも使うことが可能。 プロフィール画面にマイクロポストを表示させる。 if @user.microposts.any?を使って、ユーザーのマイクロポストが1つもない場合には空のリストを表示させないようにしている。 app/views/users/show.html.erb <% provide(:title, @user.name) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1> </section> </aside> <div class="col-md-8"> <% if @user.microposts.any? %> <h3>Microposts (<%= @user.microposts.count %>)</h3> <ol class="microposts"> <%= render @microposts %> </ol> <%= will_paginate @microposts %> <% end %> </div> </div> マイクロポストのサンプル すべてのユーザーにマイクロポストを追加すると時間が掛かるので、takeメソッドを使って最初の6人だけ追加する。 User.order(:created_at).take(6) orderメソッドを経由することで、最初の6人を明示的に呼び出すようにしている。 1ページの表示限界数(30)を越えさせるのに、それぞれ50個分のマイクロポストを追加する。 また、各投稿内容はFaker gemにLorem.sentenceというメソッドを使う。 db/seeds.rb # ユーザーの一部を対象にマイクロポストを生成する users = User.order(:created_at).take(6) 50.times do content = Faker::Lorem.sentence(word_count: 5) users.each { |user| user.microposts.create!(content: content) } end 開発環境のデータベースで生成 $ rails db:migrate:reset $ rails db:seed 参照:railsチュートリアル CSSを整えて、、 app/assets/stylesheets/custom.scss /* microposts */ .microposts { list-style: none; padding: 0; li { padding: 10px 0; border-top: 1px solid #e8e8e8; } .user { margin-top: 5em; padding-top: 0; } .content { display: block; margin-left: 60px; img { display: block; padding: 5px 0; } } .timestamp { color: $gray-light; display: block; margin-left: 60px; } .gravatar { float: left; margin-right: 10px; margin-top: 5px; } } aside { textarea { height: 100px; margin-bottom: 5px; } } span.image { margin-top: 10px; input { border: 0; } } プロフィール画面のマイクロポストをテスト プロフィール画面で表示されるマイクロポストに対して、統合テストを書く。 まずは、プロフィール画面用の統合テストを生成する。 $ rails generate integration_test users_profile マイクロポスト用のfixtureにいくつかデータを追加 test/fixtures/microposts.yml orange: content: "I just ate an orange!" created_at: <%= 10.minutes.ago %> user: michael tau_manifesto: content: "Check out the @tauday site by @mhartl: https://tauday.com" created_at: <%= 3.years.ago %> user: michael cat_video: content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk" created_at: <%= 2.hours.ago %> user: michael most_recent: content: "Writing a short test" created_at: <%= Time.zone.now %> user: michael <% 30.times do |n| %> micropost_<%= n %>: content: <%= Faker::Lorem.sentence(word_count: 5) %> created_at: <%= 42.days.ago %> user: michael <% end %> テスト内容 プロフィール画面にアクセス後 ・ページタイトルとユーザー名 ・Gravatar ・マイクロポストの投稿数 ・ページ分割されたマイクロポスト という順番でテストする。 test/integration/users_profile_test.rb require 'test_helper' class UsersProfileTest < ActionDispatch::IntegrationTest include ApplicationHelper def setup @user = users(:michael) end test "profile display" do get user_path(@user) assert_template 'users/show' assert_select 'title', full_title(@user.name) assert_select 'h1', text: @user.name assert_select 'h1>img.gravatar' assert_match @user.microposts.count.to_s, response.body assert_select 'div.pagination' @user.microposts.paginate(page: 1).each do |micropost| assert_match micropost.content, response.body end end end 第12章と同様に、response.bodyを使っている。これには、そのページの完全なHTMLが含まれている。 従って、そのページのどこかしらにマイクロポストの投稿数があれば、以下のように探してマッチできる。 assert_match @user.microposts.count.to_s, response.body 上のはassert_selectよりも抽象的なメソッド。 特にassert_selectではどのHTMLタグを探すか指定しなければならない。 assert_matchメソッドはその必要がない。 さらに、assert_selectの引数ではネストした文法を使う。 assert_select 'h1>img.gravatar' h1タグの内側にあるgrabatarクラス月のimgタグがあるかどうかをチェックしてる。 マイクロポストを操作する データモデリングとマイクロポスト表示テンプレートが完成したので、続いてWeb経由でそれらを作成するためのインターフェースに取り掛かる。 やること ・ステータスフィード実装 ・ユーザーがマイクロポストをWeb経由で破棄できるようにする 従来のRails開発と子なる点が1つある。 Micropostリソースへのインターフェイスは、主にプロフィールページとHomeページのコントローラを経由し実行されるため、Micropostコント―羅にはnewやeditアクションは不要。 つまり、createやdestroyがあればOK Micropostsリソースは下記になる。 resources :microposts, only: [:create, :destroy] Micropostsリソースが提供するRESTfulルート HTTPリクエスト URL アクション 名前付きルート POST /microposts create microposts_path DELETE /microposts/1 destroy micropost_path(micropost) マイクロポストのアクセス制御 Micropostsリソース開発では、コントローラ内のアクセス制御から始める。 関連付けられたユーザーを通してマイクロポストにアクセスするので、createアクションやdestroyアクションを利用するユーザーはログイン済である必要がある。 ログイン済か調べるテストは、Usersコントローラ用のテストがそのまま使える。 正しいリクエストを各アクションに発行し、マイクロポスト数が変化してないかどうか、リダイレクトされるかどうかを確認する。 test/controllers/microposts_controller_test.rb require 'test_helper' class MicropostsControllerTest < ActionDispatch::IntegrationTest def setup @micropost = microposts(:orange) end test "should redirect create when not logged in" do assert_no_difference 'Micropost.count' do post microposts_path, params: { micropost: { content: "Lorem ipsum" } } end assert_redirected_to login_url end test "should redirect destroy when not logged in" do assert_no_difference 'Micropost.count' do delete micropost_path(@micropost) end assert_redirected_to login_url end end ここでリファクタリングの必要性あり。 第10章では、beforeフィルタのlogged_in_userメソッドを使って、ログインを要求した。第10章では、Usersコントローラ内にこのメソッドがあったので、beforeフィルターを指定した。 なので、Micropostsコントローラでも同様に必要になる。 なので、各コントローラーが継承するApplicationコントローラに、このメソッドを移す。 app/controllers/application_controller.rb class ApplicationController < ActionController::Base include SessionsHelper private # ユーザーのログインを確認する def logged_in_user unless logged_in? store_location flash[:danger] = "Please log in." redirect_to login_url end end end Usersコントローラからは、logged_in_userは削除しておこう。 これでMicropostsコントローラーからもlogged_in_userメソッドを呼び出せるようになった。 これで、createアクションやdestroyアクションに対するアクセス制限が、beforeフィルターで簡単に実装できるようになった。 app/controllers/microposts_controller.rb class MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] def create end def destroy end end マイクロポストを作成する 第7章でやった、HTTP POSTリクエストをUsersコントローラのcreateアクションに発行するHTMLフォームを作成することで、ユーザーのサインアップをやった。 今回もこれと似てる。違いとして、別のmicropost/neeページを使う代わりに、ホーム画面にフォームを置くという点。 この節での目標 ・ユーザーのログイン状態に応じて、ホーム画面の表示を変更する事 マイクロポストのcreateアクションを作る。 app/controllers/microposts_controller.rb class MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else render 'static_pages/home' end end def destroy end private def micropost_params params.require(:micropost).permit(:content) end end ユーザー用アクションと似てるが、違いは新しいマイクロポストをbuildするためにUser/Micropost関連付けを使っているというところ。 micropost_paramsでStrong Parmetersを使っていることで、マイクロポストのcotent属性だけがWeb経由で変更可能になっている。 マイクロポスト作成フォーム構築のために、サイト訪問者がログインしてるかどうかに応じ、異なるHTMLを提供するコード app/views/static_pages/home.html.erb <% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> </div> <% else %> <div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"), "https://rubyonrails.org/" %> <% end %> 上のコードを動かすためパーシャルを作る必要がある。 Homeページの新しいサイドバー app/views/shared/_user_info.html.erb <%= link_to gravatar_for(current_user, size: 50), current_user %> <h1><%= current_user.name %></h1> <span><%= link_to "view my profile", current_user %></span> <span><%= pluralize(current_user.microposts.count, "micropost") %></span> ユーザー情報に、そのユーザーが投稿したマイクロポスト総数が表示される。 pluralizeメソッドを使って、"1 micropost"や"2 microposts"とするよう調整してる。 マイクロポスト作成フォームを定義する。ユーザー登録フォームに似てる。 app/views/shared/_micropost_form.html.erb <%= form_with(model: @micropost, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <% end %> フォームを動かすため、2か所変更が必要。 1つは、関連付けを使って以下のように@micropostと定義すること @micropost = current_user.microposts.build app/controllers/static_pages_controller.rb class StaticPagesController < ApplicationController def home @micropost = current_user.microposts.build if logged_in? end current_userメソッドはユーザーがログインしている時しか使えないので、@micropost変数もログインしているときのみ定義されるようになる。 2つめは、エラーメッセージのパーシャルを再定義すること。 <%= render 'shared/error_messages', object: f.object %> 今回は@mircropost変数を使う。フォーム変数fをf.objectとし、関連付けられたオブジェクトにアクセスすることができる。 form_with(model: @user, local: true) do |f| 上のようにf.objectが@userとなる場合と form_with(model: @micropost, local: true) do |f| f.objectが@micropostになる場合がある。 パーシャルにオブジェクトを渡すため、値がオブジェクトで、キーがパーシャルでの変数名と同じハッシュを利用する。 object: f.objectはerror_messagesパーシャルの中でobjectという変数名を作成してくれるので、この変数名を使ってエラーメッセージを更新すれば良い。 app/views/shared/_error_messages.html.erb <% if object.errors.any? %> <div id="error_explanation"> <div class="alert alert-danger"> The form contains <%= pluralize(object.errors.count, "error") %>. </div> <ul> <% object.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> この時点でテストは失敗する。error_messagesパーシャルの他の出現場所がヒント。 このパーシャルは、他の場所で使われていたため、ユーザー登録、パスワード再設定、ユーザー編集のそれぞれのビューを更新する必要がある。 各ビューを更新した結果を下記に示す。 app/views/users/new.html.erb <% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(model: @user, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %> </div> </div> app/views/users/edit.html.erb <% provide(:title, "Edit user") %> <h1>Update your profile</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(model: @user, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Save changes", class: "btn btn-primary" %> <% end %> <div class="gravatar_edit"> <%= gravatar_for @user %> <a href="https://gravatar.com/emails">change</a> </div> </div> </div> app/views/password_resets/edit.html.erb <% provide(:title, 'Reset password') %> <h1>Reset password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(model: @user, url: password_reset_path(params[:id]), local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= hidden_field_tag :email, @user.email %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Update password", class: "btn btn-primary" %> <% end %> </div> </div> フィードの原型 マイクロポスト投稿フォームが動作するようになった。 しかし、現時点では投稿した内容をすぐにみる事ができない。なぜなら、Homeページにまだマイクロポストを表示する部分が実装されないから。 フォームが正しく動作してるかチェックする場合 正しいエントリー投稿→プロフィールページに移動→ポストを表示 になるが、これは面倒だ。 なので、ユーザー自身のポストを含むマイクロポストのフィードがないと不便。 参照:railsチュートリアル すべてのユーザーがフィードを持つため、feedメソッドはUserモデルで作るのが良い。 フィードの原型では、まずは現在ログインしているユーザーのマイクロポストをすべて取得する。 第14章で完全なフィードを実装するので、今回はwhereメソッドでこれを実現する。 Micropostモデルに変更を加える。 app/models/user.rb class User < ApplicationRecord . . . # 試作feedの定義 # 完全な実装は次章の「ユーザーをフォローする」を参照 def feed Micropost.where("user_id = ?", id) end private . . . end 上のコードにある疑問符は、セキュリティ上重要だ。 Micropost.where("user_id = ?", id) 疑問符があることで、SQLクエリに代入する前にidがエスケープされるため、SQLインジェクション(SQL Injection)と呼ばれる深刻なセキュリティホールを避けられる。 この場合、id属性は単なる整数(self.idはユーザーのid)であるため危険でないが、SQL文に変数を代入する場合は常にエスケープしておこう。 サンプルアプリケーションにフィード機能を導入するために、ログインユーザーのフィード用にインスタンス変数@feed_itemsを追加、Homeページにはフィード用のパーシャルを追加する。 先ほどあった下記のコード @micropost = current_user.microposts.build if logged_in? これが if logged_in? @micropost = current_user.microposts.build @feed_items = current_user.feed.paginate(page: params[:page]) end 前置if文に変わる。 (1行の時は後置if文、2行の時は前置if文を使うのがRubyの慣習) homeアクションにフィードのインスタンス変数を追加する。 app/controllers/static_pages_controller.rb class StaticPagesController < ApplicationController def home if logged_in? @micropost = current_user.microposts.build @feed_items = current_user.feed.paginate(page: params[:page]) end end ステータスフィードのパーシャル app/views/shared/_feed.html.erb <% if @feed_items.any? %> <ol class="microposts"> <%= render @feed_items %> </ol> <%= will_paginate @feed_items %> <% end %> ステータスフィードのパーシャルは、Micropostのパーシャルとは異なる。 <%= render @feed_items %> このとき、@feed_itemsの各要素がMicropostクラスを持っていたため、RailsはMicropostのパーシャルを呼び出すことができた。 このように、Railsは対応する名前のパーシャルを、渡されたリソースのディレクトリ内から探しにいくことができる。 あとはいつものようにフィードパーシャルを表示すればHomeページにフィードを追加できる。 この結果はHomeページのフィードとして表示される。 app/views/static_pages/home.html.erb <% if logged_in? %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div> </div> しかし、マイクロポストの投稿が失敗すると、Homeページは@feed_itemsインスタンス変数を期待しているため、現状では壊れる。 解決法として、Micropostコントローラのcreateアクションへの送信が失敗した場合に備え、必要なフィード変数をこのブランチで渡しておくこと。 app/controllers/microposts_controller.rb def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else @feed_items = current_user.feed.paginate(page: params[:page]) render 'static_pages/home' end end しかし、この時点ではわざと長いマイクロポストを投稿するとエラーが出る。 ページネーション用のリンクを表示すると、"2"のリンクと"Next"のリンクがどちらも同じ次のページを指している。 createアクションはMicropostsコントローラにあるので、このURLは「/microposts?page=2」となります。しかし、これはMicropostsの存在していないindexアクションを開こうとしている。 その結果、どちらのリンクをクリックしてもエラーが発生する。 この問題は、Homeページに対応するcontrollerパラメータとactionパラメータを明示的にwill_paginateに渡せばOK 例として、static_pagesコントローラとhomeアクションを渡すと次の通り。 app/views/shared/_feed.html.erb <% if @feed_items.any? %> <ol class="microposts"> <%= render @feed_items %> </ol> <%= will_paginate @feed_items, params: { controller: :static_pages, action: :home } %> <% end %> マイクロポストを削除する 削除機能を追加する。 これは、ユーザー削除と同じように"delete"リンクでOK。 今回は自分が投稿したマイクロポストのみ削除リンクが動作するようにする。 まずは、マイクロポストのパーシャルに削除リンクを追加する。 app/views/microposts/_micropost.html.erb <li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"><%= micropost.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <% if current_user?(micropost.user) %> <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li> 次に、Micropostsコントローラのdestroyアクションを定義する。 ユーザーの実装とほぼ同じ。違いは、admin_userフィルターで@user変数を使うのではなく、関連付けを使いマイクロポストを見つけるようにする点。 これで、あるユーザーが他のユーザーのマイクロポストを削除しようとすると、自動的に失敗するようになる。 correct_userフィルター内でfindメソッドを呼び出すことで、現在のユーザーが削除対象のマイクロポストを保有してるかどうか確認する。 app/controllers/microposts_controller.rb class MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] before_action :correct_user, only: :destroy . . . def destroy @micropost.destroy flash[:success] = "Micropost deleted" redirect_to request.referrer || root_url end private def micropost_params params.require(:micropost).permit(:content) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end end destroyメソッドではリダイレクトを使っている。 request.referrer || root_url ここでrequest.referrerというメソッドを使っている。 このメソッドはフレンドリーフォワーディングのrequest.url変数と似ており、一つ前のURLを返す。(今回なら、Homeページ) なので、マイクロポストがHomeページから削除された場合でもプロフィールページから削除された場合でも、request.referrerを使うことによりDELETEリクエストが発行されたページに戻れる。 また、元に戻すURLが見つからなくても||演算子でroot_urlをデフォルトに設定してるので問題なし。 マイクロポスト作成 マイクロポスト削除 フィード画面のマイクロポストをテストする Micropostモデルとそのインターフェイスが完成した。 あとは、Micropostsコントローラの認可をチェックするテストと、それらをまとめる統合テストだ。 マイクロポスト用のfixtureに、別々のユーザーに紐づけられたマイクロポストを追加する。 別のユーザーに所属しているマイクロポストを追加 test/fixtures/microposts.yml ants: content: "Oh, is that what you want? Because that's how you get ants!" created_at: <%= 2.years.ago %> user: archer zone: content: "Danger zone!" created_at: <%= 3.days.ago %> user: archer tone: content: "I'm sorry. Your words made sense, but your sarcastic tone did not." created_at: <%= 10.minutes.ago %> user: lana van: content: "Dude, this van's, like, rolling probable cause." created_at: <%= 4.hours.ago %> user: lana 続いて、自分以外のユーザーのマイクロポストは削除しようとした時、適切にリダイレクトされることを確認する。 test/controllers/microposts_controller_test.rb test "should redirect destroy for wrong micropost" do log_in_as(users(:michael)) micropost = microposts(:ants) assert_no_difference 'Micropost.count' do delete micropost_path(micropost) end assert_redirected_to root_url end 最後に、統合テストを書く。 統合テストの内容 ・ログイン ・マイクロポストのページ分割確認 ・無効なマイクロポストを投稿 ・有効なマイクロポストを投稿 ・マイクロポストの削除 ・他のユーザーのマイクロポストには[delete]リンクが非表示 という順番でテストする。 統合テストを生成する。 $ rails generate integration_test microposts_interface マイクロポストUIに対する統合テスト test/integration/microposts_interface_test.rb require 'test_helper' class MicropostsInterfaceTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) end test "micropost interface" do log_in_as(@user) get root_path assert_select 'div.pagination' # 無効な送信 assert_no_difference 'Micropost.count' do post microposts_path, params: { micropost: { content: "" } } end assert_select 'div#error_explanation' assert_select 'a[href=?]', '/?page=2' # 正しいページネーションリンク # 有効な送信 content = "This micropost really ties the room together" assert_difference 'Micropost.count', 1 do post microposts_path, params: { micropost: { content: content } } end assert_redirected_to root_url follow_redirect! assert_match content, response.body # 投稿を削除する assert_select 'a', text: 'delete' first_micropost = @user.microposts.paginate(page: 1).first assert_difference 'Micropost.count', -1 do delete micropost_path(first_micropost) end # 違うユーザーのプロフィールにアクセス(削除リンクがないことを確認) get user_path(users(:archer)) assert_select 'a', text: 'delete', count: 0 end end マイクロポストの画像投稿 ここでは、画像付きマイクロポスト投稿の機能を実装する。 手順として、まずは開発環境のβ版を実装し、その後いくつかの改善を通して本番環境用の完成版を実装する。 画像アップロード機能を追加するための2つの視覚要素 ・1つ目は、画像をアップロードするためのフォーム ・2つ目は、1つ目に投稿された画像そのもの 参照:railsチュートリアル モックアップ画像 基本的な画像アップロード Railsでファイルをアップロードする簡単な方法は、Railsに組み込まれているActive Storage機能を使う事。 Active Storageで画像を簡単に扱えて、画像に関連付けるモデルも自由に指定可能となる。 また、Active Storageは汎用性が高く、平文テキスト、PDFファイルや音声等様々なバイナリファイルも扱える。 Active Storageインストールコマンド $ rails active_storage:install 上のコマンドで、添付ファイルの保存に用いるデータモデルを作成するためのデータベースマイグレーションが1つ作られる。 次にマイグレーションを実行 $ rails db:migrate Active Storageの中で最初に知るべきことは、has_one_attachedメソッドだ。 これは、指定のモデルとアップロードされたファイルを関連付けるのに使う。 下の場合は、imageを指定してMicropostモデルと関連付けてる。 app/models/micropost.rb class Micropost < ApplicationRecord belongs_to :user has_one_attached :image default_scope -> { order(created_at: :desc) } validates :user_id, presence: true validates :content, presence: true, length: { maximum: 140 } end このアプリケーションでは、「マイクロポスト1件につき画像は1件」という設計をしているが、Active Storageは他にもhas_many_attachedオプションも提供してる。文字通り、Active Recordオブジェクト1件につき複数のファイルを添付できるもの。 Homeページに画像をアップロードを追加するのに、マイクロポストフォームにfile_fieldタグを含める。 マイクロポストのcreateフォームに画像アップロードを追加 app/views/shared/_micropost_form.html.erb <%= form_with(model: @micropost, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <span class="image"> <%= f.file_field :image %> </span> <% end %> 最後に、Micropostsコントローラを更新し、新たに作成したmicropostオブジェクトに画像を追加できるようにする。 Active Storage APIにはそのためのattachメソッドが提供されており、これを使う。 具体的にMicropostsコントローラのcreateアクションの中で、アップロードされた画像を@micropostオブジェクトにアタッチする。 このアップロード許可のために、micropost_paramsメソッドを更新し、:imageを許可済み属性リストに追加して、Web経由で更新できるようにする。 app/controllers/microposts_controller.rb class MicropostsController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] before_action :correct_user, only: :destroy def create @micropost = current_user.microposts.build(micropost_params) @micropost.image.attach(params[:micropost][:image]) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else @feed_items = current_user.feed.paginate(page: params[:page]) render 'static_pages/home' end end def destroy @micropost.destroy flash[:success] = "Micropost deleted" redirect_to request.referrer || root_url end private def micropost_params params.require(:micropost).permit(:content, :image) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end end 一度画像がアップロードされれば、micropostパーシャルのimage_tagヘルパーを用いて、関連付けられたmicropost.imageを描画できる。 また、画像の無いテキストのみのマイクロポストでは画像を表示させないようにするため、attached?という論理値を返すメソッドを使っている。 app/views/microposts/_micropost.html.erb <li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"> <%= micropost.content %> <%= image_tag micropost.image if micropost.image.attached? %> </span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <% if current_user?(micropost.user) %> <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li> 画像の検証 画像アップロード機能を持たせたが、欠点がある。それは、アップロードされた画像に対する制限がないため、もしユーザーが巨大なファイルを上げたり、無効なファイルを上げると問題が発生する。 この欠点を直すために、画像サイズやフォーマットに対するバリデーションを実装する。 Railsチュートリアル執筆時点では、Active Storageは、こうしたフォーマット機能やバリデーションがネイティブでサポートされていないとのこと。 なので、そのような機能をgemで追加する。 gem 'active_storage_validations', '0.8.2' いつものようにbundle installする。 このgemでは、content_typeを検査する事で画像をバリデーションできる。 content_type: { in: %w[image/jpeg image/gif image/png], message: "must be a valid image format" } 上記のコードは、サポートする画像フォーマットに対応する画像MIME typeをチェックしてる。 配列構文%w[]がある。 同じく、ファイルサイズも以下のようにバリデーション可能。 size: { less_than: 5.megabytes, message: "should be less than 5MB" } 上はtimeヘルパーの時に使った構文と同じで、画像の最大サイズを5MBに制限してる。 これらのバリデーションをまとめた結果をMicropostモデルに追加する。 app/models/micropost.rb class Micropost < ApplicationRecord . . . validates :image, content_type: { in: %w[image/jpeg image/gif image/png], message: "must be a valid image format" }, size: { less_than: 5.megabytes, message: "should be less than 5MB" } end 参照:railsチュートリアル 先ほどのモデルに追加バリデーションを強化するために、クライアント側でも画像アップロードのサイズやフォーマットをチェックする仕組みを入れる。 まずは、JavaScript(jQuery)にて、ユーザーがアップロードしようとする画像が大きすぎたらアラートを表示するようにする。 jQueryでファイルサイズをチェックする。 app/views/shared/_micropost_form.html.erb <script type="text/javascript"> $("#micropost_image").bind("change", function() { var size_in_megabytes = this.files[0].size/1024/1024; if (size_in_megabytes > 5) { alert("Maximum file size is 5MB. Please choose a smaller file."); $("#micropost_image").val(""); } }); </script> 最後に、acceptパラメータをfile_field入力タグで用いれば、有効なフォーマットでないとアップロードできない事をユーザーに伝えられる。 app/views/shared/_micropost_form.html.erb <%= form_with(model: @micropost, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-primary" %> <span class="image"> <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %> </span> <% end %> <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %> このコードは最初に有効な画像フォーマットだけを選択可能にしておき、それ以外のファイルタイプを灰色で表示するもの。 ブラウザ側に色々コードを追加したが、このコードは無効なファイルをアップロードしにくくするだけ。 その気になればcurl等でPOSTリクエストを直接発行して、無効なファイルをアップロードできてしまう。 なので、サーバー側のバリデーションは必要なのだ。 画像のリサイズ ファイルサイズのバリデーションは実装した。 今度は、画像サイズ(縦横の長さ)に対する制限がない。なので、大きすぎる画像サイズがアップロードされるとレイアウトが崩壊する。 かといって、ユーザーで画像サイズを変更させるのは大変不便だ。 そのため、画像を表示させる前にサイズを変更するようにする。 参照:railsチュートリアル 画像を操作するプログラムが必要なので、ImageMagickを使う。 開発環境にインストールする。 $ sudo apt-get -y install imagemagick 続いて、画像処理のためにいくつかgemを追加する。 image_processinggem、Ruby製ImageMagickプロセッサのmini_magickgemが必要だ。 gem 'image_processing', '1.9.3' gem 'mini_magick', '4.9.5' いつものようにbundle installする。 これで、Active Storageが提供するvariantメソッドで変換済画像を作成できるようになる。 特にresize_to__limitオプションを用いて、下記のように画像の幅や高さが500ピクセルを越えないように制約をかけとく。 image.variant(resize_to_limit: [500, 500]) 上記のコードをdisplay_imageメソッドにおいて利便性を高めよう。 app/models/micropost.rb # 表示用のリサイズ済み画像を返す def display_image image.variant(resize_to_limit: [500, 500]) end display_imageをmicropostパーシャルで使えるようになった。 リサイズ済のdisplay_imageを使う。 <li id="micropost-<%= micropost.id %>"> <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <span class="user"><%= link_to micropost.user.name, micropost.user %></span> <span class="content"> <%= micropost.content %> <%= image_tag micropost.display_image if micropost.image.attached? %> </span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. <% if current_user?(micropost.user) %> <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> <% end %> </span> </li> 上記のコードに <%= image_tag micropost.display_image if micropost.image.attached? %> があるが、そこにdisplay_imageメソッドを使ってる。 variantによるリサイズは、<%= image_tag micropost.display_image if micropost.image.attached? %>で最初に呼ばれる時にオンデマンドで実行される。以後、結果をキャッシュするので効率が良い。 本番環境での画像アップロード 画像アップロード機能を実装したが、このままで本番環境に適さない。 本番環境では、ファイルシステムではなくクラウドストレージサービスに画像を保存するようにする。 今回はAWSのS3(Simple Storage Service)を使う。 S3を使うためgemを設定する。 gem 'aws-sdk-s3', '1.46.0', require: false 例のごとくbundle installを実行する。 AWSの設定方法は省く。 railsチュートリアルに画像付きで細かく載っているため。 最後に Micropostsリソースによって、良い感じにTwitterっぽくなった。 次章は、ユーザーをお互いフォローする仕組みを作っていく。 ユーザー同士のリレーションシップモデリングを学んで、マイクロポストフィードにどんな感じで関連するか学ぶ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】刃物のノコギリじゃないよ、gemのNokogiriだよ

Nokogiriとは Nokogiriを利用すると、RubyからXML/HTMLを簡単に生成することが出来ます。 これは、ドキュメントの読み取り、書き込み、変更、およびクエリを行うための、賢明で理解しやすい API を提供します。 インストール方法 Gemfileに以下を記載し、bundle installする。 gem 'nokogiri' 使い方 app/lib/html_builder.rb module HtmlBuilder def markup(tag_name = nil, options = {}) root = Nokogiri::HTML::DocumentFragment.parse("") Nokogiri::HTML::Builder.with(root) do |doc| if tag_name doc.method_missing(tag_name, options) do yield(doc) end else yield(doc) end end root.to_html.html_safe end end def notes markup(:div, class: "notes") do |m| m.span "*", class: "mark" m.text "印の付いた項目は入力必須です。" end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】簡単にRDSを使いたい

環境 ホスト:MacOS ゲスト:CentOS6 Vagrant MySQL Problem https://qiita.com/Alice_ecilA/items/54b4624f0126c7eec5e5 ↑こちらの続きです。 vagrantをコピーしたので、アプリケーション側とRDS側にしていきます。 Solve アプリケーションサーバ config/database.yml development: <<: *default database: my_database username: apps password: this_is_password host: 192.168.33.11 アプリケーション側はdatabase.ymlにRDSの接続情報を追加してあげればOK。 RDS用 mysql -u root [-p] #パスワードがある場合-p -- アプリケーションサーバからの接続を許可する mysql> GRANT all ON *.* TO apps@'192.168.33.22' IDENTIFIED BY 'this_is_password'; mysql> FLUSH PRIVILEGES; -- 先程のIPが登録されていれば接続できる状態 mysql> SELECT user,host FROM mysql.user; +------------------+---------------+ | user | host | +------------------+---------------+ | apps | 192.168.33.22 | | apps | localhost | | root | localhost | +------------------+---------------+ RDS側はmysqlに入り、アプリケーションサーバのIPアドレスを登録して接続を許可すればOK。 まとめ これでアプリケーションとRDSの通信ができるようになりました。 外部のDBを使いたい場合はこれが応用できると思います。 もしうまく行かないのであれば、RDS側のOSで外部との通信を許可しているか?(Firewall) MySQLサーバの設定ファイル(mysqld.cnf)でIPアドレスの制限を解除しているか?確認してみましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

当たり前だけどエラーに向き合う心構え

※備忘録的に記述します。 RailsでもPythonでもそうですが、エラーが発生したときにやりがちなことが 「エラーの文言をそのままGoogle検索」 だと思うんですが、はっきり言ってこれは間違っている。 まず最初にやることはエラーの内容を理解して、どこが問題なのかを理解すること。 JavaScriptが読み込まない Nomethodと出る どんなエラーでも同じですが、 ①どのようなエラーが発生しているのか ②何が原因なのか ということをきちんと把握することが大事。 プログラミング初心者だと、ついエラーから逃げがちだけども、一番大事なことはここなのかと。 いろんな言語を経験しているわけではないので、全てではないですが、どの言語においても共通していることだと思います。 まずは真剣にエラーと向き合う。 どこでエラーが発生しているのか、原因となっている箇所は何なのか、どのようなエラー内容なのか、ということを正確に把握することが大事。 ということをすごく学びました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FactoryBot・Fakerの導入

目的 Railsで作成したアプリにFactoryBotとFakerを導入する。 開発環境 macOS: Big Sur Rubyバージョン: 2.6.5 Railsバージョン: 6.0.0 前提 アプリtest-appが作成されている。 【Rails】簡単な投稿アプリの作成 deviseが導入されている。 【Rails】ユーザー管理機能(devise)の導入 手順 FactoryBotとは Fakerとは Gemのインストール 記述法 FactoryBotとは インスタンスをまとめることができるGemです。 あらかじめ各クラスのインスタンスに定める値を設定しておき、テストコードで使用します。 Fakerとは ランダムな値を生成してくれるGemです。 FakerのGitHub こちらの公式GitHubにもあるように、様々な値が生成可能です。 Gemのインストール Gemfileを以下のように編集します。 この時、group :development, :test doの中に記述するように注意してください。 Gemfile group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails', '~> 4.0.0' gem 'factory_bot_rails' gem 'faker' end ターミナル % bundle install これでインストールできました! 記述法 以下のように記述することができます! spec/factories/users.rb FactoryBot.define do factory :user do nickname {Faker::Name.initials(number: 2)} email {Faker::Internet.free_email} password {Faker::Internet.password(min_length: 6)} password_confirmation {password} end end 最後に 以上で導入は完了です。 Gemでインストールするのみなので非常に簡単に実装できます。 では。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ransackで複数ワードAND検索を作ってみた。

はじめに Rails初学者です。 以下の記事を参考にさせていただきAND検索を実装したので、メモ 追加情報として、AND検索を実装したときにある不具合が起こったので、そこについても記していきます。 やり方 ransackを用いたビューとコントローラーの実装は以下のようになっています。 ビュー <%= search_form_for @search , url: posts_path do |f| %> <%= f.search_field :title_or_body_cont, placeholder: "キーワードで質問を探す" %> <%= f.submit%> <% end %> コントローラ class PostController def index @search = Question.ransack(params[:q]) @posts = search.result end end これに対して、複数のワード用いてAND検索を行いたい場合は、以下のようにパラメータを渡す def index keyword = params[:q][:title_or_body_cont].split(' ') # 'hoge hige' @posts = Post.ransack({ combinator: 'and', groupings: { 'a' => {title_or_body_cont: keyword[0]}, 'b' => {title_or_body_cont: keyword[1]}} }) end 上記の例では、Postモデルのtitleカラムとbodyカラムに対して'hoge'と'hige'の二つの文字列でAND検索を実行しています。 ※groupings内の'a'や'b'の部分は、一意であれば何でも良い。 これらを踏まえた完全版が以下になります。 def index key_words = params[:q][:title_or_body_cont].split(/[\p{blank}\s]+/) #'hoge hige' grouping_hash = keywords.reduce({}){|hash, word| hash.merge(word => { title_or_body_cont: word })} Post.ransack({ combinator: 'and', groupings: grouping_hash }).result end 1行目、params[:q][:title_or_body_cont]には半角スペースあるいは全角スペースで区切られた形で、複数の検索ワードが入っています('hoge hige')。2行目は検索ワードを分割し、配列に格納している。3行目は、ransackに渡すgroupingsパラメータの中身を組み立てている。 ちなみにsplitの引数にある/[\p{blank}\s]+/はよくある正規表現で半角スペースと全角スペースを認識します。 これで以下のようなハッシュが生成されます。 { 'hoge' => {title_or_body_cont: 'hoge'}, 'hige' => {title_or_body_cont: 'hige'}, } 不具合の解消 これにて実装完了と思いきや以下のような不具合がおきました。 検索を実行すると。。。 検索フォームが空に。。。 しかしこの解決方法は簡単です。 viewをいじるだけです。 <%= search_form_for @search , url: posts_path do |f| %> <%= f.search_field :title_or_body_cont, placeholder: "キーワードで質問を探す" ,value: params[:q][:title_or_body_cont] %> <%= f.submit%> <% end %> f.search_fieldにvalueを追加してparams[:q][:title_or_body_cont]をしていするだけ解消できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on rails】ルート設定できないときに確認すること

元々root以外に設定していたページをルートに書き換えるときによくやってしまうミス Missing :controller key on routes definition, please check your routes. (ArgumentError) config/routes.rb root 'inquiries/new' 解決策  /を#書き換えれば解決します。 config/routes.rb root 'inquiries#new'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ルート設定できないときに確認すること

元々root以外に設定していたページをrootに書き換えるときによくやってしまうミス Missing :controller key on routes definition, please check your routes. (ArgumentError) config/routes.rb root 'inquiries/new' 解決策  /を#書き換えれば解決します。 config/routes.rb root 'inquiries#new' 無事、viewが表示されました。 余談 初学者なため何も考えずに、rootに書き換えたときに、postで設定していたページまで#を使用した記述にしていて、rails modelコマンドを使用したところ作成できない事象が発生しました。 Missing :controller key on routes definition, please check your routes. (ArgumentError) config/routes.rb Rails.application.routes.draw do root 'inquiries#new' post 'inquiries#confirm' #本来は#でなく/を記述 post 'inquiries#thanks' #本来は#でなく/を記述 end 解決策 下記、記述に直したところ、rails modelコマンドを使用できるようになりました。 config/routes.rb Rails.application.routes.draw do root 'inquiries#new' post 'inquiries/confirm' post 'inquiries/thanks' end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

if文でinclude?(xxx)のor条件を連続で何度も書くのを、簡潔でシュッとした書き方にする。

備忘録用。 前提条件 例えば、「メッセージ文の中に、東京・神奈川・千葉・埼玉が含まれるか」の場合、 if msg.include?("tokyo") || msg.include?("kanagawa") || msg.include?("chiba") || msg.include?("saitama") となってしまい、ちょっと見づらい。 少し見やすい書き方として、 if msg.include?( "tokyo" || "kanagawa" || "chiba" || "saitama" ) なんて書き方も悪くないかもしれないけど、チェックする県が増えたらどんどん横長ブサイクになってしまう。 .any? とブロックを使えばシュッとなる list = [ "tokyo", "kanagawa", "chiba", "saitama" ] if list.any? { |n| msg.include?(n) } 「msg.include?(n) がtrueとなる要素」が1つでもlistにあればtrueを返す。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap4をRailsに反映させる方法

Udemy:「はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう」を学んで詰まったところを書いていきます。今回はBootstrapの適用方法についてです。 Bootstrapが反映されない こちらの方の記事を参考にしたら反映させることができました!私がやった手順を下記に記します。 1.まずyarnパッケージからjqueryとbootstrapをインストールする $ yarn add jquery popper.js bootstrap 2.Gemfileに下記を記述し、bundle installを実行 gemfile gem 'bootstrap-sass' gem 'jquery-rails' console画面 $bundle install 3.environment.jsに下記を記述 config/webpack/environment.js const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery', Popper: ['popper.js', 'default'] }) ) 4.application.jsに下記を記述 javascript/packs/application.js require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require('bootstrap/dist/js/bootstrap.min.js') require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("jquery") require('bootstrap/dist/js/bootstrap.min.js') こうしたらやっとbootstrapが適用されました、、ちなみにindex.html.rbはこのようになっています。 <h2>Questions</h2> <div class="row"> <div class="col-md-12"> <table class="table table-striped"> <thead class="thead-light"> <tr> <th scope="col ">ID</th> <th scope="col">Title</th> <th scope="col">Menu</th> </tr> </thead> <tbody> <tr> <% @questions.each do|question| %> <th scope="row"><%= question.id%></th> <td><%= question.title%></td> <td>[Edit][Delete]</td> <br> </tr> <%end%> </tbody> </table> </div> </div> 結果として下記の画像のようになりました!!!Webpackを使った場合と通常の場合だとbootstrapのインポートの仕方が異なるみたいですね。。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]モデルによるデータベース操作について(初歩)

はじめに こないだ、面接でデータベースに関しての質問に対して惨敗したので、データベースの学習をしっかりやってこうと思いました。 手始めにRailsでデータベースを操作するために用いられるModelとデータベースについてまとめてみました。 ORM まずはこの用語ORMですが Object Relational Mappingの略でリレーショナルデータベースのデータをオブジェクト思考のプログラミング言語でオブジェクトとして使用するために変換する技術のことです。 RailsのActiviRecordのメソッド(createとかdestroyとか)もORMです。 そして、例えばdestroyを使ったときにはその裏ではSQLという言語が実行されています。 私はこの時点でリレーショナルデータベースってなんだっけとなりました。 RDB(リレーショナルデータベース) リレーショナルデータベースはデータを複数の表(テーブル)として管理し、様々な表(テーブル)同士の関係を定義をすることで、複雑なデータの関連性を扱えるデータベースのことです。 SQL(Structured Query Language) SQLはデータベースを操作するための言語で、もっと言えば、リレーショナルデータベースを操作するための言語になります。ActiveRecordのメソッドは実際には実行されるときSQLに変換されています。 このSQLという言語は大きく三つの命令に分類されます。 データを定義 DDL (Data Definition Language) データを操作 DML (Data Manipulation Language) テータを制御 DCL (Data Control Language) そしてそれぞれの命令の代表的なものを紹介します。 DDL 命令 機能 CREATE データベースやテーブルの作成 ALTER データベースやテーブルの更新 DROP データベースやテーブルの削除 DML(ちなみに私はこれを面接で突っ込まれて一つも答えることができませんでした。) 命令 機能 INSERT データの登録 UPDATE データの更新 DELETE データの削除 SELECT データの検索 DCL 命令 機能 COMMIT DBの変更の確定 ROLLBACK DBの変更の取り消し GRANT ユーザーに操作権限を付与 REBOKE ユーザーの操作権限を無効化 またSQLはどの種類のリレーショナルデータベースであったとしても基本的に使うことができます。 なぜならSQLはISO(国際基準化機構)で規格化が行われているからです。 私はまだリレーショナルデータベースはMySQLしか使ったことはないですが、他のPostgreSQLやSQLiteという他のリレーショナルデータベースでも利用可能ということです。 最後に データベースの扱いは学習していたつもりでしたが、今まで記述として書くことがほとんどなかったため、ほとんど覚えていませんでした。 サーバーサイドにおいてデータベースの知識と理解は必要不可欠になるので、しっかり復習と新たな学習は継続して行なっていきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでsessionによるwhere検索を行うときの注意点

障害内容 コントローラのある1つのアクション内でsessionを利用後にデータを削除するような処理を想定して、下記コードを書いたとする hoge_controller.rb def action # イメージ的には検索処理のような感じ # category_idはInteger型でDBに登録されている @lists = Book.where(category_id: session[:category_id]) session[:category_id].clear end 上記のコードではBookモデルからデータを取得することはできない 実際に発行されているSQLも SELECT * FROM books WHERE category_id = '' のような形で出力されることが確認できるはず 通常、Railsで定義される変数類は先行評価の形式をとっているため、 例えwhere検索等の遅延評価系メソッドの後に値をnilにしても正しくデータを取得することができる 反対に、sessionは基本的に遅延評価であるため遅延評価系メソッドで使用した後にsessionの中身をクリアするとデータの取得ができなくなってしまう 解消方法 今回の例ではwhere検索であるため、それを先行評価メソッドにしてしまえば問題ない hoge_controller.rb(whereの先行評価ver) def action # SQLを直書きする @lists = Book.where('category_id = :id', id: session[:category_id]) session[:category_id].clear # take、firstなどで即時発行させる @lists = Book.where(category_id: session[:category_id]).take @lists = Book.where(category_id: session[:category_id]).first session[:category_id].clear end # to_aなどで変換を行う(ActiveRecord::Relationクラスではなくなるため要注意) @lists = Book.where(category_id: session[:category_id]).to_a session[:category_id].clear end また、それとは逆にsessionの値をdupでコピーするなど、sessionとは別で保持させることでも解消できる 格納させるときにはid = session[:category_id]のように代入で行わないようにさえすれば大体何とかなる(代入は結局のところメモリ参照のため、参照先が変われば代入された変数の値も変化する) 参考記事 ActiveRecord各メソッドのクエリ実行タイミングについて ActiveRecordクラスのメソッドについて、詳しい解説が載っています
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQL入門①〜レコードとカラムのデータを取得〜

はじめに 今回は、SQLの基礎的な部分を学習したので、その内容をまとめていきます。 ボリュームが多くなってしまったので、何段階かに分けて、投稿していきます。 いつもお世話になっている、山浦さんの動画を参考に学習していきました。 【SQL入門講座 合併版】SQLの基本をたった1時間で学べます【初心者向けデータベース入門】 SQLとは データベースに保存したデータを、操作する際に使用する言語。 SQLでできること ・カラム操作(データの取得、更新、削除) ・レコード操作(データの取得、削除、更新、保管) ・テーブル操作(テーブルの作成、削除、更新) SQLを書く上でのポイント ✅SELECTなどの予約語は大文字で表記する(小文字でもエラーは出ない)  →予約語とカラム・テーブル名の違いをわかりやすくするため  →エラーの減らす ✅複数の予約語を使用するときは、適度にインデント・改行をいれる  →エラーを減らすため  →可読性が上がる ✅データ数に応じて、LIMITやSELECTで取得するデータを制限する  →処理が早くなる テーブル操作 テーブルを取得する(FROM) FROM テーブル名; カラム操作 カラムのデータを取得する(SELECT) SELECT カラム名 FROM テーブル名; カラム名を変更して、取得(AS) SELECT カラム名 AS '変更するカラム名' FROM テーブル名; 全件取得(*) SELECT * FROM テーブル名; 重複したデータを取り除いたデータを取得 SELECT DISTINCT(カラム名) FROM テーブル名; 条件を指定してカラムのデータを取得する(WHERE) SELECT カラム名 FROM テーブル名 WHERE 条件; 複数の条件を指定してデータを取得する(AND) SELECT * FROM テーブル名  WHERE 条件 AND 条件; 同カラム内でまとめて指定してデータを取得する(IN) SELECT * FROM テーブル名  WHERE カラム名 IN(条件,条件); 同カラム内で複数の条件に当てはまらないデータを取得する(NOT IN) SELECT * FROM テーブル名  WHERE カラム名 NOT IN(条件,条件); どちらかの条件を満たすデータを取得する(OR) SELECT * FROM テーブル名  WHERE 条件 OR 条件; 範囲を指定したデータを取得する(AND) SELECT * FROM テーブル名  WHERE カラム名 BETWEEN 条件 AND 条件; 〇〇から始まるデータを取得(LIKE) SELECT * FROM テーブル名  WHERE カラム名 LIKE 〇〇%; 〇〇で終わるデータを取得 SELECT * FROM テーブル名  WHERE カラム名 LIKE %〇〇; nullのデータを取得(IS NULL) SELECT * FROM テーブル名  WHERE カラム名 IS NULL; nullじゃないデータを取得(IS NOT NULL) SELECT * FROM テーブル名  WHERE カラム名 IS NOT NULL; レコードの操作 レコード数を指定して取得する(LIMIT) レコードの件数が多い時に使用すると便利 SELECT * FROM テーブル名  LIMIT レコード数; レコードを昇順で並び替える(ORDER BY) SELECT * FROM テーブル名 ORDER BY カラム名; レコードを降順で並び替える(ORDER BY DESC) SELECT * FROM テーブル名 ORDER BY カラム名 DESC; (補足)RailsでのDB操作 DBを起動 rail db テーブルを全て取得 .table(s) まとめ ・簡単ではありましたが、カラム・レコードのデータを操作する方法について、学習していきました。 この学習を通して、少しはSQLに対しての苦手意識がなくなったかなと感じています。 次回もSQLについて学習したことをまとめていこうと思いますので、よければそちらも読んでいただけると嬉しいです。 参考文献 SQLでできることとは?イチから学ぶデータベース操作【初心者向け】 【Rails】最初に必要なSQL操作 [SQL]インデントを正しく効果的に使おう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-compose runでstandard_init_linux.go:219 原因と解決

始めに myappディレクトリ(各自アプリ名)を作成し、その配下に Dockerfile docker-compose.yml Gemfile Gemfile.lockを用意してあります。 問題 $ docker-compose run web rails new . --force --no-deps --database=mysql --skip-test --webpacker を実行すると、 standard_init_linux.go:219: exec user process caused: exec format error のエラーが出て、新たにアプリが作成できない。 解決 shebangが抜けていたことが原因だと分かった。 新たに、entrypoint.shファイルを作成し、 entrypoint.sh #!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@" を記述。 再度、 $ docker-compose run web rails new . --force --no-deps --database=mysql --skip-test --webpacker を実行し、無事にアプリが作成できた。 参考記事 https://qiita.com/nsy_13/items/9fbc929f173984c30b5d https://teratail.com/questions/170342
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

request specでDRYなコードを書きましょう

はじめに Rspecを書き始めたばかりなのであまり詳しいことはかけませんが自分の備忘録として書いているので間違えている部分等あれば教えていただけると幸いです。 そもそもRequest specってなに? もともとRailsのcontrollerをテストする際にはRails4までのバーションだとController specが用いられてましたがRails5以降になるとそれらが非推奨となりRequest specで書くことが推奨されるようになりました。 要するにcontollerに書くテストを書けばいい!!と私は思います Railsガイドによるとコントローラの機能テストは 以下引用 Webリクエストが成功したか 正しいページにリダイレクトされたか ユーザ認証が成功したか レスポンスのテンプレートに正しいオブジェクトが保存されたか ビューに表示されたメッセージは適切か このようになっています。上記を参考にRequest specを書いていきましょう 本題 それでは上記を参考にDRYなコードを書いていきましょう!! 待った!!DRYってなに!?!? ってなった方もいると思います。僕もそうでした。 DRYというのはDRY原則と言ってWebフレームワークRuby on Railsが基本理念としの一つとして採用している開発原則です。簡単にいうとコードを重複させないという意味です。 難しい事はここでは省きますがrequest specを下記に書いていくのでそこで雰囲気だけ掴んでいってください! 例1 HTTPレスポンスコードが200かどうかを判定する場合 require 'rails_helper' Rspec.describe 'Qiita', type: :request do describe 'GET Qiita index' do it 'statusが200であること' get qiita_path expect(response).to have_http_status(200) end end describe 'GET Qiita new' do it 'statusが200であること' do get new_qiita_path expect(response).to have_http_status(200) end end end 例ですが上記のようなコードがあった場合、indexとnewで重複している場所があります。 このような場合にDRYに書くことができます。 DRYに書いた場合 require 'rails_helper' shared_examples 'statusが200であること' do it 'statusが200であること' do expect(response).to have_http_status(200) end end Rspec.describe 'Qiita', type: :request do describe 'GET Qiita index' do get qiita_path it_behaves_like 'statusが200であること' end describe 'GET Qiita new' do get new_qiita_path it_behaves_like 'statusが200であること' end end これだけで圧倒的にみやすくなりました。ここででできたshared_examplesとit_behaves_likeは何かというとshared_examplesを利用してコードを共有しit_behaves_likeでそれを呼び出すと言ったものです。 詳しい説明は下記に記事を貼るのでそこから参照してください! 参考にした記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【未経験の登竜門!】RubyのブロックとyieldとcallとProcとラムダ式を超わかりやすく解説してみた(Rails6.0でもよく出てくるよ)

はじめに 未経験エンジニアやエンジニア1年生のあなたは、「ブロック・yield・call・Proc・ラムダ式」を人に説明することができますか? この辺りは避けて通ってきた人も多い知識だと思うので、不安な人はこの機会に全部まとめて学んじゃいましょう!!! では超わかりやすい解説スタートです! そもそもブロックとは 【記法】 do ... end, { } のどちらか。この記号と記号に囲まれた中身を含めてブロックという。 【ブロックの定義】 メソッド呼び出しの際に引数と一緒に渡すことのできる処理のかたまり | たのしいRuby P199 メソッド呼び出しの際というのがポイント。 てことで例えば、 exmaple def greeting puts 'おはよー!' end # メソッドの呼び出し greeting do # 特に意味はないけど、メソッド呼び出し時に # こうやってdo ... end で囲めばブロックを渡したことになる。 end この場合の出力は、もちろん 'おはよー!' です。 ブロックの活用法 さっきのgreetingメソッドの例は全く意味のない使い方ですね。 まあ、ブロックはメソッドに渡す事ができるって理解できたらOKです! さあ、ここからはブロックの有効的な活用法をご紹介しますが、みなさんがdo ... end を見て最初に思い浮かぶのは、eachじゃないでしょうか? (私だけだったらすみません...) 未経験の方は、 「ブロックってメソッドの後につけるんだよね。そもそもeachってメソッドなの?」 こんな疑問の声が聞こえた気がしたので、一応説明しておくと、eachもメソッドです! ・「メソッドはクラスに定義されているもの」 ・「(インスタンス)メソッドはオブジェクトに対して使用する」 っていう基本ルールはOKですかね? 実はeachメソッドは、Enumerable クラスに定義されているメソッドなのです! Enumerable は、日本語で「列挙可能な」的な意味です。データが列挙されているデータ型をイメージしてみてください。 ご名答! 配列・ハッシュ・集合などが全てEnumerableクラスに属しています。(すぐ思い浮かんだ未経験の方はすごい!!!) 話がそれましたが、だからeachメソッドは、 eachメソッド [1, 2, 3].each って感じで配列の後ろに付けられるんですね! メソッドの後ろにブロックをつけることができるので、 each do ... end [1, 2, 3].each do |ele| end ってできるわけですね。 ここで、eachとブロックの役割について解説しておきます。 each ... 配列の要素を一つずつブロックの中に渡す。 ブロック ... 要素を受け取った後の処理内容。 ブロックの概要は伝わりましたでしょうか! yield 次はyieldです! yieldの役割は、「メソッドの呼び出しをブロック付きで行った際、メソッドの中のyieldでブロックの処理内容を実行」することです。 こちらの理解はなかなか難しいと思うので、またgreetingを使ったコードで説明します! 下のコードを見てください! greetingメソッド def greeting puts 'おはよー!' yield # greeting メソッドをブロック付きで呼び出した時にブロックの処理内容がここに入ります。 end # greetingメソッドをブロック付きで呼び出す greeting do puts 'こんばんはー!!' end 出力 'おはよー!' 'こんばんはー!' yieldの位置にブロック内の処理であるputs 'こんばんはー!!'が入ることで、出力に示したような結果になりました。 ※ メソッド内にyieldを書いている時に、ブロックなしでメソッドを呼び出すとエラーが発生します。お気をつけください。 greetingメソッド def greeting puts 'おはよー!' yield # greeting メソッドをブロック付きで呼び出した時にブロックの処理内容がここに入ります。 end # greetingメソッドをブロックなしで呼び出す greeting 出力 LocalJumpError: no block given (yield) こういった場合は、block_given?メソッドを使います。このメソッドはブロックが渡されているときにtrueを返します。 greetingメソッド def greeting puts 'おはよー!' if block_given? # ブロックが渡された場合にyieldを実行する yield end end # greetingメソッドをブロックなしで呼び出す greeting 出力 'おはよー!' ブロックを引数で渡す。Callでブロックを実行する。 ブロックを引数に渡したい場合は、以下の記法を使います。 ブロックを引数で渡す def method(&ブロック) # ブロックを実行する ブロック.call end ブロックを渡してブロックの処理内容をcallで実行するってことですね! これも例を見せておきます。 example.rb def greeting(&b) puts 'おはよー!' text = b.call('こんばんはー') # ブロックの処理内容を実行 puts text end # ブロックを引数に渡して実行 greeting do |text| puts "#{text}!!" end 出力 おはよー! こんばんはー!! callでブロックを呼び出す際は引数も取ることができます。 yieldとcallの違いは何? yieldもcallもブロックを呼び出すという点では同じです。 では何が違うのかというと、「callは引数から明示的にブロックを渡す事ができる」ということですね! はて?って人はもう一度yieldとcallの説明を見直していただければ納得できると思います。 yieldではメソッドの定義の際、引数としてブロックを受け取りません。メソッド呼び出し時にブロックを渡していれば、yieldでブロック内の処理内容を実行します。 対してcallは、メソッドの定義の際、引数として「&ブロック」という表記でブロックを受け取ります。ブロックを渡している場合は、ブロック.callでブロック内の処理内容を実行します。 どちらも「ブロック内の処理内容を実行する」ということを理解しておいてください! Proc Rubyには、Procクラスというクラスが存在します。 Procクラスは、今までさんざん説明してきたブロックをオブジェクト化するためのクラスです。 Procクラスのオブジェクトを作成する時に、引数でブロックを渡します。 Procオブジェクト作成 greeting = Proc.new { 'おはよー!' } このままgreetingを呼び出してもブロックオブジェクトを保存しているメモリアドレスを返すだけでブロック内の処理内容は実行されません。 (メモリアドレスの話も今度詳しく解説する予定なので、気になる方は僕のTwitterで最新情報をチェックしてみてください!) ブロックの処理内容を実行するメソッド覚えてます? そうです。callメソッドです! なので、下みたいにすれば実行できます。 Procオブジェクト(ブロック)を実行 greeting = Proc.new { 'おはよー!' } greeting.call #=> 'おはよー!' もちろん引数も渡せます! 引数ありのProcオブジェクトを実行 greeting = Proc.new {|text1, text2| text1 + text2 } greeting.call('おはよー!', 'からのこんばんはー!') #=> 'おはよー!からのこんばんはー!' まとめると、Procオブジェクトはブロックそのもの。a = Proc.new {} した時のaはProcオブジェクトの保存先のメモリアドレスが入っているだけなので、実行する際は、a.callのように呼び出す。 Procオブジェクトを引数に渡す ブロックを引数に渡す方法を思い出しましょう。 ブロックを引数に渡す def greeting(&b) puts 'おはよー!' text = b.call('こんばんはー') # ブロックの処理内容を実行 puts text end # ブロックを引数に渡して実行 greeting do |text| puts "#{text}!!" end 出力 おはよー! こんばんはー!! ここで重要なポイントは、引数に渡しているものはブロックそのものであるということ。ブロックそのものを渡す際は、&(アンパサンド)が必須です。でなければ、Rubyはブロックを普通の引数として認識してしまいます。また、ブロックは一つのメソッドに一つまでしか渡せません。 これらを解決するのがProcオブジェクト! ブロックの代わりにProcオブジェクトを渡すことを考えてみましょう。 オブジェクトを渡すのであれば普通の引数で渡せますね!てことで&が必要なくなります。 また、一つのメソッドに一つまでの制限もなくなります。 では、実際にProcオブジェクトを渡した場合のコードを見てみましょう。 ブロックの代わりにProcオブジェクトを渡す def greeting(b) puts 'おはよー!' text = b.call('こんばんはー!', 'せい!') # ブロックの処理内容を実行 puts text end # ブロックを引数に渡して実行 greeting_proc = Proc.new {|text1, text2| puts text1 + text2 } greeting(greeting_proc) 出力 おはよー! こんばんはー!せい! どうですか?Procオブジェクトがなんとなく理解できましたか? まだクリアになっていない方はこの章を何度か読み直してみてください! ラムダ式 最後の章です! 後少し頑張ってください! ラムダ式はコンピュータサイエンスの基礎であり、JavaScriptなどでも登場する呼び出し可能オブジェクトです。 ラムダ式に関して説明しようと思うとそれだけで記事が書けてしまうので、ここではProcとの絡みにのみ着目してお話します。 ラムダ式とは、以下のような表記のことです! ラムダ式 ->(a, b){ a + b } or lambda {|a, b| a + b } そしてこの表記で何ができるかというと、Procと同じくブロックのオブジェクトが作れます。 ここでProcの表記と比較してみます。 意味は一緒です! Procとラムダの比較 block_obj = Proc.new {|a, b| a + b } block_obj = ->(a, b){ a + b } 二つの挙動に細かい違いはありますが、まず使えるようになることが目的であればまずはこれでオッケーです。 では最後に例のごとくgreetingメソッドを使ってラムダ式を実践してみましょう! greeting.rb def greeting(b) puts 'おはよー!' text = b.call('こんばんはー') puts text end block_obj = ->(text){ puts "#{text}!!" } greeting(block_obj) 出力 'おはよー!' 'こんばんはー!!' まとめ ブロックとyieldとcallとProcとラムダ式について学んできました。 この辺りは未経験にとっての登竜門で、避けて通ってきた未経験の方やエンジニア1年生の方も多いのではないでしょうか。 Railsなどのフレームワークを学ぶことはもちろん大事ですが、それと同じかそれ以上にコンピュータサイエンスの基礎や、言語の理解を深めることは大事だと思っています! この記事が、皆様がそういった基礎概念に目を向けるきっかけになれば幸いです。 Twitterで「未経験の方・エンジニア1年目の方に向けて、エンジニア1年目の学びを発信」してますので、興味がある方は是非のぞいてみてください! では長らくお付き合いいただきありがとうございました! また、次の記事でお会いしましょう〜!? -END-
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】フォロー機能の実装(非同期)

目的 Railsで作成したアプリに非同期でのフォロー機能を導入する。 開発環境 macOS: Big Sur Rubyバージョン: 2.6.5 Railsバージョン: 6.0.0 前提 アプリtest-appが作成されている。 【Rails】簡単な投稿アプリの作成 deviseが導入されている。 【Rails】ユーザー管理機能(devise)の導入 jQueryが導入されている。 【Rails】iQueryの導入 手順 はじめに ルーティングの設定 Relationshipモデルの作成 アソシエーション設定 バリデーションの設定 メソッドの定義 relationshipsコントローラーの作成 アクションの定義 フォローボタンの表示 jsファイルの作成 はじめに 今回はフォロー機能を実装していきます。 フォロー機能に必要なテーブルは中間テーブルであるrelationshipsですが、 フォローするuserとフォローされるuserは同じなのでアソシエーションが通常の「多対多」とは異なります。 ルーティングの設定 それでは早速始めていきます! まずはルーティングの設定です。 config/routes.rb Rails.application.routes.draw do #省略 resources :relationships, only: [:create, :destroy] end 機能としてはフォローとフォロー解除のみなので、createとdestroyのみを設定します。 Relationshipモデルの作成 ターミナル % rails g model relationship 続いてテーブルを作成していきます。 db/migrate/20XXXXXXXXXXXX_create_relationships.rb class CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.references :user, foreign_key: true t.references :follower, foreign_key: { to_table: :users } t.timestamps t.index [:user_id, :follower_id], unique: true end end end t.references :follower, foreign_key: { to_table: :users }の記述ですが、 follower_idはusersテーブルを参照しなければならないため{ to_table: :users }という記述でテーブルを指定しています。 t.index [:user_id, :follower_id], unique: trueはuser_idとfollower_idのペアが重複しないための記述です。 ある1人のユーザーが、あるユーザーを何度もフォローできないようにしています。 ターミナル % rails db:migrate マイグレーションも忘れずに! アソシエーションの設定 app/models/relationship.rb class Relationship < ApplicationRecord belongs_to :user belongs_to :follower, class_name: 'User' end class_name: 'User'の記述で、Userモデルを参照するように指示しています。 app/models/user.rb class User < ApplicationRecord has_many :relationships, dependent: :destroy has_many :followings, through: :relationships, source: :follower has_many :passive_relationships, class_name: 'Relationship', foreign_key: 'follower_id', dependent: :destroy has_many :followers, through: :passive_relationships, source: :user end followings, through: :relationships, source: :followerの記述は、 relationshipsテーブルのfollower_idを参考にして、followingsモデルにアクセスするという意味です。 結果として、user.followingsと記述するだけで、フォローしているUserを取得できます。 has_many :followers, through: :passive_relationships, source: :userの記述も同様で、user.followersと記述するだけでm自分をフォローしているUserを取得できます。 バリデーションの設定 app/models/relationship.rb class Relationship < ApplicationRecord #省略 with_options presence: true do validates :user_id validates :follower_id end end user_idとfollow_idは必須というバリデーションを追加します。 メソッドの定義 後ほど使用するメソッドを定義します。 app/models/user.rb class User < ApplicationRecord #省略 def following?(user) followings.include?(user) end def follow(user_id) relationships.create(follower: user_id) end def unfollow(relationship_id) relationships.find(relationship_id).destroy! end end following?は既にフォローしているか?という記述、followはフォローを保存する記述、unfollowはフォロー解除する記述です。 relationshipsコントローラーの作成 ターミナル % rails g controller relationships アクションの定義 フォローとフォロー解除機能のため、createアクションとdestroyアクションを定義します。 app/controllers/relationships_controller.rb class RelationshipsController < ApplicationController before_action :authenticate_user! def create @user = User.find(params[:follower]) current_user.follow(@user) end def destroy @user = current_user.relationships.find(params[:id]).follower current_user.unfollow(params[:id]) end end 先ほど定義したメソッドはここで使用します。 フォローボタンの表示 フォローボタンの表示は部分テンプレートとして切り分け、ユーザー詳細ページに部分テンプレートを呼び出す記述をします。 app/views/users/show.html.erb <div id = "relationship_<%= @user.id %>"> <%= render partial: "relationships/relationship", locals: { user: @user } %> </div> 部分テンプレートとして切り分けたページです。 app/views/users/_relationship.html.erb <% if user_signed_in? && current_user.id != @user.id %> <% if current_user.following?(@user) %> <%= button_to "フォロー解除", relationship_path(current_user.relationships.find_by(follower: @user)), method: :delete, remote: true %> <% else %> <%= button_to "フォロー", relationships_path(follower: @user), method: :post, remote: true %> <% end %> <% end %> サインインしているか?と既にフォローしていないか?という条件でフォローボタンとフォロー解除ボタンを条件分岐しています。 jsファイルの作成 フォロー機能を非同期で行うためのjsファイルを作成します。 app/views/relationships/create.js.erb $('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>"); app/views/relationships/destroy.js.erb $('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>"); これで非同期でのフォロー機能が実装できたと思います。 確認してみてください! 最後に 以上でフォロー機能の実装は完了です。 CSSは実装していないため、簡素な見た目となっています。各自必要に応じて編集してください。 では。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fakerを利用してダミーデーター作成

使う意味 掲示板などを作成して投稿されたらどのようになるのか、確かめたい時に一個一個手作業で書くよりダミーデーターを作成して確認するため。 Gemのインストール Gemfile gem 'faker' そしてbundle install seedファイル編集 db/seeds.rb 20.times do      User.create!( first_name: Faker::Name.first_name,  last_name: Faker::Name.last_name, email: Faker::Internet.email, password: '123qwe', password_confirmation: '123qwe' ) end と私の場合このように記載する 20.times doで20個作成する first_name: Faker::Name.first_nameはfirst_nameにFakerのNameの中のfirst_nameメゾットを使うという意味 なのでpasswordに関してはfakerのメゾットは使わず20個全部のpasswordを123qweにしている。 createだとtrue/falseしか返しません。これだとuserの作成に失敗した場合に、エラーが返ってきません。なので、create! を使います。 ,はお忘れずに 最後に ターミナルで rails db:seed
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでメールが急に送れなくなった時の解決法

どうも、三町哲平です! 夜中3時にふと手動でポートフォリオのテストをしようと思い投稿機能やログイン機能などをいじり倒していたのですがその時にアカウントの新規登録をしようと思いました。 今までだと、ユーザー名/メールアドレス/パスワード/再度パスワードを入力して新規登録するボタンを押したら、認証メールが登録したメールアドレスへ送られて、そのメールのリンクをクリックすることでユーザー登録完了な訳なのですが、なぜか今回はメールが送れない... ちなみに出先だった為、本番環境で試していたのですが、 新規登録するをポチッ ・・・ このページは表示できません ちなみにこのページは、500 Internal Server Errorの表示なので、Webサーバーに何らかの問題が発生した際のエラーになります。 今回は、このエラーの解決までをまとめました。 ※新規登録は、下記の内容で登録 ユーザー名: test メールアドレス: f●●●●●●@rakuten.jp エラーを調べよう 今回発生した500 Internal Server Errorですが、あくまでこの表示は本番環境になりますので、エラー表示はデフォルトで非表示になっています。よって詳細までは分かりません。 ここで詳細を調べようと思ったら、開発環境で試してみる事が一番なのかもしれませんが、あくまで出先で本番環境しか触れない中で試せる事といったら、似たような動きをする所を手当たり次第次第調べていくことになりますが、今回の事象で考えられる仮説は、 deviseのgemがおかしな挙動をしているのではないか? メール機能が使えなくなっているのではないか? 上記の2点が思い付いたので一つずつ仮説を試していきましょう! 1. deviseのgemがおかしな挙動をしているのではないか? まず、deviseの挙動がおかしくないか調べてみますが、今回やったのは、 ① 既に登録しているアカウントでログインしてみる テストユーザーでログインできました! ② 今回、新規登録したユーザーが仮登録されているか調べる test(新規登録したユーザー)が一覧に仮登録されています! deviseは、問題無いですね。 2. メール機能が使えなくなっているのではないか? メール機能が使えるかどうか調べるには、他にメールを使用している機能を試してみたら分かりますね。 ① 新規登録したアカウントの確認メールを送ってみる ⬇︎ エラーが発生! ② 今回、新規登録したユーザーが仮登録されているか調べる ⬇︎ またしてもエラーが発生! どうも本番環境で、メールを送れなくなっているみたいです。 開発環境で調べる ここからは、自宅に帰って本番環境で行った対応と同じ対応をしましたが結果は、本番環境も開発環境も同じタイミングでエラーが発生しました。 しかし、本番環境と開発環境の大きな違いは、エラー内容を詳細に見れることです。 ちなみに今回のエラー内容の詳細は、 初めて見るエラーですね...。 とにかくわからないエラーに関しては、ググってみるに限るのでググってみました。 535-5.7.8 Username and Password not accepted. Learn more atで、検索 上記の記事がヒットしたので、記事を参考にエラー解決を試みました。 ...が、エラー解決には至りませんでしたが、答えは実は単純でした。 Username and Password not acceptedを翻訳してみると、ユーザー名とパスワードが受け入れられませんと日本語化されます。 ユーザー名とパスワードは、絶対登録してあるし、誤字脱字もない筈なのに...と思いポートフォリオで使用していたgmailのGoogleアカウントのセキュリティからアプリ パスワードの項目を見てみると、 なしって書いているんですが... つまりアプリ パスワードが登録していないことになっていました。 アプリ パスワードを消した心当たりが無いのに、何で消えているのかはこの時直ぐには分からなかったのですが、これしか考えられないということで、アプリ パスワードを再設定し直して、環境変数に追加後、開発環境でお問い合わせフォームを使用してみると、 お問い合わせいただきありがとうございました。 無事、お問い合わせ機能使用出来ていますね! ※その他、最初の本題であった新規登録や確認メールも無事復旧しました。 新規登録をしてみましょう! ⬇︎ 本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。 出来ましたね! 認証メールも問題無く届いていました! この後、開発環境だけではなく本番環境でも試しましたが共に正常に動くようになりました。 良かったです。 さいごに メールは送れるようになりましたが、結局なぜ一度登録していたはずのアプリ パスワードが消えていたのか!?その問題は解決していませんが、これに関しては実はこの記事の中にヒントががありました。 ↑一度使用したこの画像です。 画像のこの部分です。 前回の変更: 5月30日 1週間前にGoogleアカウントのパスワードをセキュリティ対策の為、再設定していました。(この記事のエラー発生は、6月6日) 100%これが原因とまでは言い切れませんが、変化点と言えばここしかないのでほぼ100%でこれが原因と言って間違い無いでしょう。 次回、パスワード変更時に確認が必要ですね。 Googleパスワードを再設定すると、アプリ パスワードが自動的に削除される。 ひとまず解決です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでメールが送れなくなった時の解決法

どうも、三町哲平です! 夜中3時にふと手動でポートフォリオのテストをしようと思い投稿機能やログイン機能などをいじり倒していたのですがその時にアカウントの新規登録をしようと思いました。 今までだと、ユーザー名/メールアドレス/パスワード/再度パスワードを入力して新規登録するボタンを押したら、認証メールが登録したメールアドレスへ送られて、そのメールのリンクをクリックすることでユーザー登録完了な訳なのですが、なぜか今回はメールが送れない... ちなみに出先だった為、本番環境で試していたのですが、 新規登録するをポチッ ・・・ このページは表示できません ちなみにこのページは、500 Internal Server Errorの表示なので、Webサーバーに何らかの問題が発生した際のエラーになります。 今回は、このエラーの解決までをまとめました。 ※新規登録は、下記の内容で登録 ユーザー名: test メールアドレス: f●●●●●●@rakuten.jp エラーを調べよう 今回発生した500 Internal Server Errorですが、あくまでこの表示は本番環境になりますので、エラー表示はデフォルトで非表示になっています。よって詳細までは分かりません。 ここで詳細を調べようと思ったら、開発環境で試してみる事が一番なのかもしれませんが、あくまで出先で本番環境しか触れない中で試せる事といったら、似たような動きをする所を手当たり次第調べていくことになりますが、今回の事象で考えられる仮説は、 deviseのgemがおかしな挙動をしているのではないか? メール機能が使えなくなっているのではないか? 上記の2点が思い付いたので一つずつ仮説を試していきましょう! 1. deviseのgemがおかしな挙動をしているのではないか? まず、deviseの挙動がおかしくないか調べてみますが、今回やったのは、 ① 既に登録しているアカウントでログインしてみる テストユーザーでログインできました! ② 今回、新規登録したユーザーが仮登録されているか調べる test(新規登録したユーザー)が一覧に仮登録されています! deviseは、問題無いですね。 2. メール機能が使えなくなっているのではないか? メール機能が使えるかどうか調べるには、他にメールを使用しているところを試してみたら分かりますね。 ① 新規登録したアカウントの確認メールを送ってみる ⬇︎ エラーが発生! ② 今回、新規登録したユーザーが仮登録されているか調べる ⬇︎ またしてもエラーが発生! どうも本番環境で、メールを送れなくなっているみたいです。 開発環境で調べる ここからは、自宅に帰って本番環境で行った対応と同じ対応をしましたが結果は、本番環境も開発環境も同じタイミングでエラーが発生しました。 しかし、本番環境と開発環境の大きな違いは、エラー内容を詳細に見れることです。 ちなみに今回のエラー内容の詳細は、 初めて見るエラーですね...。 とにかくわからないエラーに関しては、ググってみるに限るのでググってみました。 535-5.7.8 Username and Password not accepted. Learn more atで、検索 上記の記事がヒットしたので、記事を参考にエラー解決を試みました。 ...が、エラー解決には至りませんでした。 しかし、Username and Password not acceptedを翻訳してみると、ユーザー名とパスワードが受け入れられませんと日本語化されます。 ユーザー名とパスワードは、絶対登録してあるし、誤字脱字もない筈なのに...と思いポートフォリオで使用していたgmailのGoogleアカウントのセキュリティからアプリ パスワードの項目を見てみると、 なしって書いているんですが... つまりアプリ パスワードが登録していないことになっていました。 アプリ パスワードを消した心当たりが無いのに、何で消えているのかはこの時直ぐには分からなかったのですが、これしか考えられないということで、アプリ パスワードを再設定し直して、環境変数に追加後、開発環境でお問い合わせフォームを使用してみると、 お問い合わせいただきありがとうございました。 無事、お問い合わせ機能使用出来ていますね! ※その他、最初の本題であった新規登録や確認メールも無事復旧しました。 新規登録をしてみましょう! ⬇︎ 本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。 出来ましたね! 認証メールも問題無く届いていました! この後、開発環境だけではなく本番環境でも試しましたが共に正常に動くようになりました。 良かったです。 さいごに メールは送れるようになりましたが、結局なぜ一度登録していたはずのアプリ パスワードが消えていたのか!?その問題は解決していませんが、これに関しては実はこの記事の中にヒントががありました。 ↑一度使用したこの画像です。 画像のこの部分です。 前回の変更: 5月30日 1週間前にGoogleアカウントのパスワードをセキュリティ対策の為、再設定していました。(この記事のエラー発生は、6月6日) 100%これが原因とまでは言い切れませんが、変化点と言えばここしかないのでほぼ100%でこれが原因と言って間違い無いでしょう。 次回、パスワード変更時に確認が必要ですね。 Googleパスワードを再設定すると、アプリ パスワードが自動的に削除される。 ひとまず解決です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー対応】Railsでメールが送れなくなった時の解決法

どうも、三町哲平です! 夜中3時にふと手動でポートフォリオのテストをしようと思い投稿機能やログイン機能などをいじり倒していたのですがその時にアカウントの新規登録をしようと思いました。 今までだと、ユーザー名/メールアドレス/パスワード/再度パスワードを入力して新規登録するボタンを押したら、認証メールが登録したメールアドレスへ送られて、そのメールのリンクをクリックすることでユーザー登録完了な訳なのですが、なぜか今回はメールが送れない... ちなみに出先だった為、本番環境で試していたのですが、 新規登録するをポチッ ・・・ このページは表示できません ちなみにこのページは、500 Internal Server Errorの表示なので、Webサーバーに何らかの問題が発生した際のエラーになります。 今回は、このエラーの解決までをまとめました。 ※新規登録は、下記の内容で登録 ユーザー名: test メールアドレス: f●●●●●●@rakuten.jp エラーを調べよう 今回発生した500 Internal Server Errorですが、あくまでこの表示は本番環境になりますので、エラー表示はデフォルトで非表示になっています。よって詳細までは分かりません。 ここで詳細を調べようと思ったら、開発環境で試してみる事が一番なのかもしれませんが、あくまで出先で本番環境しか触れない中で試せる事といったら、似たような動きをする所を手当たり次第調べていくことになりますが、今回の事象で考えられる仮説は、 deviseのgemがおかしな挙動をしているのではないか? メール機能が使えなくなっているのではないか? 上記の2点が思い付いたので一つずつ仮説を試していきましょう! 1. deviseのgemがおかしな挙動をしているのではないか? まず、deviseの挙動がおかしくないか調べてみますが、今回やったのは、 ① 既に登録しているアカウントでログインしてみる テストユーザーでログインできました! ② 今回、新規登録したユーザーが仮登録されているか調べる test(新規登録したユーザー)が一覧に仮登録されています! deviseは、問題無いですね。 2. メール機能が使えなくなっているのではないか? メール機能が使えるかどうか調べるには、他にメールを使用しているところを試してみたら分かりますね。 ① 新規登録したアカウントの確認メールを送ってみる ⬇︎ エラーが発生! ② 今回、新規登録したユーザーが仮登録されているか調べる ⬇︎ またしてもエラーが発生! どうも本番環境で、メールを送れなくなっているみたいです。 開発環境で調べる ここからは、自宅に帰って本番環境で行った対応と同じ対応をしましたが結果は、本番環境も開発環境も同じタイミングでエラーが発生しました。 しかし、本番環境と開発環境の大きな違いは、エラー内容を詳細に見れることです。 ちなみに今回のエラー内容の詳細は、 初めて見るエラーですね...。 とにかくわからないエラーに関しては、ググってみるに限るのでググってみました。 535-5.7.8 Username and Password not accepted. Learn more atで、検索 上記の記事がヒットしたので、記事を参考にエラー解決を試みました。 ...が、エラー解決には至りませんでした。 しかし、Username and Password not acceptedを翻訳してみると、ユーザー名とパスワードが受け入れられませんと日本語化されます。 ユーザー名とパスワードは、絶対登録してあるし、誤字脱字もない筈なのに...と思いポートフォリオで使用していたgmailのGoogleアカウントのセキュリティからアプリ パスワードの項目を見てみると、 なしって書いているんですが... つまりアプリ パスワードが登録していないことになっていました。 アプリ パスワードを消した心当たりが無いのに、何で消えているのかはこの時直ぐには分からなかったのですが、これしか考えられないということで、アプリ パスワードを再設定し直して、環境変数に追加後、開発環境でお問い合わせフォームを使用してみると、 お問い合わせいただきありがとうございました。 無事、お問い合わせ機能使用出来ていますね! ※その他、最初の本題であった新規登録や確認メールも無事復旧しました。 新規登録をしてみましょう! ⬇︎ 本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。 出来ましたね! 認証メールも問題無く届いていました! この後、開発環境だけではなく本番環境でも試しましたが共に正常に動くようになりました。 良かったです。 さいごに メールは送れるようになりましたが、結局なぜ一度登録していたはずのアプリ パスワードが消えていたのか!?その問題は解決していませんが、これに関しては実はこの記事の既出画像の中にヒントががありました。 ↑一度使用したこの画像です。 画像のこの部分です。 前回の変更: 5月30日 1週間前にGoogleアカウントのパスワードをセキュリティ対策の為、再設定していました。(この記事のエラー発生は、6月6日) 100%これが原因とまでは言い切れませんが、変化点と言えばここしかないのでほぼ100%でこれが原因と言って間違い無いでしょう。 次回、パスワード変更時に確認が必要ですね。 Googleパスワードを再設定すると、アプリ パスワードが自動的に削除される。 ひとまず解決です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveRecordやRubyの!?について

絶賛Rails勉強中なので適当に使っていた!や?をまとめます ActiveRecordの! create,save,update,destroy等のメソッドの後ろに!をつけた場合、それぞれのCRUD処理が失敗した場合に例外(ActiveRecord::RecordNotFound)を返すようになります。 !を付けない場合は成功した場合true、失敗した場合falseが返却されます。 Rubyの! rubyのメソッドに!をつけた場合は「破壊的メソッド」になるそうです。なに言ってんのかわかんないですね。 破壊的メソッドとは? この方の記事がわかりやすかったです。 https://qiita.com/tentom/items/0164b68dff94702e3880 破壊的メソッドにした場合はオブジェクトが再生成されます。 非破壊的メソッどはオブジェクトは変わらないまま値だけが変わります。 ? メソッドに?を付けることで真偽値を返すメソッドだということを直感的に表すことができます。 つけなくても真偽値が返ってくることには変わりありませんが、付けた方がわかりやすくなっていいと思います。 メソッドに?を付けた場合、メソッドを呼び出すときにも?を付ける必要があります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む