- 投稿日:2020-09-19T23:10:34+09:00
フォームオブジェクトを使ったdate型カラムの保存方法
フォームオブジェクトを使って、date型のカラムを保存しようとしたところ、
フォームオブジェクトのクラスでインスタンスを生成してしまうとdate型のカラムが認識されないので、unknown attribute 'expire_date(1i)' for ItemsTag.
のようにエラーが出てしまう。。
なので、コントローラーのcreateメソッドのなかで、インスタンス生成した際に、date型にあらかじめ変形させておいた値をmergeさせてフォームオブジェクトに渡してあげる、ということをしていきます。
[やること]
songsテーブルに追加した、date型 「 t.date :sales_date, null: false」
を保存させたい。[方法]
[フォームのビュー]
new.html.erb<div class="weight-bold-text"> リリース日 <span class="indispensable">(必須)</span> </div> <div class='input-sales-wrap'> <%= raw sprintf( f.date_select( :sales_date, class:'select-birth', use_month_numbers: true, prompt:'--', start_year: (Time.now.year), end_year: 1930, date_separator: '%s'), "<p> 年 </p>", "<p> 月 </p>") + "<p> 日 </p>" %> </div>[コントローラー]
songs_controller.rbdef create @song = SongsArtist.new(song_params) date = params.require(:songs_artist).permit(:sales_date) sales_date = Date.parse( date["sales_date(1i)"] + "-" + date["sales_date(2i)"] + "-" + date["sales_date(3i)"] ) @song = SongsArtist.new(song_params.merge(sales_date: sales_date)) end def song_params params.require(:songs_artist).permit(:name, :text, :image, :translate, :youtube_url, :genre_id, :art_name,:featuring_name,:producer_name).merge(user_id: current_user.id) endsales_date以外は、song_paramsメソッド内でpermitのなかに入れてフォームオブジェクトに渡すが、
sales_dateは、
date = params.require(:songs_artist).permit(:sales_date)
sales_date = Date.parse( date["sales_date(1i)"] + "-" + date["sales_date(2i)"] + "-" + date["sales_date(3i)"] )
この2行の記述でdate型に変更し、
@song = SongsArtist.new(song_params.merge(sales_date: sales_date))
フォームオブジェクトに渡す値であるsong_paramsに、date型に変更したsales_dateをmergeさせて
一緒に渡す。song_artist.rbclass SongsArtist include ActiveModel::Model attr_accessor :name, :text, :image, :genre_id, :translate, :youtube_url, :sales_date,:art_name,:producer_name,:featuring_name, :user_id,:song_id validates :name, :text,:image,:genre_id, presence: true validates :genre_id, numericality: { other_than: 1 } def save artist = Artist.where(art_name: art_name).first_or_initialize artist.save featuring = Featuring.where(featuring_name: featuring_name).first_or_initialize featuring.save producer = Producer.where(producer_name: producer_name).first_or_initialize producer.save song = Song.create(name: name, text: text, image: image, genre_id: genre_id, translate: translate, youtube_url: youtube_url, sales_date: sales_date,user_id: user_id,producer_id: producer.id,featuring_id: featuring.id) SongArtistRelation.create(song_id: song.id, artist_id: artist.id) end endフォームオブジェクトの記述は、普段と変わらず、 attr_accessorでコントローラーから渡ってきたカラムを許可し、createアクションの引数に保存するカラムを記述する。
以上です。
- 投稿日:2020-09-19T22:48:35+09:00
【ruby】例外処理で、施されたら施し返す
例えばあるデータをパースして、特定のデータのみデータベースに保存したいとする。
その中で、パースできない拡張子があればエラーが出て、処理が中断してしまいバグを引き起こしてしまう。そこで、便利なのが例外処理というもので、処理を中断させず例外事案の場合の処理を施すことができる。
まさに、
施すこされたら施し返す、恩返しです
を実現できるのだ。今回は単純な計算を例に、例外処理を見ていこう。
エラーが発生した場合の処理 begin / rescue
Ruby内で、10を0で割ろうとするとエラーが出てしまう。
puts 10 / 0 puts "こんにちわ"divided by 0 (ZeroDivisionError)処理が途中で、中断されてしまう。
そこで、
- エラーの対象になりそうな箇所を、begin
で囲う。
- エラーが発生した時の処理を、rescue
内に記述する。begin 10 / 0 rescue p "0で割れません" end puts "こんにちわ""0で割れません" "こんにちわ"処理が中断されず、エラー時の処理と正常処理どちらも実行された。
省略法
beginなしで、rescueさせる
puts 10 / 0 rescue 0 puts 10 / nil rescue 00 0エラー内容を、変数に格納 rescue =>
begin 10 / 0 rescue => e puts e end puts "こんにちわ"divided by 0 こんにちわエラーオブジェクトが、変数eに格納されて出力できる。
エラーごとの処理を変える rescue "エラーメッセージオブジェクト"
begin 10 / 0 rescue NoMethodError puts "そのようなメソッドはない" rescue ZeroDivisionError puts "0で割れません" end0で割れません2番目のエラーメッセージに該当するので、そのrescueに反応する。
注意点
対象の例外クラスの親が、先に記述されいた場合はそちらが先に処理される
begin 10 / 0 rescue StandardError puts "基本的なエラー" rescue ZeroDivisionError puts "0で割れません" end基本的なエラー明示的なエラーを発生させて、処理を中断させる raise
使用用途は、
1. パラメータが想定されたものでないとき
2. 不正なアクセスがきた時begin raise NoMethodError rescue => e p e endNoMethodError独自のエラーを発生させる
例外クラス(StandardErrorクラス)を継承させてあげる。
class Hoge < StandardError end begin raise Hoge rescue => e p e end#<Hoge: Hoge>エラー時でも、再度初めから実行させる retry
num = 0 begin puts 10 / num rescue ZeroDivisionError => e puts e num = 1 retry end puts "終了しました"divided by 0 10 終了しましたループ1回目では、エラーが発生していて、
ループ2回目では、正常に処理がされている。エラーが発生しても、しなくても行う処理 ensure
begin puts "例外なし" rescue => e puts e ensure puts "Hello" end例外なし Helloensureは、いかなる時でも必ず実行される。
番外編 エラーオブジェクトとは
begin 10 / 0 rescue => e puts e.class puts e.class.superclass puts e.class.superclass.superclass puts e.class.superclass.superclass.superclass endZeroDivisionError StandardError Exception ObjectExceptionが元となるクラスで、Objectがその親
- 投稿日:2020-09-19T21:43:25+09:00
(ギリ)20代の地方公務員がRailsチュートリアルに取り組みます【第11章】
前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。
認証システム開発・第6段回目、第11章に入ります。セキュリティ強化の観点から、アカウント有効化のステップを入れていきます。よくある、登録後にメールが送られてきて、そのリンクを踏むと本登録になるやつですね。
本日のBGMはこちら。
Tatuki Seksu "Hanazawa EP"
いろいろあやしいのをシューゲサウンドでパッケージングしましたってかんじが好き。
【11.1.1 AccountActivationsコントローラ 演習】
1. 現時点でテストスイートを実行すると greenになることを確認してみましょう。
→ 現時点ではテスト書いてないのでGREENです。
2. 表 11.2の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: 私達はこれからメールで名前付きルートを使います。
→ pathは相対パス(/とか省略した形)、urlは絶対パス(https:~~とか完全な形)。メールというrails外の処理にはurlの完全形である絶対パスが必要と考えます。
【11.1.2 AccountActivationのデータモデル メモと演習】
コールバックではbefore_〇〇の、〇〇の動作の直前に特定のメソッドを実行することができる。メソッド参照という。ブロックを渡すよりこちらが推奨。
(おさらい)privateより下にメソッドを定義することで、外部に非公開にできる。1. 本項での変更を加えた後、テストスイートが green のままになっていることを確認してみましょう。
→ GREEN
2. コンソールからUserクラスのインスタンスを生成し、そのオブジェクトからcreate_activation_digestメソッドを呼び出そうとすると (Privateメソッドなので) NoMethodErrorが発生することを確認してみましょう。また、そのUserオブジェクトからダイジェストの値も確認してみましょう。
→ こんなかんじ>> user = User.third User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 2]] => #<User id: 3, name: "Mr. Sage Hartmann", email: "example-2@railstutorial.org", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.HyqPb.DwmFICve62DsYte1alLAVihIdeS2F8Rjndry...", remember_digest: nil, admin: false, activation_digest: "$2a$10$9VKv/p9kYrz84SdMs/7s/uzEV3mqzGMmTubIq7.Vz4b...", activated: true, activated_at: "2020-09-17 08:34:09"> >> user.create_activation_digest Traceback (most recent call last): 1: from (irb):2 NoMethodError (private method `create_activation_digest' called for #<User:0x000000000426b7a0>) Did you mean? restore_activation_digest! >> user.activation_digest => "$2a$10$9VKv/p9kYrz84SdMs/7s/uzEV3mqzGMmTubIq7.Vz4bbIb.ZeLDRy"
3. リスト 6.34で、メールアドレスの小文字化にはemail.downcase!という (代入せずに済む) メソッドがあることを知りました。このメソッドを使って、リスト 11.3のdowncase_emailメソッドを改良してみてください。また、うまく変更できれば、テストスイートは成功したままになっていることも確認してみてください。
→ email.downcase!に変えるだけやね。user.rbdef downcase_email email.downcase! end
【11.2.1 アカウント有効化のメール送信 演習】
1. コンソールを開き、CGIモジュールのescapeメソッド (リスト 11.15) でメールアドレスの文字列をエスケープできることを確認してみましょう。このメソッドで"Don't panic!"をエスケープすると、どんな結果になりますか?
→ 下記。(クエリパラメータとCGIは用語集に入れてます)>> CGI.escape('foo@example.com') => "foo%40example.com" >> CGI.escape("Don't panic!") => "Don%27t+panic%21"
【11.2.2 送信メールのプレビュー メモと演習】
AWScloud9を使用している場合、チュートリアルでは旧cloud9使用しているので'example.com'に入力する内容の見た目がかなり違うので戸惑いますが、内容は一緒です。Railsサーバーを立ち上げて、別タブで表示した画面のURLのhttps://以下をすべてコピペすればOKです。
1. Railsのプレビュー機能を使って、ブラウザから先ほどのメールを表示してみてください。「Date」の欄にはどんな内容が表示されているでしょうか?
→ 今日の日付とUTC(協定世界時)が表示される。(協定世界時って言葉カッコよくないですか?中二心をくすぐられる)
【11.2.3 送信メールのテスト メモと演習】
ここのassert_matchがいまひとつしっくり来ないですが、「メールの中身に対し、名前と有効化トークンとエスケープしたメルアドが含まれているかテストしている」ぐらいに認識しておきます。
1. この時点で、テストスイートが greenになっていることを確認してみましょう。
→ Yes, GREEN !
2. リスト 11.20で使ったCGI.escapeの部分を削除すると、テストが redに変わることを確認してみましょう。
→ REDになるのは、メールアドレスにメタ文字が含まれていて、エスケープしないと正規表現ではないからですかね。こちらの記事参照。
【11.2.4 ユーザーのcreateアクションを更新 メモと演習】
deliver_now:名前のとおり、その瞬間にメール送信処理を実行。
1. 新しいユーザーを登録したとき、リダイレクト先が適切なURLに変わったことを確認してみましょう。その後、Railsサーバーのログから送信メールの内容を確認してみてください。有効化トークンの値はどうなっていますか?
→ homeに戻りました。Welcome to the Sample App! Click on the link below to activate your account: 以下のURLに含まれているランダムな文字列が有効化トークンです。
2. コンソールを開き、データベース上にユーザーが作成されたことを確認してみましょう。また、このユーザーはデータベース上にはいますが、有効化のステータスがfalseのままになっていることを確認してください。
→ activated: false になっていました。
【11.3.1 authenticated?メソッドの抽象化 メモと演習】
もう何のダイジェストの話してんのか混乱してきましたね。ってなったら11章の始めにあった表11.1を見返しましょう。何の話をしているのか、しっかり把握しながら進めないと。
そんで、この節のタイトルは抽象化より一般化ってかんじがする。authenticated?メソッドを表11.1のどのパターンでも使用できるようにするわけやし。1. コンソール内で新しいユーザーを作成してみてください。新しいユーザーの記憶トークンと有効化トークンはどのような値になっているでしょうか? また、各トークンに対応するダイジェストの値はどうなっているでしょうか?
→ こんな感じっすね。ユーザー作っただけだから記憶系はnilのまま…>> user = User.create(name: "muteki", email: "muteki@man.com", password: "mutekimuteki", password_confirmation: "mutekimuteki") (0.1ms) SAVEPOINT active_record_1 User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "muteki@man.com"], ["LIMIT", 1]] SQL (2.5ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest", "activation_digest") VALUES (?, ?, ?, ?, ?, ?) [["name", "muteki"], ["email", "muteki@man.com"], ["created_at", "2020-09-17 12:57:50.404544"], ["updated_at", "2020-09-17 12:57:50.404544"], ["password_digest", "$2a$10$eDPAP444JbjJDGucKnoFE.MWFBTAR8dxQ.wXPJfzql9E0TPRVDQfq"], ["activation_digest", "$2a$10$zql927sHRszT.bjitRxBn.slJil.Zvc74AJkztqBZzt7kUiSqBgx."]] (0.1ms) RELEASE SAVEPOINT active_record_1 => #<User id: 102, name: "muteki", email: "muteki@man.com", created_at: "2020-09-17 12:57:50", updated_at: "2020-09-17 12:57:50", password_digest: "$2a$10$eDPAP444JbjJDGucKnoFE.MWFBTAR8dxQ.wXPJfzql9...", remember_digest: nil, admin: false, activation_digest: "$2a$10$zql927sHRszT.bjitRxBn.slJil.Zvc74AJkztqBZzt...", activated: false, activated_at: nil>なので、記憶系をわざわざ作ります。
>> remember_token = User.new_token => "5dyf7BoW9H3H9SYH6VPRYg" >> remember_digest = User.digest(remember_token) => "$2a$10$1CymqXEPzP.b05TblQ3Zye/ukhNblEpGlDxI4kT2VoiLUJK1EHVy2" >> user.update_attribute(:remember_token, remember_token) (0.1ms) SAVEPOINT active_record_1 (0.1ms) RELEASE SAVEPOINT active_record_1 => true >> user.update_attribute(:remember_digest, remember_digest) (0.1ms) SAVEPOINT active_record_1 SQL (0.2ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2020-09-17 13:45:51.944577"], ["remember_digest", "$2a$10$1CymqXEPzP.b05TblQ3Zye/ukhNblEpGlDxI4kT2VoiLUJK1EHVy2"], ["id", 102]] (0.0ms) RELEASE SAVEPOINT active_record_1 => trueということで、各トークンはこんな感じ。ダイジェストは上で出てます。
>> user.remember_token => "5dyf7BoW9H3H9SYH6VPRYg" >> user.activation_token => "p5rorOE7trfF4L-YJynnxg"
2. リスト 11.26で抽象化したauthenticated?メソッドを使って、先ほどの各トークン/ダイジェストの組み合わせで認証が成功することを確認してみましょう。
→ 確認するだけ…ってあれー??activation_tokenでNameError??
って調べたら解決。あくまでユーザーに紐づいてるものだからuser.activation_tokenか。>> user.authenticated?(:remember, remember_token) => true >> user.authenticated?(:activation, activation_token) Traceback (most recent call last): 1: from (irb):11 NameError (undefined local variable or method `activation_token' for main:Object)よって以下でtrue。remember_token作るときに、新しく定義するのではなく、user.remember_tokenで紐付けしたほうがよかったな。
>> user.authenticated?(:activation, user.activation_token) => true
【11.3.2 editアクションで有効化 演習】
1. コンソールから、11.2.4で生成したメールに含まれているURLを調べてみてください。URL内のどこに有効化トークンが含まれているでしょうか?
→ 11.2.4の演習1と同じです。
2. 先ほど見つけたURLをブラウザに貼り付けて、そのユーザーの認証に成功し、有効化できることを確認してみましょう。また、有効化ステータスがtrueになっていることをコンソールから確認してみてください。
→ 認証成功、有効化できていました。
【11.3.3 有効化のテストとリファクタリング メモと演習】
・テストに出てくる配列deliveriesは変数。なので、他のテストに支障が出ないよう、setupでclearしている。
・sizeメソッド:lengthメソッドと同じ。ここではメールの数(=1)を確認している。
・assignsメソッド:対応するアクション内(ここではsignupをテストしているのでcreateアクション)のインスタンス変数(@user)にアクセスできるようになる。
・whereメソッド:与えられた条件にマッチするレコードをすべて返す。こちらにfind,find_byとの比較がありました。1. リスト 11.35にあるactivateメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 11.39に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。
→ 下記user.rbdef activate update_columns(activated: true, activated_at: Time.zone.now) end
2. 現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。そこで、リスト 11.40のテンプレートを使って、この動作を変更してみましょう9 。なお、ここで使っているActive Recordのwhereメソッドについては、13.3.3でもう少し詳しく説明します。
→ 下記users_controller.rbdef index @users = User.where(activated: true).paginate(page: params[:page]) end def show @user = User.find(params[:id]) redirect_to root_url and return unless @user.activated? end
3. ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。
訳注: updateメソッドは、コールバックとバリデーションを実行せずにスキップしますので、コールバックやバリデーションをかける必要がある場合は注意が必要です。
→ ここ難しいですね。発想としては、「有効化してないユーザー(@non_activated)がindexに表示されていない」のと、有効化してないユーザーのページ(user_path(@non_activated))にアクセスしようとすると、homeに飛ばされる」のを確かめればいいわけだけど…。前者のアサーションが分からず悩みました。
結局調べたところ、「user_path(@non_activated)のリンクが表示されていない(0である)」のを確かめればいいと。なるほど。ということで下記です。テスト名もてきとうに追記しました。あと、setupに@non_activatedとして、uses.ymlの3番目のユーザーをactivated: falseに書き換えてから追加しています。users_index_test.rbdef setup @admin = users(:michael) @non_admin = users(:archer) @non_activated = users(:lana) end test "index as admin including pagination and delete links, not to show non activated user" do log_in_as(@admin) get users_path assert_template 'users/index' assert_select 'div.pagination' first_page_of_users = User.where(activated: true).paginate(page: 1) first_page_of_users.each do |user| assert_select 'a[href=?]', user_path(user), text: user.name assert_select 'a[href=?]', user_path(@non_activated), count: 0 unless user == @admin assert_select 'a[href=?]', user_path(user), text: 'delete' end end assert_difference 'User.count', -1 do delete user_path(@non_admin) end get user_path(@non_activated) assert_redirected_to root_url end
【11.4 本番環境でのメール送信 グチと演習】
ついにきた!!SendGrid!!
これ酷くないですか?登録してもすぐにアカウント凍結されてメール飛ばせなくなるんですよ。前回こいつに時間取られた挙句、結局解決するにはサポートに連絡しないといけないらしく、やってられるかって放置しました。そんなもんチュートリアルに使うなよな…。
ということで、別の手段を導入しようしていろいろ試したのですがどれもうまくいかず…。不服ですが今回はsendgridで済まします。これは今後の課題として置いておきます。
……結局そっこーで凍結されました。もうええわ。1. 実際に本番環境でユーザー登録をしてみましょう。ユーザー登録時に入力したメールアドレスにメールは届きましたか?
→ うん、無理でした。
2. メールを受信できたら、実際にメールをクリックしてアカウントを有効化してみましょう。また、Heroku上のログを調べてみて、有効化に関するログがどうなっているのか調べてみてください。ヒント: ターミナルからheroku logsコマンドを実行してみましょう。
→ やりたいんやけどね、無理ですわ。
第11章まとめ
・終わり悪ければすべて悪い。SendGrid嫌い。
・今後は純粋にActionMailerでメール送信に取り組みたい。
・URLに有効化トークンとエスケープしたメールアドレスを盛り込む。
SendGrid問題、なんとか解決したかったんやけどなあ…。これ以上時間かけるのももったいないので、次に進めます。今後絶対なんとかしてやる!
さて、次は第12章、パスワードの再設定です。認証システム開発の最終章ですね!
⇨ 第12章へ!
⇦ 第10章はこちら
学習にあたっての前提・著者ステータスはこちら
なんとなくイメージを掴む用語集
・クエリパラメータ
URLの末尾で疑問符「?」に続けてキーと値のペアを記述したもの。ユーザーがどこから来たのか解析する手段(パッシブパラメータ)や、指定された変数によってコンテンツの内容を変化させたりできる(アクティブパラメータ)。クエリのこと全般も含めて詳しくはこちら。・CGI(Common Gateway Interface)
クライアント側のWebブラウザの要求に応じてWebサーバが外部プログラムを呼び出して、その実行結果がHTTPを介してクライアントのWebブラウザに送信される仕組みのこと。掲示板、アクセスカウンター、アンケートフォームなどを実装できる。・assert_mutch
与えられた文字列が与えられた正規表現にマッチした場合、検査にパス。・SMTP(Simple Mail Transfer Protocol)
インターネットで電子メールを転送するプロトコル。
- 投稿日:2020-09-19T21:35:05+09:00
【Rails5】Railsで新規アプリを作る【初心者】
概要
『どうも皆さん、おはこんばんにちは』
(…一度言ってみたかった)
筆者は最近RoRアプリを作り始めた、思いっっきり初心者です。
Qiita初投稿という事で、めちゃくちゃ初歩的な「Railsで新規アプリを作る過程」について投稿したいと思います!前提条件
- OS: MacOS
- Rubyバージョン: 2.5.1
- Railsバージョン: 5.2.3
- データベース: MySQL
- Rubyバージョン管理: rbenv
- IDE: VisualStudioCode
上記の環境でアプリを作っていきます!
Railsで新規アプリを作る
ターミナル上で
rbenv local 2.5.1
rails _5.2.3_ new app_name --database=mysql --skip-bundle
を実行します。何を行っているのか
rbenv
rbenvコマンド実行の宣言local 2.5.1
このプロジェクトで使用するRubyのバージョン指定(今回はver.5.2.3)rails
railsコマンド実行の宣言_5.2.3_
rubyのバージョン指定(今回はver.5.2.3)new
新しいアプリの作成コマンドapp_name
作りたいアプリの名前の定義--database=mysql
使いたいデータベースの指定(今回はmysql)
--skip-bundle
bundle install
をスキップする指示
app_name
を.
とすると、カレントディレクトリ上に作られる--database=mysql
は、-d mysql
でもOK--skip-bundle
は、-B
でもOKrbenvの使い方と仕組みについては、以下の記事がわかりやすいので参照してください。
rbenvの使い方と仕組みについて - Qiitaアプリ内のファイル
上記のようにターミナル上で
rails new
をすると、たくさんのフォルダやファイルが作られます。中身は以下のようになっているはずです。app
アプリケーションのコントローラ、モデル、ビュー、ヘルパー、メイラー、チャンネル、ジョブズ、アセットが置かれています。
bin
アプリケーションを起動・アップデート・デプロイするためのRailsスクリプト等のスクリプトファイルが置かれています。
config
アプリケーションの設定ファイル (ルーティング、データベース等) がここに置かれています。
db
現時点のデータベーススキーマと、データベースマイグレーションファイルが置かれています。
lib
アプリケーションで使う拡張モジュールが置かれています。
log
アプリケーションのログファイルが置かれています。
public
このフォルダの下にあるファイルは外部 (インターネット) からそのまま参照できます。静的なファイルやコンパイル済みアセットをここに置きます。
storage
Diskサービスで用いるActive Storageファイルが置かれています。
test
Unitテスト、フィクスチャなどのテスト関連ファイルをここに置きます。
tmp
キャッシュ、pidなどの一時ファイルが置かれます。
vendor
サードパーティによって書かれたコードはすべてここに置きます。通常のRailsアプリケーションの場合、外部からのgemファイルをここに置きます。
.gitignore
Gitに登録しないファイル(またはパターン)をこのファイルで指定します。
.ruby-version
デフォルトのRubyバージョンがこのファイルで指定されています。
config.ru
アプリケーションの起動に必要となる、Rackベースのサーバー用のRack設定ファイルです。
Gemfile
Railsアプリケーションで必要となるgemの依存関係を記述します。このファイルはBundler gemで使われます。
package.json
Railsアプリケーションで必要なnpm依存関係をこのファイルで指定できます。このファイルはYarnで使われます。
Rakefile
このファイルには、コマンドラインから実行できるタスクを記述します。ここでのタスク定義は、Rails全体のコンポーネントに対して定義されます。
独自のRakeタスクを定義したい場合は、Rakefileに直接書くと権限が強すぎるので、なるべくlib/tasksフォルダの下にRake用のファイルを追加するようにしましょう。
README.md
アプリケーションの概要を説明するマニュアルをここに記入します。このファイルにはアプリケーションの設定方法などを記入し、これさえ読めば誰でもアプリケーションを構築できるようにしておく必要があります。
gemのインストール
Rubyにおけるgemは、下記2つの役割を持ちます。
- パッケージ
- パッケージ管理ツール
パッケージを使うことで開発を効率的に進めることができるので、実際の現場でも使用されることが多いようです。
簡単にパッケージをインストール可能なので、Ruby on Rails 初心者でも素早く本格的なアプリ機能を装備することができるんですね。アプリ内で使いたい機能を持つgemを Gemfileに、
Gemfilegem 'gem名'のように記述します。(ここでは、具体的なgemの種類については割愛します。)
また、Gemfile内のrailsの記述を、
Gemfile~ gem 'rails', '5.2.3' ~のように使いたいバージョン(今回はver.5.2.3)で固定します。
先ほど
--skip-bundle
コマンドでgemのインストールをスキップしたので、ターミナル上で
bundle install
を実行します。何を行っているのか
bundle install
bundler というgemを使って、Gemfileの記載内容に従ってgemをインストールするためのコマンドサーバーの立ち上げ
実際にサーバーを立ち上げてみましょう!ターミナル上で
rails server
を実行します。何を行っているのか
rails server
rails new
コマンド で作ったRailsアプリケーションを、local環境でサーバーに繋ぐ指示
rails server
は、rails s
でもOKターミナル上で以下のようなログが流れればサーバーとの接続が完了です!
=> Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.6 (ruby 2.5.1-p57), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stopブラウザ上で確認してみましょう!
localhost:3000 にアクセスします。無事にサーバーへ接続できました!
終わりに
最後までご覧いただきありがとうございました。
今後はdeviseでのユーザー登録方法、Docker上でのRoR環境構築なども記事にしたいなあと考えています(いつになるかは未定)。また、この記事に関して不明な点等ありましたら、お知らせくださると幸いです。
早くエンジニアとして「駆け出したい」ですね。
それでは!参考にしたWebサイト
- 投稿日:2020-09-19T21:10:36+09:00
【Rails】deviseの会員登録に確認画面と完了画面を加える。
環境
Ruby 2.5.7
Rails 5.2.4gem
gem 'devise'
前提
deviseの新規登録フォームがカスタムできる状態(デフォルトではない状態)
※事前に、新規登録フォームがカスタムでき問題なく動作することをご確認ください。やりたいこと
deviseのデフォルト設定ではsign_upページで情報が入力され送信されると、そのまま登録されると共にあらかじめ指定した(されている)ページに遷移します。
登録の前に入力された情報が確認できるページを挟んだり、登録が完了したことをお知らせするページがあると、より親切かと思いますので、今回は「入力フォーム」に加えて、「入力された情報の確認画面」、「登録完了画面」を作成していきます。
今回の作成で完成するページの流れとしては、
新規会員情報の入力フォームusers/new.html.erb(新規作成)
→入力情報の確認画面users/registrations/new.html.erb
→登録完了画面users/completion(新規作成)
という順番で画面が遷移していきます。少し違和感があるかもしれませんが、deviseで用意されている
users/registrations/new.html.erb
は前のページで入力された情報の確認のみで、見た目上はその前のページで入力された値を表示するだけになります。手順
1.views/usersに入力画面(
new.html.erb
)、完了画面(completion.html.erb
)を用意
2.controllers(controllers/users/registrations_controller.rb
)に、会員登録後のリダイレクト先(users/completion.html.erb
)を追記
3.routes.rb
に、1.で追加したviewファイルのルーティング、registrations_controller.rb
の使用を追記
4.(controllers/users_controller.rb
)にrender用newとcompletionを作成1.確認画面、完了画面の作成
冒頭で記した通り、新規登録時の画面の流れとしては、
新規登録入力フォームusers/new.html.erb
→入力確認画面users/registrations/new.html.erb
→登録完了画面users/completion
となります。
確認画面で"登録する"ボタンを押すとdeviseの標準機能でユーザー情報が登録されます。入力画面
users/new.html.erb<div> <h2>新規会員登録</h2> <%= form_with url: new_user_registration_path, method: :get, local: true do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :phone_number %> <%= f.number_field :phone_number %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <% if @minimum_password_length %> (<%= @minimum_password_length %>文字以上) <% end %> <%= f.password_field :password %> <%= f.password_field :password_confirmation %> <%= render "users/shared/links" %> <%= f.submit "確認" %> <% end %> </div>元々用意されている
registrations/new.html.erb
のフォームを参考に新規作成します。
登録情報に名前や電話番号情報を追加する方法は割愛します。
追加される場合は先にそちらの設定をして動作が確認できた上で、今回の方法を試していただくか、カラムは増やさずにデフォルトの設定(メールアドレス、パスワードのみ)でお試しください。ポイントとしてはフォームタグです。
users/new.html.erb... <%= form_with url: new_user_registration_path, method: :get, local: true do |f| %> ... <% end %>ここでは保存するモデルは指定せず、urlだけを指定しています。
つまり、フォーム内でsubmitされると、ここで指定したurlに遷移され、フォームに入力されている値も"パラメータ"として遷移先に渡されます。
ここで指定するurlは次の確認画面のurl: new_user_registration_path
を指定します。
このurlはdeviseがデフォルトで用意しているregistrations/new.html.erb
です。このフォームはurlを遷移するだけなので、HTTPメソッドは"POST"ではなく"GET"を指定します。
フォームのHTTPメソッドはデフォルトが"POST"になっているので、method: :get
を明示する必要があります。今回使うフォームタグは
form_with
です。
form_with
はデフォルトの送信方法がAjax(remote: true
)で、画面遷移が行われない仕様なので、local: true
も明示する必要があります。
form_for
の場合はデフォルトがlocal: true
なので、普段は意識する必要はありません。確認画面
users/registration/new.html.erb<div> <h2>入力情報の確認</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <p><%= params[:name] %></p> <p><%= params[:phone_number] %></p> <p><%= params[:email] %></p> <% unless params[:password].nil? %> <% i = 0 password = "" while i < params[:password].length password << "*" i += 1 end %> <% end %> <p><%= password %></p> <%# 実際に送信するパラメータ %> <%= f.hidden_field :name_family, value: params[:name] %> <%= f.hidden_field :phone_number, value: params[:phone_number] %> <%= f.hidden_field :email, value: params[:email] %> <%= f.hidden_field :password, value: params[:password] %> <%= f.submit "登録する" %> <% end %> </div>この画面がdeviseで用意されている
users/registrations/new.html.erb
です。
元々のこのページには入力フォームがありますが、今回は確認だけなので、フォームは表示しません。
form_for
から始まるフォームタグは元からあるものをそのまま利用しています。前のページから渡されてるパラメータを
params[:name]
などを用いて取得し、確認できるように表示します。パスワード部分については入力されたものが何なのかわからない状態にしています。
(パラメータには載っているのですが、ここの隠し方がわからなかったので、詳しい方ご教授いただけると幸いです。)users/registrations/new.html.erb<% i = 0 password = "" while i < params[:password].length password << "*" i += 1 end %>
params[:password].length
でパスワードの文字数を取得しています。
while i < params[:password].length ... end
で処理の中でi
が1ずつカウントアップしていきます。
同時に、直前で定義した変数password = ""
に"*"
が足されていき、文字数に達したら処理が終了します。
<%= password %>
変数の中身を表示します。users/registration/new.html.erb<%= f.hidden_field :name_family, value: params[:name] %> ...
f.hidden_field
は表示はされませんが、そこに指定されているvalue: params[:name]
などはフォームとして送信されます。
今回のフォームは保存するモデルも(resource
でdeviseが自動的に)指定されているので、このf.hidden_field
で指定された値はそのまま保存される値になります。完了画面
users/completion.html.erb<div> <% if request.referrer.include?('users/sign_up') %> <p>登録が完了しました!</p> <% else %> <p>既に登録済みです。</p> <% end %> </div>前のページで
登録する
が押されると、確認した会員情報が保存されると同時に、この画面に遷移します。
request.referrer
を使うと、どのページからここに遷移してきたか、前のページのURLが取得できます。
request.referrer.include?('users/sign_up')
のinclude?メソッドは()の中の値がそこに含まれているかどうかを返すメソッドです。
ここでusers/sign_up
が前のページのURLに含まれていたかどうかを判断して、そのページで表示するテキストを変えています。
users/registrations/new.html.erb
以外からの遷移であれば、登録済みと表示され、そもそも未登録(未ログイン)の場合に表示しようとするとdeviseのbefore_action :authenticate_user!
で、他のページにリダイレクトが行われます。2.users_controller.rbにnewとcompletionを追加
前の手順ではviewファイルを作成しましたが、このままでは画面表示ができないので、コントローラーでアクションを記述する必要があります。
users_controller.rbclass UsersController < ApplicationController before_action :authenticate_user!, except: %i[new] ... def new end def completion end ... endnewページは新規会員登録フォームなので、下記のように記述する必要があります。
before_action :authenticate_user!, except: %i[new]
before_action :authenticate_user!, except: [:new]
before_action :authenticate_user!, except: :new
どれも意味は一緒ですが、記述するアクションが増えた時に、よりコードが短くなるのは1行目のものです。ちなみに
%i
で複数指定するときはbefore_action :authenticate_user!, except: %i[new action1 action2 action3]
という感じでスペースで区切るだけです。completionは未ログインの場合はリダイレクトさせたいので
except
に含めません。3.新たに作成したviewとusers/registrations_controller.rbをroutes.rbに記述する
routes.rbRails.application.routes.draw do ... # registrations_controller.rbを有効にします。 devise_for :users, controllers: { ... registrations: 'public/users/registrations' } # 作成したusers/new.html.erbとusers/completion.html.erbをルーティングに追加します。 resources :users, only: %i[new] do get 'completion', to: 'users#completion' end end4.登録後のリダイレクト先を変更
確認画面で
登録する
が押されたときに、前の手順で用意したusers/completion.html.erb
に遷移させる必要があります。users/registrations_controller.rbclass Public::Users::RegistrationsController < Devise::RegistrationsController ... def after_sign_up_path_for(resource) public_user_completion_path(resource) end ... endデフォルトか、もしくは任意で別のページがリダイレクト先になっているかと思いますが、そのpathを前の手順で作成した
public_user_completion_path(resource)
(users/completion.html.erbのpath)に設定します。まとめ
より実用的な使い方については、私のGitHubに実際に使っているファイルを公開しているのでそちらも参考にしていただければと思います!
GitHub - MasaoSasaki/matchi質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました。
- 投稿日:2020-09-19T20:54:17+09:00
【Active Storage】ファイルアップロード時のバリデーション設定
概要
Active Storageにはデフォルトのバリデーションがないため、自前でバリデーション設定をした時のことを備忘録として記録します。
環境
・ruby '2.5.7'
・rails '5.2.3'過程
1.準備
Active Storageを導入するために以下のコマンドを実行します。
$ rails active_storage:install以下の2つのテーブルを作成するマイグレーションファイルが作成されます。
・active_storage_attachments
・active_storage_blobsrails db:migrateしてテーブルを作成します。
$ rails db:migrate2.モデルへの関連付け
モデルへの関連付けをしていきます。
ここでは、Userモデルに複数のプロフィール画像を設定する場合とします。models/user.rbclass User < ApplicationRecord has_many_attached :avatars end
:avatars
はファイルの呼び名で、:photos
、:images
などファイルの用途に合わせて好きなものが指定出来ます。Userモデルに画像用のカラムを用意する必要はありません。3.バリデーションの設定
Userモデルにバリデーションの設定をしていきます。
ここでは、次の3つの設定をしていきます。・avatar_type
アップロード出来るファイル形式を指定します。・avatar_size
アップロード出来る1ファイルの大きさ(容量)を指定します。・avatar_length
アップロード出来るファイルの数を指定します。models/user.rbclass User < ApplicationRecord (省略) validate :avatar_type, :avatar_size, :avatar_length private def avatar_type avatars.each do |avatar| if !avatar.blob.content_type.in?(%('image/jpeg image/png')) avatar.purge errors.add(:avatars, 'はjpegまたはpng形式でアップロードしてください') end end end def avatar_size avatars.each do |avatar| if avatar.blob.byte_size > 5.megabytes avatar.purge errors.add(:avatars, "は1つのファイル5MB以内にしてください") end end end def avatar_length if avatars.length > 4 avatars.purge errors.add(:avatars, "は4枚以内にしてください") end end endちなみに、
avatars.purge
で一時データを削除しています。
他の入力項目でバリデーションエラーが起きてしまうと、Rails5.2系〜6.0系以前ではモデルのインスタンスのattributeに代入した時点で、アップロードしたファイルがストレージに保存されてしまい、その結果として、blobにエラー用の一時データが溜まってしまう可能性があるためです。
(ただ、Rails6.0へのアップデートでActive Storageの機能変更があり、saveメソッドが実行された後に、ストレージに保存される仕様に変更されたので、6.0以降であれば不要です)結果
参考
ActiveStorageがCarrierWaveの代用として使えるか考える
Rails5 Active Storageを使って画像アップロード機能を実装する
- 投稿日:2020-09-19T19:38:29+09:00
【Rails】Action Cableについて
Action Cableが誕生した背景
ユーザーの入力なしに、最新の情報をWebに表示する「Realtime性」をもつリッチな体験を実現するために誕生しました。
Action Cableは、RailsのRESTとWebSocketのシームレスな統合です。
例えばAction Cableを使うことで、みなさんが使っているようなリアルタイムチャットアプリを
作成することもできます。用語について
Websocket
・Web上でクライアント、サーバ間の双方向通信を実現するための通信規格
・送信できるデータはテキストまたはバイナリ
・HTTPとは別のプロトコルなのでCookieは送られない
・通信が確立した後はクライアント、サーバーどちらからでもメッセージを送ることが可能Connection
・Websocketとしてのコネクションを表すクラス
・Websocketとして通信が確立した時点でインスタンスが生成される
・開始時のリクエストはふつうのHTTPなのでCookieも送られる
・認証処理はこのクラス内に記述Consumer
・Websocketコネクションのクライアント(ブラウザなど)
・同じブラウザの異なるタブは完全に別のクライアントとして扱うサーバー側(Rails)の構成要素
Conection
コネクションは、ApplicationCable::Connectionのインスタンスです。
このクラスでは、受信したコネクションを承認し、ユーザーを特定できた場合にコネクションを確立します。
そのための仕組みとして、identified_byを使います。
identified_byはコネクションIDであり、後で特定のコネクションを見つけるときに利用できます。# app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user if verified_user = User.find_by(id: cookies.encrypted[:user_id]) verified_user else reject_unauthorized_connection end end end endChannel
チャネルはMVCでいうコントローラーの役割を担います。
Subscription
コンシューマーは、チャネルを購読するサブスクライバ側(Subscriber)の役割を果たします。
そして、コンシューマーのコネクションはサブスクリプション(Subscription: 購読)と呼ばれます。# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel # コンシューマーがこのチャネルのサブスクライバ側になると # このコードが呼び出される def subscribed end end例1. ユーザーアピアランスの表示
これはユーザーがオンラインかどうかという情報を追跡するチャネルの例が以下です(オンラインユーザーの横に緑の点を表示する機能を作成する場合などに便利)。
まずは、サーバー側のアピアランスチャンネルを作成します。
# app/channels/appearance_channel.rb class AppearanceChannel < ApplicationCable::Channel def subscribed current_user.appear end def unsubscribed current_user.disappear end def appear(data) current_user.appear(on: data['appearing_on']) end def away current_user.away end endサブスクリプションが開始されると、subscribedコールバックがトリガーされ、
そのユーザーがオンラインであることを示します。例2. 新しいWeb通知を受信する
Websocketコネクションを使って、サーバーからクライアント側の機能をリモート実行させます。
このWeb通知チャネルは、正しいストリームにブロードキャストを行ったときに、クライアント側でWeb通知を表示します。
サーバー側のWeb通知チャネルは以下です。
# app/channels/web_notifications_channel.rb class WebNotificationsChannel < ApplicationCable::Channel def subscribed stream_for current_user end endアプリケーションのどこからでも、web通知チャネルのインスタンスにコンテンツをブロードキャストできます。
# このコードはアプリケーションのどこか(NewCommentJob あたり)で呼び出される WebNotificationsChannel.broadcast_to( current_user, title: 'New things!', body: 'All the news fit to print' )参考
- 投稿日:2020-09-19T17:30:00+09:00
改めてRSpecの目的、方針をまとめてみた
RSpecをしっかり学び直そうと思い、再度RSpecでテストを書く目的や、方針をまとめてみました。
@jnchito さんの記事やYouTubeをベースでの発信をベースに、自分の好みや状況を織り込んだ感じです。なぜRSpecか
minitestより広く普及していて、情報量が多いから
前提
良いRSpecとは何か
を突き詰めて議論すると、「正しい/間違い」ではなく、「好き/嫌い」の世界になる。自分やチームにとっての落としどころを決めることが大切。何のためにテストを書くか
以下の負担を軽減して自分が楽をするため
- リリース前の動作確認
- 機能追加時の動作確認
- gemやライブラリをアップデートする時の動作確認
基本方針
費用対効果が高いテストを書く。
読みやすいテストを書く。やらないこと
- 通常の運用では起こりえないことはテストしない
- 「RSpecらしいコード」は追い求めない
- subject使わない。
- ワンライナー構文(
it { is_expected...}
)は使わない- 厳格な教義に従わない(1つのitの中に必ず1つのアサーション、など)
- describe、contextでのグループ分けも基本しない
- 費用対効果の低いテストコードは書かない(自作のロジック以外の部分のテスト)
- 単純な関連づけ(has_many)
- 単純なバリデーション(presence: true)
- 自動生成されたコード
controller spec
、request spec
- system specで検証できる
- RailsをAPIサーバーとして使う場合のみrequest specを書く
- 過度なDRYは求めない
- テストコードはDRYより読みやすさを優先
- プライベートメソッドはテストしない
- プライベートメソッドもちゃんと実行されるようなテストをパブリックメソッドに対して行う
- そうすることでプライベートメソッドをいくらリファクタリングしてもテストは壊れなくなる
- テスト駆動開発
- RSpecに十分に慣れてから検討する
- テストコードの中で、ループ等プログラミングっぽいことはなるべくしない。
やること
- 使うのは
rspec
、factorybot
、capybara
system spec
とmodel spec
を書く
- なるべくcontrollerが薄くなるように処理を書く
- controller内の分岐の数の数だけsystem specを書く
- リファクタリングして、modelのメソッドをちょうど良い粒度にする
- modelのメソッドの単体テストで、想定されるいろんなパターンのテストを書く
- 慣れるまでは、処理を実装するたびにテストを書く
- 慣れてきたら「この処理はテスト不要」みたいな勘所がわかりそう
- 十分にメリットがある時だけ
before
やlet
を使う
- 乱用はしない
- 同じコードをベタ書きした方が良い場合はそうする
- 期待値はベタ書きする
eq price/2
じゃなくてeq 499
- 修正の手間は増えるけど、読みやすくなる
- system specでクリックするボタン等はテスト用のdata属性をつけて対応する
- ベタ書きだと、labelが無いinput等に対応できない。指定方法は統一したい
- Capybaraで変更に強いE2Eテストを書く / TokyuRubyKaigi12 - Speaker Deck
- セットするデータは「テストデータ」などとは書かず、実運用で想定される値を書く
- 「◯◯の場合」のような説明はコメントで書く
- 本来
context
に書くものだけど、めんどい- ひたすらitを繰り返し、ネストを減らす
- ログイン用のヘルパーメソッドを作る
定義module LoginMacros def login(user) visit sign_in_path fill_in 'email', with: user.email fill_in 'password', with: '12345678' click_button 'ログイン' expect(page).to have_content 'ログインしました。' end def logout click_link 'ログアウト' expect(page).to have_content 'ログアウトしました。' end def act_as(user) login user yield logout end end呼び出しdescribe '掲示板' do let(:alice) { create :user } let(:bob) { create :user } it 'トピックの投稿とコメントの返信' do # aliceとして操作 act_as alice do visit root_path ... end # bobとして操作 act_as bob do visit root_path ... end end endその他
- テスト内でidは決め打ちしない
- 境界値をテストする
- 20歳以上が大人」という仕様なら、19歳のときと20歳のときを検証する
- nezumiというchrome拡張を使うと、capybaraの記法を確認に便利
- Better Specsという「RSpecはかくあるべき」がまとまっているサイトがある
- 厳し過ぎたりするから従う必要は無いけど、どこかで目を通してみても良いかも
example
とit
とspecify
は同じメソッドlet(:user)
を使った方が、before内で@user = User.create
とするよりtypoに気付きやすくて良い- エラーメッセージのテストは、文字列を検証する以外にも、個数やキーを検証する方法もある
- system specとmodel specの特徴、使い分け
- system spec: controllerもmodelもviewも全部テストできる
- model spec: 動作が速い、必要なコード量が少ない
- よくあるパターンをsystem specで書き、それで担保できない部分をmodel specで書く
TODO
- テスト技法を学ぶ
- 以下の書籍から「どんなケースのテストを書くべきか」等を学ぶ
参考
- 投稿日:2020-09-19T15:36:28+09:00
Rails6 ArgumentError: Invalid formats: "json"エラー
この記事について
APIの勉強をしているとエラーが出たので、その解決方法を投稿
環境
・Ruby 2.6.6, Rails 6.0.3.2
・Docker,Docker-compose(開発環境)発生したエラー
該当コード
api/books_controller.rbclass Api::BooksController < ApplicationController def show @book = Book.find(params[:id]) render 'show', formats: 'json', handlers: 'jbuilder' end end原因
https://railsguides.jp/layouts_and_rendering.html#formats%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3
railsガイドではformats: 'json'
ではなく、render formats: :xml
のようにシンボルで記述するようになっている。rails6からはシンボルになったのかな?コードを修正
api/books_controller.rbclass Api::BooksController < ApplicationController def show @book = Book.find(params[:id]) - render 'show', formats: 'json', handlers: 'jbuilder' + render "show", formats: :json, handlers: "jbuilder" end endシンボルに変更すると、エラーが解消されました。
まとめ
記事を参考にするときはしっかり環境が同じかどうか、確認をした上で勉強をしていく。
全く同じように進めているのにエラーが出るのであれば、バージョン起因を考えるようにする。
- 投稿日:2020-09-19T12:25:47+09:00
実務経験1週間のまとめ
- 投稿日:2020-09-19T12:12:56+09:00
《未経験→webエンジニア》実務5日目
昨日投稿できなかったので、今日投稿します。
この記事の目的
自分がやったこと、知らなかったこと、やるべきことを明確にし
1日あたりの成長速度を速める。【今日やったこと】
APIテスト
イシューに上がっている軽微な内容の修正【知らなかったこと】
・APIのレスポンスは、コントローラーに種別で作られている
・メモ帳のデフォルト設定で””が逆さになっており、エラーが起きる
・現在いるブランチ名はVScodeの左下に表示されている!知らなかったテストbreakman→セキュリティ系のテストを行ってくれる
今回の案件では、breakman,rspec,rubocopの3段階でテストする
expected: "期待されている文言" got: "実際の文言"ここが違わないかをチェックする
API文言の修正だけでなく、ステータスコードもセットで直すようにする
HTTPのステータスコードhttps://qiita.com/terufumi1122/items/997e24dde87f807e3944
- 投稿日:2020-09-19T11:18:20+09:00
Rails 6で認証認可入り掲示板APIを構築する #14 seed実行時間の表示
←Rails 6で認証認可入り掲示板APIを構築する #13 認証ヘッダの付与
完成図
$ rails db:seed -----user start----- count from: 0 count to: 10 3.4542s -----user end----- -----post start----- count from: 0 count to: 13 0.1095s -----post end----- 3.5883sseedsファイルの編集
まずはcontrollerの修正と同じく、postはuserから生成するため、user_seeds.rbから作っていきます。
db/seeds/user_seeds.rb# frozen_string_literal: true unless User.exists? 10.times do |i| email = "test#{i + 1}@example.com" User.create!(email: email, password: "password", uid: email, provider: "email", name: Faker::Name.name) end endとりあえず10ほぼユーザーを生成してみましょう。
続いて、post_seeds.rbを上記で生成したユーザーに所属させる形で生成します。
db/seeds/post_seeds.rb# frozen_string_literal: true unless Post.exists? users = User.all users.each do |user| Random.rand(0..3).times do user.posts.create!(subject: Faker::Lorem.word, body: Faker::Lorem.paragraph) end end end全ユーザーを取得し、0から3件ランダムで投稿を生成します。
Random.rand(x..x)
はseedでよく使う手法なので覚えておくと良いです。db/seeds.rbの編集
db/seeds.rb# frozen_string_literal: true seed_models = %i[user post] all_process_time = Benchmark.realtime do seed_models.each do |model| puts "-----#{model} start-----" puts "count from: #{model.to_s.classify.constantize.count}" process_time = Benchmark.realtime do require "./db/seeds/#{model}_seeds" end puts "count to: #{model.to_s.classify.constantize.count}" puts "#{format('%.4<time>f', time: process_time)}s" puts "-----#{model} end-----" end end puts "#{format('%.4<time>f', time: all_process_time)}s"$ rails db:seed -----user start----- count from: 0 count to: 10 3.4542s -----user end----- -----post start----- count from: 0 count to: 13 0.1095s -----post end----- 3.5883smodelが増えてきて処理時間が伸びてきたら、どこがボトルネックになっているのか調査しやすくなるはずです。
続き
- 投稿日:2020-09-19T10:52:23+09:00
[Rails] User検索機能を実装する
概要
Railsプロジェクトで掲示板サイトを作成中、User検索機能を実装する機会がありました。
備忘録としてここに手順を残しておきます。ルーティングの追加
GET /users/searchでUsersコントローラのsearch アクションにルーティングされるようにconfig/routes.rbを編集します。
config/routes.rbresources :users do get :search, on: :collection endusersコントローラーの編集
searchアクションを作成します。
検索文字列はsearch_keywordフィールドへの入力とするので、params[:search_keyword]で取得します。app/controllers/users_controller.rbdef search if params[:search_keyword].present? @users = User.where('name LIKE ?', "%#{params[:search_keyword]}%") else @users = User.none end endビューの追加
app/views/users/search.html.erb を作成します。
views/users/search.html.erb<h1>User search</h1> <%= form_tag search_users_path, method: :get do %> <%= text_field_tag :search_keyword %> <%= submit_tag "Search", username: :nil, class: "button is-info" %> <% end %> <%= render 'users/users', users: @users %>User の情報が入っている app/views/users/_users.html.erb を既に作っていたので render で呼び出しました。
完成
以上でRailsプロジェクトにUser検索機能を実装することができました。
ありがとうございます。
- 投稿日:2020-09-19T10:33:31+09:00
[Rails]ストロングパラメーターにuser_id(外部キー)を記述する方法
投稿内容
今回はストロングパラメータに外部キーを記述する方法をアウトプット。
実装方法
controller.rbprivate def item_params params.require(:item).permit(:name, :description, :category, :brand, :condition, :shipping_cost, :prefecture_id, :shipping_day, :price, images_attributes: [:image]).merge(user_id: current_user.id) endこのように
mergeメソッド
を使い記述。
今回の場合は、ログインしているユーザーのidがuser_id
としてパラメーターに保存される。最後に
今回は簡潔にアウトプット!参考にして下さい!
- 投稿日:2020-09-19T08:05:52+09:00
Docker rails + Vue.js localhostで接続が拒否された
この記事について
Dockerの理解が乏しいが故に解決に時間がかかったことを忘れないために投稿する記事となっております。
Vue.jsの導入にあたって参考にした記事
https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253
この記事で関連するファイル
- Dockerfile
FROM ruby:2.6 # install package to docker container RUN apt-get update -qq && apt-get install -y build-essential libpq-dev \ && apt-get install apt-transport-https \ && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update && apt-get install -y yarn \ && curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs mariadb-client \ && mkdir /vue_app WORKDIR /vue_app COPY Gemfile /vue_app/Gemfile COPY Gemfile.lock /vue_app/Gemfile.lock COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000
- docker-compose.yml
compose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3320:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql web: build: . command: bundle exec puma -C config/puma.rb environment: RAILS_ENV: development volumes: - .:/vue_app - bundle:/usr/local/bundle - /app/vendor - /app/log - /app/.git ports: - "3000:3000" depends_on: - db tty: true stdin_open: true volumes: mysql-data: driver: local bundle: driver: local起こった問題
https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253#-foreman%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E8%A8%AD%E5%AE%9A
参考URLと同じようにforeman
を導入して2つのサーバーが同時に立ち上げられるように設定を行い、docker-copmpose up
を実行してブラウザでlocalhost:5000
を見にいくと以下が表示された。やったこと
①Procfile.devに -b 0.0.0.0を追記して
docker-compose run --rm web bin/server
を実行Procfile.dev+ web: bundle exec rails s -b 0.0.0.0 - web: bundle exec rails s②そもそもdocker-compose.ymlのwebのポート番号を変更するのを忘れていたので、ポート番号を変更して
docker-compose run --rm web bin/server
を実行docker-compose.ymlports: - - "3000:3000" + - "5000:5000"しかし、解決ができない。。。
これでやっと解決した
https://docs.docker.jp/compose/reference/run.html
ポート番号までコマンドで改めて指定してあげて、サーバーの立ち上げに成功docker-compose run -p 5000:5000 --rm web bin/serverただ、なぜポート番号の指定を明示的にしないと繋がらないのかが分からないよ。。。
これまでにこんなコマンド使ったことなかったし。
- 投稿日:2020-09-19T08:05:52+09:00
Docker + rails + Vue.js localhostで接続が拒否された
この記事について
Dockerの理解が乏しいが故に解決に時間がかかったことを忘れないために投稿する記事となっております。
foreman
によるサーバの設定をし、Ruby on RailsにVue.jsを導入しようとしたが、localhost:5000に接続ができず、どん詰まりしてしまった。Vue.jsの導入にあたって参考にした記事
https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253
この記事で関連するファイル
- Dockerfile
FROM ruby:2.6 # install package to docker container RUN apt-get update -qq && apt-get install -y build-essential libpq-dev \ && apt-get install apt-transport-https \ && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ && apt-get update && apt-get install -y yarn \ && curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs mariadb-client \ && mkdir /vue_app WORKDIR /vue_app COPY Gemfile /vue_app/Gemfile COPY Gemfile.lock /vue_app/Gemfile.lock COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000
- docker-compose.yml
compose.ymlversion: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3320:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql-data:/var/lib/mysql web: build: . command: bundle exec puma -C config/puma.rb environment: RAILS_ENV: development volumes: - .:/vue_app - bundle:/usr/local/bundle - /app/vendor - /app/log - /app/.git ports: - "3000:3000" depends_on: - db tty: true stdin_open: true volumes: mysql-data: driver: local bundle: driver: local
- Procfile.dev
Procfile.devweb: bundle exec rails s webpacker: ./bin/webpack-dev-server
- bin/server
bin/server#!/bin/bash -i bundle install bundle exec foreman start -f Procfile.dev起こった問題
https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253#-foreman%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E8%A8%AD%E5%AE%9A
参考URLと同じようにforeman
を導入して2つのサーバーが同時に立ち上げられるように設定を行い、docker-copmpose up
を実行してブラウザでlocalhost:5000
を見にいくと以下が表示された。やったこと
①Procfile.devに -b 0.0.0.0を追記して
docker-compose run --rm web bin/server
を実行Procfile.dev+ web: bundle exec rails s -b 0.0.0.0 - web: bundle exec rails s②foremanのポート番号指定
foremanのポート番号はデフォルトで5000番が設定されているようですが、改めて明示的に5000番を指定。
bin/server#!/bin/bash -i bundle install + bundle exec foreman start -p 5000 -f Procfile.dev③そもそもdocker-compose.ymlのwebのポート番号を変更するのを忘れていたので、ポート番号を変更して
docker-compose run --rm web bin/server
を実行docker-compose.ymlports: - - "3000:3000" + - "5000:5000"しかし、解決ができない。。。
これでやっと解決した
https://docs.docker.jp/compose/reference/run.html
ポート番号までコマンドで改めて指定してあげて、サーバーの立ち上げに成功docker-compose run -p 5000:5000 --rm web bin/serverただ、なぜポート番号の指定を明示的にしないと繋がらないのかが分からないよ。。。
これまでにこんなコマンド使ったことなかったし。
- 投稿日:2020-09-19T02:40:41+09:00
bundle updateをしても、繰り返し促される時の解決法
To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.1.4`このようなエラーが繰り返し表示されたときは、
gem update --system
を実行すると解決する場合があります。
- 投稿日:2020-09-19T01:53:00+09:00
form_withメソッドの使用方法
【概要】
1.結論
2.どのように使用するか
1.結論
<%= form_with(model or URL, ) do |f| %>と使用する!
2.どのように使用するか
自分はこのように使用しました!
views/time/new.html.erb<%= form_with(model: @time, local: true) do |f| %>自分は”model”に@timeでインスタンス変数を入れました。
modelの後にurlでpath指定してあげるとpathによってmethodも自動で振り分け機能になり、そしてアクションも指定できます。またpathの中に(****.id)と指定するとidを引っ張った状態でアクションを行えます!class=""指定してあげて、cssにあてることもできます!
|f|はヘルパーメソッドに適用でき、form_withの中身を"f."がついているものに入れ込むことができます!
|f|は変数のようなものなので使いやすい名前で統一したもらえばOKです!f.label
f.checkout
f.password_field
f.email_field等のヘルパーメソッドに適用できます!