【Rails】ActiveRecord::StatementInvalid: Mysql2::Error: Error dropping database (can't rmdir './app_name_test', errno: 39): DROP DATABASE IF EXISTS `app_name_test`
RSpecを実行しようとしてエラー発生
RSpecをdocker環境で実行しようとしてタイトルのようなエラーが発生しました。
>>> docker-compose run web rspec spec/system/records_spec.rb rails aborted! ActiveRecord::StatementInvalid: Mysql2::Error: Error dropping database (can't rmdir './app_name_test', errno: 39): DROP DATABASE IF EXISTS `app_name_test`
テスト環境のdbに何らかの異常があると思い、とりあえずリセットを試みました。
しかし、いずれのコマンドも同様のエラーになりました。
# すべてエラーに! docker-compose run web rails db:reset RAILS_ENV=test docker-compose run web rails db:drop RAILS_ENV=test docker-compose run web rails db:migrate RAILS_ENV=test
mysql/app_name_testを手動削除して元気!
結局、ローカルに存在する、テストのデータベースを右クリック削除し、docker-compose run web rails db:create RAILS_ENV=test
(からのmigrate)することで解決しました。
[:300]
RSpecも無事に動くようになりました。
【Rails】RSpecでインスタンス変数を変更した後はreloadしよう
createした後にインスタンス変数を設定したい場合の話
RSpecでテストを行う前にletでインスタンスを生成することが多いと思います。
# 例 productクラスのインスタンスを生成しproduct_1に代入 let(:product_1) { create(:product, name: 'macbook') }
上の例では生成したインスタンスが値が'macbook'のname変数を持ちます。生成と同時に定義されています。
では、次のような場合どうなるでしょう?
# name変数なしで生成 let(:product_1) { create(:product) } # 後でnameを定義 product_1.name = 'macbook'
1つ目の例と一緒のようですが、これではnameは未定義のままでした。(コンソールでたたけ無いので実行結果を載せれませんすみません)
reloadしないと適応されないぞ♪
# name変数なしで生成 let(:product_1) { create(:product) } product1.name = 'macbook' product_1.reload
reloadをしてあげることでしっかりと反映されrspecもすべてクリアできました。
【Rails】Could not find rake-12.3.1 in any of the sourcesのエラー解消
環境
Docker for mac gem 'spring-commands-rspec'
bin/rspecでrakeが存在しないと言われた
結構あるあるなエラーだと思います。
spring-commands-rspecというrspecを高速化するgemをインストールし、試しにdocker-compose exec web bin/rspec
としたらエラーが出ました。
➜ $docker-compose exec web bin/rspec Could not find rake-12.3.1 in any of the sources Run `bundle install` to install missing gems.
そしてGemfile.lockを確認すると当然のようにrakeの12.3.1がインストール済みであるんです。さて困ったとなるわけです。
なんで解決したかはまだ調査中
ruby on rails5 はまったこと · GitHub
こちらの記事を参考に
bundle install --binstubs
とすると
➜ $docker-compose exec web bin/rspec No examples found. Finished in 0.00103 seconds (files took 0.48512 seconds to load) 0 examples, 0 failures
なおりました。
なぜなんだ…
【Rails5】Active::Recordが存在しないときにnilを返したい(find_byを使おう)
find(id)では存在しない時はエラーになる
[2] pry(main)> p = Spree::Product.find(0) => Spree::Product Load (0.9ms) SELECT `spree_products`.* FROM `spree_products` WHERE `spree_products`.`deleted_at` IS NULL AND `spree_products`.`id` = 0 LIMIT 1 ActiveRecord::RecordNotFound: Couldn't find Spree::Product with 'id'=0 [WHERE `spree_products`.`deleted_at` IS NULL] from /potepanec/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.1/lib/active_record/relation/finder_methods.rb:346:in `raise_record_not_found_exception!'
(モデルがSpreeなのは無視してください)
この通り存在しないid=0を取得しようとするとNotFoundエラーとなりました。
しかし、エラーではなくnilが欲しいときがあると思います。
例えば、" || "を使って、いくつかの方法で取得を試みる場合。
@user = User.find_by(id: params[:id]) || User.find_by(name: params[:name])
上では最初にidでuserの取得を試みます。そこで取得できなかった場合はnilとなり、次にnameで取得することができます。
ポイントは、idで取得する際に、find(id)ではなくfind_by(id: ...)で取得していることです。find_byの場合、存在しないときはnilを返します。
【Rails5】SpreeおよびSolidusの仕組みメモ(執筆中)
公式ドキュメントより
「あるproductは最低1つのSpree::OptionTypeを持つ必要あり」
→しかしproductモデルにはSpree::OptionTypeの外部キーなし。中間テーブルSpree::ProductOptionTypeを通して保有していると思われる。
例)複数の色を提供したければ、"Color" option typeを作る必要あり。
「あるOption Typeは、最低1つのSpree::OptionValueと連携している必要がある。」
例)"Color" option typeが100以上のoption valueを持つこともありうる。
→ Spree::OptionValueがSpree::OptionTypeの外部キーをもつ。
Variantの使用
「master variantのみの場合、master variantがLineItemとつながる。」
「複数のvariantをもつ場合、master variantがLineItemに情報を与えることはない。(masterを買い物カゴにいれることが不可)」
variantのメソッド variatn.options_text: amount_stock_items(variant): variant.stock_items variant.option_value("tshirt-size") @variant.display_price
productメソッド product.master.id: product.display_price: product.images.first.attachment(:product): image.attachment(:product) image.attachment(:large)
【Rails5】find_byで取得するobjは1つ、whereは全部
スクール課題中に気づく
今までの誤解がやばかったので自分用のメモ
このQiitaのおかげで今までの誤解がとける
【rails】find・find_by・whereについてまとめてみた - Qiita
要約すると
# 条件に該当するもののうち最初の1つのみ取得 @images = Image.find_by(product_id: p.id) # 条件に該当するものをすべて取得 @images = Image.where(prodcut_id: p.id)
てっきりfind_byでもすべて取得するかと思っていました。
【Rails5】コメント機能でのStrongParameterの設定方法
ハッシュが2つ出てくる
私が今回実装したコメント投稿formはこんな感じ。
= form_with(model: [@post, @comment]) do |f| .form-group = f.text_area :body, placeholder: 'コメント追加.', class:'form-control' = f.submit '投稿する', class:'btn'
一見普通ですがいつもと違うのはmodel: [@post, @comment]
の部分です。普段であれば、渡すmodelは1つですが、今回は関連元、関連先の2つのインスタンスを指定します。
これによって、POSTしたときに渡されるパラメーターが以下のようになります。
Parameters: { "utf8"=>"XXX", "authenticity_token"=>"XXXXXX", "comment"=>{"body"=>"はじめまして!"}, "commit"=>"XXXXX", "post_id"=>"8" }
commentテーブルに保存するデータは、"comment"=>{"body"=>"はじめまして!"}
の"はじめまして!"
で、params.require(:comment).permit(:body)
でストロングパラメーターを作ればいいじゃないかと思うかもしれません。しかし、commentsテーブルには:post_id
のカラムもあるため、 "post_id"=>"8"
の保存も必要です。
単純な発想ではArgumentErrorになる
まず思いつくのがこのような形かもしれません。これはエラーです。
params.require(:comment, :post_id).permit(:body)
では:post_id
をpermitの方に入れてしまうのはどうでしょう?
これももちろんエラーです。:post_idは:commentの値ではありませんので。
params.require(:comment).permit(:body, :post_id)
そもそもrequire, permitとは何か
こちらの記事 RailsのStrong Parametersを調べる - Qiita で紹介されていますが、requireは以下のように定義されています。
def require(key) self[key].presence || raise(ActionController::ParameterMissing.new(key)) end
self[key]
の 部分は、アクセプター(params)の中に引数で渡したkeyのvalueが存在しているとtrueを返すようです。
他の記事ではrequire()の引数を2つ以上にしていてもいけるようなことが書かれていましたが、今回の場合はエラーとなりました(なぜだ)
引数を2つ指定 params.require(:comment, :post_id) =>ArgumentError (wrong number of arguments (given 2, expected 1)):
次にpermit()ですが
def permit(*filters) params = self.class.new filters.flatten.each do |filter| case filter when Symbol, String permitted_scalar_filter(params, filter) when Hash then hash_filter(params, filter) end end unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters params.permit! end
これははっきりと引数を複数とれることが明記されていますね。 処理も単純で渡されたパラメーターを許可するようです。
個別にStrongParameterして連結する
解決方法ですが、合わせてStrongParameterを指定することが困難でしたので、:commnetと:post_idで別個に指定し、後でmerge()でハッシュを連結しました。
class CommentsController < ApplicationController def create comment = current_user.comments.build(comment_params) comment.save 〜 end private def comment_params params.require(:comment).permit(:body).merge(params.permit(:post_id)) end end
これで一応うまくいきました。
きっともっとクールな方法があるんでしょうが思いつきませんでした。
知っている方ぜひ教えて下さい!