20200911のRailsに関する記事は18件です。

rails チュートリアル

4章まで終了
次回5章から

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

ユーザー管理機能をウィザード形式で実装する

概要

今回は、ユーザー管理機能をウィザード形式で実装することに関してです。
ウィザード形式とは、画面遷移しながらユーザー登録を行っていく形式です。

今回、userモデルの登録要件は
nickname, grade, email, password

続いて画面遷移してuser_infoモデルに
subject, school, profile

を登録します。

ちなみに、user_infoは得意教科、在籍校、プロフィールとなっています。

実装

まずはdeviseをインストールして、userモデルを作成します。

emailとpasswordはデフォルトで入っているので、nicknameとgradeを追加します。

カラムを追加したので、次のようにコントローラーを編集します。

controllers.application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :grade])
  end
end

次に、userモデルにバリデーションをかけます(コードは省略)

そして、ビューファイルを作ります。

views.devise.registrations.new.html.erb
<h2>ユーザー情報登録</h2>

<%= form_for(@user, url: user_registration_path) do |f| %>
  <%= render "devise/shared/error_messages", resource: @user %>

  <div class="field">
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname %>
  </div>

  <div class="field">
    <%= f.label :grade %><br />
    <%= f.text_field :grade %>
  </div>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

ログイン画面を作ります。

views.home.index.html.erb
<h1>トップページ</h1>

<% if user_signed_in?%>
  <h2>ログインしています</h2>
  <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
<% else %>
  <h2>ログインしていません</h2>
  <%= link_to "新規登録", new_user_registration_path %>
  <%= link_to "ログイン", new_user_session_path %>
<% end %>

画面遷移後のuser_infoモデルを作ります。

この時、外部キーとして、user_idを紐づけるのを忘れないようにします。

その後、userモデルとuser_infoモデルのアソシエーションを組みます。

次はdeviseのコントローラーを作成。

rails g devise:controllers users

次にルーティング。

routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  root to: "home#index"
end

ここまできたらuser_infoを登録する実装をしていきます。

registrationsコントローラーにnewメソッドを記述し、ビューファイルにformを入れます。

views/devise/registrations/new.html.erb
<%= form_for(@user, url: user_registration_path) do |f| %>
  <%= render "devise/shared/error_messages", resource: @user %>

---省略---

 <div class="actions">
    <%= f.submit "Next" %>
  </div>
<% end %>

ここからが少し記述が増えてきます。
createアクションを記述していきましょう。

controllers/users/registrations_controller.rb
def create
   @user = User.new(sign_up_params)
    unless @user.valid?
      render :new and return
    end
   session["devise.regist_data"] = {user: @user.attributes}
   session["devise.regist_data"][:user]["password"] = params[:user][:password]
   @user_info = @user.build_user_info
   render :new_user_info
 end

sessionを用いて1ページ目に記述したデータを持ってきます。
そしてattributesメソッドでデータを整形しています。

build_user_infoで今回生成したインスタンス@userに紐づくUserInfoモデルのインスタンスを生成します。ここで生成したUserInfoモデルのインスタンスは、@user_infoというインスタンス変数に代入します。そして、住所情報を登録させるページを表示するnew_user_infoアクションのビューへrenderします。

user_infoを登録させるページを表示するnew_user_infoアクションと住所情報を登録するcreate_user_infoアクションのルーティングを設定しましょう。

routes.rb
Rails.application.routes.draw do

  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  devise_scope :user do
    get 'user_infos', to: 'users/registrations#new_user_info'
    post 'user_infos', to: 'users/registrations#create_user_info'
  end

  resources :posts

  root to: "home#index"
end

このルーティングに書いた通り、user_infoを登録するビューファイルを作成していきます。

views/devise/registrations/new_user_info.html.erb
<h2>ユーザー情報登録</h2>

<%= form_for @user_info do |f| %>
  <%= render "devise/shared/error_messages", resource: @user_info %>

  <div class="field">
    <%= f.label :subject, "頑張りたい教科" %><br />
    <%= f.text_field :subject %>
  </div>

  <div class="field">
    <%= f.label :school, "学校名" %><br />
    <%= f.text_field :school %>
  </div>

  <div class="field">
    <%= f.label :profile, "自己紹介を記入しましょう" %><br />
    <%= f.text_area :profile %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

このuser_infoを保存する記述をコントローラーにしましょう。

controllers/users/registrations_controller.rb
def create_user_info
    @user = User.new(session["devise.regist_data"]["user"])
    @user_info = UserInfo.new(user_info_params)
      unless @user_info.valid?
        render :new_user_info
      end
    @user.build_user_info(@user_info.attributes)
    @user.save
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
  end

  protected

  def user_info_params
    params.require(:user_info).permit(:subject, :school, :profile)
  end

userをsaveした後に、.clearでsessionを削除しています。
そして、保存された後にログインされるように記述されています。

最後に、create_user_infoアクションに対応するビューを作成します。

views/devise/registrations/create_user_info.html.erb
<h2>登録が完了しました</h2>
<%= link_to "トップへ戻る", root_path%>

以上です。

感想

初めてウィザード形式の登録機能を実装しました。

本当はgradeの学年のところをactive_hashで実装したかったのですが、二時間エラーに悩まされた末に断念しました、、、

一通り実装してみて余裕があったらまたチャレンジしてみます。

日々頑張ってはいるのですが、あまり成長を感じられないです。
めちゃくちゃ記事みないと何もできません、、、

頑張ります。

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

《コロナに負けず!!》29歳で営業職からジョブチェンジ。未経験で受託開発企業から内定を頂けました。

はじめに

昨年「エンジニアになるぞ!」と決意をし、
今年の3月に前職を退職(最終出社)、
3月下旬からプログラミングスクールに通いました。

現在、29歳(今年で30歳!)です。
新卒からずっと営業職で、プログラミングは未経験。

そんな自分でも、webエンジニアのスタートラインに
立つ事が出来ました。

自分なりに戦略を立てて行った就活でもありましたので、
就職活動中の方の参考にして頂ければ幸いです。

目次

・工夫して就活を行おうとした背景
・就活の結果
・学習開始〜内定までの流れ
・今回の転職活動でのポイント
・ポートフォリオについてのポイント
・学習方法について(おまけ)
・最後に

工夫して就活を行おうとした背景

(興味ない方は読み飛ばして下さい!笑)

スクールに入った時点の私は転職に関しての認識が甘く、

「高い金額のスクールで基礎をしっかり学べば、転職はできるはず!」
「スクールからも紹介があるし!」
などと、このくらいの認識でした。

ですが、この認識は間違いです!!!

少なくとも、20代前半の若い方でない限りは
エージェントからの紹介案件はごく少数になります。
※SESならいっぱいあります!

企業側としてはエージェントを通して採用をすると、
その人の年収の30%程を支払う必要があります。

つまり年収350万円の人材なら105万円もお金がかかります。

それならば、エージェント経由ではより若く優秀な人材だけを
選びたいですよね?

そのため、エージェントから紹介される案件は
応募者の年齢や経験などが非常に大事になります。

そのため、年齢が高くなるほど
そもそも紹介される案件の数は絞られます。

そこに、今回はコロナによる経済打撃もあり
自分がスクールでの学習を終えた2020年6月、
「未経験」でのスクール経由の募集案件は3件のみでした。

しかし、3社とも書類選考が通らず、、、涙

そこでやっと、このままでは「やばいぞ!!」と
気付かされました。。。

そこで、ここから必死に情報の収集と
採用までの戦略を立てることにしました。

就活結果について

《内定》
・2社(受託開発)

《応募》
・企業HPからの直接応募:20社
・スクールのエージェント経由での応募:3社
・求人サイトでの応募:10社ほど
・DODAのエージェント経由での応募:20社ほど

学習開始〜内定までの流れ

◇学習期間
2020年3月末月〜7月末(4ヶ月間)

◇転職活動期間
2020年8月(1ヶ月間)

《3月末〜5月末まで》
プログラミングスクールにて学習、卒業

《6月》
1ヶ月かけて初めての自主制作アプリを作成!

ですが、他の方のポートフォリオのレベルの高さを見て絶望し、
2つ目のポートフォリの作成を決意しました!

《7月》
2週間程で2つ目のポートフォリを作成!
そこから2週間はインフラの環境構築やCI/CDに
挑戦していました。

《8月》
企業HPからのweb応募を開始しました!
8月末に内定を獲得!

今回の転職活動でのポイント

  1. しっかり作り込んだポートフォリオを用意した
    →スクール卒業後、すぐに転職活動を開始せずにポートフォリオの強化に努めました

  2. 応募は企業のHPから送ることを徹底した
    →書類選考の突破が重要です

上記の2点が今回の就活でのポイントになります。

ポートフォリオについてのポイント

自主制作アプリ:「BestYourTitle」

このアプリは、求人広告のライター初心者や採用担当者が、
求人広告を書く際に「応募したい!」と思ってもらえるような
キャッチコピーを書けるよう、練習出来るサイトです。

▼URL
https://www.bestyourtitle.com/
※かんたんログイン機能で、ゲストとしてログイン出来ます。

▼GitHub
https://github.com/y-kiku1111/docker_byt
※ポートフォリオに関しての詳細は、上記Githubに記載しています

0807b84593e579d5e9515addd31381f5.gif

87216988-a08bc980-c37f-11ea-8c7a-46716366a1cb.jpg

内定頂いた2社とも、ポートフォリオについて
非常に高評価を頂けました。

特にDocker、AWSのFargateは
印象が良かったです。

ただ、実際に見て頂くと
サイト自体はレベルの高いものではないので、

・モダンな技術に挑戦し、その企業で活用している技術とマッチした
・サイトデザインやインフラ構成図など、目に見える差別化を行う

この2点に尽きるのかなと思います。

また、ポートフォリオに関しましては
youtuberの勝又さんの動画やオンラインサロンで
「高評価されるポートフォリオ」がどの程度のレベルなのかを
知ることが出来、とても参考にさせて頂きました!

スクールではポートフォリオに関してのアドバイスは
一切ありませんでしたので、

実際に採用された初学者のポートフォリオが
数多く見れるのは、非常に有り難かったです。

就職活動についてのポイント

・求人サイトを使って応募したい企業のリストアップ
・エージェントサービスを利用しての面接練習や情報の入手(応募もしましょう)
・上記を行った上でHPからの直接応募

これが自分の中では、最も効率的かと思います。

リストアップに利用した求人サイト
・wantedly
・Green
・Find job!

特にFindjobは、業務未経験OKの企業で絞ることが出来て
重宝しました。

基本的には上記のサイトから応募するのではなく、
サイトからリストアップをして直接応募をします。

なぜ直接、採用HPから応募をするかと言うと、
求人サイトによっては、エージェントと同じく
掲載は無料だが、採用すると企業にお金がかかるサイトもあるため
企業HPからの応募が、企業側にメリットが大きいためです。

採用課金型でなければ、
求人サイトからの応募も一緒にした方が
良いかと思います。

例えば、1日10社を目標に各企業のHPから
応募をし続けると良いと思います。
(自分は1日5社と決めていました)

書類選考さえ突破すれば、あとはエージェントさんと
練習した面接だけです。

しっかりと自分がエンジニアになりたい理由、
また作り込んだポートフォリについて熱弁できれば問題無いはずです!

学習方法について(おまけ)

個人的には、強制的に勉強に集中し基礎を学べるスクールは
重宝しましたので、やるかやらないかなら、お勧めです。

ただ、スクールはあくまで基礎を学ぶための環境にお金を出すようなイメージです。
そのため、スクール費用が高すぎると割に合わない可能性もあります!

スクールを卒業するだけでは、レベルとしては確実に低いと思いますので、
例えばメンターさんを雇いながらポートフォリオのレベルを高めるなど、
基礎を身につけてからの自己学習を強く推奨します。

自分はMENTAというサービスを利用していました。
(https://menta.work/about)

最後に

ここまで長文を読んで頂き、
本当にありがとうございました!

長くなりましたが、上記が自分の転職活動で
意識したことや、実践したことになります。

少しでも、お役に立てましたら幸いです。

また、今回やっとエンジニアのひよっことして
スタートラインに立てたのは、自分の転職活動を支えて下さった
周りの方々のお陰です。

無職になったのにも関わらず応援してくれた同居人や、
期待を込めて送り出して頂いた前職の上司や同僚、
心配しながらも応援して下さった両親、

本当にありがとうございました!!!

就職した企業は、ひよっこではなく
成果を出す人材を期待して頂いているかと思いますので、
その期待を超えられるよう、頑張って行きます!

改めまして、最後までお読み頂きありがとうございました!

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

[Rails]Formオブジェクト使用時にエラーメッセージが日本語化されない[locale]

概要

オリジナルアプリ制作時、formオブジェクトを利用した箇所のエラーメッセージだけ日本語化されなかったのでその備忘録を。

https://qiita.com/rytr823/items/e9fbbb16884b6f52d463
調べていたら上記の方とほとんど同じでした。感謝。

前提

ja.ymlファイルの作成までは終わっている

環境

ruby 2.6.5
rails 6.0.0
gem 'rails-i18n'導入済み

formオブジェクトの配置場所

app/forms/guest_data.rb

EZ図

この図のguestsテーブルとbathsテーブルとdrinksテーブルの3つをformオブジェクトを使用して保存しようとしていた。

兵声援EZ図.png

内容

ja.ymlに記述しても日本語化が適用されない

間違ってた記述

config/locales/ja.yml
ja:
  activerecord:
    attributes:
      guest:
       first_name: 苗字(全角)
       last_name: 名前(全角)
       first_name_kana: 苗字(カタカナ)
       last_name_kana: 名前(カタカナ)
       gender_id: 性別
       visit1_id: 1回目の利用日
       visit2_id: 2回目の利用日
       description: 利用者の詳細
      bath:
       bathing_id: 入浴の種類
       infection_id: 対応が必要な感染症
       timing_id: 入浴の順番
       remark_bath: 入浴の備考
      drink:
       drink_type_id: 飲み物の種類
       thickness_id: とろみの量
       warm: 温めるか
       diabetes: 糖尿病

正しい記述

config/locales/ja.yml
ja:
  activemodel:
    attributes:
      guest_data:
        first_name: 苗字(全角)
        last_name: 名前(全角)
        first_name_kana: 苗字(カタカナ)
        last_name_kana: 名前(カタカナ)
        gender_id: 性別
        visit1_id: 1回目の利用日
        visit2_id: 2回目の利用日
        description: 利用者の詳細
        bathing_id: 入浴の種類
        infection_id: 対応が必要な感染症の有無
        timing_id: 入浴の順番
        remark_bath: 入浴の備考
        drink_type_id: 飲み物の種類
        thickness_id: とろみの量
        warm: 温めるか
        diabetes: 糖尿病

原因

記述をしたapp/forms/guest_data.rbはactiverecordではなくactivemodelを継承していたため。

参考記事

https://qiita.com/rytr823/items/e9fbbb16884b6f52d463
https://qiita.com/jacoyutorius/items/e0d2cfdb61ff4c4fd1e7
https://qiita.com/astap/items/c07e0b08273fd97aaa91

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

Railsコンソールを使用してレコードを追加する

動機

Railsでアプリケーション開発を行っている際DBへレコードの追加をどう行うか忘れてしまうため記録

前提:コンソールの立ち上げ

$ rails c

レコードの追加

レコードの追加は createメソッドを使用

[1] pry(main)> モデル名.create(カラム名: '追加したいvalue')

例)

[1] pry(main)> Todo.create(title: 'Rails',content: 'Rails Practice' )

他にも

・create!
  何かが足りない時にエラーを返してくれる、絶対に不足しては困るものがある場合使用

・save
  インスタンスを作成しsaveを使用することでcreateと同じ
  こちらもsave!がある

インスタンス名 = モデル名.new({ カラム名: ‘文言’, カラム名: ‘文言’ })
インスタンス名.save

・update
  データの更新を行いたい時に使用
  update!もある

モデル名.update({ カラム名: ‘文言’ })

・assign_attributes
  下記の使用でupdateと同じ

変数名 定義
変数名.assign_attributes({ カラム名: ‘文言’ })
変数名.save
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

errors.full_messagesの配列を文字に変えて出力する

起こった問題とやりたいこと

errors.full_messagesが配列なので、flashメッセージが、["hoge hoge"]が出力される
→ 文字に変えて、hogehogeと出力したい

やったこと

errors.full_messagesにto_sentenceメソッドをつけるだけ

user_contorller.rb
class Users::UserController < Users::ApplicationController
#中略

def update
 @user = user.Users
flash[:alert] = @user.errors.full_messages.to_sentence
 end

#中略
end

今日もお疲れ様でした。

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

Railsで「一度クリックしたら二度と表示させない」を実装

今回は、ページ更新の度にDBにあるテーブルからランダムでレコードをひとつ取り出して表示。
その表示をクリックしたら、以降はクリックしたユーザー(ログインユーザー)のみ、ランダム選択のプールからクリック済みの項目が排除されるという仕様を実装します。

準備

ランダムプールのテーブルをlistsとします。
usersとlistsを結びつける、user_listsテーブルを作成しておきます。(カラムはuser_idとlist_idです)

deviseの導入が前提です。

実装方針

①クリックする度に、user_listsテーブルにユーザーとクリックしたlistsが保存されるようにします。
②user_listsテーブルから、ユーザーがクリックしたlistsをwhereで抽出します。
③抽出した配列を条件にwhere.notで排除していきます。



では、やってみます。

#ログイン中ユーザーのidでレコードを検索し、取得したレコード群のlist_idを配列化
list_ids = UserLists.where(user_id: current_user.id).pluck(:list_id)

#list_idsを条件にレコードを抽出し、order以下でさらにその中からランダムで1つ選出
@list_element = List.where.not(id: list_ids).order("rand()").limit(1)[0]

これでコントローラー側は完成です。

あとはビュー側で@list_elementを使って表示すれば良いのですが、
このままだとlistsテーブルに存在する全ての項目をクリックし終えるとエラーが出てしまうので、

<% if @list_element == nil %>
 <p> 未クリックの項目はありません </p>
<% else %>
 <%# 以下で@list_elementを表示 %>

としておくと、確実です。

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

【Rails】「&.」あ!それみたことある!!(意味は知らない)

 「&.」とは(概要)

ぼっち演算子と呼ばれます。

Rubyでは通常、レシーバーに対してメソッドが実行された時、レシーバー(オブジェクト)がnilだった場合にエラーを返します。

しかしプログラムによっては、エラーを返したくない時があります。そんな時に使うのが「&.」(ぼっち演算子)です。

ぼっち演算子はオブジェクトがnilだった場合にエラーではなく、nilを返してくれます。

 「&.」は何が嬉しい??

返り値が「〇〇かnil」を返すみたいなメソッドがRubyにはあります。それを受け取ったレシーバーに、レシーバーに「nilが想定されていないメソッド」を使ってしまうと、エラーが出力されてしまいます。

そんな時に「&.」を使うことで、そのメソッドはエラーではなく、nilを返してくれるようになります。

 「&.」の懸念点

ぼっち演算子を使ったからと言って、確実にnilを返してくれるというわけではありません。そのメソッド名が存在しない(スペルミス等)場合にはNoMethodErrorが出力されてしまいます。

ぼっち演算子は「レシーバがnilでもエラーを返さない」というだけなので上記のような要因からくるエラーは防げません。

 基本的な使い方

オブジェクト&.メソッド

基本的な形は上記の通りです。

上手な例が思いつかなかったのですが、下記の記事がいい感じだったので拝借します。
https://qiita.com/yoshi_4/items/e987b698c1978d248cfc

@nickname = current_user.nickname
@nickname = current_user&.nickname

前者では、ログインしていない場合、エラーが出てしまいます。currrent_userがnilだからです。
後者では、ぼっち演算子を使っているのでエラーははかれません。

 まとめ

ともかく

  • 「ぼっち演算子」を使うと、レシーバーがnilでもエラーではなく、nilが返される
  • メソッドが存在しない(スペルミス)場合はエラーが出力されてしまう

という感じでしょうか。ご指摘等ありましたら、よろしくお願いします?‍♂️

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

ECサイトの店舗名を表示させたい ミスの箇所はコンソールで確認出来る

ふるさと納税のようなサイトをイメージして、サイトのTopページに商品とその商品を出品している店名表示をさせようとした所、NoMethodErrorが出てしまいました。
備忘録兼ねて、こういう所を見ると良いのかと勉強になりましたので記載致します。

スクリーンショット 2020-09-10 21.12.49.png

現状は
・itemとclientは既にモデル上でアソシエーションは構築済み。
(itemが子でclientが親)
・clientにはshop_nameのカラム持たせている。
・Adminコントローラーには@items = Items.allでを指定済み

原因が思い当たらず、ターミナルにてコンソール($rails c)で確認。スクリーンショット 2020-09-10 22.27.58.png

表示が小さくて大変恐縮ですが
3行目に「Unpermitted parameters」という表示があります。
右記の"shop_name","shop_name_kana","last_name_kana","first_name_kana",
"telephone_number"の項目は許可されてませんよ、という意味のようです。

新規登録時に関わってくるのは基本的にdeviseかapplicationコントローラーで
自身で触っていたのはapplicationコントローラーだったのでそちらをチェック。

スクリーンショット 2020-09-11 14.57.31.png

parametersの中には見事に弾かれていた項目の記載が抜けていました…。
なので、項目を追加して
スクリーンショット 2020-09-11 15.02.03.png

コンソールで再度チェックをしてみた所、Unpermittedという言葉は消え無事に店名表示が出来ました!

スクリーンショット 2020-09-10 22.29.21.png

コンソールのそんな所、今まで見ていなかったので記載させていただきました。
簡単ですが、以上となります。

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

【Rails】DBをリセットしてmigrateをやり直す方法

はじめに

Railsで作成したアプリで、一番はじめに作ったモデルのテーブルが間違っていたことに気づいたため、DBをリセットしてmigrateをやり直しました。
以下の記事を参考にやり直すことができました。
参考 Rails:migrateでDBをリセットして最初からつくり直す方法。
参考 【Rails】$rails db:rollbackしたい時の間違えない手順

何が間違っていたのか

deviseでユーザー登録をさせる時に、生年月日のカラム型をstring型にしてしまっていた。このままでも登録は出来たが、生年月日の欄を入力した状態で他の部分のバリデーションに引っ掛かり、エラー文を表示させる画面に移った時に以下の表記。

ターミナルに表示されたエラー
ActionView::Template::Error (undefined method `day' for "{1=>1932, 2=>nil, 3=>nil}":String):

ページを遷移した時に、フォームに入っていた情報がdata型では無かったのでおかしいことになってしまった様子。(string型だと、ハッシュにして、それぞれの番号を年月日に当てはめていた?)

解決策

db:reset

カラム型を変更すると、既に登録されている生年月日のデータがおかしくなるかなと思って、まずはテーブルのデータを削除しようと思い、以下の記事を見つけた。

①全てのレコードを空にしたい場合はrake db:resetを行う。
rake db:resetは全てのテーブルを dropし、"db/schema.rb"を元にテーブルの再作成を行います。

②"db/migrate/" 以下の全ての migration を実行してテーブルを再作成したい場合は、rake db:migrate:reset
全てのテーブルを dropし"db/migrate/"以下の全ての migration を実行してテーブルを再作成を行うため、こちらの方が効力が強いかと思料されます。

migrationファイルを編集して②やればOKだなと思ったけど、なんか怖かったのでとりあえず①でDBをリセットすると無事に無事に、DB内のデータは全て削除されていた。

データベースをリセット
% rails db:reset

Dropped database 'database名_development'
Dropped database 'database名_test'
Created database 'database名_development'
Created database 'database名_test'

rollback

続いて、カラム型を変更するためにmigrationファイルを修正した。
rails db:rollbackで良いのかなと思ってたけど、既にmigrationファイルが5つあったため、

指定箇所までrollbackする
% rails db:rollback STEP=(num)
# num個前のファイルまでrollbackできる

rollbackして、migrationファイルを修正して

migration
% rails db:migrate

一応確認すると無事に反映されていました。

ちゃんと反映されたかを確認
% rails db:migrate:status

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200831104449  Devise create users
   up     20200902085108  Create items
   up     20200903070748  Create active storage tablesactive storage
   up     20200907074332  Create orders
   up     20200910081458  Create addresses

無事にカラム型も修正できてエラーも解決できました。

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

既存の.erbファイルをhamlに変換する方法

手順

gemファイルに以下を記載

gem 'hamlit'

group :development do
  gem 'erb2haml' 
end

'erb2haml' はhamlに変換する際に利用するため記載を忘れないこと

$ bundle install

イントール完了後

$ bundle exec rake haml:replace_erbs

これで変換が完了します。

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

railsにVue.jsを導入しようとしたらエラーがでた物語

環境

・Ruby 2.6.6
・Rails 6.0.3.2
・Docker/docker-compose

やったこと

https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253
参考URLを見ながらVue.jsを導入しようとすると、以下のエラーが発生。

% docker-compose run web rails new . --force --no-deps --database=mysql --skip-test --webpack=vue 

 create  README.md
 create  Rakefile
 create  .ruby-version

#~~~~~~~~~なんやらなんやら。。。~~~~~~~~~~~~~~~

Webpacker successfully installed ? ?
       rails  webpacker:install:vue
error Couldn't find an integrity file                                          
error Found 1 errors.                                                          


========================================
  Your Yarn packages are out of date!
  Please run `yarn install --check-files` to update.
========================================


To disable this check, please change `check_yarn_integrity`
to `false` in your webpacker config file (config/webpacker.yml).


yarn check v1.22.5
info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.

「無事にWebpacker successfully installedが出たしやったー!」と思ったら、なんやらエラーが出ている。。。

解決方法

Please run yarn install --check-files to update.と言っているので、その通りに実行

% docker-compose run web rails new . --force --no-deps --database=mysql --skip-test --webpack=vue

Webpacker successfully installed ? ?
       rails  webpacker:install:vue
Copying vue loader to config/webpack/loaders
      create  config/webpack/loaders/vue.js
Adding vue loader plugin to config/webpack/environment.js
      insert  config/webpack/environment.js
      insert  config/webpack/environment.js
Adding vue loader to config/webpack/environment.js
      insert  config/webpack/environment.js
      insert  config/webpack/environment.js
Updating webpack paths to include .vue file extension
      insert  config/webpacker.yml
Copying the example entry file to /vue_app/app/javascript/packs
      create  app/javascript/packs/hello_vue.js
Copying Vue app file to /vue_app/app/javascript/packs
      create  app/javascript/app.vue
Installing all Vue dependencies
         run  yarn add vue vue-loader vue-template-compiler from "."


#~~~~~~~~~なんやらなんやら。。。~~~~~~~~~~~~~~~

Webpacker now supports Vue.js ?

無事に成功。

yarn install --check-filesの役目は何?

https://classic.yarnpkg.com/ja/docs/cli/install/
yarnで入ってるパッケージが古いのよ!って怒られてるからアップデートでもしてるのかしら?と思ったけど、参考URLには以下のように説明されている。

node_modules に既にインストールされたファイルが削除されていないことを確認します。

う〜ん、根本的になぜ解決できたのかよく分からん。

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

【form_with】でcheck_boxメソッドにclassやIDを付与する

記述方法

{}のなかにまとめて記述すれば良い。

具体的な記述
<%= f.check_box :diabetes ,{checked: true, class: "form-check-input", id: "diabetes", style: "transform:scale(2.0);" }, "true", "false" %>
間違った例
<%= f.check_box :diabetes ,{ }, "true", "false", class: "form-check-input", id: "diabetes" %>

{}の中にはcheckd: :trueしか入れてはいけないと思っていたため、ハマってしまった。
備忘録として書き残しておく。

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

【form_with】でhtmlタグのcheck_boxにclassやIDを付与する

記述方法

{}のなかにまとめて記述すれば良い。

具体的な記述
<%= f.check_box :diabetes ,{checked: true, class: "form-check-input", id: "diabetes" }, "true", "false" %>
間違った例
<%= f.check_box :diabetes ,{ }, "true", "false", class: "form-check-input", id: "diabetes" %>

{}の中にはcheckd: :trueしか入れてはいけないと思っていたため、ハマってしまった。
備忘録として書き残しておく。

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

Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装

Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装

showテストの実装

前回のindexに続き、showのテストとcontrollerを実装していきます。
showは、パラメータのidを元にfindして返すという挙動です。

テストは正常にレスポンスが返ってくることと、存在しないIDの時に404が返ってくることあたりでしょうか。

spec/requests/v1/posts_controller.rb
...

+  describe "GET /v1/posts#show" do
+    let(:post) do
+      create(:post, subject: "showテスト")
+    end
+    it "正常レスポンスコードが返ってくる" do
+      get v1_post_url({ id: post.id })
+      expect(response.status).to eq 200
+    end
+    it "subjectが正しく返ってくる" do
+      get v1_post_url({ id: post.id })
+      json = JSON.parse(response.body)
+      expect(json["post"]["subject"]).to eq("showテスト")
+    end
+    it "存在しないidの時に404レスポンスが返ってくる" do
+      get v1_post_url({ id: post.id + 1 })
+      expect(response.status).to eq 404
+    end
+  end
...

説明が漏れていましたが、letは呼び出された時に遅延評価されます。
つまりpostという変数が呼ばれるまでは実行されず、呼ばれた時点で実行されます。

上記例だと、itブロックの中でpost.idと呼ばれた時にcreateされます。
当然、controller未実装なのでテストはコケます。

showの実装

app/controllers/v1/posts_controller.rb
...
     def show
-      # TODO
+      render json: { post: @post }
     end
...

これで追加した3テストのうち2つは通過しますが、残り1個404エラーのテストが通過しません。
RailsでRecordNotFound例外が起き、jsonじゃないレスポンスが返っちゃっていますね。

全てのcontrollerのsuperクラスであるapplication_controller.rbを修正します。

app/controller/application_controller.rb
 # frozen_string_literal: true

 #
 # controllerのsuperクラス
 #
 class ApplicationController < ActionController::API
+  rescue_from ActiveRecord::RecordNotFound, with: :render_404


+  def render_404
+    render status: 404, json: { message: "record not found." }
+   end
 end

※無駄に2行空いているのはQiitaのシンタックスハイライトがうまく効かないだけなので、本来は1行でOKです

上記のように対応することで、レコードが見つからない(ActiveRecord::RecordNotFound)例外が投げられた時にrescueされ、render_404が実行されます。
そしてstatus: 404の通り404レスポンスコードでjsonが返されます。

ここまで実装するとテストも通過します。
念の為curlでも確認してみましょう。

$ curl localhost:8080/v1/posts/0
{"message":"record not found."}
$ curl localhost:8080/v1/posts/1
{"post":{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}}

createテストの実装

createは新しいレコードを生成するので、1レコード増えること、増えたレコードが登録時と一致すること、不正なパラメータの時に生成できないことを確認します。

spec/requests/v1/posts_controller.rb
+  describe "POST /v1/posts#create" do
+    let(:new_post) do
+      attributes_for(:post, subject: "create_subjectテスト", body: "create_bodyテスト")
+    end
+    it "正常レスポンスコードが返ってくる" do
+      post v1_posts_url, params: new_post
+      expect(response.status).to eq 200
+    end
+    it "1件増えて返ってくる" do
+      expect do
+        post v1_posts_url, params: new_post
+      end.to change { Post.count }.by(1)
+    end
+    it "subject, bodyが正しく返ってくる" do
+      post v1_posts_url, params: new_post
+      json = JSON.parse(response.body)
+      expect(json["post"]["subject"]).to eq("create_subjectテスト")
+      expect(json["post"]["body"]).to eq("create_bodyテスト")
+    end
+    it "不正パラメータの時にerrorsが返ってくる" do
+      post v1_posts_url, params: {}
+      json = JSON.parse(response.body)
+      expect(json.key?("errors")).to be true
+    end
+  end

attributes_forを使うとbuildと同じくDBにsaveしないでオブジェクトが返ってきます。
その際にActiveRecord形式ではなくシンプルなオブジェクトとして返ってくるかつ、idやcreated_at, updated_atというpost時に不要なカラムは存在しないので便利です。

なお、この際に変数名をpostにしないようご注意ください。
筆者は記事執筆当時postにしてしまい、post v1_posts_url, params: postとなりget, postのpostがオーバーライドされてテストが正常に動かない状態となりました…解消までかなり時間がかかりました:sweat_smile:

expect do
post v1_posts_url, params: new_post
end.to change { Post.count }.by(1)

このブロックはpostをすることでPost.countが1増加するテストとなります。expect.to eqではなくexpectがブロックになっているのがポイントです。

createの実装

controllerを実装します。

app/controllers/v1/posts_controller.rb
...

     def create
-      # TODO
+      post = Post.new(post_params)
+      if post.save
+        render json: { post: post }
+      else
+        render json: { errors: post.errors }
+      end
    end
...
$ curl localhost:8080/v1/posts -X POST -H 'Content-Type: application/json' -d '{"subject":"moge","body":"hoge"}'        
{"post":{"id":3,"subject":"moge","body":"hoge","created_at":"2020-09-06T10:31:38.375Z","updated_at":"2020-09-06T10:31:38.375Z"}}

rubocopとrspecが正常に動いたらcommit。

本記事においてとても参考にさせていただきました。ありがとうございます。
参考:Railsで超簡単API

続き

Rails 6で認証認可入り掲示板APIを構築する #7 update, destroy実装

連載目次へ

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

HerokuでDocker環境Rails MySQLなものを動かす。deviseかつtwitter APIを隠しながら

人に見せる用に書いていません。

自作記事のお父上、Rails+MySQL+Nginx+Unicorn+Docker+CircieCIな開発環境を作りたいえ〜の記事の続き。

上で作った環境に[Rails] deviseの使い方(rails5版)を追加し、かつTwitterAPIをgem 'dotenv-rails'で隠しながら、本番環境のherokuにアップロードした。

①docker-compose.ymlのコマンドを変更する

docker-compose.yml
command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

② gem 'dotenv-rails'を導入する

目的:TwitterAPIキーをGitHubのローカルに入れたくない

envファイルはGitの管理に入れたくないので、.gitignoreファイルに以下を追記しておきましょう。

.gitignore.envを追加する。

これで.envはGitの管理下から外れます。

https://pikawaka.com/rails/dotenv-rails
http://vdeep.net/rubyonrails-dotenv

③ APIキーをdotenv-railsの.envで隠しながら導入する

[Rails] deviseの使い方(rails5版)の記事のtwitterAPIを追加する場所をENV[]に書き換える

config/initializer/devise.rb
config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['DATABASE_URL']

④ Herokuの環境変数を変更する

多分、DATABASE_URLとTWIITER_API_KEY,DATABASE_URLはマスト。

herokuのGUIのSettingのConfig VarsHerokuアプリの環境変数設定(CUIで設定)で設定する

Herokuアプリの環境変数設定

  • ClearDBのURL確認
    以下のコマンドで、ClearDBのURLが確認できる。
$ heroku config
=== <アプリの名前> Config Vars
CLEARDB_DATABASE_URL: mysql://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true
  • 環境変数の設定
    上記コマンドで表示されたそれぞれの値を変数に設定する。
$ heroku config:add DB_NAME='<データベース名>'
$ heroku config:add DB_USERNAME='<ユーザー名>'
$ heroku config:add DB_PASSWORD='<パスワード>'
$ heroku config:add DB_HOSTNAME='<ホスト名>'
$ heroku config:add DB_PORT='3306'
$ heroku config:add DATABASE_URL='mysql2://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true'

※ RailsのGemfileで'mysql2'を使用しているので、DATABASE_URLmysql2://で始める必要がある。

設定内容を確認すると、以下のように表示される。

$ heroku config
=== <アプリの名前> Config Vars
CLEARDB_DATABASE_URL: mysql://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true
DATABASE_URL:         mysql2://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true
DB_HOSTNAME:          <ホスト名>
DB_NAME:              <データベース名>
DB_PASSWORD:          <パスワード>
DB_PORT:              3306
DB_USERNAME:          <ユーザー名>

Herokuへデプロイ

Railsプロジェクトに必要な準備

  • config/environments/production.rbに以下を追記
    (この辺りの設定に関しては、後々修正します。)
config.assets.compile = true
config.assets.initialize_on_precompile=false

これプラス、gem 'dotenv-rails'.envで作ったTwitterAPIをheroku側に設定する。

参考
https://qiita.com/ymstshinichiro/items/d6ea229f6eb4778006c2
https://golikyua.hatenablog.com/entry/2020/01/09/135254
https://www.sejuku.net/blog/tutorial/111347

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

rails チュートリアル

次回2.2.2から

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

【Rails】俺のための初期設定 + Gem導入

rails newをするときに、随時設定をしたりGemを入れるのが面倒だったので、自分用にcloneするだけでいいリポジトリを作りました。
https://github.com/aiandrox/rails_app

ついでに作業をざっくりまとめました。

DockerやCircleCI用の設定はしていません。
実際に導入する際にはGitHubでバージョン指定や手順などを確認してください。

バージョン

  • Ruby 2.7.1
  • Rails 6.0.3.3
  • yarn 1.22.5

初期コマンドはrails new rails_app --skip-test-unit --database=mysqlです。

日本語化・時刻の設定

config/application.rb
...
module RailsApp
  class Application < Rails::Application
    ...
    # 言語・タイムゾーンを日本に設定
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
  end
end
Gemfile
gem 'rails-i18n'
gem 'enum_help'

ついでにモデル用の日本語データも準備しておく。
en.ymlは不要なら消してしまってもいい。

config/locales/model.ja.yml
ja:
  activerecord:
    # User.model_name.human
    models:
      user: ユーザー
    # User.human_attributes_name(:name)
    attributes:
      id: ID
      created_at: 作成日時
      updated_at: 更新日時
      user:
        name: 名前

  enums:
    user:
      role:
        admin: 管理者
        general: 一般
        guest: ゲスト

リンク

デバッグ用のGem

binding.pryを使いたいので、byebugを削除してpryを入れる。

Gemfile
group :development, :test do
  # byebugは不要なら削除してよい
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  # 以下3行を追加
  gem 'bullet'
  gem 'pry-byebug'
  gem 'pry-rails'
end

group :development do
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  # 以下2行を追加
  gem 'better_errors'
  gem 'binding_of_caller'
end
config/environments/development.rb
Rails.application.configure do
  ...
  # bulletの設定
  config.after_initialize do
    Bullet.enable = true
    Bullet.bullet_logger = true
    Bullet.console = true
    Bullet.rails_logger = true
    Bullet.add_footer = true
  end
end

リンク

フォーマッター

Rails Best PracticesとRubocopの導入

Gemfile
group :development do
  # 以下2行を追加
  gem 'rails_best_practices'
  gem 'rubocop', require: false
end

カスタマイズする場合、.rubocop.ymlでオーバーライドする。

.rubocop.yml
# This file overrides https://github.com/bbatsov/rubocop/blob/master/config/default.yml

AllCops:
  Exclude:
    - 'tmp/**/*'
    - 'vendor/**/*'
    - 'db/**/*'
    - 'bin/**/*'
    - 'spec/**/*'
    - 'node_modules/**/*'
  DisplayCopNames: true

# 日本語でのコメントを許可
Style/AsciiComments:
  Enabled: false

# Admin::BaseControllerのような書き方を許可
Style/ClassAndModuleChildren:
  Enabled: false

# frozen_string_literal: trueはなくていい
Style/FrozenStringLiteralComment:
  Enabled: false

# each_key, value, transform_keys, valueを使用する
Style/HashEachMethods:
  Enabled: true
Style/HashTransformKeys:
  Enabled: true
Style/HashTransformValues:
  Enabled: true

# クラスにコメントを残さない
Style/Documentation:
  Enabled: false

# コントローラのメソッド名に「set_」「get_」を許可
Naming/AccessorMethodName:
  Exclude:
    - "app/controllers/**/*"

# 一行の長さは100字まで。コメントは制限しない
Metrics/LineLength:
  Max: 100
  IgnoredPatterns: ['\A#']
  Exclude:
    - "db/migrate/*.rb"

# メソッドの行数を20行までにする
Metrics/MethodLength:
  CountComments: false
  Max: 20

# ABC sizeは緩めにする
Metrics/AbcSize:
  Max: 30 # default 15

ついでにbundle exec rubocop -aで自動整形しておく。

ESlintの導入

$ yarn add -D eslint eslint-loader
config/webpack/loaders/eslint.js
module.exports = {
  test: /\.js$/,
  loader: "eslint-loader",
  enforce: "pre",
  options: {},
};
config/webpack/environment.js
const { environment } = require("@rails/webpacker");
const eslint = require("./loaders/eslint");  // 追加

environment.loaders.append("eslint", eslint);  // 追加
module.exports = environment;
$ yarn run eslint --init
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · commonjs
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · JSON
Successfully created .eslintrc.json file in /Users/k_end/study/rails_app
✨  Done in 92.50s.

なんとなく読みながらオプションを選んでいくと.eslintrc.jsonが生成される。

.eslintrc.json
{
    "env": {
        "browser": true,
        "commonjs": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12
    },
    "rules": {
    }
}

これでWebpackerのコンパイル時にESlintが走るようになる。bin/webpack-dev-serverで確認。
yarn run eslint --fixでESlint自動修正。

リンク

RSpec, FactoryBot

test/ディレクトリは削除する。

Gemfile
group :development, :test do
  # 以下2行を追加
  gem "rspec-rails"
  gem "factory_bot_rails"
end

bundle exec rails generate rspec:installでファイルが生成される。

spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'factory_bot'  # 追加
require 'rspec/rails'

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }  # コメントアウト

begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  puts e.to_s.strip
  exit 1
end
RSpec.configure do |config|
  config.use_transactional_fixtures = true
  config.infer_spec_type_from_file_location!

  config.include FactoryBot::Syntax::Methods  # 追加
end

spec/spec_helper.rbconfig.filter_run_when_matching :focusをコメントアウトする。

.rspec
--require rails_helper
--format documentation
  • rails_helperでspec_helperを読み込んでいる
  • 素のRubyファイルをテストすることはまずない

という点から、--require rails_helperに変更しておく。
これにより、それぞれのspecファイルでrequire rails_helperを付けなくてもよくなる。

config/application.rb
module RailsApp
  class Application < Rails::Application
    ...
    # generateで作成するファイルの制限
    config.generators do |g|
      g.assets false
      g.skip_routes true
      g.helper false
      g.test_framework :rspec,
                       view_specs: false,
                       helper_specs: false,
                       routing_specs: false,
                       controller_specs: false,
                       request_specs: true,
                       model_spec: true,
                       fixtures: true
      g.fixture_replacement :factory_bot, dir: 'spec/factories'
    end
  end
end

ここのtrue or falseは必要に応じて変える。

リンク

seed_fu

Gemfile
gem 'seed-fu'

seed-fuが読み込むのはdb/fixtures/配下のファイルなので、fixtures/ディレクトリを作成しておく。

リンク

foreman

Gemfile
group :development do
  gem 'foreman'  # 追加
end
Procfile
rails: rails s --port=3000
webpacker: bin/webpack-dev-server
$ bundle exec foreman check
valid procfile detected (rails, webpacker)
$ bundle exec foreman start

リンク

slim

Gemfile
gem 'slim-rails'
gem 'html2slim'

bundle exec erb2slim app/views app/views -dで置換する。ERB 2 SLIMを使うのもあり。
置換したらhtml2slimは不要なので削除する。

config/application.rb
module RailsApp
  class Application < Rails::Application
    ...
    config.generators do |g|
      ...
      g.template_engine = :slim  # 追加
    end
  end
end

リンク

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