20190227のRailsに関する記事は17件です。

payjp.jsの導入方法<Rails>

Payjp.jsを導入するにあたって、理解するのに時間がかかってしまったので(なにせ記事が少ない!)今後導入される方の時間短縮のために、ここに残しておきます。

Pay.jpとは

Pay.jpのサービスを利用することでクレジットカード決済機能を簡単に導入することができます。
gem'payjp'をインストールすることで使用可能に。
利用方法については大きく2通りあります。

「チェックアウト」

公式で用意されたスクリプトタグをHTMLに記述すれば、デザインされた決済フォーム、カード情報のバリデーション、カード情報のトークン化を行うフォームを生成することができます。こちらを使用すれば、ささっとすぐに実装可能です。
ビューについてこだわりがないという方であればこちらで十分だと思います。

「カスタムフォーム(payjp.js)」

payjp.jsを使うことで、好きなデザインや挙動でトークン化を行うフォームを組み込めます。
ここで注意をしたいのが、payjp.jsはトークン化に特化したライブラリであり、これを使用したからといって決済ができる訳ではありません。
クレジットカード情報入力ページは自分の好みのビューにしたい!という方はこちらです。
今回はこのpayjp.jsで実装していくための説明となります。

トークンとは

クレジットカード情報を自身のデータベース上で管理するのはセキュリティの観点で好ましくないので、顧客が入力したカード情報をトークン化しなくてはいけません。
顧客情報を持った情報の塊のことをトークンを呼びます。IDを使用することで顧客情報を呼び出すことができます。

トークン化については公式を参考にしてください。
https://pay.jp/docs/cardtoken

またトークンは1度のみの使用しか出来ないので、2度以上使いたい場合は顧客カードを作成してカード情報をそこに紐付けることが必要です。顧客カードへの紐付けというのは後ほど説明します。

いざ導入!

ここからは実際のコードを交えて説明します。
まずは、顧客がカード情報を入力するためのフォームを作成します。

qiita.html.haml
%h2 クレジットカード情報入力
  = form_tag("/users/purchase", method: "PATCH", id: "charge-form") do

  %label カード番号
    %input{maxlength: "16", type: "text", class: "number", id: "payment_card_no" name: "number", placeholder: "カード番号"}
  %label CVC
    %input{type: "text", class: "cvc", id: "payment_card_security_code", name: "cvc", maxlength: "3", placeholder: "CVC"}
  %label 有効期限
    %input{type: "text", class: "exp_month", id: "payment_card_expire_mm",  name: "exp_month", maxlength: "2", placeholder: "月"}
    %input{type: "text", class: "exp_year", name: "exp_year", maxlength: "4",  placeholder: "年"}

Pay.jpにトークンを作成してもらうには、公式で用意されているフォームを活用しましょう。
(本記事では所々書き換えています。)

qiita.js
$(document).on('turbolinks:load', function() {
  var form = $("#charge-form");
  Payjp.setPublicKey('pk_test_0383a1b8f91e8a6e3ea0e2a9');

  $("#charge-form").on("click", "#submit-button", function(e) {
    e.preventDefault();
    form.find("input[type=submit]").prop("disabled", true);
    var card = {
        number: parseInt($("#payment_card_no").val()),
        cvc: parseInt($("#payment_card_security_code").val()),
        exp_month: parseInt($("#payment_card_expire_mm").val()),
        exp_year: parseInt($("#payment_card_expire_yy").val())
    };
    Payjp.createToken(card, function(s, response) {
      if (response.error) {
        alert("error")
        form.find('button').prop('disabled', false);
      }
      else {
        $(".number").removeAttr("name");
        $(".cvc").removeAttr("name");
        $(".exp_month").removeAttr("name");
        $(".exp_year").removeAttr("name");

        var token = response.id;
        $("#charge-form").append($('<input type="hidden" name="payjp_token" class="payjp-token" />').val(token));
        $("#charge-form").get(0).submit();
      }
    });
  });
});

顧客がsubmitボタンを押した瞬間にこのjsファイルが反応するようにしてあげます。
このフォームの中のPayjp.createTokenで作成されたトークンIDがresponse.idに返ってきます。
後はif文で分岐をさせてあげて、最後にe.preventDefault();によって停止させていたsubmitを実行させます。

setPublicKeyにはpay.jpにログインした際の管理画面で確認できるAPIキーを入れてください。
APIキーはconfig/initializers/setting.rbを作成してその中に変数を定義して入れてあげるとrbファイル間で共有できるので、利便性・セキュア面で良いですね!
(記載しているAPIキーは公式のものなのでご心配なく)

qiita.html.haml
 %input.payjp-token{ type: "hidden", name: "payjpToken", value: "" }

そしてHTML上で上記の記述をして、jsファイルで作成したトークンIDが空のvalueに入ることで、paramsとしてコントローラーへ投げることができます。
inputタグをhiddenにすることでビュー上には反映されていません。
以上でトークン作成と、トークンIDをparamsとしてコントローラーへ渡せるようになりました。

顧客カードとの紐付け

カード情報非通過対応により2018年6月頃からサーバーサイドからのカード情報のPOSTリクエストができなくなりました。
そのためトークン化はビュー上で行いましたが、顧客作成はコントローラーで行います。

users.controller.rb
Payjp.api_key = PAYJP_SECRET_KEY
customer = Payjp::Customer.create(card: params[:payjp_token])

Payjp::Customer.createで顧客カードが作成されたので、その中のcardキーのバリューとしてトークンIDを指定してあげます。
カード情報を取り出す際にもこのIDは使用するので、私はUserテーブルのカラムに入れてあげました。

他にも支払い等の詳しいやり方は公式のリファレンスを参照ください。
https://pay.jp/docs/api/#introduction

終わりに

私自身まだエンジニアとしてプログラミングを学んで3ヶ月満たないので、ツッコミどころがあるかもしれません。
こういったやり方もあるんだな〜という、一つの実装方法として参考程度に見てください。
他に効率の良いやり方があるよ!この記述無駄じゃない?と気づいた方がいらしたらご指摘ください(_ _)

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

Ruby on RailsのIDEをどれにしようかなぁと軽く調べた

Cloud9で開発するの、地味に疲れる

今までAWSのクラウド開発環境であるCloud9を利用してきたわけですが、それをやめようかなぁと思い始めています。
理由は単純で、Command+W 暴発問題があるから。

ファイルを閉じたくてうっかりCommand+Wを叩くと、Safariのタブが閉じてしまい、開発環境そのものが閉じてしまいます。
そんなの気をつけれてば済む話じゃんって言われるとそこまでなのですが、ロジックを書くことに集中しているといつの間にか頭から飛んでしまうのだ…つらぁ…。

Macに入れるIDEを考え始める

元々Java屋でEclipseを使っていたので、とりあえずイマドキなIDEを入れればいいかって考えに至り、探してみることに。
パッと検索した限り、以下の3つが良いっぽい。

  • Eclipse (+RadRails)
  • RubyMine
  • VS Code

Eclipse (+RadRails)

スクリーンショット 2019-02-27 23.17.44.png

Javaをやってる方で(使用有無はさておき、)Eclipseを知らない方はいないでしょう。
Pleiadesでフル日本語化されたAll in Oneを落としてくればささっと開発に取りかかれる簡単さがあります。
元々使っていたところにプラグインを入れれば使えるというのは学習コストをかなり抑えられそうです。

良さそうなところ:

  • 学習コスト低め

ちょっとなーってところ:

  • Javaやってた時と代わり映えしないので飽きが…

RubyMine

スクリーンショット 2019-02-27 23.01.02.png

使いやすいIDEを出してくることに定評のある(と伝え聞いている)JetBrains社製IDE。
ちょっと前までデファクトスタンダードだったとどこかで見ました。

良さそうなところ:

  • 情報量が豊富、何かあっても検索してどうにかなりそう

ちょっとなーってところ:

  • 有料か…

VS Code

スクリーンショット 2019-02-27 23.00.30.png

プロプライエタリ製品の名称だったVisual Studioの名を冠して登場した無料版だと思ってたら、あっという間にWeb業界へ浸透している感のあるIDE。
色んなところから「VS Codeはいいぞ」と聞こえてきます。

良さそうなところ:

  • 今超絶ホット。使ってるだけで最前線感バリバリ。

ちょっとなーってところ:

  • まだ情報が蓄積されていなさそう(& バージョン依存のトラブルシュートが引っかかる)

とりあえずVS Codeからかじってみようかなー

ローカルでRailsの開発をするのはこれからなので、切り替えの手間を考えなくていい分、新しいやつをやってみようという安直な理由でVS Code。
ダメだったらEclipseに転ぶことにしよう。

IDE?テキストエディタ使えよ

わかる、わかるよ、その方が軽いし環境選ばないしハッカーっぽいしで良いってのはわかるよ。
でもこちとらRailsに関してはホビープログラマ、楽なやり方で開発したいのだ…本当にすまない…。

おすすめあったら教えてくださいなんでもしますから!

「(なんでもするとは言ってない)」の文脈。
「RailsでこのIDEいいよ!」って意見、お待ちしております。

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

RailsでI18n.localeを書き換えていたらたまに言語設定がバグる問題に遭遇した

RailsでI18n.localeを書き換えてたらたまに言語設定がバグる問題に遭遇した

他のユーザー(:en)で画面を参照したあとに元のユーザー(:ja)で画面を見ると、
稀に、locale=:enで画面が表示されてしまうという問題に遭遇しました。

実装はこんな感じ。

class HogeController < ApplicationController
  before_action :set_locale

  def set_locale
    I18n.locale = current_user.locale || I18n.default_locale
  end
end

https://railsguides.jp/i18n.html#リクエスト間でロケールを管理する

Railsガイドでもだいたいおんなじ感じのコードが乗ってる。ApplicationControllerに書いて全コントローラーに継承させるのならこれでもいけるんですが、継承されないコントローラーがあったりするとバグる。

https://github.com/rails/rails/pull/34356

こんなPull RequestもありI18nはスレッドセーフだと言っていますが、
スレッドセーフなだけで、after_actionとかで元の言語に戻しておいてあげないと次に同じスレッドを使ったときに変更されたままの言語が使われてしまうといった感じなんだと思われます。
(違うコントローラーなどでset_localeが実行されない場合に起こる)

こんなふうに実装を変えたら解消しました。

class HogeController < ApplicationController
  around_action :set_locale

  def set_locale
    locale = current_user.locale || I18n.default_locale
    I18n.with_locale locale do
      yield
    end
  end
end

I18n.with_locale を使うことにより、ロケールの変更をアクション実行時に限定しました。
after_action でもとに戻すでも行けると思いますが、こっちのほうがドヤり感が高い。

around_action 初めて使った。

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

RSpec

SPecのモデルを作成

Userモデル用に作成

$ rails g rspec:model user

RSpecのテスト実行

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

Ruby コメント、変数、定数、リテラル

動作環境はMacとなります。
主に自分の勉強用メモとして残しています。

コメント

特定の処理を向こうにしたいときや、メモを残したい時にしようする。

comment.rb
# puts "Hello, World!" # 処理を向こうにしたいときは、プログラムの先頭に#をつける。改行までがコメント扱いとなる。
$ruby comment.rb 
$ #コメントアウトしているため出力されない。

複数行コメントアウトしたい場合

comment.rb
=begin
puts "Hello, World!"
puts "Hello, World!"
puts "Hello, World!"
=end
# =beginから=endの間の処理がコメントアウトされる
$ruby comment.rb 
$ #コメントアウトしているため出力されない。

実務上複数行に渡る場合でも、行頭に一行ごとに#をつけて実行されることが多い。

コメントを解除したい場合

commandキー+/キーで解除可能 ※複数行の場合、解除したい行をまとめて選択

メモとして残したい場合でも使用可能。

comment.rb
# This is a comment. 
$ruby comment.rb 
$ #コメントアウトしているため出力されない。

変数

扱うデータに名前をつけて、一時的に利用できるようにしたもの

変数名 = 式


name = "suzuki"
fruit = "apple"
x = 10


#変数sに Helloという文字列を代入
irb(main):001:0> s = "Hello!" 
=> "Hello!"
irb(main):002:0> puts s
Hello!
=> nil
#出力

#型の宣言は不要
irb(main):001:0> char s = "Hello!" 
irb(main):001:0> var s = "Hello!" 

数値の代入

irb(main):003:0> n = 2 * 10 #値20を変数nに代入
=> 20
irb(main):004:0> puts n  #変数nの出力
20 
=> nil

変数には何かしら代入しないといけない


#変数に何も値が入っていないとエラーになる。
irb(main):005:0> i
NameError: undefined local variable or method `i' for main:Object
    from (irb):5
    from /Users/toripurug884/.rbenv/versions/2.4.1/bin/irb:11:in `<main>'

#中身を入れて再出力
irb(main):006:0> i = nil
=> nil
irb(main):007:0> i
=> nil
#実行された!

変数名の命名規則

アルファベットの小文字かアンダースコアで始まる。
それに続けてアルファベット、数字、アンダースコアで記述する。

irb(main):014:0> price = 100
=> 100

#以下原則しないほうが良い命名規則

irb(main):015:0> price1 = 100  #数字をつける
=> 100

irb(main):016:0> _price = 100  #アンダースコアから始まる変数
=> 100

#数字から始まる変数名はエラーになる。

irb(main):017:0> 1price = 100
SyntaxError: (irb):17: syntax error, unexpected tIDENTIFIER, expecting end-of-input
1price = 100


#2語以上になる場合は原則_で繋げる。
#キャメルケース(単語と単語の間の文字を大文字にする)は原則使わない。

irb(main):018:0> price_cost = 100
=> 100
irb(main):019:0> priceCost = 100
=> 100
irb(main):020:0> 

よくない変数の使い方

エラーではないが、メンテナンス性が悪くなるので推奨されない変数の使い方

同じ変数に違う型を代入

irb(main):022:0> x = "abc" #文字列を代入
=> "abc"
irb(main):023:0> x = 123 #数字を代入
=> 123
irb(main):024:0> x = "def" #文字列を代入
=> "def"
irb(main):025:0> x = [1,2,3] #配列を代入
=> [1, 2, 3]

#同じ値を入れてしまうとわかりにくくなる

複数の変数に代入

irb(main):026:0> x,y = 1,2
=> [1, 2]
irb(main):027:0> x
=> 1
irb(main):028:0> y
=> 2

#あまりメリットがない。
irb(main):029:0> a = b = 10
=> 10
irb(main):030:0> a
=> 10
irb(main):031:0> b
=> 10

#あえて使う必要はない

定数

あまり変化しない値を扱う時に使う
例)消費税、円周率、手数料など

定数は原則全て大文字


irb(main):032:0> TAX = 1.08 #定数TAXに値を代入
=> 1.08
irb(main):036:0> puts TAX
1.08
=> nil

irb(main):037:0> TAX_RATE = 1.08 #2語以上の定数の場合単語は_で繋ぐ
=> 1.08
irb(main):038:0> puts TAX_RATE
1.08
=> nil

コードのメンテナンスを保つため、定数には再代入は原則しない。

irb(main):039:0> TAX_RATE = 1.1 #一度定義した定数に再代入


#警告が出るが、再代入されてしまうので注意
(irb):39: warning: already initialized constant TAX_RATE
(irb):37: warning: previous definition of TAX_RATE was here
=> 1.1
#再代入後の値が出力される
irb(main):040:0> puts TAX_RATE 
1.1
=> nil

リテラルとは

数値の123や文字列の"Hello! World"など
Rubyのプログラムの中に直接記述できる値

ソースコードに書いたもの

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

Herokuデプロイ時に「 `relative_path_from': different prefix」がでたよ

heroku本番環境にS3を使いたかったので、gem aws-sdkを登録し、
bundle installおよびcommit、リモートリポジトリにpushまでしたあと

$ git push heroku dev:master

を実行したら

Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 5.73 KiB | 2.87 MiB/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Ruby app detected
remote: 
remote:  !
remote:  !     different prefix: "" and "/tmp/build_edc3150bda146e5ca10eb1b67d801667"
remote:  !
remote: /app/tmp/buildpacks/b7af5642714be4eddaa5f35e2b4c36176b839b4abcd9bfe57ee71c358d71152b4fd2cf925c5b6e6816adee359c4f0f966b663a7f8649b0729509d510091abc07/vendor/ruby/heroku-18/lib/ruby/2.5.0/pathname.rb:522:in `relative_path_from': different prefix: "" and "/tmp/build_edc3150bda146e5ca10eb1b67d801667" (ArgumentError)

というエラーがでた。

解決策

rails sをすると、

$ rails s
Your lockfile is unreadable.
Run `rm Gemfile.lock` and then `bundle install` to generate a new lockfile.

とでたので、rm Gemfile.lockを実行し、bundle installを実行。

その後、再度git push heroku dev:masterをすると、deploy成功。


ググってもまったくヒットしなかったので、
この記事にヒットした方は試してみてください。。


おわり

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

助けてください

スクリーンショット 2019-02-27 17.44.14.png
rails consoleを閉じたいのですが、謎の ? が出現してしまい,rails consoleが終了できません。
これが解除できないと先に進めません。助けてください。よろしくお願いします。

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

ActiveStorageのファイルをアップロードするタイミングについて

ActiveStorageを使うにあたって、アップロードできたかどうかを調べるために、アップロードをどの処理で実施しているか調べたので、覚書として。(アップロードできたかどうかについては、これから改めて調査)

アプリケーションコードに近い方から遠い方へ順に追っていきます。

前提条件

  • activestorage のバージョンは5.2.2を使用する
  • 属性の定義に has_one_attached を使用する
  • attachするファイルはHTMLからアップロードしたファイルを使用する
  • ファイル保存サービスはローカル(S3やGCSではない)を使用する

ActiveStorage::Attached::Macros

https://github.com/rails/rails/blob/v5.2.2/activestorage/lib/active_storage/attached/macros.rb#L37

ActiveRecord::Baseを継承するモデルクラス内に定義したhas_one_attachedの属性に対して、値が代入された時にattachメソッドを実行します。

ActiveStorage::Attached::One

https://github.com/rails/rails/blob/v5.2.2/activestorage/lib/active_storage/attached/one.rb#L24

ActiveStorage::Attached::One.attach メソッドが create_blob_from を実行します。

ActiveStorage::Attached

https://github.com/rails/rails/blob/v5.2.2/activestorage/lib/active_storage/attached.rb#L23

ActiveStorage::Attached. create_blob_from メソッドで、attachableのクラスによって処理を分けています。
今回はアップロードされたファイルを属性に指定したので、
ActiveStorage::Blob.create_after_upload! を実行します。

ActiveStorage::Blob

https://github.com/rails/rails/blob/v5.2.2/activestorage/app/models/active_storage/blob.rb#L65

ActiveStorage::Blob.create_after_upload!ActiveStorage::Blob.build_after_upload を実行します。

https://github.com/rails/rails/blob/v5.2.2/activestorage/app/models/active_storage/blob.rb#L57

ActiveStorage::Blob.build_after_uploadActiveStorage::Blobnewして、upload を実行します。

https://github.com/rails/rails/blob/v5.2.2/activestorage/app/models/active_storage/blob.rb#L159

ActiveStorage::Blob#uploadserviceインスタンスのuploadを実行します。
このserviceは、公式ガイドのセットアップで service に指定した DiskS3ActiveStorage::Service::DiskServiceActiveStorage::Service::S3Serviceをnewしたインスタンスが格納されています。

今回はローカルディスクを保存サービスに指定しているので、ActiveStorage::Service::DiskServiceuploadを実行します。

ActiveStorage::Service::DiskService

https://github.com/rails/rails/blob/v5.2.2/activestorage/lib/active_storage/service/disk_service.rb#L20

ActiveStorage::Service::DiskService.uploadIO.copy_stream でファイルを Rails.root/storage/にコピーして、アップロード処理が終わります。

instrument のブロックで囲まれていますが、これは ActiveSupport::Notifications.instrument をラップするためのメソッドです。
ログを見ると、

logs/development.log
Disk Storage (31.0ms) Uploaded file to key: fH4PhWtse6qwM6hBqsoqqmtz (checksum: ci+Mp/YmvV385qYcTJnlgg==)

こんな感じのログが、アップロードファイルを属性に代入、またはattachの処理あたりで流れてくると思います。

感想

調べ終わってから記事を書いたので思考とは逆順になっています。

調べ始める前は、モデルクラスをsaveするタイミングでupload的なメソッドをアプリケーションコードに近いところで実行していると予想していたので、attachがそれに相当すると気づくまで時間がかかりました。
saveに合わせてファイルを保存するようにしたい場合は、直接属性に代入するのではなく、after_saveかそれに近いタイミングでattachしてあげると良さそうです。

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

RSpec+FactoryBot+Springでエラーが発生したお話

はじめに

Deviseを利用したWebアプリを作成しており、RSpecを用いたテストを実装しています。
FactoryBotを使い早速Userモデルのモデルスペックを作成し始めたのですが、エラーが発生してしまいました。
一応、解決することができたので、事態をメモしたいと思います。

環境

ruby                  : 2.5.1
rails                 : 5.2.2
device                : 4.6.1
rspec-rails           : 3.8.2
factory_bot_rails     : 5.0.1
capybara              : 3.13.2
spring-commands-rspec : 1.0.4

事態

前提

ユーザーに関するファクトリーとモデルスペックは以下の通りです。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "User#{n}" }
    sequence(:email) { |n| "tester#{n}@example.com" }
    password "password"
    password_confirmation "password"
  end
end
spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  it "is valid with a name, email,password and password_confirmation" do
    expect(build(:user)).to be_valid
  end
end

エラー内容

1. NoMethodError

上記の通りファクトリーとモデルスペックを作成してbundle exec rspec spec/models/user_spec.rbを執行したところ以下のエラーが発生しました。

An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error: password "password"


NoMethodError:
  undefined method 'password' in 'user' factory
# ./spec/factories/users.rb:5:in `block (2 levels) in <main>'
# ./spec/factories/users.rb:2:in `block in <main>'
# ./spec/factories/users.rb:1:in `<main>'
# ./config/environment.rb:5:in `<top (required)>'

# ./spec/rails_helper.rb:4:in `require'
# ./spec/rails_helper.rb:4:in `<top (required)>'
# ./spec/models/user_spec.rb:1:in `require'
# ./spec/models/user_spec.rb:1:in `<top (required)>'
No examples found.

Finished in 0.00064 seconds (files took 2.23 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples

ファクトリーにパスワード設定しているのになんでpasswordがないと言われるのか全く分からず途方に暮れましたが以下で解決しました。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "User#{n}" }
    sequence(:email) { |n| "tester#{n}@example.com" }
    password  { "password" }
    password_confirmation { "password" }
  end
end

そうです、"password"を{}でくくったことで解決しました。
RSpecを使用するにあたり、Everyday Rails - RSpecによるRailsテスト入門やQiitaで勉強していたのですが、特段{}をつけていなかったので同様に私も外していました。
ただFactoryBot-Getting startedを見ると、すべて{}で囲われていたので試しに囲ってみたら解決した次第です。
今のFactoryBotのバージョンには{}が必要なのでしょうか、、、

2.KeyError

そういえばspring-commands-rspecをインストールしていたので、bin/rspec spec/models/user_spec.rbでRSpecを実行してみたところ、今度は違うエラーが発生しました。

$ bin/rspec spec/models/user_spec.rb
Running via Spring preloader in process 99000
Failures:

  1) User is valid with a name, email,password and password_confirmation
     Failure/Error: expect(build(:user)).to be_valid

     KeyError:
       Factory not registered: "user"
     # ./spec/models/user_spec.rb:5:in `block (2 levels) in <main>'
     # -e:1:in `<main>'
     # ------------------
     # --- Caused by: ---
     # KeyError:
     #   key not found: "user"
     #   ./spec/models/user_spec.rb:5:in `block (2 levels) in <main>'

ユーザーが見つからないってどういうこと、、、とこれまた途方に暮れたのですが、spec/factories以下に正しいfactoryの定義が存在するのにFactory not registeredArgumentError: Factory not registeredを参考にして、spec_helper.rbに以下追加したところ解決しました。

spec/sepc_helper.rb
RSpec.configure do |config|
  config.before(:all) do
    FactoryBot.reload
  end
end

参考記事

Railsの開発効率をあげる - Springを使ってRailsのコンソールコマンドの実行を早くする

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

Ruby on Rails 開発メモ

開発中のメモを備忘録程度にまとめます。(随時更新)

メイラーに2つの引数を渡す

controller

  def create
    @contact = Contact.new(contact_params)
    @user = current_user.email
    if @contact.save
      ContactMailer.contact_mail(@contact, @user).deliver
      redirect_to blogs_path, notice: 'Contact was successfully created.'
    else
      render :new
    end
  end

mailer

class ContactMailer < ApplicationMailer
  def contact_mail(contact, user)
   @contact = contact
   @user = user

   mail to: @user, subject: "お問い合わせの確認メール"
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecの導入

はじめに

RSpecをインストールしたのでその手順をメモします。

*環境
ruby : 2.5.1
rails : 5.2.2

導入手順

1. gemのインストール

Gemnfile
group :development, :test do
  # 省略
  gem 'rspec-rails'
end

bundle installします。

2. RSpecの設定

rails generate rspec:installすると結果は以下。

Running via Spring preloader in process 93674
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

次にRSpecの出力形式を設定します。形式の種類についてはProject: RSpec Core 2.6 --format optionを参照ください。
今回はドキュメント形式にします。

/rspec
--require spec_helper
--format documentation  # この一行を追加

3. springを使ってRSpecの起動時間を速くする

Gemnfile
group :development do
  # 省略
  gem 'spring-commands-rspec'
end

bundle installし、次にbundle exec spring binstub rspecをするとbinディレクトリにrspecファイルが生成されます。

bin/rspec
#!/usr/bin/env ruby
begin
  load File.expand_path('../spring', __FILE__)
rescue LoadError => e
  raise unless e.message.include?('spring')
end
require 'bundler/setup'
load Gem.bin_path('rspec-core', 'rspec')

bin/rspecもしくはbundle exec rspecを実行して以下の通り結果がでればrspecは正常にインストールされています。

Running via Spring preloader in process 93755
No examples found.


Finished in 0.00079 seconds (files took 0.2886 seconds to load)
0 examples, 0 failures

4. ジェネレーターを(お好み)設定

rails gコマンドを実行した際に自動生成されるファイルについて、生成の要否を各自お好みで設定します。

config/application.rb
# 省略
module アプリ名
  class Application < Rails::Application

    # 省略

    config.generators.test_framework      = :rspec
    config.generators.system_tests        = false
    config.generators.stylesheets         = false
    config.generators.javascripts         = false
    config.generators.helper              = false
    config.generators.helper_specs        = false
    config.generators.view_specs          = false
    config.generators.controller_specs    = false
  end
end

その他

Factory Botのインストール

テスト用のデータを簡単に作成できるFactory Botを導入します。

Gemnfile
group :development, :test do
  # 省略
  gem 'factory_bot_rails'
end
/spec/rails_helper.rb
RSpec.configure do |config|
  # 省略
  config.include FactoryBot::Syntax::Methods
end

ファクトリを作成するにはbin/rails g factory/bot:model モデル名を実行します。

Capybaraの設定

Capybaraを導入することで、Webアプリのブラウザ操作をシミュレーションすることができます。
Rails5.1からデフォルトで同梱されていますが、RSpecでCapybaraを扱えるようにするために以下の一行を追加する必要があります。

/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', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'capybara/rspec'  # この一行を追加
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rspec導入まとめ

最低限の導入手順を記載
後ほどいつもやりそうな初期設定を追記

事前設定

config/database.ymlにtestの指定がある事

config/database.yml
test:
  <<: *default
  database: プロジェクト名_test

テストを導入したい環境に以下のgemを指定している事

Gemfile
group :development, :test do
    
    
  gem 'rspec-rails', '~> 3.6'
  gem 'factory_bot_rails'
  gem 'database_cleaner'
    
    
end

導入

rspecに必要なモジュールをインストールする

bundle install
rspec:install
rails generate rspec:install

下記を追記

spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

テスト用DBの作成

rails db:migrate RAILS_ENV=test

実行方法

rspec spec/対象の階層下 or ファイル
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】No route matches [GET]の原因と解決策

エラー

post/indexのブラウザ上で、postコントローラーのdestroyアクションを実施した結果、以下のエラーが起きた。

image.png

エラーの該当箇所

<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "delete"}) %>

解決策

rake routesでひとまず確認

rake routes
 posts_create POST   /posts/create(.:format)           posts#create
               GET    /posts/:id/edit(.:format)         posts#edit
               POST   /posts/:id/update(.:format)       posts#update
               POST   /posts/:id/destroy(.:format)      posts#destroy

解決策

routesと、メソッドを変えたら解決した。

①POSTをDELETEに変更

routesで、POST /posts/:id/destroy(.:format) を、DELETE /posts/:id/destroy(.:format)に変更

参考記事:https://railsguides.jp/routing.html

②link_toをbutton_toに変更

<%= button_to("削除", "/posts/#{@post.id}/destroy",method: :delete) %>

参考記事:https://qiita.com/y-temp4/items/2d50feb3ff0d65acdf67

メモ

link_toからbutton_toに変更すると、Viewはこうなった。

image.png

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

No route matches [GET]の原因と解決策

エラー

post/indexのブラウザ上で、postコントローラーのdestroyアクションを実施した結果、以下のエラーが起きた。

image.png

エラーの該当箇所

<%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "delete"}) %>

解決策

rake routesでひとまず確認

rake routes
 posts_create POST   /posts/create(.:format)           posts#create
               GET    /posts/:id/edit(.:format)         posts#edit
               POST   /posts/:id/update(.:format)       posts#update
               POST   /posts/:id/destroy(.:format)      posts#destroy

解決策

routesと、メソッドを変えたら解決した。

①POSTをDELETEに変更

routesで、POST /posts/:id/destroy(.:format) を、DELETE /posts/:id/destroy(.:format)に変更

参考記事:https://railsguides.jp/routing.html

②link_toをbutton_toに変更

<%= button_to("削除", "/posts/#{@post.id}/destroy",method: :delete) %>

参考記事:https://qiita.com/y-temp4/items/2d50feb3ff0d65acdf67

メモ

link_toからbutton_toに変更すると、Viewはこうなった。

image.png

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

Docker + Rails + MySQL + Vue で Todo アプリ作成 〜その1〜

はじめに

前回: Docker + Rails で開発環境を作った。
せっかく作ったのに開発しないのはもったいないよね。
でもできることって限られてる。
ということで、TODOアプリを作ってみる

目標

  • trello
  • 今回はとりあえず、seed データを投入して、その一覧を表示させるとこまで。
  • タイトル詐欺してますね、「その1」では Vue は使いません(そこまでいけない)

1.準備

Docker を起動し、コンテナに接続していきます。

$ cd myapp
$ docker-compose up
$ docker ps

myapp_webmysqlCONTAINER ID を控える
そぞれのコンテナに接続をする。

web
$ docker exec -it CONTAINER_ID bash
root@CONTAINER_ID:/myapp#    これでOK
db
docker exec -it CONTAINER_ID bash
root@CONTAINER_ID:/# mysql -u root -p
Enter password:  # database.yml に記載したパスワード
mysql>     これでOK

2. Rails の操作

2.1 モデルを作る

Task モデルを作る。

web
# rails g model Task title:string context:string level:integer
# rails db:migrate
2.2 コントローラーも作る

こっちは Tasks
複数形であることに注目、Rails Tutorial で確かそう習った

# rails g controller Tasks

とりあえず一覧を見たい

tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def show
  end

  def new
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end

ついでに他のも CRUD の7つも枠だけ用意しておく。

2.3 index で表示させる seed データを作る
seeds.rb
Task.create(
  [
    {
      title: 'task 001',
      context: 'hogehoge',
      level: '2',
    },
    {
      title: 'task 002',
      context: 'fugafuga',
      level: '3',
    },
    {
      title: 'task 003',
      context: 'piyopiyo',
      level: '1',
    },
  ],
)

web
# rails db:seed
2.4 seed データを作成したら db を覗く

MySQL 側でデータベース名を確認し
sequel pro で実際にデータが入っているか確認する。

db
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| myapp_development  | # こいつを使うよ
| myapp_test         |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

終わったらコンテナごと exit しちゃう。

image.png
接続をしたら
image.png
やったぜ
次はこのデータを画面に表示させる。

3. View とその準備

さっき rails db:seed で作成したデータを表示させたい。
表示させるページは /tasks/(ルートページ)

3.1 ルーティング
routes.rb
Rails.application.routes.draw do
  root to: 'tasks#index'

  resources :tasks
end
3.2 View ファイルの作成

model, controller は rails g があるけど
view にはないらしい。
ので、手動で作っていく、

$ touch app/views/tasks/index.html.erb
$ touch app/views/tasks/show.html.erb
$ touch app/views/tasks/new.html.erb
$ touch app/views/tasks/edit.html.erb
index.html.erb
<h1>Your Tasks</h1>

<ul>
  <% @tasks.each do |task| %>
  <li><%= task.title %><%= task.context %></li>
  <% end %>
</ul>

Task.all の数だけ Task の持っている title と context を表示

...なんかだんだんモデルの作りがイケてない気がしてきた。

そしてローカルホストに接続して確認。

image.png
できた。
とりあえずこれでおしまい。
続きは次回。

参考

Railsでタスク管理ができるWebアプリを作成してみた(Rails入門)
【Ruby on Rails】ToDoアプリを簡単に作ってみる

ありがとうございます。

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

【備忘録】undefined method `name' for nil:NilClassの原因と解決策

エラー

2行目のpost.user.nameの「name」のメソッドが見当たらない

post.index
ActionView::Template::Error (undefined method `name' for nil:NilClass):
    1: <% @posts.each do |post| %>
    2:       <P><%= link_to(post.user.name, "/users/#{post.user.id}") %></p>
    3:       <p><%= link_to(post.content, "/posts/#{post.id}") %></p>
    4:   <% end %>

原因:postテーブル内の何かしらのレコードがなかった

考えられる原因は2つ

(1)postはpost.controller.rbに定義されていない
(2)データベースのpostモデルに、あるべきレコードがない

前者は、コントローラーで定義されていたので、後者の方に原因があると見立てた。

解決策:DBの全てのレコードを空にした

rake db:reset

全てのテーブルを dropして、"db/schema.rb" を元にテーブルを再作成する。(参考記事:http://o.inchiki.jp/obbr/183)

メモ

DBの全てのレコードを空にした後、改めて、post.createしたら無事にpost.indexに反映された。

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

undefined method `name' for nil:NilClassの原因と解決策

エラー

2行目のpost.user.nameの「name」のメソッドが見当たらない

post.index
ActionView::Template::Error (undefined method `name' for nil:NilClass):
    1: <% @posts.each do |post| %>
    2:       <P><%= link_to(post.user.name, "/users/#{post.user.id}") %></p>
    3:       <p><%= link_to(post.content, "/posts/#{post.id}") %></p>
    4:   <% end %>

原因:postテーブル内の何かしらのレコードがなかった

考えられる原因は2つ

(1)postはpost.controller.rbに定義されていない
(2)データベースのpostモデルに、あるべきレコードがない

前者は、コントローラーで定義されていたので、後者の方に原因があると見立てた。

解決策:DBの全てのレコードを空にした

rake db:reset

全てのテーブルを dropして、"db/schema.rb" を元にテーブルを再作成する。(参考記事:http://o.inchiki.jp/obbr/183)

メモ

DBの全てのレコードを空にした後、改めて、post.createしたら無事にpost.indexに反映された。

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