20200218のRailsに関する記事は25件です。

SEしてるけど実はあんまりコード書いたことないんだよねって人に捧ぐ、Rails on Dockerハンズオン vol.8 - Sign up -

はじめに

第8回目です。前回、Userモデルを作り上げユーザー詳細ページを作成するところまでいきました。
今回は、ユーザーをUIから登録すべくSign upページを作っていきたいと思います。

いままではRailsコンソールからモデルを操作してユーザーを作成していましたが、今回からはMVCを連携させてユーザーを作成させるようにします。

Sign upページの作成

今回はSign upページでユーザーのnameemailpasswordを入力してもらい、登録完了後そのユーザーのユーザー詳細ページに遷移させます。
まずは、このアプリを実現するために必要なファイルや設定を作成していきます。

今回、Sign upページにアクセスするためのRouting、Action、Viewと、実際にUserをDBに登録するRouting、Actionが必要になります。

Scaffoldを思い出してみてください。
あの時、新規にユーザーを作成するには/users/newのページにアクセスしており、newアクションがありました。
そこで登録ボタンを押すと、/usersのURLにPOSTメソッドでリクエストが飛びcreateアクションがDBにユーザーを保存していました。

では、これらの設定を元にRouting、Action、Viewをコーディングしていきましょう!

Routing

まず、users#newusers#createへのルーティングを作成します。
しかし、Sign upページと呼んでいるのですから/users/newのようなURLでは少しカッコつきません。
今回は/sign_upにGETリクエストしたときにusers#newにルーティングされるように定義していきましょう。
users#createへのルーティングも/sign_upにPOSTリクエストしたときに定義します。

config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'

  get   '/sign_up',  to: 'users#new',    as: :sign_up
  post  '/sign_up',  to: 'users#create', as: :create_user
  resources :users, only: [:show]
end

追加した行は以下の通り。

get   '/sign_up',  to: 'users#new',    as: :sign_up
post  '/sign_up',  to: 'users#create', as: :create_user

読んでいきましょう。
これは、resoucesroot以外のルーティングの書き方としてとても一般的なものです。
static_pages#homeを作った時もこんな感じのルーティングが自動生成されたことを覚えていますか?

書き方は以下の通りです。

[method] '[path]', to: '[controller_name]#[action_name]', as: '[route_name]'

このコーディングで
pathmethodでリクエストされた場合、controller_nameコントローラーのaction_nameアクションにルーティングする。アプリのコードとしてroute_name_pathと表現された場合は、このルーティングを表すことと定義する。」
ということをRailsアプリケーションに定義立てています。

route_nameはシンボル(:sign_upのように:を頭につけてる文字)で書いてます。シンボルは複数あっては困るものなどに使用するそうです(「Rubyのシンボルと文字列の違い | UX MILK)。
ルートの名前も重複しては困りますので、シンボルを使っています。

ルーティングの設定はこれで完了です。次はコントローラーに新しくアクションを用意しましょう。

Action

次にUserコントローラーにnewアクションを追加します。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end

  # newアクションを追加する
  def new
    @user = User.new
  end

  # createアクションをとりあえず追加しておく
  def create
  end
end

newアクションでは、インスタンス変数@userUser.newを代入しています。
これはView側でUserモデルを扱ってフォームを作っていくために空のモデルが必要なのです。
詳しくはView側で説明しますので、ひとまずそのままコピペでお願いします。

createアクションはとりあえず定義だけしておきます。処理はのちのち。

View

ではでは、Viewファイルを作っていきます。
newアクションでrenderされるViewなのでnew.html.erbファイルを作成します。

# touch app/views/users/new.html.erb
app/views/users/new.html.erb
<div class="container">
  <h1 class="my-5">Sign up</h1>

  <%= form_with model: @user, url: create_user_path, local: true do |form| %>
    <div class="form-group">
      <%= form.label :name %>
      <%= form.text_field :name, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= form.label :email %>
      <%= form.text_field :email, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= form.label :password %>
      <%= form.password_field :password, class: "form-control" %>
    </div>

    <div class="form-group mt-5">
      <%= form.submit "Sign up!", class: "form-control btn btn-primary" %>
    </div>
  <% end %>
</div>

構文自体はそこまで難しくないですね。http://localhost:3000/sign_upにアクセスしてみましょう。
image.png
なかなかぽいページになったんじゃないでしょうか?
password_confirmationは使わないことにしました。「登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK」の記事を参考にしまして。
いくつかrubyコードが埋め込まれているので説明していきますね。

form_with

こういうのをビューヘルパー(View helper)っていいます。
form_with』ヘルパーはformタグを生成してくれるヘルパーです。
modelをオプションに指定することでそのモデルオブジェクトのためのフォームを作ってくれるようになります。上のコードのようにブロック形式でform変数を使うことで、例えばform.input_field :nameとするだけで@username属性に関するinputフィールドを作ってくれるようになるので、これは超便利です。

urlオプションはフォームの送信先を定義します。create_user_pathroutes.rbusers#createへのルーティングにつけた名前ですね。
このようにコーディングすることでcreateアクションにフォームを送信できるようになります。

また、local: trueオプションも付けています。form_withヘルパーではデフォルトでAjax通信でフォームをポストします。つまり、画面遷移なしでフォームをリクエストするんです。これはこれで便利な場面もあって今時な感じなんですが、今回はユーザーの新規作成が成功したらそのユーザーのマイページに画面遷移させたいので、local: trueオプションでAjax通信をオフにしています。
ちなみに『Ajax』は『エイジャックス』または『アジャックス』と読むそうです。僕は『エイジャックス』って読んでます。

今回のform_withは実際にクライアントに返却される場合は以下のように変換されて返却されています。

<%= form_with model: @user, url: create_user_path, local: true do |form| %>
  ...
<% end %>

<form action="/sign_up" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="xxxxxxxxxx">
  ...
</form>

authenticity_tokenが第3回の時に少し触れた、CSRFトークンですね。XSSを防いでくれるやつです。

label

labelはラベルを付けてくれるやつです。ページで『お名前』とか『メールアドレス』と出ているやつのことです。

<%= form.label :name %>

<label for="user_name">お名前</label>

text_field

text_fieldinput type="text"タグでテキストフィールドを作り出してくれます。
使い方も簡単で、text_field [attribute_name], [options]なだけです。
今回はBootstrapのform-control classをオプションで付けてあげています。

<%= form.text_field :name, class: "form-control" %>

<input class="form-control" type="text" name="user[name]" id="user_name">

nameidも勝手に付けてくれているのがいいですよね。
ここでid="user_name"を付与してくれているので、labelfor="user_name"と紐付けができるようになっています。

password_field

passwordを入力するフォームのところではpassword_fieldを使いました。
これはtext_fieldと似ていますが、input type="password"タグのフィールドを作ってくれるやつです。

<%= form.password_field :password, class: "form-control" %>

<input class="form-control" type="password" name="user[password]" id="user_password">

input type="password"はパスワード用の入力フィールドを作り出せるので、入力した文字が『●』でマスク化されるようになります。

submit

submitタグはフォーム送信用のボタンを作ってくれるタグです。input type="submit"を作ってくれるんですね。

<%= form.submit "Sign up!", class: "form-control btn btn-primary" %>

<input type="submit" name="commit" value="Sign up!" class="form-control btn btn-primary" data-disable-with="Sign up!">

HTMLの方にあるdata-disable-withはダブルクリック抑止のためのパラメータですね。RailsのERBを使えばこういった必要だけどついつい忘れてしまいがちなHTMLオプションも自動で付与してくれるので積極的に利用するといいと思います。

これ以外にもいろいろなヘルパーが用意されているので、HTMLのあのタグはどうやって書けばいいのか、と思った時は直接HTMLタグを書くのではなく一度Railsでの書き方をググってみるといいでしょう!

これでViewがいい感じに作れました。次は「Sign up!」ボタンを押した時の処理をコーディングしていきます。

Createアクションをコーディング

ここまででnew.html.erbのフォームからcreateアクションにフォームをリクエストできるようになりました。
ここからはcreateアクションでリクエストされたフォームのデータからDBにUserを登録できるようにします。

では、先ほど"とりあえず"作ったcreateアクションをコーディングしていきます。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  ...
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render :new
    end
  end

  private
    def user_params
      params.require(:user).permit(:name, :email, :password)
    end
end

はい。初見だとけっこう複雑に見えるんじゃないかなーと思います。
1行ずつみていきましょう。

@user = User.new(user_params)

まず、@userUser.new(user_params)を代入しています。
user_paramsとは何でしょう?

private
  def user_params
    params.require(:user).permit(:name, :email, :password)
  end

user_paramsはここで定義されている関数ですね。
params.require(:user).permit(:name, :email, :password)の部分は『ストロングパラメーター(Strong parameter)』と呼ばれています。Railsが用意しているこのストロングパラメーターは意図しないデータのPOSTを防いでくれます。

params.require([model_name]).permit([attribute_names])

これでリクエスト内のパラメーター(params)からmodel_nameモデルに紐づくデータのみを抽出し、さらにその中からattribute_namesに指定されているデータのみを抽出して返却します。
それ以外のデータはドロップします。これによってユーザーのいたずらで意図しないデータが作られてしまうことを防ぐということです。
例えばモデルにはこっそりadminフラグみたいなものがあって1のユーザーはなんでもできちゃう権限を持っているとします。いたずらっ子のユーザーがそれに気づいてSign upページにdeveloper toolsとかを使ってadmin=1のフォームを追加してきたりしたとします。ここでストロングパラメーターを使っていない場合はadmin=1でユーザーが作成されてしまいアプリが乗っ取られてしまう、みたいなことが起きちゃうんですね。
ストロングパラメーターはそういった意図しないフォームからのリクエストを無視するようになるので、安全なアプリケーションの作成に不可欠なものなのです。

今回はUserモデルのnameemailpassword以外のデータは無視することを定義しています。

また、この関数はprivateより下に書かれています。これはクラス内からのみアクセスできる関数であることを定義しています。

さて、ここまでで@useruser_paramsで抽出されたデータが初期値として設定されたUserモデルオブジェクトが代入される、ということになります。

if @user.save
  redirect_to @user
else
  render :new
end

次は、この@usersaveして条件分岐しています。ModelのCRUDの時にも話がでてきましたがsaveメソッドはバリデーションを通りデータをDBに保存できた場合trueを、バリデーションに引っかかった場合falseを返却します。
その結果を元にtrueの場合はredirect_to @userfalseの場合はrender :newが実行されます。

redirect_to @user

redirect_toメソッドは引数にGETメソッドでリダイレクトします。
引数には@userを指定してます。これは、user_path(@user)と同義です。user_pathshowメソッドにつけられている名前です。@userには今DB保存が成功したUserモデルオブジェクトが入っていますので、今保存したユーザーのユーザー詳細ページに遷移させています。

render :new

falseの場合はこちらが実行されます。
renderメソッドは指定したアクションのViewファイルをレンダリングするメソッドです。(いろいろと使い方はありますが、今回はこんな説明で)
通常アクションは自分の名前と同じViewファイルをレンダリングするようになっていますが、renderメソッドを使うことで別のアクションのViewファイルをレンダリングするようにすることもできます。
この場合は、newアクションのViewファイルがレンダリングされます。さらに@userには今falseになったUserモデルオブジェクトが入った状態でnew.html.erbに引き渡されます。
そのため、レンダリングされたとしても今入力してた属性データは再表示されるはずだし、@user.errorsの中にはsaveに失敗した理由も残っているわけです。new.html.erb側を少し更新すればエラーメッセージを表示することもできます。

new.html.erbでエラーメッセージを表示できるように更新

ということでnew.html.erbを更新して、saveでエラーが発生した場合はerrors.full_messagesが表示されるようにしてみましょう。

app/views/users/new.html.erb
...
<h1 class="my-5">Sign up</h1>

<%# ここから追加 %>
<% if @user.errors.any? %>
  <div class="alert alert-danger">
    <ul class="mb-0">
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>
<%# ここまで追加 %>

<%= form_with model: @user, url: create_user_path, local: true do |form| %>
...

これも少し説明します。

<% if @user.errors.any? %>
  ...
<% end %>

まず一番外側を囲っている部分です。
any?メソッドは対象が"空"でないかどうかを検証して「空でない」場合にtrueを返します。
今回は@user.errorsに対してany?メソッドを適用しているので、validationによるエラーがある場合にtrueになります。

<div class="alert alert-danger">
  <ul class="mb-0">
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
  </ul>
</div>

@user.errors.any?trueの場合にはこの部分が表示されることになります。
ulタグはリストを表示するためのタグですね。liタグがulタグのアイテムとして『・』を頭につけた箇条書き形式で表示されるようになっています。
liタグは以下のコードに囲まれています。

<% @user.errors.full_messages.each do |msg| %>
  <li><%= msg %></li>
<% end %>

@user.errors.full_messagesはバリデーションの回でも使ったので覚えている人が多いと思います。
バリデーションでエラーが起きた時にユーザーにそのまま見せれるようなエラーメッセージが配列で格納されていたやつです。
それをeachでひとつずつ取り出し、msgに格納して、@user.errors.full_messagesがなくなるまでブロックの中の処理を繰り返します。
ブロックの中の処理は<li><%= msg %></li>だけなので、ここで@user.errors.full_messagesのエラーメッセージを一つずつliタグで囲って表示してあげていますね。
結果として、全てのエラーメッセージが箇条書きの形式で表示されるようになると期待できます。

ここまでできたら実際に動作確認をしてみましょう!

動作確認(エラー編)

まずはエラーの場合の動作確認をしてみます。
以前Userモデルのバリデーションを確認したときのように、いくつかのエラーケースでエラーメッセージが表示されるかどうかを確認していきましょう。

存在性チェック

Userモデルではnameemailpassword全てが入力必須(presence)な項目です。
何も入力していなければエラーになるはず。
image.png
実際に何も入力せずに「Sign up!」ボタンを選択したページがこちらです。
「お名前」「メールアドレス」「パスワード」について、それぞれエラーが出ていてUserの作成がうまくいかなかったことがわかりますね。

文字数チェック

nameemailにはそれぞれ最大50文字、255文字の制限を設けていました。
それ以上の文字数でエラーになるか確認してみましょう。

51文字と256文字の文字列は↓をお使いください。

Name
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Email
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb@sample.com

image.png
文字数制限のエラーが出ていますね。
ちなみに「パスワード」には適当にパスワードを入力して「Sign up!」ボタンを選択したので「パスワード」に関するエラーは出ていないです。「パスワード」はtype="password"のフィールドを使っているので、エラー時に元の値が反映されないようになっているんですね。

フォーマットチェック

emailにはフォーマットの検証もいれていました。例えば「@」がない場合はメールアドレスとしておかしいのでちゃんとエラーが返却されるか確認してみましょう。
image.png
ちゃんとエラーになっていますね。

重複チェック

最後にemailの重複チェックもみてみましょう。

とりあえずjohn@sample.comemailをもつユーザーをRailsコンソールから作っておきます。
前回までに作ったユーザーなどがいればそれを使っていただいても構いません。

> User.create(name: "John Smith", email: "john@sample.com", password: "password")

ユーザーを作ったので、同じメールアドレスを大文字とかにしてもエラーになるかを確認してみましょう。

image.png
これもエラーになりましたね!

ここまでチェックすれば、バリデーションはちゃんと効いていて、バリデーションエラーが起きた時のエラーハンドリングも正常に動作していることが確認できましたね。

動作確認(正常編)

ではいよいよユーザーを実際にUIから作ってみましょう。

今回は以下のユーザーを作ってみます。

name: Taro Tanaka
email: taro@sample.com
password: taro1234

image.png
ユーザー詳細ページに遷移しましたね!さらに今入力したユーザー情報もちゃんと表示されています!

Welcomeメッセージを表示する

ユーザーが作成されて、ユーザー詳細ページにも遷移できたのですが何か物足りないですね。
そうだ、Welcomeメッセージがないんだ。

ユーザー詳細ページは、Welcomeページというわけではないので今のまま装飾していけばいいのですが、会員登録した時に限ってでも「ようこそ!」みたいなメッセージがほしいですよね。

こういう一時的にページにメッセージを表示したいときに使えるRailsの機能としてflashがあります。
flashはControllerで設定してあげます。今回はcreateアクションでsaveに成功した時に表示されるようにしてみましょう。
createアクションを通ってshow.html.erbが表示された場合はWelcomeメッセージが表示されますが、通常show.html.erbが表示される場合はcreateアクションは通らずshowアクションだけを通るので、例えばリロードすればメッセージが消えるという寸法です。

app/controllers/users_controller.rb
...
def create
  @user = User.new(user_params)
  if @user.save
    # 以下の1行を追加する
    flash[:success] = "サインアップありがとう!"
    redirect_to user_path(@user)
  else
    render :new
  end
end
...

Controller側の設定はこんな感じです。flash[:success]のような形でメッセージのkeyを定義して= 文字列の形でvalueを与えます。

View側でこれを受け取って、flashがある場合だけ表示するように、show.html.erbも更新しましょう。

app/views/users/show.html.erb
<div class="container my-5">
  <%# ここから追加 %>
  <% flash.each do |msg_type, msg| %>
    <div class="alert alert-<%= msg_type %>"><%= msg %></div>
  <% end %>
  <%# ここまで追加 %>

  <%= @user.name %>
  <br>
  <%= @user.email %>
</div>

今回はflash[:success] = "サインアップありがとう!"と定義しているので、msg_typesuccessが、msgサインアップありがとう!が格納されます。
alert-<%= msg_type %>alert-successとなるわけですね。Bootstrapでalert-successのCSSが用意されているのでそれを適用するためにflashのkeyをsuccessにしていたわけです。
おんなじようにエラー系のメッセージにしたい場合はdangerにしてあげたりするとViewファイルをいじらなくてもエラーっぽいCSSを適用することができます。

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

次は花子さんを作ってみます。http://localhost:3000/sign_upにアクセスして以下のデータで登録してみましょう。

Name: Hanako Yamada
Email: hanako@sample.com
Password: hanako1234

image.png
Welcomeメッセージが表示されました!

この状態でリロードしてみてください。
image.png
メッセージが消えましたね。期待通りの動作です。

パスワードの表示非表示を制御する

あとそうだ。先ほどUX的にpassword_confirmationは使いませんという話をしました。
しかし、今のままではパスワードが非表示しか対応していないので、ユーザーは自分の思ったパスワードが正しく入力できているか確認できません。
ただ、周りから見えないようにするのが目的でマスク化しているので、常に平文にすることもUX的によろしくないでしょう。

ということで、「パスワードを表示する」チェックボックスを用意して、チェックが入っている間だけ表示されるようにしようと思います。
まず、new.html.erbにチェックボックスを追加します。
今回は@userに紐づかないフォーム要素になるのでcheck_box_tagを利用します。これはinput type="check_box"タグを作ってくれるやつです。

app/views/users/new.html.erb
...
<div class="form-group">
  <%= form.label :password %>
  <%= form.password_field :password, class: "form-control" %>      
</div>
<div class="form-check">
  <%= check_box_tag :visible_password, :visible, false, class: "form-check-input" %>
  <%= label_tag :visible_password, "パスワードを表示する" %>
</div>
...

...
<div class="form-group">
  <label for="user_password">パスワード</label>
  <input class="form-control" type="password" name="user[password]" id="user_password">      
</div>
<div class="form-check">
  <input type="checkbox" name="visible_password" id="visible_password" value="visible" class="form-check-input">
  <label for="visible_password">パスワードを表示する</label>
</div>
...

はい。これでチェックボックスを追加することができました。
image.png

ただ今のままではただのチェックボックスにチェックを入れたり外したりできるだけです。
このチェックボックスの状態に合わせて「パスワード」のフィールドをマスク化したりマスク解除したりしていきましょう。
どうやるかというと、今マスク化されているのはinputtypepasswordになっているからです。他のinputのフィールドは普通に文字が見えていますが、これはtypetextになっているからです。
なので、チェックボックスの状態に合わせて、チェックが入っている場合はtype="text"、チェックが入っていない場合はtype="password"と切り替えることができれば、パスワードの表示非表示を制御できるはずです。

これを実装するには、フロントエンドで動作するjavascript(jQuery)を使います。

ごちゃごちゃするといけないので、application.jsではなくファイルを追加してそちらでコーディングしていきましょう。

# touch app/javascript/packs/visible_password.js
app/javascript/packs/visible_password.js
$(function() {
  $("#visible_password").change(function() {
    type = "password"
    if ($(this).prop("checked")) { 
      type = "text"
    }
    $("#user_password").attr("type", type)
  })
})

まず、型として$(function() { ~ })で囲みまして、{}の中に処理をコーディングしていきます。

$("#visible_password").change(function() {
  ...
})

最初に$("#visible_password").changeと書きました。これはidvisible_passwordinputタグに変化が起きたときに処理されることを意味しています。こういった何かが起こったときに処理が実行されることを『トリガー』とか『発火する』とか言うので覚えておきましょう!
今回の場合はidvisible_passwordinputの変化をトリガーにする、とか、変化が起きた時に発火する、みたいな表現をするってことです。

type = "password"

処理の中をみると、まず最初にtype = "password"の変数定義をしていますね。

if ($(this).prop("checked")) { 
  type = "text"
}

その後、if ($(this).prop("checked"))の条件分岐を書いています。
$(this)はjQueryの特殊な書き方だと思いますが、トリガーの元になった要素のことです。
prop("checked")はその要素のチェックボックスがチェックされているかを取得するメソッドなので、$(this).prop("checked")visible_passwordのチェックボックスにチェックが入っていればtrue、そうでなければfalseになります。
つまりif文の部分は、「チェックボックスにチェックが入っていればtype変数を"text"に更新する」、ことが記述されています。

$("#user_password").attr("type", type)

最後に、$("#user_password")、つまりiduser_passwordの要素(パスワードのinputタグ)にattr("type", type)をしています。
attrはその要素の属性を取得したり更新したりするメソッドです。引数が2つの場合は1つ目の引数の属性を2つ目の引数の値に更新します。今回の場合はid="user_password"typeを変数typeに更新しています。

変数typeはデフォルトではpassword、チェックボックスにチェックが入っている場合はtextになるので、チェックボックスにチェックを入れるとパスワードが表示される仕組みです。

さて、実際に使えるようにするには、このファイルを読み込めるようにする必要があります。
このファイルはアプリケーションの全てのページで使うのではなく、Sign upページでのみ利用するようなファイルなので、new.html.erbで読み込みを定義してあげます。

app/views/users/new.html.erb
...
<%= javascript_pack_tag 'visible_password' %>

Sign upページをリロードして試してみましょう!

image.png

チェックなし??チェックあり

image.png

期待通りの動作になりましたね!

トップページからリンクさせる

最後にトップページの「Sign up now!」ボタンからSign upページにリンクを貼ってあげましょう。

前回時点ではapp/views/static_pages/home.html.erbの該当のボタンのコードは以下のようになっていますね。

app/views/static_pages/home.html.erb
...
<%= link_to "Sign up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
...

この"#"の部分がリンク先になるので、これをルーティングで名前づけしたsign_up_pathに更新します。

app/views/static_pages/home.html.erb
...
<%= link_to "Sign up now!", sign_up_path, class: "btn btn-lg btn-primary mt-5" %>
...

はい、これで完了です!
トップページ(http://localhost:3000/)にアクセスして「Sign up now!」ボタンをクリックしてみましょう。ちゃんとSign upページに遷移するはずです。

後片付け

いつものように、次回に向けてデータを消します。

$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset

DBコンテナが立ち上がった状態だと思うのでdownさせます。

$ docker-compose down

まとめ

今回は、サインアップページとユーザー登録機能を作っていきました。
今までと比べると少し複雑なControllerのコーディングをしましたね。バリデーションの結果に合わせてエラーメッセージを表示させてみたり、登録完了後のウェルカムメッセージを表示させたりしてみました。

また、jQueryを使ってパスワードの表示非表示をクライアントサイドで変更するコーディングもやってみました。
jQuery(javascript)はWebアプリケーションを作る上では欠かせない技術ですのでぜひ覚えてくださいね。

さて、今回はユーザー登録はできたものの、サインイン(ログイン)してそのユーザー独自の機能を提供する、ということはできていません。例えば、今のまま開発を進めてしまっては誰もが全てのユーザーの情報をみれてしまったり更新できてしまったりします。
そこで次回は、セッション管理をすることでサインイン機能を実装してみようと思います。

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

Links

Vol.1 - Introduction -
Vol.2 - Hello, Rails on Docker -
Vol.3 - Scaffold, RESTful, MVC -
Vol.4 - Static pages -
Vol.5 - Model and CRUD -
Vol.6 - Model validation -
Vol.7 - Secure password -
・ Vol.8 - Sign up - ?この記事

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

RailsアプリをMySQLで作成してherokuへデプロイする。

今回は初めからMySQLでRailsアプリを作成してherokuへデプロイする手順です。

こちらの記事を参考に進めました。
RailsDBをMySQLに変更してHerokuでデプロイまでする手順

ありがとうございました。ほぼこの通りにすれば完璧です。自分はデプロイするときにいくつか詰まったところがあったので記載していこうと思います。

MySQLでRailsアプリを作成

こちらの記事を参考にRailsアプリのDBをMySQLにします。

【Rails/MySQL】RailsにMySQLを導入する方法【プログラミング学習149日目】

多少詰まりますがエラーメッセージ見ながら進めればrails sできると思います。

上記記事の①から⑥まで進める。

③は割愛。④はheroku configで出てきます。入力ミスに気をつけてください。この通りに進めてできなければ他の記事を参考にしてください。(クレジットカードが登録されてないパターンもある)

デプロイ時

$ git push heroku master
~
error: unable to rewind rpc post data - try increasing http.postBuffer
error: RPC failed; curl 56 LibreSSL SSL_read: SSL_ERROR_SYSCALL, errno 54
fatal: the remote end hung up unexpectedly
Writing objects: 100% (8167/8167), 29.25 MiB | 54.00 KiB/s, done.
Total 8167 (delta 988), reused 0 (delta 0)
fatal: the remote end hung up unexpectedly
Everything up-to-date

調べたらgitのbufferが足りないということでした。容量を増やすか、既存のアプリを削除するかして突破できます。自分は他のアプリを削除しました。

デプロイ後

heroku run rake db:migrateheroku openできました!と思ったらエラー発生。logを見たら記事投稿時にエラーになってました。
ローカルのフォルダからpushするとcssが反映されないことがあるらしいです。
/config/environments/production.rb

config.assets.compile = true <=最初はfalseになってる

これで解決しました。

heroku openできました。お疲れ様でした。

参考

RailsDBをMySQLに変更してHerokuでデプロイまでする手順
【Rails/MySQL】RailsにMySQLを導入する方法【プログラミング学習149日目】

参考にさせていただきありがとうございました。

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

rubyのdeviseを後付けで、ユーザー登録エラーNo route matches [POST] "/people/sign_up.person"の解決方法

deviseを最初からインストールしている記事はあるものの、追加でしかもmodel名のuserを使わない記事が皆無だったので後学者の為に。

環境
ruby 2.6.3
rails 5.2.4

作業時間を報告するアプリを作成。

先にpersonをmodelとして、複数形としてpeopleを使用。
一応これで課題は終了。

応用追加課題として、先のアプリに後付けで、
ログイン機能のdeviseを追加実装。

以下状況
gem deviseとdevise viewをインストールし、devise controllersはインストールせず。
personモデルはそのままにして、deviseのuserモデルのカラムを利用する為、userモデルを一度インストール。
インストールしたuserのmigrationファイルに記載されているカラムとfirst_name&second_nameを追加してファイル名をrenameし、migrate。
利用し終わったので、userモデルは削除。

ログイン画面と登録画面を実装して、動作確認時にエラー

エラー文
No route matches [POST] "/people/sign_up.person"

該当箇所

registration/new.html.haml
= form_for(resource, as: resource_name, url: new_person_registration_path(resource_name)) do |f|
devise/shared/_links.html.haml
- if devise_mapping.registerable? && controller_name != 'registrations'
  = link_to "Sign up", new_registration_path(resource_name)
$ rails routes
   new_person_registration GET    /people/sign_up(.:format) devise/registrations#new
  edit_person_registration GET    /people/edit(.:format)      devise/registrations#edit
       person_registration PATCH  /people(.:format)           devise/registrations#update
                           PUT    /people(.:format)           devise/registrations#update
                           DELETE /people(.:format)           devise/registrations#destroy
                           POST   /people(.:format)           devise/registrations#create

create画面に飛ばすべき所をnewパスにしていた。

registration/new.html.haml
= form_for(resource, as: resource_name, url: person_registration_path(resource_name)) do |f|

path修正

devise/shared/_links.html
- if devise_mapping.registerable? && controller_name != 'registrations'
  = link_to "Sign up", new_registration_path(resource_name)

こちらもルーティングに従い修正(person追加)

- if devise_mapping.registerable? && controller_name != 'registrations'
  = link_to "Sign up", new_person_registration_path(resource_name)

修正するも次のエラー分
ActionController::UnknownFormat in Devise::RegistrationsController#new

受け取るパラメーターをfirst_nameとsecond_nameを指定していなかった為
こちらを参考にして、
https://aliceblog1616.com/devise_parameter_sanitizer%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A8%E3%81%AF%EF%BC%9F/

class ApplicationController < ActionController::Base
  before_action :authenticate_person!
  before_action :configure_permitted_parameters, if: :devise_controller?

protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :second_name])
    devise_parameter_sanitizer.permit(:sign_in, keys: [:first_name, :second_name])
    devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :second_name])
  end
end

deviseのcontrollersをインストールしてないが、deviseが裏で作動しているので、if: :devise_controller?も記述。
エラー分変わらず。

ActionController::UnknownFormat in Devise::RegistrationsController#new

https://stackoverflow.com/questions/19193222/rails-4-devise-3-1-1-actioncontrollerunknownformat-in-deviseregistrationscon
を参考に

registration/new.html.haml
= form_for(resource, as: resource_name, url: person_registration_path(resource_name)) do 

から
(resource_name)を消す。

registration/new.html.haml
= form_for(resource, as: resource_name, url: person_registration_path) do |f|

修正

$ rails s

再起動忘れずに。
無事開通!

動作確認する前にroutesでしっかりpath確認して、parameterで追加カラムを指定する。
この工程を忘れやすいので、改めていい勉強になりました。

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

Rails 7つのアクション以外

7つのアクション以外でルーティングを設定する方法を説明します。
検索フォームを作成する場合、searchアクション等を利用。

7つのアクション以外でルーティングを設定する方法は2つある。

collectionとmember

collectionはルーティングに:idがつかない、memberは:idがつく。

#collectionで定義

Rails.application.routes.draw do
  resources :tweets do
    collection do
      get 'search'
    end
  end
end
#collectionのルーティング

Prefix           Verb    URI                                 Pattern
search_tweets    GET    /tweets/search(.:format)              tweets#search
#memberで定義

Rails.application.routes.draw do
  resources :tweets do
    member do
      get 'search'
    end
  end
#memberのルーティング

Prefix           Verb    URI                                 Pattern
search_tweet      GET    /tweets/:id/search(.:format)       tweets#search

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

Rails deviseヘルパーメソッド

user_signed_in?

ユーザーがサインイン済みかどうかを判定する

例 elseの時はサインインしていない場合
app/views/layouts/application.html.erb

<div class="header__right">
        <% if user_signed_in? %>
          <%= link_to "新規投稿", new_post_path, class: "header__right--btn" %>
          <%= link_to "ログアウト", destroy_user_session_path, method: :delete, class: "header__right--btn" %>
        <% else %>
          <%= link_to "ログイン", new_user_session_path, class: "header__right--btn" %>
          <%= link_to "新規登録", new_user_registration_path, class: "header__right--btn" %>
        <% end %>
      </div>

current_user メソッド

サインインしているユーザーを取得する。もし、サインイン中のユーザーidを取得したい場合、current_user.idと記述することで取得できる。

merge メソッド

2つのハッシュを結合することができる。

private
  def post_params
    params.require(:post).permit(:title, :content).merge(user_id: current_user.id)
  end

以下をイメージするとわかりやすい
a.png

configure_permitted_parameters メソッド

deviseでは初期状態でサインアップ時にメールアドレスとパスワードのみを受け取るようにストロングパラメーターが設定してあるので、追加したキー(nickname)のパラメーターは許可してない。
なので追加のパラメーターを許可したい場合は、application_controller.rbにおいてbefore_actionにconfigure_permitted_parametersメソッドを設定する。

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])
  end
end

上記のように記載すればnicknameも許可される。

before_action :authenticate_user!

コントローラーに設定して、ログイン済ユーザーのみにアクセスを許可する。以下の記載ならばshowはログインユーザーのみ実行可能となる。

class ArticlesController < ApplicationController
  before_action :authenticate_user!, only: [:show]

  def index
  end

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

[初歩なこと]画像投稿をしたらGithubに大量の画像が上がっちゃうのを阻止したい時

画像投稿機能をつけるときにやっておくこと

投稿した画像のデータを保存する必要がないとき、
ディレクトリの.gitignoreというファイルに以下を記入する。

.gitignore
#末尾に追記
public/uploads/*

完了です

gitignoreとは?

コミットしたくないファイル・ディレクトリを指定できる設定ファイルのことです。
通常画像投稿の機能をつけると画像は自動的にpublic/uploadというディレクトリに
格納されます。
そこで、このファイル名をgitignoreへ追加しておくことでgithubへ画像がコミットされなくなるということです。(コミットされないだけで画像自体はフォルダへ入ってきますが。)

終わりに

gitignoreのことすっかり忘れて、コミットの時に大量の画像に危く履歴を圧迫されそうになりました。
備忘録として残しておこうと思います。

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

RspecでDevise認証を使ったテストを行う方法【Rails5編】

背景

私は現在、渋谷の某プログラミングスクールに通っている。
チーム開発では、アジャイル開発と呼ばれるモダンな開発手法をスクラムを用いてチーム開発を行い、チームでECサイト(メルカリ)のクローンアプリを開発した。
その開発工程の中でRspecでDevise認証を使ったテストを行ったのだが、Rails5でこれを行う方法を簡潔にまとめられている記事がなかったので、これを機にアウトプットします。

参考記事

rspecのテスト環境でdeviseにログインする方法【rails】

こちらに大まかな実装の流れを分かりやすく解説して頂いていたので、共有させていただきます。

やりたいこと

上記、参考記事に書かれている方法をRails5でも行えるようにする。

実装

参考記事と照らし合わせながら下記の実装解説をお読みください。

1. support配下を読み込み可能にする

spec_rails_helper.rb (修正前)
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
spec_rails_helper.rb (修正後)
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

コメントアウトのままだとsupport配下が読みこめていないのでコメントアウトを解除。

2. controller_macrosの作成

spec/support/controller_macros.rb (修正前)
module ControllerMacros
  def login_admin(admin)
    @request.env["devise.mapping"] = Devise.mappings[:admin]
    sign_in admin
  end

  def login_user(user)
    controller.stub(:authenticate_user!).and_return true
    @request.env["devise.mapping"] = Devise.mappings[:user]
    sign_in user
  end
end
spec/support/controller_macros.rb (修正後)
module ControllerMacros
  def login_admin(admin)
    @request.env["devise.mapping"] = Devise.mappings[:admin]
    sign_in admin
  end

  def login_user(user)
    allow(controller).to receive(:authenticate_user!).and_return(user)
    @request.env["devise.mapping"] = Devise.mappings[:user]
    sign_in user
  end
end

8行目に注目してください。修正前はstubと書かれていますがこのままだとエラーが出ちゃうので変更。

3. deviseのtest_helperとmacrosをcontroller内で使えるようにする

spec_rails_helper.rb (Rails5未満)
RSpec.configure do |config|
  config.include Devise::TestHelpers, type: :controller
  config.include ControllerMacros, type: :controller
end
spec_rails_helper.rb (Rails5)
RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include ControllerMacros, type: :controller
end

3行目に注目してください。config.include Devise::TestHelpers, type: :controllerRails5からはこの書き方は廃止されたのでconfig.include Devise::Test::ControllerHelpers, type: :controllerに変更。以上です。

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

each_with_objectでwith_indexしたい

いけた

が、使える引数の順番が複雑な感じ。カッコも必要。
カッコ内の引数:each_with_objectの第1引数、第2引数
カッコ外の引数:with_indexで追加された引数
ということのようだ。

(1..3).each_with_object([]).with_index do |(data, arr), index|
 data # (1..3)の配列内のデータ
 arr # each_with_objectの引数に渡した配列
 index # インデックス
end

参考

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

Rails 日時フォーマットにI18nを使うときはレコード数に注意

[追記] 以下の内容ですが、違った可能性があります。ja.yamlの初回読み込みに時間がかかり、2回目以降はキャッシュが使われ早くなるという可能性があります(未検証)

日付・日時のフォーマットにI18nを使用していたところ、処理時間がだいぶかかってしまいました。
そこで

  • I18n#l
  • Time#to_s
  • Time#strftime

の処理時間を調べてみました。
I18nに関してはこちら

測定

time = Time.now
sum_i18n = 0
sum_to_s = 0
sum_strftime = 0

# I18n#lの測定
100.times do
  s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  I18n.l(time)
  e = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  sum_i18n += e-s
end

# Time#to_sの測定
100.times do
  s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  time.to_s
  e = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  sum_to_s += e-s
end

# Time#strftimeの測定
100.times do
  s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  time.strftime("%a %b %d %H:%M:%S %z %Y")
  e = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  sum_strftime += e-s
end

結果

I18n#l [s] Time#to_s [s] Time#strftime [s]
1回目 0.8247481999860611 0.002034699995419942 0.0011146999459015206
2回目 0.9185206999682123 0.0023584999435115606 0.0014091000193729997
3回目 0.8858548999996856 0.0011633000249275938 0.0014278999879024923

I18nは圧倒的に遅いですね。
レコード数が多い時はTime#strftimeを使った方が良さそうです。

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

お前らまだ引数に順番指定してんの?

はじめに

vueとnuxtの記事も書こうかなあ、、、

お前らのプロジェクト壊れる可能性あるぞ

先日、仕事中に学んだこと。
インスタンス変数を作成するためにinistializeメソッドを活用していた際に今までの自分は引数の順番に依存していました。

Human.rb
class Human
  attr_render :name, :age, :sex
  def initialize(name, age, sex)
     @name = name
     @age = age
     @sex = sex
  end

〜省略〜

end

Human.new('Taro',20,'male')

この例では、Humanのinitializeメソッドは次の3つの引数を取ります。name, age, sexの3つですね。
つまりHuman.newでインスタンスを作成するところでは引数を3つ渡しています。
まあ教科書通りのinistializeメソッド及び引数の使い方ですね。
しかしこの方法には大きな欠点があります。

  1. 引数を順番に渡さなければならない
  2. 3つの引数をもれなく、だぶりなく渡さなければならない

この引数の使い方は小規模かつ個人での開発なら何ら問題有りません。
自分が作成したインスタンスなので勝手はわかっているし、他のクラスとの依存関係も頭に入っているはずです。
小規模の開発なら変更も少ないですし、この仕様が変わることはないかもしれません。

しかし、大規模な開発かつチームでの開発だったらどうでしょう?
チームでの開発/実務レベルでの大規模な開発では常に変更を考えて実装しなければなりません。
プログラムに機能が追加されたり、改修要望が入った際には、インスタンスの引数の数が変わるかもしれないし、
そもそも既存のインスタンス変数が削除されるかもしれません。
また、この一つのインスタンスだけなら良いですが、別のクラスでHumanクラスのインスタンスを作成していた場合その箇所をすべて直さなければなりません。(10箇所の変更とか普通にあります、、、)
この書き方ではまずそうですね、、、。

ではどのように書きましょう?

引数にハッシュを使う

先程述べた「引数の順番固定」を簡単に回避する方法があります。
ハッシュです!!!

Human.rb
class Human
  attr_render :name, :age, :sex
  def initialize(args)
     @name = args[:name]
     @age = args[:age]
     @sex = args[:sex]
  end

〜省略〜

end

Human.new(name: 'Taro', age: 20, sex: 'male')

このテクニックにはいくつかの利点があります。

  1. 引数の順番依存を取り除ける
  2. 変更に強い
  3. key名が明示的なドキュメントになってくれている

この方法のおかげでどんなに引数を変更しても他のコードに対する副作用がなくなりました。
よって、仕様変更の際になんの気兼ねもなく引数の追加や除去ができます。
安心感がすごいです。
またkey名がなんの情報を渡しているのかを示してくれているため、
インスタンスを生成する際にとてもわかりやすいですね。

その他のテクニック①

Human.rb
class Human
  attr_render :name, :age, :sex
  def initialize(args)
     @name = args[:name] || 'Jiro'
     @age = args[:age]   || '10'
     @sex = args[:sex]   || 'male'
  end

〜省略〜

end

Human.new(name: 'Taro', age: 20, sex: 'male')

||メソッドを使用することでデフォルト値を設定することができます。
||メソッドはor演算子と同様に作用します。
左辺を評価し、その結果がfalseまたはnilであれば右辺の評価に移ります。
こうすることで引数に値を入れ忘れたとき(意図的に入れないとき)でもインスタンス変数を生成することができるでしょう。
例えば「sexの値はほとんどの人がmaleだな」と思えば

@sex = args[:sex]   || 'male'

と設定しておくだけでインスタンス生成時に引数を渡さなくても良くなります。

その他のテクニック②

Human.rb
class Human
  attr_render :name, :age, :sex
  def initialize(args)
     args = defaults.merge(args)
     @name = args[:name]
     @age = args[:age]
     @sex = args[:sex]
  end

〜省略〜

  def defaults
    {name: 'Jiro', age: 10, sex: 'male'}
  end

end

この方法もデフォルト値を設定するのに大変役立ちます。
隔離テクニックとでも名付けましょうか。
defaultsメソッドを設定し、initializeメソッド内で呼び出しています。
この方法は特にデフォルト値が長いときや複雑な時に活用すべきです。
コードの可視性が高まりますね。

終わりに

プログラムは普遍的なものではありません。
常に変更と改善の繰り返しです。
変化に強いコードを書けるスキルは今の時代(アジャイルでの開発が主流になってきた時代)、必須のスキルと言っても過言ではありません。
あなたのコードがチームメンバーに多くの手間と工数をかけさせるかもしれません。
ひどいときにはプログラム自体を破壊してしまう可能性もあるのです。
ただ動くだけではなく、本当に"良いコード"を書けるように一緒に邁進しましょう!!!

(今回はちょっと真面目に書きすぎたな、、、:robot::robot:

参考

オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方
Sandi Metz (著), 髙山泰基 (翻訳)

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

Routing Error : uninitialized constant ◯◯Controller解決出来ない。

コントローラちゃんと作ってルーティングしたのに!?

今日のメモは、きちんと設定できてるはずなのに、何故かルーティングエラーが出て、検索しまくるハメになってしまった話です。

解決までの流れ

まずは、テーブル からご紹介。

novel_listsテーブル
novel_postsテーブル

この二つを使って、作成画面である、new.html.erbを作っていた訳ですが。
resources novel_lists only: ・・・・略 do
 resources novel_posts only: ・・・・・略
end

novel_listsのnewアクションのnew.html.erbファイルにてformを作り。
createアクションでsaveされ、novel_postsのnewアクションのnew.html.erbに飛ぶ。

こんな流れで

 @novel_list = current_user.novel_lists.build(novel_list_params)

    if @novel_list.save
      flash[:success] = 'タイトルを作成しました'
      redirect_to new_novel_list_novel_post_path(@novel_list)
    else
      flash[:danger] = '作成に失敗しました。'
      render :new
    end
  end

作っていました。

飛んだ直後にエラーが。

Routing Error : uninitialized constant NovelPostsController

!!??ちゃんとルーティング設定したはず・・・
rails routesで確認してもルーティングはしっかりと出来ています。

new_novel_list_novel_post GET /novel_lists/:novel_list_id/novel_posts/new(.:format) novel_posts#new

何故だ・・・と色々な所をチェックしていくが、治らない。
仕方なくネット検索をかけ、同じようなエラー記事をみて行く。
しかし、中々、解決に至る記事を見つけられない。

その中で、見つけたのが
http://blog.livedoor.jp/tokyo_kinako/archives/21045311.html
このブログ記事。コントローラを作った際に
rails g controller NovelPost (s)  複数形にしていない事で起きた。と書かれていた。
複数形にしたつもりではいたが、まさか。と思って・・・・コントローラをチェックすると。

novelposts.controller←ファイル名が違ってる!?

はい、その通りです。
novelposts→正しくは、novel_posts.controllerにならなければいけません。

_ ただの棒線1本の違い。違いが細かすぎて出来てると思っている自分には、見えていませんでした。

という事で、今後は気をつけようとメモでした。

同じようなエラーが出た方はまず。

コントローラのファイル名を確認してみてください。

名前が違ったり、単数形になっていたりしているかもしれません。

気をつけて欲しいのは、作り直した場合、viewsファイルと、コントローラの中身は消えますので、
どこかにコピーなどしておくかしないと、書き直しです。

では以上となります。!!

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

Rails tutorialを一周した。&不安しかないんで見てほしいっていう話

さきほどRails tutorialを一周してこれからどうしようかなと思ったので、
ひとまず書いてみることにしました。
長くなるかもしれませんがどうか片手間にお付き合いくださるとうれしいです。

次にどうするか

現在大学1年生で、もともと「何か役に立つ物を作ってみてぇ.....」といった理由でプログラミング学習を始めました。
Rubyを少し触っていたこともあり、Rails Tutorialの理解も進み、一周できたということで
いよいよWebアプリの作成にかかりたいと思います。
.....しかし

やはりお金も稼ぎたい

プログラミングは学べば学ぶほどできることが増えて、それに伴う達成感もあります。
しかし、やはりお金は必要ッ...!!現実は無常無慈悲であるッ....!!
おまけに文系大学生。ある意味地雷の塊です。
というわけでポートフォリオ等を充足させつつ、
インターン、或いは学生アルバイトやらを現段階、目指す必要があります。
本来なら、自発的な欲求を貫徹させて道筋を立てたいところですが、
社会的な干渉は避けえないものです。
ありがたいことに東京内の大学に在籍させていただいて、インターンを募集している企業様は地域内に数多く、環境には恵まれています。
インターンを視野に入れた、実践的技術を用いたプロダクトを作成することが要になりそうです。
......ですが

Dockerが使えない

まず最初に思いついた実践技術がDockerだったので、試してみることにしました。
この、一年間を共にしてきたWindowsならどこまでも行ける。そう意気揚々に信じていました。
Macがおすすめとか知ったもんかい!
こちとらバリバリのWindowsユーザーなんじゃ!Mac怖い!(本心)
しかし、その勢いは早々に裏切られます。
動かない。
いくら参考資料漁って調べて実行しても画面が固まる....。なんでや....。
原因はPCそのものにありました。
 

自分の所有するPCの弊害


やはりMacは偉大だったようです。ここで引っかかるとは....。
Dockerなどの仮想化技術をWindowsで用いる際、VT-X/AMD-vといったBIOS内の設定が必要なのですが、自分の持っているPCにそもそも装備されていませんでした。
WindowsのPCにもいろいろ機能差があったようで、無知が仇となった瞬間でした...。
Dockerは採用する側の方々から重要視されるとよく目にしていたので、ショックでした...。

ひとまず何をするべきか

ここまで紆余曲折ありましたが、自分のレベルを過大評価して、焦ってはいけません。
僕はまだ初心者。仕方ない。そう考えていったん落ち着くことにしました。
では、どうするか。
ひとまず、最初の目標である「役に立つもの」を作りたい。
この思いは何よりも大切な、内発的な動機です。
なのでこの動機で自らを動かすことにしました。
そして何を作りたいか考えました。

これから作るもの

結論として、学校の評価をすることができるSNSのようなものを作ってみたいと思いました。
その根拠として、この一年の大学生活から、授業の当たりはずれというものは意外と大きく、
個人的に苦労したというのがあり、学生アンケートはあるのですが、
フィードバックが少ない、或いは目にできる機会がないということで、授業の情報蓄積をしつつ、授業の概要を履修登録前に知れたら便利だな~と感じたからです。
ひとまずはRails Tutorialの2週目をしながら、触れることのなかったSlim, Rspec等のgemやVue.js等を使用してアプリ作成をしようと思います。

学生のくせに授業に文句を言うな勉強しろという意見は至極当然ですすいません!

最後に

ここまでご覧いただきありがとうございました。
東京在住の一学生の、不安を書き綴った技術発信の場にそぐわない記事ですが、
同じような状況にいる方々、あるいはこの記事にふらりと来てくださった方が面白いなと感じてくださったら幸いです。
上記内では述べませんでしたが、渋谷、世田谷近辺の勉強会にも参加できたらいいなあと考えています!
ご精読ありがとうございました!

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

【Ruby】大文字→小文字変換(アッパーケース→キャメルケース)

用意されているメソッド

大文字・小文字変換に使えるもの

upcase

全部大文字に変換

'abc'.upcase # => ABC

downcase

全部小文字に変換

'ABC'.downcase # => abc

capitalize

最初の文字を大文字に、他を小文字にする

'abc'.capitalize # => Abc

swapcase

小文字と大文字の入れ替え

'AbCd'.swapcase # => aBcD

camelize

キャメルケースに変換する。通常はアッパーキャメルケース。

'john_do'.camelize # => JohnDo

引数に:lowerを指定するとローワーキャメルケースになる

'john_do'.camelize(:lower) # => johnDo

underscore

スネークケースにする

'JohnDo'.underscore # => john_do

アッパーケース → ローワーキャメルケースへの変換

メソッドを組み合わせれば可能

"UPPER_CASE".downcase.camelize(:lower)
# -> upperCase

参考

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

dockerで環境構築する際にrails5.2.0環境以降でcredential.ymlを編集する方法

はじめに

初投稿です。よろしくお願いします。
忘備録として投稿します。

内容

docker-compose.ymlにvimを入れてインストールしたはずなのにcredential.ymlを編集しようとしても暗号化されたまま編集できませんでした。
他にも方法があるかもしれませんが、全くの初心者レベルの人が解決できると仮定してその道筋を投稿します。

開発環境

docker 19.03.5
docker-compose 1.25.4
rails 5.2.4.1
ruby 2.5.1
nginx 1.15.8
Mysql 5.7

docker-compose.yml

docker-compose.yml
version: '3'
services:
  app: &app_base
    build:
      context: .
    env_file:
      - ./environments/db.env
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
    depends_on:
      - db
    tty: true
    stdin_open: true

  db:
    image: mysql:5.7
    env_file:
      - ./environments/db.env
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - "4306:3306"

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app
volumes:
  public-data:
  tmp-data:
  log-data:
  db-data:

Dockerfile

FROM ruby:2.5.1

# リポジトリを更新し依存モジュールをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       nodejs \
                       vim    

# ルート直下にwebappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ)
RUN mkdir /webapp
WORKDIR /webapp

# ホストのGemfileとGemfile.lockをコンテナにコピー
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock

# bundle installの実行
RUN bundle install

# ホストのアプリケーションディレクトリ内をすべてコンテナにコピー
ADD . /webapp

# puma.sockを配置するディレクトリを作成
RUN mkdir -p tmp/sockets

docker-compose build→up -d

コンテナをbuildしてvimインストールして起動したが、あれ? credential.yml編集できない...

なぜだ....

vimインストール出来ているか見てみよう!

作業ディレクトリでwhich vimコマンド

which vim
/usr/bin/vim

インストールはされているな.....どうやって起動すれば良いんだ

コンテナの中に入ってみよう

コンテナの中に入ればできるかも....

docker-compose exec app bash
                    ⬆️docker-composeのrails名

そうするとroot@というところに入るので以下のコマンドを打ちましょう

EDITOR=vim bin/rails credentials:edit

credentialに入れた!

credentials.yml.enc
#   aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 8be8e637d755f79c799048bed8be0c...

最後に

手順を書いてある記事が少なかったので少しでも参考になればと思います。
何かあればご意見ください!

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

[Rails]FullCalenderでカレンダー機能の実装

実装すること

①マイページで予定管理ができるようにする
②日付をクリックで予定の作成・編集をできるようにする
※Userモデル等は作成した前提で進めていきます。

FullCalenderとは

オープンソースのカスタマイズ可能なjqueryのカレンダーライブラリです。
上手く活用することで、カレンダーのスクラッチ開発をせずにリッチなカレンダーを表示できます。

ER図

calenderER図.png

モデルの作成

$ rails g model Event title:string body:text disp_flg:boolean start:datetime end:datetime allDay:string

アソシエーションの確認

app/models/user.rb
class User < ApplicationRecord

    has_many :events

end
app/models/event.rb
class Event < ApplicationRecord

    belongs_to :user

end

ダウンロード

FullCalenderの公式ドキュメントからダウンロードし、zipを解凍します。
https://fullcalendar.io/

Gemの追加

Gemfile.
gem 'jquery-rails', '4.3.3'
gem 'fullcalendar-rails'
gem 'momentjs-rails'
$ bundle inatall

読み込み

application.htmlのhead内で読み込みます。

app/views/layouts/application.html
<head>
~
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/4.2.0/core/main.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/4.2.0/daygrid/main.min.css">
~
</head>
app/assets/javascripts/application.js
//= require jquery
//= require moment
//= require fullcalendar

表示する(マイページ)

カレンダー.png

カレンダー登録.png

コントローラー

app/contorollers/users_contoroller.rb
  def show
    @user = User.find(params[:id])
    @events = Event.where(user_id: @user.id)
    @event = Event.new
  end
app/controllers/events_controller.rb
    def create
        event = Event.new(event_params)
        event.save!
        @events = Event.where(user_id: current_user.id)
    end

    def update
        event = Event.find(params[:id])
        @events = Event.where(user_id: current_user.id)
        event.update(event_params)
    end

    def destroy
        @user = User.find(params[:id])
        event = Event.find(params[:id])
        event.destroy
        redirect_to user_path(@user)
    end

    private
    def event_params
        params.require(:event).permit(:title, :start, :end, :user_id, :body)
    end

ビュー

予定を表示する・作成する

<div id="calendar"></div>でカレンダーを表示しています。
・予定を編集するときのモーダル内容はパーシャルにします。

app/views/users/show.html
<h3 class="text-center">カレンダー</h3>
<!-- カレンダーの表示 -->
<div id="calendar"></div>
<!-- もしユーザーがログインしていたら -->
<% if user_signed_in? %>
    <!-- ページのparams.idがログインユーザー.idと同じなら -->
    <% if @user.id == current_user.id %>
        <div id="inputScheduleForm" class="modal fade" tabindex="-1">
            <div class="modal-dialog modal-nm">
                <div class="modal-content">
                    <div class="modal-header">
                        <h4 class="modal-title">スケジュール登録</h4>
                    </div>
                    <div class="modal-body">
                        <%= form_with model: @event, url: events_path do |f| %>
                            <div  class="col">
                                    <p>
                                        <span>タイトル</span>
                                        <span><%= f.text_field :title, class: "form-control", placeholder: "タイトルを入力してください" %></span>
                                    </p>
                                    <span>開始日時</span>
                                    <span><%= f.datetime_field  :start, placeholder: "XXXX-XX-XX", class:"field" %> ~ </span><br>
                                    <span>終了日時</span>
                                    <span><%= f.datetime_field :end, placeholder: "XXXX-XX-XX", class:"field" %></span><br>
                                    <span>詳細
                                    <%= f.text_field :body, class: "form-control", placeholder: "タイトルの詳細を記入してください" %></span>
                            </div>
                            <div class="modal-footer">
                                <%= f.submit "登録する", class: "btn btn-primary" %>
                                <%= f.hidden_field :user_id, :value => current_user.id %>
                                <button type="button" class="btn btn-default" data-dismiss="modal">閉じる</button>
                            </div>
                        <% end %>
                    </div>
                </div>
            </div>
        </div>
<!-- 編集 ------------------------------------------------------------------>
        <div id="inputEditForm">
            <%= render 'events/edit', events: @events %>
        </div>
    <% end %>
<% end %>
    <script>
    // CRUDを行う際にCSRF対策のtokenを発行
        $(document).ready(function() {
        var prepare = function(options, originalOptions, jqXHR) {
          var token;
          if (!options.crossDomain) {
            token = $('meta[name="csrf-token"]').attr('content');
            if (token) {
              return jqXHR.setRequestHeader('X-CSRF-Token', token);
            }
          }
        };
        }
        )

        // カレンダー表示
        $('#calendar').fullCalendar ({
            header: {
                left: 'prev,next today',
                center: 'month,agendaWeek,agendaDay',
                right: 'title'
            },

            buttonText: {
                  prev: "<",
                  next: ">"
            },

            timezone: 'UTC',
            events: '/users/events.json',
            navLinks: true,
            selectable: true,
            selectHelper: true,
            // 日付クリック
            dayClick : function ( date , jsEvent , view ) {
                $('#inputScheduleForm').modal('show');
                },

            // event クリックで編集、削除
            eventClick : function(event, jsEvent , view) {
                jsEvent.preventDefault();
                $(`#inputScheduleEditForm${event.id}`).modal('show');
            },

            eventMouseover : function(event, jsEvent , view) {
                jsEvent.preventDefault();
            }
        })

    </script>

app/views/events/create.js
//フォームを空にする
$('input[type="text"]').val('');
//モーダルを消す
$('#inputScheduleForm').modal('hide');
//作成した予定を差し替える
$('#inputEditForm').html('<%= escape_javascript(render("events/edit", events: @events )) %>');
// フルカレンダー を一度消しもう一度表示
$('#calendar').fullCalendar('refetchEvents')

予定を編集する

app/views/events/_edit.html
<% events.each do |event| %>
    <div id='inputScheduleEditForm<%= event.id %>' class="modal fade" tabindex="-1">
        <div class="modal-dialog modal-nm">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title">スケジュール編集</h4>
                </div>
                <div class="modal-body">
                    <div  class="col">
                        <%= form_with(model: event, url: users_event_path(event), method: :put) do |f| %>
                            <p>
                                <span>タイトル</span>
                                <span id="inputTitle" value=""><%= f.text_field :title, class: "form-control", placeholder: "タイトルを入力してください" %></span>
                            </p>
                            <span>開始日時</span>
                            <span><%= f.datetime_field  :start, placeholder: "XXXX-XX-XX", class:"field" %> ~ </span><br>
                            <span>終了日時</span>
                            <span><%= f.datetime_field  :end, placeholder: "XXXX-XX-XX", class:"field" %></span>
                            <span><br>
                                詳細
                                <%= f.text_field :body, class: "form-control", placeholder: "タイトルの詳細を記入してください" %>
                            </span>
                            <div class="modal-footer">
                                <%= f.hidden_field :user_id, :value => current_user.id %>
                                <%= f.submit "編集する", class: "btn btn-primary" %>
                                <button type="button" class="btn btn-default" data-dismiss="modal">閉じる</button>
                            </div>
                        <% end %>
                    </div>
                </div>
            </div>
        </div>
    </div>
<% end %>
app/views/events/update.js
//updateした内容を差し替え
$('#inputEditForm').html('<%= escape_javascript(render("events/edit", events: @events)) %>');
//モーダル背景画面を消す
$('.modal-backdrop').remove();
// フルカレンダー を一度消しもう一度表示
$('#calendar').fullCalendar('refetchEvents')

最後に

最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。

twitter:https://twitter.com/yto_oct
note:https://note.com/yto_oty

参考

公式ドキュメント
https://fullcalendar.io/
[Rails]FullcalendarのイベントをDBに保存・編集
https://qiita.com/ShoutaWATANABE/items/3d0cddafadb4f275991e

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

windows/heroku/railsでhello world

とうとう作りたいwebサイトを思いついた

家のPC windowsなので
個人開発はherokuだって聞いたので
仕事でruby書いてるので
そんな環境でwebサイトをつくる

(これは金曜夜に日本酒を飲みながら作りはじめ、書きはじめたサイト作成の記録なので、
酔っ払いが回り道をしながら、転びながら作っていることをご承知おきください。)

VScodeとrailsとgitHub desktopの設定は前やってた

herokuいれた

herokuCLI windows用 いれた

heroku loginしてプロジェクトを作った。

dashboardに作ったプロジェクトが表示されない

  → git pushしてないな

pushした

  →エラーでる。buildpackがどうとか。調べたところherokuでrubyでアプリ作るっていったのにrubyのフォルダ構成になってない(正確にはGemFileがない)からって怒られてるらしい

rails new appしてpush

  →エラーでる。ちゃんと大事なところだけ赤字で怒ってくれる。子曰く、

$ Failed to install gems via Bundler.
$ remote:  !     Detected sqlite3 gem which is not supported on Heroku:
$ remote:  !     https://devcenter.heroku.com/articles/sqlite3

そうですかサポート外ですか。

bundle installの時はエラー出ないのになぜなんだ。
pushする際に行われるinstallでエラーがでる。
sqlite3のバージョン指定がいるとのことなのでGemFileを以下のように設定。

GemFile
gem 'sqlite3', '~> 1.3.13'

ERROR: sqlite3.h is missing

そしてエラー。越えられない。

MySQLにしよう!

こちらに書いてある通りにしてMySQLいれる

終わったらWorkBenchが開くとあるが必要なものが全部ダウンロードできなくてエラーがでるのであきらめた。

MySQLがだめならpostgreだ!!

こちらと、
こちらを参照しつつproductionだけpostgreにしました。testとdevelopmentはsqlite3に戻しました。

GemFile
group :development, :test do
  gem 'sqlite3', '~> 1.3.6'
end
group :production do
  gem 'pg'
end

git push heroku masterできた。

herokuとの連携をgithub経由にしたのだけど、その過程で公開鍵の設定が必要になったのでそれも行ってます。(参照記事

・・・hello worldが遠い(ここですでに4時間ほどが経過している)

heroku run rails db:migrateがエラー

 PG::ConnectionBad: could not connect to server: No such file or directory
 Is the server running locally and accepting
 connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

なるほどSUNDAYじゃねーの(よくわかっていない)

エラー文をそのままぐぐったところアドオンがいるらしい
https://qiita.com/suzuki-x/items/b878723080aea1a673ed

heroku addons:create heroku-postgresql

実食!!

必要とされる作業は終わったので開いてみる。

heroku open

キャプチャ.PNG

ふぁー
ぐぐって(略)

rootファイルを書かなきゃらしい。
ここまで来てローカルで何も書いてないし動かしてないことに一抹の不安を覚える。

ローカルでrails s

なんかある程度書かないとHello Worldできないっぽいし一回ローカル叩いてみるか、と
おもむろにrails sしてみたところ、また件のsqliteが怒っている。

can not load such file — sqlite3/sqlite3_native

色々やってみてなんとか解決した。
こちらsqlite3のインストールを少し変更した。
soファイル生成までは同じだが、Ruby26だったので、ファイルパスにある2.5は2.6に置換して、そこにsoファイルを置いた。

これの2.5を2.6に修正する
mkdir C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13-x64-mingw32\lib\sqlite3\2.5
copy C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13\lib\sqlite3\sqlite3_native.so C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13-x64-mingw32\lib\sqlite3\2.5

rails s自体は通るようになったが接続すると以下のエラーが出る。

LoadError: Error loading the 'sqlite3' Active Record adapter. 
Missing a gem it depends on? can't activate sqlite3 (~> 1.4), already activated sqlite3-1.3.13-x64-mingw32.
Make sure all dependencies are added to Gemfile.

調べても、GemFileに1.3を指定すればいいとしか出ない。指定してbundle installしなおすも、同じエラー。
自分がちゃんとエラー読めていないのかもしれないが。。。
えっじゃあ逆に1.4にしたらどうなるのと思ってやってみたら通った。

Gemfile
group :development, :test do
  gem 'sqlite3', '~> 1.4'
end

Hello World!(ローカルです)

routes
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root 'pages#index'
end

PagesContorollerとviews/pages/index.html.erbを作ってそこに魂を込めて
Hello World!!と打ち込む。

Desktop screenshot.png

みれた!!!

で、これはherokuで見れるのか?
git heroku push masterを再度行う。

herokuでHello World!!できた!

ででできた・・・!!

イメージはローカルと同じなので省略。
お付き合いいただきありがとうございました。

あとがき

読み返したら、らしいらしいばっか言ってて恥ずかしい。
自己解決もだんだんできるようになってくるのだろうか。

環境構築だけで3日も使った。時間にして8時間くらいだろうか。
もっとお布団の中だけでさくっと開発できるようにしてくださいかしこいひと。

さも一度も失敗しなかったかのようなバージョンもまとめておきたい。
Qiitaの記事を書いたのも初めてなのでマークダウンの使い方とか多分あんまり良くない。

あと酔っ払いだったから色んな記事を拾い読みしていて、最初の方に見たはずの
【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】にミスったところ全部載ってたし、
これ通りにしていれば、こんなに時間かからなかったよなあと思う。

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

windows10/heroku/rails6でhello world

とうとう作りたいwebサイトを思いついた

家のPC windowsなので
個人開発はherokuだって聞いたので
仕事でruby書いてるので
そんな環境でwebサイトをつくる

(これは金曜夜に日本酒を飲みながら作りはじめ、書きはじめたサイト作成の記録なので、
酔っ払いが回り道をしながら、転びながら作っていることをご承知おきください。)

VScodeとrailsとgitHub desktopの設定は前やってた

herokuいれた

herokuCLI windows用 いれた

heroku loginしてプロジェクトを作った。

dashboardに作ったプロジェクトが表示されない

  → git pushしてないな

pushした

  →エラーでる。buildpackがどうとか。調べたところherokuでrubyでアプリ作るっていったのにrubyのフォルダ構成になってない(正確にはGemFileがない)からって怒られてるらしい

rails new appしてpush

  →エラーでる。ちゃんと大事なところだけ赤字で怒ってくれる。子曰く、

$ Failed to install gems via Bundler.
$ remote:  !     Detected sqlite3 gem which is not supported on Heroku:
$ remote:  !     https://devcenter.heroku.com/articles/sqlite3

そうですかサポート外ですか。

bundle installの時はエラー出ないのになぜなんだ。
pushする際に行われるinstallでエラーがでる。
sqlite3のバージョン指定がいるとのことなのでGemFileを以下のように設定。

GemFile
gem 'sqlite3', '~> 1.3.13'

ERROR: sqlite3.h is missing

そしてエラー。越えられない。

MySQLにしよう!

こちらに書いてある通りにしてMySQLいれる

終わったらWorkBenchが開くとあるが必要なものが全部ダウンロードできなくてエラーがでるのであきらめた。

MySQLがだめならpostgreだ!!

こちらと、
こちらを参照しつつproductionだけpostgreにしました。testとdevelopmentはsqlite3に戻しました。

GemFile
group :development, :test do
  gem 'sqlite3', '~> 1.3.6'
end
group :production do
  gem 'pg'
end

git push heroku masterできた。

herokuとの連携をgithub経由にしたのだけど、その過程で公開鍵の設定が必要になったのでそれも行ってます。(参照記事

・・・hello worldが遠い(ここですでに4時間ほどが経過している)

heroku run rails db:migrateがエラー

 PG::ConnectionBad: could not connect to server: No such file or directory
 Is the server running locally and accepting
 connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

なるほどSUNDAYじゃねーの(よくわかっていない)

エラー文をそのままぐぐったところアドオンがいるらしい
https://qiita.com/suzuki-x/items/b878723080aea1a673ed

heroku addons:create heroku-postgresql

実食!!

必要とされる作業は終わったので開いてみる。

heroku open

キャプチャ.PNG

ふぁー
ぐぐって(略)

rootファイルを書かなきゃらしい。
ここまで来てローカルで何も書いてないし動かしてないことに一抹の不安を覚える。

ローカルでrails s

なんかある程度書かないとHello Worldできないっぽいし一回ローカル叩いてみるか、と
おもむろにrails sしてみたところ、また件のsqliteが怒っている。

can not load such file — sqlite3/sqlite3_native

色々やってみてなんとか解決した。
こちらsqlite3のインストールを少し変更した。
soファイル生成までは同じだが、Ruby26だったので、ファイルパスにある2.5は2.6に置換して、そこにsoファイルを置いた。

これの2.5を2.6に修正する
mkdir C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13-x64-mingw32\lib\sqlite3\2.5
copy C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13\lib\sqlite3\sqlite3_native.so C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\sqlite3-1.3.13-x64-mingw32\lib\sqlite3\2.5

rails s自体は通るようになったが接続すると以下のエラーが出る。

LoadError: Error loading the 'sqlite3' Active Record adapter. 
Missing a gem it depends on? can't activate sqlite3 (~> 1.4), already activated sqlite3-1.3.13-x64-mingw32.
Make sure all dependencies are added to Gemfile.

調べても、GemFileに1.3を指定すればいいとしか出ない。指定してbundle installしなおすも、同じエラー。
自分がちゃんとエラー読めていないのかもしれないが。。。
えっじゃあ逆に1.4にしたらどうなるのと思ってやってみたら通った。

Gemfile
group :development, :test do
  gem 'sqlite3', '~> 1.4'
end

Hello World!(ローカルです)

routes
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root 'pages#index'
end

PagesContorollerとviews/pages/index.html.erbを作ってそこに魂を込めて
Hello World!!と打ち込む。

Desktop screenshot.png

みれた!!!

で、これはherokuで見れるのか?
git heroku push masterを再度行う。

herokuでHello World!!できた!

ででできた・・・!!

イメージはローカルと同じなので省略。
お付き合いいただきありがとうございました。

あとがき

読み返したら、らしいらしいばっか言ってて恥ずかしい。
自己解決もだんだんできるようになってくるのだろうか。

環境構築だけで3日も使った。時間にして8時間くらいだろうか。
もっとお布団の中だけでさくっと開発できるようにしてくださいかしこいひと。

さも一度も失敗しなかったかのようなバージョンもまとめておきたい。
Qiitaの記事を書いたのも初めてなのでマークダウンの使い方とか多分あんまり良くない。

あと酔っ払いだったから色んな記事を拾い読みしていて、最初の方に見たはずの
【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】にミスったところ全部載ってたし、
これ通りにしていれば、こんなに時間かからなかったよなあと思う。

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

System Specによる開発

はじめに

こちらは社内技術勉強会用の資料として作成したものです。
Ruby on Railsにおいて、RSpecの機能である、System Specを使用して開発をしてみます。

サンプルプログラムを使用して、小さな開発を行いつつ、その動作確認をRSpecで行います。

サンプルプログラムについて

概要

サンプルプログラムUV Eatsは、Webサイト上で料理のメニューを注文すると配達してもらえる、というサービスであるとします。以下のような動作をするものとします。

  • あらかじめ登録されているユーザでログインして利用します。
  • メニュー一覧からひとつのメニューを選択して注文します。
  • 注文時は配達先住所、支払方法、クレジットカード番号を入力します。

今回使用する画面は以下の4つです。

  • ログイン画面 (Sessionsコントローラ)
  • メニュー画面 (Menusコントローラ)
  • 注文画面 (Ordersコントローラ)
  • 注文完了画面 (Ordersコントローラ)

サンプルプログラムのソースコードはこちらを参照してください。

画面イメージ

uveats_pages.png

DB

DBは以下の3つのテーブルで構成されています。

  • ユーザ(users)
  • メニュー(menus)
  • 注文(orders)

uveats tables.png

RSpecの使い方について

その1: テストコードの作成

テストコードは、specディレクトリ内に、specファイルを作成して記述します。ログイン処理のテストであれば、ログイン処理を実装しているSessionsコントローラに関連するものとして、spec/system/sessions_spec.rb に記述します。

ログイン画面を表示してみるコードは、以下のような内容になります。

spec/system/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :system, js: true do
  it 'ログイン画面が表示されること' do
    visit '/sessions/new'
  end
end

System Specは、describe のオプション type に、:system を指定することで記述します。

jsというオプションが指定されていますが、これはこのサンプルプログラムの都合によるものです。付加しておいてください。

テストの内容は、it のブロック内に記述していきます。

その2: 初期データの登録

例えば、サービスにログインできるようにするためには、あらかじめログイン可能なユーザが登録されている必要があります。あらかじめデータを登録しておく方法として、FactoryBot を使用します。

FactoryBotの使い方は、GETTING_STARTEDこちらの記事 を参照してください。

その3: RSpecの実行

RSpecを実行するには、以下のようにコマンドを実行します。$WEB_ROOT は、Railsプロジェクトのルートディレクトリであるとします。

cd $WEB_ROOT
bundle exec rspec -fd spec/system/sessions_spec.rb

以下のように表示されます。これは、1件のテストを実行し、0件が失敗した、ということを表しています。

1 example, 0 failures

その4: ページ内の文字列を検出

ログイン画面には、「メールアドレス」というラベルが表示されるとします。

ログイン画面が正しく表示されたかどうかを判定するには、URLにアクセスした後、このラベル文字列が含まれるページが表示されたかどうかを調べることでわかります。RSpecの構文で、以下のように記述します。

expect(page).to have_content('メールアドレス')

specファイル全体では以下のようになります。

spec/system/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :system, js: true do
  it 'ログイン画面が表示されること' do
    visit '/sessions/new'
    expect(page).to have_content('メールアドレス')
  end
end

CapybaraによるWebブラウザの操作方法

その1: Webページへのアクセス

System Specは、自動でWebブラウザを操作してWebアプリケーションにアクセスし、動作の確認を行う仕組みです。Webブラウザの操作には、Capybara を使用します。

Capybaraの構文を使用して、Webページにアクセスするには visit メソッドを使用します。

例えば、ログインページのパスが /sessions/new であったとすると、以下のように記述することで、ログインページにアクセスすることができます。

visit '/sessions/new'

その2: テキストフィールドへの入力

たとえばメニューを注文する画面において、配達先住所の入力欄が以下のように定義されていたとします。

<label for="order_delivery_address">配達先住所</label>
<input type="text" id="order_delivery_address" name="order[delivery_address]" value="">

この要素に対して「東京都新宿区内藤町11番地」と入力したい場合は、Capybaraの fill_in メソッドを使用し、以下のいずれかのように記述します。

# 入力対象をラベルで指定する場合
fill_in '配達先住所', with: '東京都新宿区内藤町11番地'
# 入力対象をid属性で指定する場合
fill_in 'order_delivery_address', with: '東京都新宿区内藤町11番地'
# 入力対象をname属性で指定する場合
fill_in 'order[delivery_address]', with: '東京都新宿区内藤町11番地'

その3: ドロップダウンの項目の選択

メニューを注文する画面において、支払方法の洗濯欄が以下のように定義されていたとします。

<select name="order[payment_method]">
  <option value="01">現金</option>
  <option value="02">クレジットカード</option>
</select>

このドロップダウンで「クレジットカード」を選択したい場合は、Capybaraの select メソッドを使用して、以下のように記述します。

select 'クレジットカード', from: 'order[payment_method]'

選択したい項目はvalueではなく、テキストで指定していることに注意してください。

その4: ボタンのクリック

メニューを注文する画面において、入力フォームの内容を送信するボタンが以下のように定義されているとします。

<input type="submit" value="注文する">

このボタンをクリックするには、click_button メソッドで、以下のように記述します。

click_button '注文する'

リンクをクリックしたい場合や、ボタンとリンクを区別したくない場合などについては、Clicking links and buttons を参照してください。

課題の実施にあたって

RSpecとCapybaraの簡単な使い方を確認したところで、課題を始めましょう。ここで、みなさんに無理難題を申し付けます。

課題にあたっては、ローカルPCで普段使用しているWebブラウザを使用しないでください。使用してもよいWebブラウザは、dockerコンテナ内にインストールされているheadless chromeのみとします。

ページがどのように表示されているのか分からなくて困る?まぁ、とりあえずはじめましょう。

課題1

ログイン画面が表示されることを確認してください。

説明

ログイン画面は、サンプルプログラムではSessionsコントローラ、newアクションに実装されています。Webブラウザでこのアクションに向かってアクセスし、ログイン画面が表示されることを確認してください。

Hint 1

ログイン画面を表示するためのテストコードは、前述「RSpecの使い方について」の「その4: ページ内の文字列を検出」に記載されています。実際にテストコードをspecファイルに記述して実行してみてください。

Hint 2

パスを固定文字列で直接指定してもかまいませんが、Sessionsコントローラ、newアクションにアクセスするためのURL(またはパス)は、URLヘルパーメソッド new_session_urlnew_session_path で取得することができます。これらのヘルパーメソッドでURL部分を置き換えてみましょう。

Hint 3

headless chromeの画面を直接見ることはできませんが、必要なタイミングでスクリーンショットを作成することができます。スクリーンショットを作成するには以下のように記述します。

take_screenshot

ログイン画面にアクセスした後に、スクリーンショットを作成してみましょう。テストコード内、visit メソッド呼び出しの次の行に追加して、実行してみてください。

スクリーンショットは tmp/screenshots/ というディレクトリに、png形式のファイルで作成されます。フォルダウィンドウでこのフォルダを開いておき、画像がプレビュー表示される状態にしておくとよいでしょう。

uveats_login.png

また、スクリーンショットではなく、生成されたHTMLを確認したい場合は、visit メソッド呼び出しの後に以下のように記述しておくと、RSpecを実行しているターミナルの画面に出力されます。

puts page.body

課題2

ログイン画面にメールアドレスとパスワードを入力し、ログインを成功させてください。

説明

ログイン画面にはメールアドレス、パスワードの2つの入力欄と、ログインボタンがあります。DBに存在するユーザのメールアドレスとパスワードを入力し、ログインボタンをクリックしてください。

Hint 1

テスト用のデータベースには、最初はレコードが1件も登録されていない状態です。ログインするためには、あらかじめユーザを1件登録しておく必要があります。

ユーザのレコードは、FactoryBotを使用すると、以下のようなコードで登録することができます。

user = create(:user)

user変数にUserモデルのインスタンスが代入されます。このインスタンスにはFactoryBotによって自動的に生成されたメールアドレスとパスワードがセットされています。それぞれ、user.mail、user.password とすると値を参照することができます。これらの値を、入力欄に与えてみましょう。

Hint 2

入力フォームのへの入力とボタンのクリックは、前述のように、fill_inclick_button で行うことができます。

Hint 3

ログインに成功すると、メニュー一覧画面が表示されます。ログインに成功したかどうかは、スクリーンショットを作成する、または、ページの内容にMenusという文字が含まれているかどうかを判定する、などの方法により確認することができます。

課題3

注文画面を完成させてください。

説明

ユーザがWebサイトにログインし、メニュー画面でいずれかのメニューを選択したとします。すると、注文画面が表示されます。

uveats_order.png

この注文画面のビューとコントローラの開発を行ってください。

注文画面には以下の入力項目があるとします。これらの項目がある入力フォームをビュー(app/views/orders/_form.html.erb)に追加してください。

入力項目 物理名 入力方法 選択肢
配達先住所 delivery_address テキスト -
支払方法 payment_method ドロップダウン 01:現金、02:クレジットカード
カード番号 card_number テキスト -

Ordersコントローラ(app/controllers/orders_controller.rb)では、createアクションで注文画面から送信された内容を受け取り、Orderモデルで、DBのordersテーブルにレコードを登録します。

Orderモデルのインスタンスを生成するには、以下の属性値を指定します。

属性名 物理名 値の取り出し元
メニューID menu_id 数値 params[:menu_id]
ユーザID user_id 数値 @me.id
配達先住所 delivery_address テキスト paramsorder内、delivery_address
支払方法 payment_method ドロップダウン paramsorder内、payment_method
カード番号 card_number テキスト paramsorder内、card_number

Ordersコントローラのorder_paramsメソッドに、上記の属性値を取り出すコードを記述してください。

テストコードは、spec/system/orders_spec.rbに記述してください。

Hint 1

ビューファイル _form.html.erb 内での、テキスト入力欄、送信ボタンなどの記述方法は、menususersなどのビューファイルを参考にしてください。

ドロップダウンの記述方法は、Railsガイドの SelectタグとOptionタグ を参考にしてください。以下のようなコードになります。

<%= form.select :payment_method, options_for_select([['現金', '01'], ['クレジットカード', '02']]) %>

Hint 2

どのようなHTTPリクエストが送信されているかを確認したくなった場合は、Railsのログを見ることで確認することができます。ログは log/test.log に出力されます。たとえば、注文画面での入力内容は以下のように送信されていることがわかります。

Started POST "/orders?menu_id=11" for 127.0.0.1 at 2020-02-18 23:38:23 +0900
Processing by OrdersController#create as HTML
  Parameters: {"order"=>{"delivery_address"=>"MyText1", "payment_method"=>"01", "card_number"=>"MyText1"}, "commit"=>"登録する", "menu_id"=>"11"}

新しいターミナルを開き、以下のコマンドを実行して、常にログを確認できる状態にしておくとよいでしょう。

tail -f log/test.log

Hint 3

注文を行うためには、あらかじめDBに以下のデータが必要です。

  • ログインする(=注文する)ユーザ
  • 注文するメニュー

このうち、ログインするユーザは、spec/system/orders_spec.rb内の以下の行により、自動的に登録されるようになっています。

  include_context :system_shared_context

メニューは、FactorbyBotを使用して、以下のように記述すると登録することができます。

create(:menu)

データを登録したら、Capybaraを使用して入力欄を埋め、「登録する」ボタンをクリックしてください。

課題4 おまけ

サンプルプログラムでは、注文画面において、支払方法に「クレジットカード」、カード番号に「0000」を入力すると、決済に失敗したとして例外が発生するようになっています。

例外を検知し、例外のメッセージと共に注文画面を再表示するようにプログラムを修正してください。

課題1の解答例

spec/system/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :system, js: true do
  it 'ログイン画面が表示されること' do
    # ここにテストコードを書く
    visit new_session_path
    take_screenshot
  end
end

課題2の解答例

spec/system/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :system, js: true do
  it 'ログインに成功すること' do
    # ここにテストコードを書く
    login_user = create(:user)
    visit new_session_path
    fill_in 'user[mail]', with: login_user.mail
    fill_in 'user[password]', with: login_user.password
    click_button 'ログイン'
    expect(page).to have_content('Menus')
  end
end

letを使って書く場合は以下のようになります。

spec/system/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :system, js: true do
  describe 'ログインに' do
    let (:login_user) { create(:user) }

    it '成功すること' do
      visit new_session_path
      fill_in 'user[mail]', with: login_user.mail
      fill_in 'user[password]', with: login_user.password
      click_button 'ログイン'
      expect(page).to have_content('Menus')
    end
  end
end

課題3の解答例

ビュー

app/views/orders/_form.html.erb
  <div class="field">
    <%= form.label :delivery_address %>
    <%= form.text_field :delivery_address %>
  </div>

  <div class="field">
    <%= form.label :payment_method %>
    <%= form.select :payment_method, options_for_select([['現金', '01'], ['クレジットカード', '02']]) %>
  </div>

  <div class="field">
    <%= form.label :card_number %>
    <%= form.text_field :card_number %>
  </div>

  <div class="actions">
    <%= form.submit nil, { class: 'button' } %>
  </div>

コントローラ

app/controllers/orders_controller.rb
    def order_params
      params.require(:order).permit(
        :delivery_address,
        :payment_method,
        :card_number,
      ).merge({
        menu_id: params[:menu_id]
      }).merge({
        user_id: @me.id
      })
    end

テストコード

spec/system/orders_spec.rb
require 'rails_helper'

RSpec.describe "Orders", type: :system, js: true do
  include_context :system_shared_context

  # ここにテストコードを書く
  describe '新規登録に' do
    let (:menu) { create(:menu) }
    let (:order) { build(:order) }
    let (:payment_trans) { { '01' => '現金', '02' => 'クレジットカード' } }

    it '成功すること' do
      visit new_order_path({ menu_id: menu.id })
      fill_in 'order[delivery_address]', with: order.delivery_address
      select payment_trans[order.payment_method], from: 'order[payment_method]'
      fill_in 'order[card_number]', with: order.card_number
      click_button '登録する'
      expect(page).to have_content('Order was successfully created.')
    end
  end
end

おわりに

開発において、DBの値のリセットや入力フォームへの値の入力などの作業をテストコードに任せる、ということを体験していただくことができましたでしょうか?

今回はRuby on RailsとSystem Specでしたが、他の開発言語、フレームワークでも同様なことができるものもあるでしょう。

テストコードはテストを行う手段というだけでなく、開発時にも使ってみていただけるとよいと思います。

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

Rails.root.join("foo", "bar")よりも、Rails.root.join("foo/bar")が良いのでは?というお話

はじめに:joinに渡す引数はいくつ?

たとえば、Railsのテストを書いたりするときに、テスト用のCSVファイルのパスを指定することがあると思います。

# 入力フォームでCSVファイルをアップロードするコード例
attach_file 'CSV file', csv_file_path

このとき、Rails.root.joinを使うと簡潔にファイルパスを取得できるのですが、いろんな人のコードを見ていると、大きく分けて以下のような2パターンがあるようです。

# "/" で区切った1つの文字列を渡すパターン
csv_file_path = Rails.root.join('spec/fixtures/sample.csv')

# パスの要素ごとに区切って複数の文字列を渡すパターン
csv_file_path = Rails.root.join('spec', 'fixtures', 'sample.csv')

僕はふだん前者のパターンで書いています。その理由は以下のとおりです。

  • 後者のパターンより短く書けるから
  • 'spec/fixtures/sample.csv'のようなパスが、プロジェクトルートから見た相対パスとして、ぱっと認識しやすいから

一方、後者のパターンで書く人の理由を聞いてみると、「実行環境によってはパスの区切り文字が/と限らないから」と答える人が多かったです。
これはおそらく、Windows環境でパスの区切り文字が\になることを意識しているんだと思います。

検証:"/"でパスを区切っても、Windows環境でちゃんと動く

しかし、後者のパターンで書く理由が「Windows環境を意識しているから」なのであれば、その心配はおそらく無用です。

Windows環境のrails consoleで、先ほどのようなRails.root.joinの2つの書き方を比較した結果を以下に載せます。
(僕はWindowsをふだん使わないため、Railsのバージョンが少し古いですが、おそらく挙動は今でも同じだと思います)

C:\dev\rails-sandbox>rails c
Loading development environment (Rails 5.0.2)
irb(main):001:0> Rails.root
=> #<Pathname:C:/dev/rails-sandbox>
irb(main):002:0> p1 = Rails.root.join('config', 'database.yml')
=> #<Pathname:C:/dev/rails-sandbox/config/database.yml>
irb(main):003:0> File.exist? p1
=> true
irb(main):004:0> p2 = Rails.root.join('config/database.yml')
=> #<Pathname:C:/dev/rails-sandbox/config/database.yml>
irb(main):005:0> File.exist? p2
=> true

すこし見づらいかもしれませんが、Rails.root.join('config', 'database.yml')と書いた場合も、Rails.root.join('config/database.yml')と書いた場合も、どちらも同じようにファイルの存在チェックに成功しています。

まとめ:"/"で区切る書き方でも問題ないのでは?

どちらが絶対に正しい書き方、というのはないと思いますが、短くシンプルに書ける「/で区切る書き方」を積極的に採用するのは悪くない考えだと思います。

もし「パスの区切り文字が/とは限らないから(Windows環境が心配だから)」という理由でRails.root.join('spec', 'fixtures', 'sample.csv')のような書き方をしている人がいたら、次回からRails.root.join('spec/fixtures/sample.csv')のような書き方も検討してみてください。

また、Rails.root.join('spec', 'fixtures', 'sample.csv')の方がメリットが大きい、という方がいたら、コメントをお待ちしています。

補足1:"/"で始まるパスを渡さないように注意!

ただし、この書き方には1つだけ注意点があります。それは「/ではじまる文字列を引数として渡さないこと」です。
/で始まる文字列を引数にすると、プロジェクトルートのパスが付与されず、引数がそのまま絶対パスとして扱われます。

# OK: 最初に"/"を付けない場合→プロジェクトルートのパスが付与される
Rails.root.join('spec/fixtures/sample.csv').to_s
#=> "/Users/jnito/dev/rails-sandbox/spec/fixtures/sample.csv"

# NG: 最初に"/"を付けた場合→プロジェクトルートのパスが付与されない
Rails.root.join('/spec/fixtures/sample.csv').to_s 
#=> "/spec/fixtures/sample.csv"

補足2:Rails.rootメソッドが返すオブジェクトは何?

Rails.rootメソッドが返すのはRuby標準のPathnameオブジェクトです。

Rails.root.class
#=> Pathname

また、joinメソッドもRuby標準の実装が使われています。(ActiveSupportによって拡張されているわけではありません)

Rails.root.method(:join).source_location
#=> ["/Users/jnito/.rbenv/versions/2.6.5/lib/ruby/2.6.0/pathname.rb", 407]

Pathname#joinメソッドの仕様については以下のドキュメントを参照してください。

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

RailsでGenerateしたらArgumentErrorで怒られた話

ルー大柴みたい

Railsチュートリアルも完走したし、オリジナルWebアプリ作るぞーと意気込んでGenerateしてみたところ、
ArgumentErrorと怒られたのでその備忘録です。

$ rails generate controller Companys show
Traceback (most recent call last):
        58: from bin/rails:4:in `<main>'
        〜〜省略〜〜
         1: from /Library/Ruby/Gems/2.6.0/gems/actionpack-5.1.6/lib/action_dispatch/routing/mapper.rb
            :307: in `check_controller_and_action'
/Library/Ruby/Gems/2.6.0/gems/actionpack-5.1.6/lib/action_dispatch/routing/mapper.rb
:327:in `check_part': Missing :controller key on routes definition, please check your routes. 
(ArgumentError)

ArgumentErrorとは

PixelSnap 2020-02-18 at 07.41.04@2x.png

メソッドの引数が正しくない時や足りない時に発生するエラーです。

wrong number of arguments (given 0, expected 1+) というエラーメッセージが表示されています。
これは 引数の数が間違っています(現状0個です、正常な引数の数は1個以上です) という意味です。
つまり form_for のメソッドの引数が足りないために起こっているエラーだということが分かります。

メソッドのリファレンス(http://railsdoc.com/)を見ながら、正しい引数が設定されいるかを確認し修正すると解決できることが多いです。

参考記事「エラーメッセージから学ぶ」

なるほど、ArgumentErrorは引数のエラーなんですね
そしてエラー文を読み込むのが大事と。

エラーメッセージに立ち返る

Missing :controller key on routes definition, please check your routes. (ArgumentError)
「routes上にあるコントローラーのキーが未定義ですので、routesを確認してね。」

なんかよくわからんけど、please check your routes.って言われたし仕方ねえ確認してやるか。

routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
  get ''  # <= 何これ?
end

とりあえず削除してみて、もう一度generateしてみる

$ rails generate controller Companys show
      create  app/controllers/companys_controller.rb
       route  get 'companys/show'
      invoke  erb
      create    app/views/companys
      create    app/views/companys/show.html.erb
      invoke  test_unit
      create    test/controllers/companys_controller_test.rb
      invoke  helper
      create    app/helpers/companys_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/companys.coffee
      invoke    scss
      create      app/assets/stylesheets/companys.scss

上手く行った!!!

結論:作業途中のコードを放置するのはやめよう

そう心に誓いました。

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

【Rails】ユーザー情報変更後投稿一覧ページに遷移する

現在作成中のポートフォリオでは、ユーザー機能にdeviseを利用しています。

今回はユーザー情報変更後、指定のページに戻る方法を学びましたのメモしておきます。

registrations_controllerの設定

registrations_controller.rbで以下の部分のコメントアウトを外してください。

registrations_controller.rb
def after_update_up_path_for(resource)
    # ここにページ遷移したいパスをいれる
    home_show_path(resource)
  end

私の場合はhome/showページに飛ばしたかったのでhome_show_pathと記入しています。

routesの設定

次はルートです。
以下のように記述してください。

routes.rb
as :user do
  get 'home/show',:to =>'devise/registrations#edit',:as => :user_root
end

asメソッドを使うと、どのリソースがルートかを伝えてくれるため、指定ページに簡単にリダイレクトしてくれます。

まとめ

今回のパターンを含め、他パターンは公式で紹介されているのでそちらもぜひ参考にしてください。
How To: Customize the redirect after a user edits their profile

deviseは便利ですが結構複雑な印象があるので、色々試してなれていきたいです。

この記事が誰かの助けになれば幸いです。
ではでは。

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

rails s が立ち上がらない "Booting Puma", "server is already running"

$ rails s
=> Booting Puma
=> Rails version application starting in development 
=> Run `rails server --help` for more startup options
A server is already running. Check app/tmp/pids/server.pid.
Exiting

備忘録的に、、、

前回メソッドをいじっていたらループで抜けられなくなり強制終了(ごめんなさい!)

rails sを起動したまま終了したので、次に起動しても already running.(もう動いてるよ!)だそうです。

メッセージの通りに app/tmp/pids/sever.pid.をCheck。server.pid.なるものを右クリックで削除。

再度rails sで起動しました。

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

Railsのannotateでハマった話

ハマったところ

Railsでannotateのgemを入れて

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
  gem 'pry-byebug'
  gem 'annotate'
end

インストールしたのに

bundle install

annotateコマンドが反映されない,なぜか

$ bundle exec annotate
// なにも結果が帰ってこない

こうやって解決

Railsのプロジェクトに明示的に導入しないと使えないみたい.

$ rails g annotate
// これでannotateできるようになった

gemがinstallされていることと,今作業しているプロジェクトで使えることは別モノだということに気づけた.

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

画像をリモートで表示させる

使用例)
 publicファイルにlogo.pngがある
 title__logoクラスがある

(haml)
.title__logo
 = image_tag"/logo.png",alt""


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

Rails6 シンプルなカレンダーを実装する

目的

  • Rails6のアプリ作成からシンプルなカレンダーを表示させるまでの手順をまとめる

環境

  • Rails version
    • 6.0.2.1
  • SQLite3
    • 1.4
  • OS
    • macOS 10.13.6

目標

  • http://localhost:3000/home/topにアクセスした時にカレンダーが表示される。
  • データベースからのデータをセルに表示して。。。などややこしいことはせずとにかくカレンダーを表示する。

公式の手順

実施方法

  1. Rails6のアプリを作成

    1. 下記コマンドを実行して「simple_calendar」という名前のアプリを作成する。

      $ rails _6.0.0_ new simple_calendar
      
    2. 下記コマンドを実行して一度simple_calendarを起動する。

      $ cd simple_calendar
      $ rails s
      
    3. ブラウザでhttp://localhost:3000/にアクセスし下記の画面が表示されるか確認する。

      スクリーンショット 2020-02-17 22.09.47.png

    4. 下記キーを押下して一旦アプリを停止する。

      • 「Ctrl」 + 「c」
  2. http://localhost:3000/home/topの画面表示準備

    1. 下記コマンドを実行して「homeコントローラ」と「topアクション」と対応するビューファイルを作成する。

      $ rails g controller home top
      
    2. 下記コマンドを実行して一度simple_calendarを起動する。

      $ rails s
      
    3. ブラウザでhttp://localhost:3000/home/topにアクセスし下記の画面が表示されるか確認する。

      スクリーンショット 2020-02-17 22.13.44.png

    4. 下記キーを押下して一旦アプリを停止する。

      • 「Ctrl」 + 「c」
  3. Gemのインストール

    1. アプリ名ディレクトリ直下にあるGemFileの最終行にgem "simple_calendar", "~> 2.0"と追記する。
    2. 下記に記載前後のGemFileを記載する。

      • 記載前

        source 'https://rubygems.org'
        git_source(:github) { |repo| "https://github.com/#{repo}.git" }
        
        ruby '2.5.0'
        
        # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
        gem 'rails', '~> 6.0.0'
        # Use sqlite3 as the database for Active Record
        gem 'sqlite3', '~> 1.4'
        # Use Puma as the app server
        gem 'puma', '~> 3.11'
        # Use SCSS for stylesheets
        gem 'sass-rails', '~> 5'
        # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
        gem 'webpacker', '~> 4.0'
        # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
        gem 'turbolinks', '~> 5'
        # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
        gem 'jbuilder', '~> 2.7'
        # Use Redis adapter to run Action Cable in production
        # gem 'redis', '~> 4.0'
        # Use Active Model has_secure_password
        # gem 'bcrypt', '~> 3.1.7'
        
        # Use Active Storage variant
        # gem 'image_processing', '~> 1.2'
        
        # Reduces boot times through caching; required in config/boot.rb
        gem 'bootsnap', '>= 1.4.2', require: false
        
        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]
        end
        
        group :development do
          # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
          gem 'web-console', '>= 3.3.0'
          gem 'listen', '>= 3.0.5', '< 3.2'
          # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
          gem 'spring'
          gem 'spring-watcher-listen', '~> 2.0.0'
        end
        
        group :test do
          # Adds support for Capybara system testing and selenium driver
          gem 'capybara', '>= 2.15'
          gem 'selenium-webdriver'
          # Easy installation and use of web drivers to run system tests with browsers
          gem 'webdrivers'
        end
        
        # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
        gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
        
      • 記載後

        source 'https://rubygems.org'
        git_source(:github) { |repo| "https://github.com/#{repo}.git" }
        
        ruby '2.5.0'
        
        # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
        gem 'rails', '~> 6.0.0'
        # Use sqlite3 as the database for Active Record
        gem 'sqlite3', '~> 1.4'
        # Use Puma as the app server
        gem 'puma', '~> 3.11'
        # Use SCSS for stylesheets
        gem 'sass-rails', '~> 5'
        # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
        gem 'webpacker', '~> 4.0'
        # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
        gem 'turbolinks', '~> 5'
        # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
        gem 'jbuilder', '~> 2.7'
        # Use Redis adapter to run Action Cable in production
        # gem 'redis', '~> 4.0'
        # Use Active Model has_secure_password
        # gem 'bcrypt', '~> 3.1.7'
        
        # Use Active Storage variant
        # gem 'image_processing', '~> 1.2'
        
        # Reduces boot times through caching; required in config/boot.rb
        gem 'bootsnap', '>= 1.4.2', require: false
        
        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]
        end
        
        group :development do
          # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
          gem 'web-console', '>= 3.3.0'
          gem 'listen', '>= 3.0.5', '< 3.2'
          # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
          gem 'spring'
          gem 'spring-watcher-listen', '~> 2.0.0'
        end
        
        group :test do
          # Adds support for Capybara system testing and selenium driver
          gem 'capybara', '>= 2.15'
          gem 'selenium-webdriver'
          # Easy installation and use of web drivers to run system tests with browsers
          gem 'webdrivers'
        end
        
        # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
        gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
        
        gem "simple_calendar", "~> 2.0"
        
    3. 下記コマンドを実行してGemをインストールする。

      $ bundle install
      
  4. Gemの設定

    1. 下記のファイルのファイルを開く。
      • アプリ名ディレクトリ/app/assets/stylesheets
        • application.css
    2. *= require simple_calendarを追記する。

      • 記載前

        /*
         * This is a manifest file that'll be compiled into application.css, which will include all the files
         * listed below.
         *
         * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
         * vendor/assets/stylesheets directory can be referenced here using a relative path.
         *
         * You're free to add application-wide styles to this file and they'll appear at the bottom of the
         * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
         * files in this directory. Styles in this file should be added after the last require_* statement.
         * It is generally better to create a new file per style scope.
         *
         *= require_tree .
         *= require_self
         */
        
      • 記載後

        /*
         * This is a manifest file that'll be compiled into application.css, which will include all the files
         * listed below.
         *
         * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
         * vendor/assets/stylesheets directory can be referenced here using a relative path.
         *
         * You're free to add application-wide styles to this file and they'll appear at the bottom of the
         * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
         * files in this directory. Styles in this file should be added after the last require_* statement.
         * It is generally better to create a new file per style scope.
         *
         *= require simple_calendar
         *= require_tree .
         *= require_self
         */
        
  5. 表示確認

    1. 下記のファイルを開く。
      • アプリ名ディレクトリ/app/views/home
        • top.html.erb
    2. 下記のコードを追記する。

      <%= month_calendar do |date| %>
        <%= date %>
      <% end %>
      
    3. top.html.erbの追記前後の様子を記載する。

      • 追記前

        <h1>Home#top</h1>
        <p>Find me in app/views/home/top.html.erb</p>
        
      • 追記後

        <h1>Home#top</h1>
        <p>Find me in app/views/home/top.html.erb</p>
        
        <%= month_calendar do |date| %>
          <%= date %>
        <% end %>
        
    4. 下記コマンドを実行して一度simple_calendarを起動する。

      $ rails s
      
    5. ブラウザでhttp://localhost:3000/home/topにアクセスし下記の画面が表示されるか確認する。(月日はいつを表示していてもOK)

      スクリーンショット 2020-02-17 22.39.52.png

予告

  • ビューファイルを下記のように記載するとカレンダーのセルに指定のコンテンツを記載できることがわかったため、DBの特定レコードのcreate_atなどとリンクさせてDB内コンテンツの記載をしてみようと思う。
  • 忘れないように予告として書かせていただいた。

    <h1>Home#top</h1>
    <p>Find me in app/views/home/top.html.erb</p>
    
    <%= month_calendar do |date| %>
      <%= date %>
      <p>わろた</p>
    <% end %>
    
  • 下記に前述のコードを記載した際のhttp://localhost:3000/home/topのプレビューを記載する。

    スクリーンショット 2020-02-17 23.15.37.png

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