- 投稿日:2021-06-03T23:19:21+09:00
RailsでPOSTしようとした時のActionController::InvalidAuthenticityTokenエラー
Rails4 - 解決法 /app/assets/controllers/application_controller.rb class ApplicationController < ActionController::Base # protect_from_forgery with: :exception end protect_from_forgery with: :exception をコメントアウトする Rails5.2以上 - 解決法1 /app/assets/controllers/application_controller.rb class ApplicationController < ActionController::Base skip_before_action :verify_authenticity_token, raise: false end skip_before_action :verify_authenticity_token, raise: false を追記する Rails5.2以上 - 解決法2 /app/assets/controllers/application_controller.rb class ApplicationController < ActionController::Base protect_from_forgery end protect_from_forgery を追記する
- 投稿日:2021-06-03T23:11:25+09:00
ログインの有無で条件指定
authenticate_user! コントローラーのクラスの下に定義し、ログインしていないユーザーが行えるアクションを制限する。 ○○_controller.rb before_action :authenticate_user!, only: [:index, :create] only 指定するアクションのみ except 指定するアクション以外 終わり ログイン状態の有無をIF文で定義しようとして時間を使ってしまいました。 authenticate_user!備忘録
- 投稿日:2021-06-03T22:35:33+09:00
docker-compose buildで詰まった
エラー 以下のエラーが出た。 $ docker-compose build db uses an image, skipping test-db uses an image, skipping chrome uses an image, skipping Building app Traceback (most recent call last): File "site-packages/docker/utils/build.py", line 97, in create_archive File "tarfile.py", line 1970, in addfile File "tarfile.py", line 250, in copyfileobj File "tempfile.py", line 481, in func_wrapper OSError: [Errno 28] No space left on device During handling of the above exception, another exception occurred: Traceback (most recent call last): File "docker-compose", line 3, in <module> File "compose/cli/main.py", line 67, in main File "compose/cli/main.py", line 126, in perform_command File "compose/cli/main.py", line 302, in build File "compose/project.py", line 468, in build File "compose/project.py", line 450, in build_service File "compose/service.py", line 1125, in build File "site-packages/docker/api/build.py", line 160, in build File "site-packages/docker/utils/build.py", line 31, in tar File "site-packages/docker/utils/build.py", line 100, in create_archive OSError: Can not read file in context: /Users/nami/Desktop/名称未設定フォルダ/Study_quest/名称未設定フォルダ/Study_quest/app/assets/fonts/font_1_honokamin.ttf [8624] Failed to execute script docker-compose 初めは OSError: Can not read file in context で調べていたが解決せず OSError: [Errno 28] No space left on device こちらに注目して調べたところ dockerの容量が足りないせいでcontextを読み込めずエラーになっていた。 解決策 $ docker image prune とすることで使っていないdockerイメージを削除することができる。 上記を実行しエラー解決に至った。
- 投稿日:2021-06-03T19:48:48+09:00
mimemagic -v0.3.2がbundle installに失敗する理由と解決法
なにこれ mimemagicがrubygems.orgから取り下げられたせいで、 mimemagicのbundle installが失敗するようになった問題の理解と解決方法を備忘録として書く。 なぜbundle installが失敗するか ↓のようにmimemagic -v0.3.2を書いてるGemfile.lockがあるとします。 Gemfile.lock …略 mimemagic (0.3.2) …略 bundle installする時の挙動は、 GemfileとGemfile.lockを見て、 欲しいgemがGemfile.lockに記載されてれば該当のversionをinstallしようとする。 (上の例だとv0.3.2) ただ、mimemagicはv0..3.2がrubygemsに存在しない。 なので、bundle installが失敗する。 解決策 ↓のように、rubygemsに存在するversionをinstallするようにする。 Gemfile.lock …略 mimemagic (0.3.10) …略 参考記事 mimemagicの最新動向 セマンティック・バージョニングと、Gemfileのバージョン指定方法 - Gemfileでよく見る~>を使いこなす
- 投稿日:2021-06-03T19:39:41+09:00
TECHCMPで未経験から自社開発企業へ内定をもらうまでのロードマップ
はじめに 2020年11月から学習を開始して、内定をもらえるまでの道のりをこちらの記事でまとめていきます。できる限り皆様に有益な情報となるようにまとめましたので、よろしければご覧ください 章 内容 私のスペック 簡単な自己紹介を載せています 学習開始〜内定までに行ったこと 学習を始めてから行ったことの内容を時系列順にまとめています ポートフォリオについて ポートフォリオの詳細について機能紹介やインフラ構成図等を載せています テックキャンプについて テックキャンプについて詳細です。回し者ではない感想をご覧ください 転職活動について 転職活動の実績や内容をまとめています。 その他差別化戦略 私がしていた未経験エンジニアの差別化戦略をご紹介します。 私のスペック 旧帝大理系出身 社会人2年目(1年働いて退職) 前職が某鉄道会社の社内システム担当(エンジニアとは程遠い) 大学時代にプログラミング経験あり(C言語、Java) 特にインターン行ったとかの実績なし 研究での実績なし 学習開始〜内定までに行ったこと 2020年11月 エンジニアになりたいと思い独学で学習開始 まずは、ProgateでHTML、CSS、Ruby、Railsを学ぶ この時期が一番辛いです(笑) 2020年12月 Progateが終わり、「あれ、わんちゃんポートフォリオ作れるんじゃね?」と感じ作成。3週間かけて、ログイン機能、投稿機能、いいね機能を搭載したサービスをデプロイするも、ログインができずボタンの配置などがグチャグチャなゴミサービスが爆誕(笑) 再度、丁寧に学びたいと思いドットインストールでHTML、CSS、Javascriptを勉強する。 2021年1月~3月上旬 もっと勉強したい+プログラミングスクールで体系的に学びたいと思い、当時所属していた企業を3月末に退職することを伝える。Twitterを使用して、転職成功された方に色々質問したりしてプログラミングスクールをテックキャンプにすると決める。(別途参照) また、Udemyの存在を知り、以下の講座で基本的なこと抑えまくった。 Git: もう怖くないGit!チーム開発で必要なGitを完全マスター(山浦先生) AWS:ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得(山浦先生) もう怖くないLinuxコマンド。手を動かしながらLinuxコマンドラインを5日間で身に付けよう(山岸先生) フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座(小島先生) ゼロからはじめる Dockerによるアプリケーション実行環境構築(小島先生) 【JavaScript&CSS】ガチで学びたい人のためのWEB開発徹底実践(フロントエンド編)(CodeMafia先生) 米シリコンバレーDevOps監修!超Docker完全入門(2020)【優しい図解説とハンズオンLab付き】(CS Career Kaizen先生) 2021年3月22日~4月上旬 テックキャンプ98期として入学。サービスの作り方の一連の流れを学習。10週間かかるカリキュラムを17日で終わらす。 (テックキャンプについては別途参照) 2021年4月中旬~5月22日 1ヶ月半かけて、じっくりポートフォリオを作成しました。 行ったことは以下です。 いくつかある社会課題の中からアプリケーションで解決できそうなものをピックアップして市場調査を行い、テーマを決める そのあと、要件定義やペルソナにあたる友人へヒアリングを行い、必須機能の選定を行う。 実装・テスト・リリース (ポートフォリオについては別途参照) 2021年5月22日〜31日 履歴書・職務経歴書を史上最高レベルに持っていくことを行う。また、テックキャンプの「よく聞かれる質問リスト」を参考に、面接対策を死ぬほどする。この際喉が死んだ。 2021年6月〜 GreenとWantedlyのみで就活開始。 約1週間ちょいで急成長スタートアップの自社開発に内定!!! (書類選考等は100%通過、面接も辞退を除けば100%通過、自慢ながら落ちませんでした笑) ポートフォリオについて ポートフォリオの概要 ポートフォリオの概要まとめスライドをみた方が早いです! 以下はサイト内のGIFです。参考までに、、、 一応使用技術等についても以下に載せておきます。 機能一覧 ユーザー登録、ログイン機能(devise) ゲストログイン機能 ユーザ編集機能 練習内容投稿機能 複数枚画像投稿機能 複数タグ付機能 インクリメンタルサーチ機能(Ajax) 文字数カウント機能(Ajax) 画像プレビュー機能 投稿編集機能 投稿削除機能 スライドショー機能(Slick) 試合募集機能 試合削除機能 試合申し込み機能 いいね機能(Ajax) いいね数ランキング機能 自己いいね一覧機能 いいね数表示機能 コメント機能(Ajax) コメント数表示機能 フォロー機能(Ajax) フォロー一覧機能 フォロワー一覧機能 ページネーション機能(kaminari) パンくずリスト機能(gretel) 検索機能(複数ワード対応) 通知機能 コメント通知 フォロー通知 いいね通知 リアルタイム DM 機能(ActionCable) 未読件数表示機能 既読表示機能 カレンダー機能(SimpleCalendar ※モバイルの場合、表として表示) 予定追加機能 レスポンシブ対応 ハンバーガーメニュー等 使用技術 Ruby 2.6.5 Ruby on Rails 6.0.0 MySQL 5.6 Nginx AWS VPC EC2 RDS Route53 ALB ACM RSpec Docker/Docker-compose CircleCi 自動テスト インフラ構成図 DB 設計 ポートフォリオの講評 ほとんど全ての自社開発企業様において一定の評価をいただきました。特に、ユーザに対して実際にヒアリングを行った点や、プログラミングスクールでは扱わないような技術(Docker、AWS、CircleCI )を導入したことが高く評価されました。また市場分析までする人はほとんどいないので、そうした点も評価していただけました。 個人的には他との差別化のためにできる限り類似サービスがないものを選択したことも良かった点だったと思います。よくあるようなサービス(例えば、旅系アプリ、写真系アプリ、口コミ系アプリ)は採用担当者様は見飽きているので、目が止まるように斬新なテーマかつわかりやすいテーマにするといいのではないかと思います。とはいえ、業務関連の専門性の高いアプリもアピールしにくいと思いますので、テーマ決めは慎重にするべきかなと思います。 なお、技術力自体はさほど評価はされていません。見ての通りポートフォリオ自体はめちゃめちゃレベル高いわけでもないし、そもそもポテンシャル採用ですので。 テックキャンプについて 結論 最高です。世間で色々言われている節がありますが、ここで一蹴させてください。本気でエンジニアを目指し準備してきた人にとっては最高の環境が用意されています。おそらく世間で色々言われているのは、テックキャンプ以上の実力をもともと持っていたか、逆に全てをテックキャンプ任せにし、なんの戦略もないまま退職した人なのではないかと思います。 以下、細かく講評していきたいと思います。 カリキュラム 拍手喝采です。独学の時、なんとなーくの理解だったものが、なぜ動くのかの裏側まで理解できるようになりました。また、実際に作りながら学ぶのでとても楽しくできます。さらに、自身の努力でどんどんカリキュラムを進めることができるので、モチベーションを高く保つことができました。なお、決して高難易度のカリキュラムではないので、「簡単すぎる!」と思っても、最初14日間は無条件で返金してもらえるので安心です。 メンター 世間ではレベルが低いと言われていますが、そんなことはありません。質問に対してわかりませんという回答は一度もありませんでしたし、レスポンスをとても早いです。ただし、質問の仕方を注意しなければなりません。相手ももちろん人間ですのでわからないこともあります。自分なりに仮説を立てた上で、相手が「Yes or No」で答えられるような質問にするのがいいかと思います。いくらなんでも1から全部教えろは乱暴ですからね。 CA(キャリアアドバイザー) これは人によるみたいですが、私は形容し難いほど最高のCAさんだったので文句はありませんでした。自身の強み、弱み、そして転職理由など、私の魅力を最大限に相手に伝えられるように添削してくださいました。結果、書類選考率100%、面接突破率100%、テックキャンプ史上最速卒業(?)という記録を打ち出したのではないかと思います。 ちなみに、週何社応募が条件だとかSESしか求人がないという噂がありますが、希望を伝えれば全部自社開発の求人にすることも可能です。私はSESを紹介されたことはなかったです。噂は全くのデマです。 環境 一部教室利用制限ありであまり使えていませんでしたので、なんとも言えませんが、コロナ禍でもZoomを利用してチームメンバーとアウトプットする時間があるのでサボるということはありません。(というか、環境に極度に依存しないと勉強できない程度のやる気でエンジニア転職は辞めた方がいいです) とりあえず、勉強するのには十分な環境だったかなと思います。 オリアプ質問禁止について これは正直きついです。でも、質問禁止だからこそ価値があります。冷静に考えてみてください。某プログラミングスクールのようなオリアプも質問OKの環境で「ポートフォリオは自力でやりました」と面接の場で言われても説得力もないし、自分に自信もつかないですよね。私は、むしろ質問禁止だからこそテックキャンプを選びました。なお、どうしてもわからない時は「MENTA」とか使ってましたけど、そうした行動もプログラミングスクール任せよりも絶対評価されるはずです。 総合評価 まだ改善の余地はあるものの、私はエンジニア転職するには十分すぎるサービスと感じました。なぜ世間でこんなふうに言われているのかわかりませんね。 転職活動について 実績 以下は全て50人規模以下の自社開発企業様に応募した結果です。 フェーズ 社数 書類選考 10社(うち7社選考辞退) 1次・2次面接 3社 最終面接 3社 内定 1社(残り2社選考中) 求人媒体 まず転職活動では、GreenとWantedlyのみを使用しました。あとはテックキャンプ経由の求人です。結果として志望度の高い自己応募の企業様からすぐに内定をいただいたのでテックキャンプ経由の会社様は全て辞退しましたが、テックキャンプさんの求人もかなり魅力的でした。 履歴書・職務経歴書 テックキャンプでの転職サポートでCAさんが添削してくださいます。なのでここで、しっかりとブラシュアップしてました。ちなみに、自己応募ではテックキャンプであることは隠しも公表もせず、特に書いてませんでしたが、書類を求められた時はテックキャンプであることを明かしていました。最初はテックキャンプ卒は落とされるという心配がありましたが、そんなことはありませんでした。テックキャンプだろうとなかろうと、ポートフォリオや履歴書等がしっかり書いてあれば問題はありません。 面接対策 毎日声が枯れるまでやってました。よく聞かれる質問に対してすらすらと聞きやすく伝える努力は絶対に必要です。たまに暗記では意味ないと言う方がいますが、暗記すらできない人間の需要はありません(笑)。ちなみに目安としては、1日2時間半はこの練習に当てていました。おすすめ練習方法を以下に記載します。 自分の声を録音して、話したら聞くの繰り返し 一人でZoom開催+録画で練習して表情の確認 ネットで「怖い面接官 顔」で検索してヒットした写真に向かって練習する 最近はWeb面接がかなり増えてきているので、Web面接ならではの声のボリューム調整などに苦労しましたが、一人Zoomであればかなり慣れてくるのでおすすめです。 ちなみに、私はこの一人Zoomで自分の顔がちょっと左に傾くくせがありキモかったので強制しました(笑) 差別化戦略 この章では、圧倒的に市場に溢れている未経験エンジニアの人とどんな差別化を行ってきたか解説します。 独学で何かした経験 私は、エンジニアになる上では「自走力」があることの証明が必要と考え、とりあえず独学でポートフォリオを作るということをしました。中身は見せていませんが、そうした姿勢は一定の評価がもらえました。とりあえず、プログラミングスクールに行くのではなく、まず自身でやってみたと言う経験は語れるほうがいいです。 Qiita・Twitterで毎日発信。 毎日学んだことをアウトプットしてました。まじの毎日です。 結果、Qiitaの投稿数もかなり多くなりました。別にアピールするためにするわけではないですが、企業様からはかなり高い評価をいただけました。(求人アプリでもスカウトが増えました) 良質なポートフォリオ 自画自賛ではないですが、私は少なくとも未経験の方がパッと作れるようなポートフォリオではありません。それなりに時間を投下してつくったものですので、やはりここは評価点としてあったかなと思います。ちなみに、私の場合は以下を参考にしてポートフォリオ作りに励みました。 https://www.youtube.com/watch?v=N0yetny4Zco また、ポートフォリオのテーマに関する市場分析や開発の仕方などが、しっかりと工夫されていたことも評価点の一つであったのではないかと感じます。 スライド この記事にも載せていますが、スライドの準備は大事です。やはりREADMEでもいいのですが、スライドで個性あふれるかっこいい感じにした方が印象がいいですし、アプリケーション概要を知るにはこちらの方がシンプルでみやすいです。なので、履歴書には必ずスライドのURLを貼り付けておきましょう! おわりに 生意気にも未経験からエンジニアのロードマップをまとめてみました。誰かの参考になれば嬉しいです!
- 投稿日:2021-06-03T19:39:41+09:00
テックキャンプで未経験から自社開発企業へ内定をもらうまでのロードマップ
はじめに 2020年11月から学習を開始して、内定をもらえるまでの道のりをこちらの記事でまとめていきます。できる限り皆様に有益な情報となるようにまとめましたので、よろしければご覧ください 章 内容 私のスペック 簡単な自己紹介を載せています 学習開始〜内定までに行ったこと 学習を始めてから行ったことの内容を時系列順にまとめています ポートフォリオについて ポートフォリオの詳細について機能紹介やインフラ構成図等を載せています テックキャンプについて テックキャンプについて詳細です。回し者ではない感想をご覧ください 転職活動について 転職活動の実績や内容をまとめています。 その他差別化戦略 私がしていた未経験エンジニアの差別化戦略をご紹介します。 私のスペック 旧帝大理系出身 社会人2年目(1年働いて退職) 前職が某鉄道会社の社内システム担当(エンジニアとは程遠い) 大学時代にプログラミング経験あり(C言語、Java) 特にインターン行ったとかの実績なし 研究での実績なし 学習開始〜内定までに行ったこと 2020年11月 エンジニアになりたいと思い独学で学習開始 まずは、ProgateでHTML、CSS、Ruby、Railsを学ぶ この時期が一番辛いです(笑) 2020年12月 Progateが終わり、「あれ、わんちゃんポートフォリオ作れるんじゃね?」と感じ作成。3週間かけて、ログイン機能、投稿機能、いいね機能を搭載したサービスをデプロイするも、ログインができずボタンの配置などがグチャグチャなゴミサービスが爆誕(笑) 再度、丁寧に学びたいと思いドットインストールでHTML、CSS、Javascriptを勉強する。 2021年1月~3月上旬 もっと勉強したい+プログラミングスクールで体系的に学びたいと思い、当時所属していた企業を3月末に退職することを伝える。Twitterを使用して、転職成功された方に色々質問したりしてプログラミングスクールをテックキャンプにすると決める。(別途参照) また、Udemyの存在を知り、以下の講座で基本的なこと抑えまくった。 Git: もう怖くないGit!チーム開発で必要なGitを完全マスター(山浦先生) AWS:ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得(山浦先生) もう怖くないLinuxコマンド。手を動かしながらLinuxコマンドラインを5日間で身に付けよう(山岸先生) フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座(小島先生) ゼロからはじめる Dockerによるアプリケーション実行環境構築(小島先生) 【JavaScript&CSS】ガチで学びたい人のためのWEB開発徹底実践(フロントエンド編)(CodeMafia先生) 米シリコンバレーDevOps監修!超Docker完全入門(2020)【優しい図解説とハンズオンLab付き】(CS Career Kaizen先生) 2021年3月22日~4月上旬 テックキャンプ98期として入学。サービスの作り方の一連の流れを学習。10週間かかるカリキュラムを17日で終わらす。 (テックキャンプについては別途参照) 2021年4月中旬~5月22日 1ヶ月半かけて、じっくりポートフォリオを作成。 行ったことは以下記載。 いくつかある社会課題の中からアプリケーションで解決できそうなものをピックアップして市場調査を行い、テーマを決める。 そのあと、要件定義やペルソナにあたる友人へヒアリングを行い、必須機能の選定を行う。 実装・テスト・リリース。 (ポートフォリオについては別途参照) 2021年5月22日〜31日 履歴書・職務経歴書を史上最高レベルに持っていくことを行う。また、テックキャンプの「よく聞かれる質問リスト」を参考に、面接対策を死ぬほどする。この際喉が死んだ。 2021年6月〜 GreenとWantedlyのみで就活開始。 約1週間ちょいで急成長スタートアップの自社開発に内定!!! (書類選考等は100%通過、面接も辞退を除けば100%通過、自慢ながら落ちませんでした笑) ポートフォリオについて ポートフォリオの概要 ポートフォリオの概要まとめスライドをみた方が早いです! 以下はサイト内のGIFです。参考までに、、、 一応使用技術等についても以下に載せておきます。 機能一覧 ユーザー登録、ログイン機能(devise) ゲストログイン機能 ユーザ編集機能 練習内容投稿機能 複数枚画像投稿機能 複数タグ付機能 インクリメンタルサーチ機能(Ajax) 文字数カウント機能(Ajax) 画像プレビュー機能 投稿編集機能 投稿削除機能 スライドショー機能(Slick) 試合募集機能 試合削除機能 試合申し込み機能 いいね機能(Ajax) いいね数ランキング機能 自己いいね一覧機能 いいね数表示機能 コメント機能(Ajax) コメント数表示機能 フォロー機能(Ajax) フォロー一覧機能 フォロワー一覧機能 ページネーション機能(kaminari) パンくずリスト機能(gretel) 検索機能(複数ワード対応) 通知機能 コメント通知 フォロー通知 いいね通知 リアルタイム DM 機能(ActionCable) 未読件数表示機能 既読表示機能 カレンダー機能(SimpleCalendar ※モバイルの場合、表として表示) 予定追加機能 レスポンシブ対応 ハンバーガーメニュー等 使用技術 Ruby 2.6.5 Ruby on Rails 6.0.0 MySQL 5.6 Nginx AWS VPC EC2 RDS Route53 ALB ACM RSpec Docker/Docker-compose CircleCi 自動テスト インフラ構成図 DB 設計 ポートフォリオの講評 ほとんど全ての自社開発企業様において一定の評価をいただきました。特に、ユーザに対して実際にヒアリングを行った点や、プログラミングスクールでは扱わないような技術(Docker、AWS、CircleCI )を導入したことが高く評価されました。また市場分析までする人はほとんどいないので、そうした点も評価していただけました。 個人的には他との差別化のためにできる限り類似サービスがないものを選択したことも良かった点だったと思います。よくあるようなサービス(例えば、旅系アプリ、写真系アプリ、口コミ系アプリ)は採用担当者様は見飽きているので、目が止まるように斬新なテーマかつわかりやすいテーマにするといいのではないかと思います。とはいえ、業務関連の専門性の高いアプリもアピールしにくいと思いますので、テーマ決めは慎重にするべきかなと思います。 なお、技術力自体はさほど評価はされていません。見ての通りポートフォリオ自体はめちゃめちゃレベル高いわけでもないし、そもそもポテンシャル採用ですので。 テックキャンプについて 結論 最高です。世間で色々言われている節がありますが、ここで一蹴させてください。本気でエンジニアを目指し準備してきた人にとっては最高の環境が用意されています。おそらく世間で色々言われているのは、テックキャンプ以上の実力をもともと持っていたか、逆に全てをテックキャンプ任せにし、なんの戦略もないまま退職した人なのではないかと思います。 以下、細かく講評していきたいと思います。 カリキュラム 拍手喝采です。独学の時、なんとなーくの理解だったものが、なぜ動くのかの裏側まで理解できるようになりました。また、実際に作りながら学ぶのでとても楽しくできます。さらに、自身の努力でどんどんカリキュラムを進めることができるので、モチベーションを高く保つことができました。なお、決して高難易度のカリキュラムではないので、「簡単すぎる!」と思っても、最初14日間は無条件で返金してもらえるので安心です。 メンター 世間ではレベルが低いと言われていますが、そんなことはありません。質問に対してわかりませんという回答は一度もありませんでしたし、レスポンスもとても早いです。ただし、質問の仕方を注意しなければなりません。相手ももちろん人間ですのでわからないこともあります。自分なりに仮説を立てた上で、相手が「Yes or No」で答えられるような質問にするのがいいかと思います。いくらなんでも1から全部教えろは乱暴ですからね。 CA(キャリアアドバイザー) これは人によるみたいですが、私は形容し難いほど最高のCAさんだったので文句はありませんでした。自身の強み、弱み、そして転職理由など、私の魅力を最大限に相手に伝えられるように添削してくださいました。結果、書類選考率100%、面接突破率100%、テックキャンプ史上最速卒業(?)という記録を打ち出したのではないかと思います。 ちなみに、週何社応募が条件だとかSESしか求人がないという噂がありますが、希望を伝えれば全部自社開発の求人にすることも可能です。私はSESを紹介されたことはなかったです。噂は全くのデマです。 環境 一部教室利用制限ありであまり使えていませんでしたので、なんとも言えませんが、コロナ禍でもZoomを利用してチームメンバーとアウトプットする時間があるのでサボるということはありません。(というか、環境に極度に依存しないと勉強できない程度のやる気でエンジニア転職は辞めた方がいいです) とりあえず、勉強するのには十分な環境だったかなと思います。 オリアプ質問禁止について これは正直きついです。でも、質問禁止だからこそ価値があります。冷静に考えてみてください。某プログラミングスクールのようなオリアプも質問OKの環境で「ポートフォリオは自力でやりました」と面接の場で言われても説得力もないし、自分に自信もつかないですよね。私は、むしろ質問禁止だからこそテックキャンプを選びました。なお、どうしてもわからない時は「MENTA」とか使ってましたけど、そうした行動もプログラミングスクール任せよりも絶対評価されるはずです。 総合評価 まだ改善の余地はあるものの、私はエンジニア転職するには十分すぎるサービスと感じました。なぜ世間でこんなふうに言われているのかわかりませんね。 転職活動について 実績 以下は全て50人規模以下の自社開発企業様に応募した結果です。 フェーズ 社数 書類選考 10社(うち7社選考辞退) 1次・2次面接 3社 最終面接 3社 内定 1社(残り2社選考中) 求人媒体 まず転職活動では、GreenとWantedlyのみを使用しました。あとはテックキャンプ経由の求人です。結果として志望度の高い自己応募の企業様からすぐに内定をいただいたのでテックキャンプ経由の会社様は全て辞退しましたが、テックキャンプさんの求人もかなり魅力的でした。 履歴書・職務経歴書 テックキャンプでの転職サポートでCAさんが添削してくださいます。なのでここで、しっかりとブラシュアップしてました。ちなみに、自己応募ではテックキャンプであることは隠しも公表もせず、特に書いてませんでしたが、書類を求められた時はテックキャンプであることを明かしていました。最初はテックキャンプ卒は落とされるという心配がありましたが、そんなことはありませんでした。テックキャンプだろうとなかろうと、ポートフォリオや履歴書等がしっかり書いてあれば問題はありません。 面接対策 毎日声が枯れるまでやってました。よく聞かれる質問に対してすらすらと聞きやすく伝える努力は絶対に必要です。たまに暗記では意味ないと言う方がいますが、暗記すらできない人間の需要はありません(笑)。ちなみに目安としては、1日2時間半はこの練習に当てていました。おすすめ練習方法を以下に記載します。 自分の声を録音して、話したら聞くの繰り返し 一人でZoom開催+録画で練習して、自身の表情の確認をする ネットで「怖い面接官 顔」で検索してヒットした写真に向かって練習する 最近はWeb面接がかなり増えてきているので、Web面接ならではの声のボリューム調整などに苦労しましたが、一人Zoomであればかなり慣れてくるのでおすすめです。ちなみに、私はこの一人Zoomで自分の顔がちょっと左に傾くくせがありキモかったので強制しました(笑) 差別化戦略 この章では、圧倒的に市場に溢れている未経験エンジニアの人とどんな差別化を行ってきたか解説します。 独学で何かした経験 私は、エンジニアになる上では「自走力」があることの証明が必要と考え、とりあえず独学でポートフォリオを作るということをしました。中身は見せていませんが、そうした姿勢は一定の評価がもらえました。とりあえず、プログラミングスクールに行くのではなく、まず自身でやってみたと言う経験は語れるほうがいいです。 Qiita・Twitterで毎日発信。 毎日学んだことをアウトプットしてました。まじの毎日です。 結果、Qiitaの投稿数もかなり多くなりました。別にアピールするためにするわけではないですが、企業様からはかなり高い評価をいただけました。(求人アプリでもスカウトが増えました) 良質なポートフォリオ 自画自賛ではないですが、私のものは少なくとも未経験の方がパッと作れるようなポートフォリオではありません。それなりに時間を投下してつくったものですので、やはりここは評価点としてあったかなと思います。ちなみに、私の場合は以下を参考にしてポートフォリオ作りに励みました。 https://www.youtube.com/watch?v=N0yetny4Zco また、ポートフォリオのテーマに関する市場分析や開発の仕方などが、しっかりと工夫されていたことも評価点の一つであったのではないかと感じます。 スライド この記事にも載せていますが、スライドの準備は大事です。やはりREADMEでもいいのですが、スライドで個性あふれるかっこいい感じにした方が印象がいいですし、アプリケーション概要を知るにはこちらの方がシンプルで見やすいです。なので、履歴書には必ずスライドのURLを貼り付けておきましょう! これから未経験を目指す方へ 生意気に少し語らせてください! いま、未経験エンジニアの市場は溢れかえっています。そのため、納得のいく就活は本当に大変だと思います。それでも強い思いや、しっかりとした戦略があればきっとエンジニアになれます!!諦めずにぜひ頑張ってください!!
- 投稿日:2021-06-03T19:39:41+09:00
テックキャンプで未経験から自社開発企業へ内定をもらうまでのお話
はじめに 2020年11月から学習を開始して、内定をもらえるまでの道のりをこちらの記事でまとめていきます。できる限り皆様に有益な情報となるようにまとめましたので、よろしければご覧ください 章 内容 私のスペック 簡単な自己紹介を載せています 学習開始〜内定までに行ったこと 学習を始めてから行ったことの内容を時系列順にまとめています ポートフォリオについて ポートフォリオの詳細について機能紹介やインフラ構成図等を載せています テックキャンプについて テックキャンプについて詳細です。回し者ではない感想をご覧ください 転職活動について 転職活動の実績や内容をまとめています。 その他差別化戦略 私がしていた未経験エンジニアの差別化戦略をご紹介します。 私のスペック 旧帝大理系出身 社会人2年目(1年働いて退職) 前職が某鉄道会社の社内システム担当(エンジニアとは程遠い) 大学時代にプログラミング経験あり(C言語、Java) 特にインターン行ったとかの実績なし 研究での実績なし 大学時代の就活は落ちまくりで、靴舐めますよ精神でなんとか内定した雑魚大学生。 学習開始〜内定までに行ったこと 2020年11月 エンジニアになりたいと思い独学で学習開始 まずは、ProgateでHTML、CSS、Ruby、Railsを学ぶ この時期が一番辛いです(笑) だいたい業後に3時間くらいと休日に8時間くらいやってました! 2020年12月 Progateが終わり、「あれ、わんちゃんポートフォリオ作れるんじゃね?」と感じ作成。3週間かけて、ログイン機能、投稿機能、いいね機能を搭載したサービスをデプロイするも、ログインができずボタンの配置などがグチャグチャなゴミサービスが爆誕(笑) 再度、丁寧に学びたいと思いドットインストールでHTML、CSS、Javascriptを勉強する。 勉強時間がさらに増え、休日は10時間くらいやってました 2021年1月~3月上旬 もっと勉強したい+プログラミングスクールで体系的に学びたいと思い、当時所属していた企業を3月末に退職することを伝える。Twitterを使用して、転職成功された方に色々質問したりしてプログラミングスクールをテックキャンプにすると決める。(別途参照) また、Udemyの存在を知り、以下の講座で基本的なこと抑えまくった。 Git: もう怖くないGit!チーム開発で必要なGitを完全マスター(山浦先生) AWS:ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得(山浦先生) もう怖くないLinuxコマンド。手を動かしながらLinuxコマンドラインを5日間で身に付けよう(山岸先生) フルスタックエンジニアが教える 即戦力Railsエンジニア養成講座(小島先生) ゼロからはじめる Dockerによるアプリケーション実行環境構築(小島先生) 【JavaScript&CSS】ガチで学びたい人のためのWEB開発徹底実践(フロントエンド編)(CodeMafia先生) 米シリコンバレーDevOps監修!超Docker完全入門(2020)【優しい図解説とハンズオンLab付き】(CS Career Kaizen先生) 動画閲覧きつくて進まないこともありましたが、とりあえず全部見切りました!!! 理解は7割程度で、実装は1ミリもできません。あくまで理解のみ。 2021年3月22日~4月上旬 テックキャンプ98期として入学。サービスの作り方の一連の流れを学習。10週間かかるカリキュラムを17日で終わらす。 (テックキャンプについては別途参照) 独学で詰め込んだ知識を整理しました! 2021年4月中旬~5月22日 1ヶ月半かけて、じっくりポートフォリオを作成。 行ったことは以下記載。 いくつかある社会課題の中からアプリケーションで解決できそうなものをピックアップして市場調査を行い、テーマを決める。 そのあと、要件定義やペルソナにあたる友人へヒアリングを行い、必須機能の選定を行う。 実装・テスト・リリース。 (ポートフォリオについては別途参照) 2021年5月22日〜31日 履歴書・職務経歴書を史上最高レベルに持っていくことを行う。また、テックキャンプの「よく聞かれる質問リスト」を参考に、面接対策を死ぬほどする。この際喉が死んだ。 2021年6月〜 GreenとWantedlyのみで就活開始。 約1週間ちょいで急成長スタートアップの自社開発に内定!!! (書類選考等は100%通過、面接も辞退を除けば100%通過、自慢ながら落ちませんでした笑) ポートフォリオについて ポートフォリオの概要 ポートフォリオの概要まとめスライドをみた方が早いです! 以下はサイト内のGIFです。参考までに、、、 一応使用技術等についても以下に載せておきます。 機能一覧 ユーザー登録、ログイン機能(devise) ゲストログイン機能 ユーザ編集機能 練習内容投稿機能 複数枚画像投稿機能 複数タグ付機能 インクリメンタルサーチ機能(Ajax) 文字数カウント機能(Ajax) 画像プレビュー機能 投稿編集機能 投稿削除機能 スライドショー機能(Slick) 試合募集機能 試合削除機能 試合申し込み機能 いいね機能(Ajax) いいね数ランキング機能 自己いいね一覧機能 いいね数表示機能 コメント機能(Ajax) コメント数表示機能 フォロー機能(Ajax) フォロー一覧機能 フォロワー一覧機能 ページネーション機能(kaminari) パンくずリスト機能(gretel) 検索機能(複数ワード対応) 通知機能 コメント通知 フォロー通知 いいね通知 リアルタイム DM 機能(ActionCable) 未読件数表示機能 既読表示機能 カレンダー機能(SimpleCalendar ※モバイルの場合、表として表示) 予定追加機能 レスポンシブ対応 ハンバーガーメニュー等 使用技術 Ruby 2.6.5 Ruby on Rails 6.0.0 MySQL 5.6 Nginx AWS VPC EC2 RDS Route53 ALB ACM RSpec Docker/Docker-compose CircleCi 自動テスト インフラ構成図 DB 設計 ポートフォリオの講評 ほとんど全ての自社開発企業様において一定の評価をいただきました。特に、ユーザに対して実際にヒアリングを行った点や、プログラミングスクールでは扱わないような技術(Docker、AWS、CircleCI )を導入したことが高く評価されました。また市場分析までする人はほとんどいないので、そうした点も評価していただけました。 個人的には他との差別化のためにできる限り類似サービスがないものを選択したことも良かった点だったと思います。よくあるようなサービス(例えば、旅系アプリ、写真系アプリ、口コミ系アプリ)は採用担当者様は見飽きているので、目が止まるように斬新なテーマかつわかりやすいテーマにするといいのではないかと思います。とはいえ、業務関連の専門性の高いアプリもアピールしにくいと思いますので、テーマ決めは慎重にするべきかなと思います。 なお、技術力自体はさほど評価はされていません。見ての通りポートフォリオ自体はめちゃめちゃレベル高いわけでもないし、そもそもポテンシャル採用ですので。 テックキャンプについて 結論 最高です。世間で色々言われている節がありますが、ここで一蹴させてください。本気でエンジニアを目指し準備してきた人にとっては最高の環境が用意されています。おそらく世間で色々言われているのは、テックキャンプ以上の実力をもともと持っていたか、逆に全てをテックキャンプ任せにし、なんの戦略もないまま退職した人なのではないかと思います。 以下、細かく講評していきたいと思います。 カリキュラム 拍手喝采です。独学の時、なんとなーくの理解だったものが、なぜ動くのかの裏側まで理解できるようになりました。また、実際に作りながら学ぶのでとても楽しくできます。さらに、自身の努力でどんどんカリキュラムを進めることができるので、モチベーションを高く保つことができました。なお、決して高難易度のカリキュラムではないので、「簡単すぎる!」と思っても、最初14日間は無条件で返金してもらえるので安心です。 メンター 世間ではレベルが低いと言われていますが、そんなことはありません。質問に対してわかりませんという回答は一度もありませんでしたし、レスポンスもとても早いです。ただし、質問の仕方を注意しなければなりません。相手ももちろん人間ですのでわからないこともあります。自分なりに仮説を立てた上で、相手が「Yes or No」で答えられるような質問にするのがいいかと思います。いくらなんでも1から全部教えろは乱暴ですからね。 CA(キャリアアドバイザー) これは人によるみたいですが、私は形容し難いほど最高のCAさんだったので文句はありませんでした。自身の強み、弱み、そして転職理由など、私の魅力を最大限に相手に伝えられるように添削してくださいました。結果、書類選考率100%、面接突破率100%、テックキャンプ史上最速卒業(?)という記録を打ち出したのではないかと思います。 ちなみに、週何社応募が条件だとかSESしか求人がないという噂がありますが、希望を伝えれば全部自社開発の求人にすることも可能です。私はSESを紹介されたことはなかったです。噂は全くのデマです。 環境 一部教室利用制限ありであまり使えていませんでしたので、なんとも言えませんが、コロナ禍でもZoomを利用してチームメンバーとアウトプットする時間があるのでサボるということはありません。(というか、環境に極度に依存しないと勉強できない程度のやる気でエンジニア転職は辞めた方がいいです) とりあえず、勉強するのには十分な環境だったかなと思います。 オリアプ質問禁止について これは正直きついです。でも、質問禁止だからこそ価値があります。冷静に考えてみてください。某プログラミングスクールのようなオリアプも質問OKの環境で「ポートフォリオは自力でやりました」と面接の場で言われても説得力もないし、自分に自信もつかないですよね。私は、むしろ質問禁止だからこそテックキャンプを選びました。なお、どうしてもわからない時は「MENTA」とか使ってましたけど、そうした行動もプログラミングスクール任せよりも絶対評価されるはずです。 総合評価 まだ改善の余地はあるものの、私はエンジニア転職するには十分すぎるサービスと感じました。なぜ世間でこんなふうに言われているのかわかりませんね。 転職活動について 実績 以下は全て50人規模以下の自社開発企業様に応募した結果です。 Wantedly等の返答率100%、書類選考突破率100%、面接突破率100%という結果でした。 内定のでた2社のうち1社は倍率240倍でした。また、もう1社はテックキャンプなどの未経験は特に欲しいというわけではないのに採用してくださいまいした(元々未経験可の求人ではなかったです) フェーズ 社数 書類選考 10社(うち7社選考辞退) 1次・2次面接 3社 最終面接 3社 内定 2社(残り1社結果待ち) 求人媒体 まず転職活動では、GreenとWantedlyのみを使用しました。あとはテックキャンプ経由の求人です。結果として志望度の高い自己応募の企業様からすぐに内定をいただいたのでテックキャンプ経由の会社様は全て辞退しましたが、テックキャンプさんの求人もかなり魅力的でした。 履歴書・職務経歴書 テックキャンプでの転職サポートでCAさんが添削してくださいます。なのでここで、しっかりとブラシュアップしてました。ちなみに、自己応募ではテックキャンプであることは隠しも公表もせず、特に書いてませんでしたが、書類を求められた時はテックキャンプであることを明かしていました。最初はテックキャンプ卒は落とされるという心配がありましたが、そんなことはありませんでした。テックキャンプだろうとなかろうと、ポートフォリオや履歴書等がしっかり書いてあれば問題はありません。 面接対策 毎日声が枯れるまでやってました。よく聞かれる質問に対してすらすらと聞きやすく伝える努力は絶対に必要です。たまに暗記では意味ないと言う方がいますが、暗記すらできない人間の需要はありません(笑)。ちなみに目安としては、1日2時間半はこの練習に当てていました。おすすめ練習方法を以下に記載します。 自分の声を録音して、話したら聞くの繰り返し 一人でZoom開催+録画で練習して、自身の表情の確認をする ネットで「怖い面接官 顔」で検索してヒットした写真に向かって練習する 最近はWeb面接がかなり増えてきているので、Web面接ならではの声のボリューム調整などに苦労しましたが、一人Zoomであればかなり慣れてくるのでおすすめです。ちなみに、私はこの一人Zoomで自分の顔がちょっと左に傾くくせがありキモかったので強制しました(笑) 差別化戦略 この章では、圧倒的に市場に溢れている未経験エンジニアの人とどんな差別化を行ってきたか解説します。 独学で何かした経験 私は、エンジニアになる上では「自走力」があることの証明が必要と考え、とりあえず独学でポートフォリオを作るということをしました。中身は見せていませんが、そうした姿勢は一定の評価がもらえました。とりあえず、プログラミングスクールに行くのではなく、まず自身でやってみたと言う経験は語れるほうがいいです。「なんでリスク負ってまで仕事辞めて勉強したの」と言う質問に対して「いえ、元々独学でやっていてプログラミングスクールに行くために〜〜」という回答もしやすいですしね。 Qiita・Twitterで毎日発信。 毎日学んだことをアウトプットしてました。まじの毎日です。 結果、Qiitaの投稿数もかなり多くなりました。別にアピールするためにするわけではないですが、企業様からはかなり高い評価をいただけました。(求人アプリでもスカウトが増えました)ここまでやる人はいないので、絶対評価につながります! 良質なポートフォリオ 自画自賛ではないですが、私のものは少なくとも未経験の方がパッと作れるようなポートフォリオではありません。それなりに時間を投下してつくったものですので、やはりここは評価点としてあったかなと思います。ちなみに、私の場合は以下を参考にしてポートフォリオ作りに励みました。 https://www.youtube.com/watch?v=N0yetny4Zco また、ポートフォリオのテーマに関する市場分析や開発の仕方などが、しっかりと工夫されていたことも評価点の一つであったのではないかと感じます。 ここらへんはスライド等を参考にしてもらえるといいのではないかと思います。 スライド この記事にも載せていますが、スライドの準備は大事です。やはりREADMEでもいいのですが、スライドで個性あふれるかっこいい感じにした方が印象がいいですし、アプリケーション概要を知るにはこちらの方がシンプルで見やすいです。なので、履歴書には必ずスライドのURLを貼り付けておきましょう! これから未経験を目指す方へ 生意気に少し語らせてください! いま、未経験エンジニアの市場は溢れかえっています。そのため、「未経験からエンジニアはまじでやめとけ!!」みたいなことを言う方もいます。私としてもそれは賛成です。半端な覚悟でエンジニア転職はやめるべきです。しかし、それでも私はエンジニアなってこうなるんだ!世の中をこうするんだ!という強い気持ちがあればエンジニア転職は超簡単です。正しい情報と正しい勉強順序を知りさえすれば大丈夫です。この記事通りやれば、技術的なことを理由に落とされることはまずありません(ただし、退職理由や前職実績との兼ね合いはありますが)。まだまだ駆け出しの私ですが、この記事をきっかけに誰かの人生が大きく変わることを願っております!!! 余談 ちなみに地味に頑張っていたのが、寿司打です笑 もともと5000円コースが4800円くらいでかなりギリギリいかないくらいでしたが、今では1万円コースで1万5千円を打ち出せるようになりました。もちろん多少のブラインドタッチとやらもできます笑 連絡先 もしもっとお話しを聞きたい等ありましたらこちらにご連絡ください! ツイッター https://twitter.com/dn3wrHrwK7NktOt おわりに なにかご質問等あればコメントください! また、頑張ったのでいいねください笑
- 投稿日:2021-06-03T17:29:21+09:00
[Ruby on Rails] ログインしていないユーザーが「1度だけ」いいねを押せる機能の実装
はじめに 今回ポートフォリオ制作で、 日本各地の名所を投稿できるサイトを制作しました。 実際に製作したサイトと、コード(GitHub)は下記のURLからご覧ください。 ・サイトURL : https://japansiteinfo.com (今後予告なく公開停止する場合があります。ご了承ください。) ・GitHubのURL : https://github.com/yuta-pharmacy2359/dwc_JapanSiteInfo_app その中で、特に「ログインしていないユーザーが「1度だけ」いいねを押せる機能」で苦戦しましたので、 今回ご紹介させていただきたいと思います。 本題 1.背景 投稿サイトのみならず、Webアプリケーションではもはや欠かせない機能となっている「いいね機能」。 「ログインユーザーがいいねを押す機能」であればネットで沢山の検索結果が出てくると思います。 通常、投稿ID: 1 の投稿にいいねを押した場合、 その情報はDBに以下のように登録されます。 いいねID 投稿ID (いいねを押した)ユーザーID 1 1 2 2 1 3 3 1 5 4 1 nil ログインユーザーの場合、いいねID: 1〜3 のように、 「投稿IDとユーザーID」を照合させることで、いいねが押されているかどうかの判定ができます。 (その判定によって「いいねを押すボタン」と「いいねを取り消すボタン」を切り替えるという仕組みです。) しかし、ログインしていないユーザー(以後「ゲスト」)の場合は、ユーザーIDを持たないため、 いいねID: 4 のようにユーザーIDが「nil」となり、 そのままではそのブラウザでいいねが押されたかどうかの判定ができません。 (そのため、1つの投稿に対して無限回いいねが押せてしまいます。) ゲストがいいねを押した情報を保存する方法はいくつかあるかと思いますが、 今回は「cookie」にいいねを押した投稿IDの情報を持たせ、ブラウザ上に保存する方法を紹介します。 2.cookieについて ステートレスなプロトコルであるHTTPにおいて、状態を保持し管理する仕組みの一つがcookieです。 Webサーバーへ接続してきたWebブラウザに対して、 コンテンツとともにWebブラウザに保存してもらいたい情報(今回の場合はいいねを押した情報)がcookieとして送られます。 cookieを受け取ったWebブラウザはそれを保存しておき、次にWebサーバーに接続する際にcookieを送信することで、 サーバーが接続してきたブラウザを認識し、情報を反映させることができます。 以下、上記の説明を図にしたものです。パワポで作成したものですが、 是非参照いただければと思います。 使用画像サイト: フレームぽけっと(https://www.illust-pocket.com/illust/625) エコのモト(https://economoto.org/illust/2929/) イラストAC(https://www.ac-illust.com/main/search_result.php?word=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B5%E3%83%BC%E3%83%90) 参考文献: 「この一冊で全部わかる Web技術の基本(SB Creative出版)」 3.開発環境・前提条件など ◎開発環境 ・Ruby: 2.6.3 ・Rails: 5.2.3 ◎前提条件 ・「Userモデル」 ・「Spotモデル(投稿のモデルです)」 ・「Favoriteモデル(ログインユーザーの部分)」 以上3つのモデルについて実装済みである想定で話を進めたいと思います。 (ゲストとの比較の関係上、ログインユーザー向けのコードも記載していますが、 詳細な説明は省略させていただいております。) 4.実装 favoriteモデル app/models/favorite.rb class Favorite < ApplicationRecord belongs_to :user, optional: true belongs_to :spot end 解説 1.の項で「ログインしていないユーザーがいいねを押すとuser_id: nilのレコードが挿入される」 と述べました。さらにこの後favoritesコントローラーにuser_idがnilのレコードを挿入する文を記述しますが、 その場合は上記のようにuserモデルのbelongs_toの記述のところで optional: true(外部キーとして挿入される値としてnilも許容する)を加える必要があります。 favoritesコントローラー app/controllers/favorites_controller.rb class FavoritesController < ApplicationController def create @spot = Spot.find(params[:spot_id]) if current_user.nil? # ログインしていない場合(ゲスト) if cookies[:favorite_spot_id].nil? #cookieがブラウザに保存されていない場合 cookies.permanent[:favorite_spot_id] = @spot.id.to_s else #cookieがブラウザに保存されている場合 cookies.permanent[:favorite_spot_id] = cookies.permanent[:favorite_spot_id] + "," + @spot.id.to_s end Favorite.create(user_id: nil, spot_id: @spot.id) # 「user_idがnil」のレコードを追加 @favorites_count = @spot.favorites.count #いいね数の表示 @cookies = cookies[:favorite_spot_id] #インスタンス変数化 else # ログインしている場合(詳しい説明は省略) @favorite = current_user.favorites.new(spot_id: @spot.id) @favorite.save @favorites_count = @spot.favorites.count end # (省略) end def destroy @spot = Spot.find(params[:spot_id]) if current_user.nil? # (今回、ゲストのいいね取消し機能は実装しませんでした。) else # ログインしている場合(説明は省略) @favorite = current_user.favorites.find_by(spot_id: @spot.id) @favorite.destroy end # (省略) end end 解説 ◎createアクション ログインユーザーかゲストかで分岐させた後、cookieが該当のブラウザに保存されているかどうかで分岐しています。 ◯cookieが保存されていない(当該ブラウザでどの投稿にもいいねが押されていない)場合 永続化(permanent、とはいっても実際は20年間ですが)cookieに対象のspot_idを文字列化して代入します。 (数値のまま代入してしまうと上手くいきません。) 例えば、spot_id: 5 の投稿にいいねが押された場合は、 cookies.permanent[:favorite_spot_id] = "5" となります。 ◯cookieが保存されている(当該ブラウザで既にいずれかの投稿にいいねが押されている)場合 既に保存されているcookieに対し、新たなspot_idをカンマをつけて付け足します。 例えば、先程のspot_id: 5 の投稿に続きspot_id: 7 および10の投稿にいいねが押された場合は、 cookies.permanent[:favorite_spot_id] = "5,7,10" 上記の通り、いいねを押されたspot_idがあたかも配列のごとく並ぶのがわかると思います。 そして、このあと解説するviewファイルのところでこの「カンマをつけて配列のごとく並べる」ことが重要となります。 これでブラウザにいいねを押した情報がcookieとして保存されますが、 これだけでは当然DBにその情報が保存されないため、以下のコードで同時に「user_idがnil」のレコードが追加されるようにします。 Favorite.create(user_id: nil, spot_id: @spot.id) ◎destroyアクション こちらもログインユーザー同様、ゲストも「再度ボタンを押せばいいねが取り消される」という仕様にしたかったのですが、 そのためには、 「保存したcookieの中に当該のspot_idがある場合はそのidをカンマとともに削除し、 DBにおいても当該のspot_idかつuser_idがnilであるレコードを1つ削除する」 という内容を実装する必要があり、非常に難しくなることが予想されたため、 誠に勝手ながら今回は一旦保留とさせていただきました。 (もちろん、機会があればチャレンジしてみたいと思いますが) なお、上記の機能を実装するしないにかかわらず、 ゲストユーザーの場合はelse以下の文が成立しない(「current_user? なにそれ美味しいの?」状態)ので、 ログインしているかどうかの分岐は必要となります。 viewファイル ※各viewファイル(spotやuserの詳細、一覧画面など)における部分テンプレートとしているため、インスタンス変数の@は省略しています。 (今回の場合は@spot, @cookieです。) app/views/favorites/_list_favorite.html.erb <% if current_user.present? %> # ログインユーザー <% if spot.favorited_by?(current_user) %> # 当該のspot_idの投稿にいいねを押している場合 <%= link_to spot_favorites_path(spot), method: :delete, remote: true do %> <span class="fas fa-heart unlike" id="unlike-<%= spot.id %>"></span> <% end %> <% else %> # 当該のspot_idの投稿にいいねを押していない場合 <%= link_to spot_favorites_path(spot), method: :post, remote: true do %> <span class="far fa-heart like" id="like-<%= spot.id %>"></span> <% end %> <% end %> <% else %> # ゲスト <% if cookies.nil? %> # cookieが保存されていない(対象のブラウザでどの投稿にもいいねが押されていない)場合 <%= link_to spot_favorites_path(spot), method: :post, remote: true do %> <span class="far fa-heart like" id="like-<%= spot.id %>"></span> <% end %> <% else %> # cookieが保存されている(対象のブラウザでいずれかの投稿にいいねが押されている)場合 <% arr = cookies.split(",").map(&:to_i) %> <% if arr.include?(spot.id) %> # 保存されたcookieの中に当該のspot_idが含まれている(いいねが押されている)場合 <span class="fas fa-heart unlike" id="unlike-<%= spot.id %>"></span> <% else %> # 保存されたcookieの中に当該のspot_idが含まれていない(いいねが押されていない)場合 <%= link_to spot_favorites_path(spot), method: :post, remote: true do %> <span class="far fa-heart like" id="like-<%= spot.id %>"></span> <% end %> <% end %> <% end %> <% end %> 解説 まずログインユーザーとゲストで条件分岐するところはコントローラーと一緒です。 そして、ゲストの中でさらに「cookieがブラウザに保存されているか」で分岐します。 この後の、if arr.include?(spot.id)の行で 「保存されたcookieの中に当該のspot_idが含まれているか(つまり、いいねが押されているか)」 によりさらに分岐することになりますが、cookieが保存されていない場合、 この「保存されたcookie」という前提条件が成立せずエラーとなってしまいます。 そのため、まず「cookieがブラウザに保存されているか」で分岐させることが必要となります。 まず、「cookieがブラウザに保存されていない」場合ですが、 これは「どの投稿にもいいねが押されていない状態」と同じですので、 「いいねを押す」ボタン(灰色)を表示します。 次に、「cookieがブラウザに保存されている」場合ですが、上記の通り、 「保存されたcookieの中に当該のspot_idが含まれているか」でさらに分岐させます。 <% arr = cookies.split(",").map(&:to_i) %> 続いて、上記の行に関して一つずつ説明します。 まずsplitは「引数で指定した区切り文字(今回の場合は",")で文字列を分割し、配列として返す」メソッドです。 そのため、先程のfavoriteコントローラーの項で挙げた例で説明すると、 @cookies = cookies.permanent[:favorite_spot_id] @cookies = "5,7,10" ↓ @cookies.split(",") = ["5", "7", "10"] となるわけです。 続いてmapは「配列の要素の数だけ繰り返し処理を行う」メソッドです。 そして、map(&:to_i)に関してですが、詳しい説明に関しては以下の参考文献をご覧いただきたく思います。 https://qiita.com/snyt45/items/7beb719ab0c4a25aa585 とりあえず今回は「下の行のif文で当該spot_id(数値)が保存したcookie中に含まれているかどうかを調べるために、 文字列の配列を数値の配列に変換した」理解していただければOKです。 @cookies = cookies.permanent[:favorite_spot_id] @cookies.split(",") = ["5", "7", "10"] ↓ @cookies.split(",").map(&:to_i) = [5, 7, 10] こうして次のif arr.include?(spot.id)の文で、 得られたspot_idの配列の中に当該spot_idが含まれているかどうかを 判断し、表示するいいねボタンの種類を分けているというわけです。 最後に いかがだったでしょうか。 初めての投稿のため、一部読みづらい部分もあったかと思いますが、 参考にしていただけますと幸いです。 また、ゲストのいいね削除機能を含め、その他の機能についても気になるところを見つけ次第記事にしたいと思います。
- 投稿日:2021-06-03T17:28:13+09:00
【Rails】データ保存時にrollbackされて原因がわからない時
Railsで開発している時に、データ保存をしたはずなのに、rollebackされてその原因が分からないことがあるかと思います。 私の場合、createメソッドでsaveしている状況ですが、パラメータは正しく渡っており、エラーメッセージも表示されていないのにデータベースには保存されていませんでした。 結論としては、save!と入力することでエラーの原因が分かります。 やり方としては、現在のcreateアクションに記載されているsaveに!を付けるだけです。 item_controller.rb def create @item = Item.new(items_params) if @item.save flash[:success] = "商品を登録しました" redirect_to items_path else flash.now[:danger] = "登録に失敗しました" render("admin/items/new") end end 上記は新規商品を登録するための記述です。このsaveの部分をsave!と変更すればOKです。 save!とした状態で実際に何か商品を登録しようとすると、ターミナル上にエラーメッセージが表示されます。 私の場合は、以下のメッセージが表示され、メーカー名が空白になっていたのでエラーになっていたと分かりました。 ターミナル ActiveRecord::RecordInvalid: Validation failed: Content can't be blank, Maker can't be blank エラーメッセージは表示されないのにsaveできない時は、save!としてみてエラー内容を表示させて原因を突き止めてみることが大切ですね。
- 投稿日:2021-06-03T15:34:47+09:00
M1macでのRubyonRails環境構築【初学者向け】
概要 今までWindwsPCでRialsを勉強していましたが、今回M1チップのMacbookAirを購入しました。 環境構築に大変苦慮したため、成功までの手順を残しておきます。 Qiitaの記事を参考に色々頑張りましたが、msgpackやnokogiriなどgem周りのエラーが頻出し、なかなかはうまくいきませんでした。 最終的に一度mac自体を初期化し、OSをアップデート後に以下URLの手順を参考にすることで、無事Rials sまで辿り着きました。 参考にしたサイト:https://github.com/osmszk/m1-mac-rails-install マシンスペック ・macOS:BigSur ver.11.4 ・チップ:Apple M1 目次 環境確認と事前準備 PATHの確認 ruby,rails,rbenvの確認 Command Line Toolsをインストール homebrewをインストール rbenvをインストール rubyをインストール railsをインストール railsコマンドの実行 まとめ 環境確認と事前準備 各アプリケーションのインストール前に、インストール予定のアプリケーションのインストール済み有無とPATHの確認をします。 (これをしないと後々手詰まった時の状況整理が面倒です) 【1】pathの確認 % echo $path /usr/local/bin /usr/bin /bin /usr/sbin /sbin 「PATH」とは、コマンドの実行時に参照するディレクトリのルーリングになります。 アプリケーション自体は複数ディレクトリにインストールすることも可能ですが、コマンド実行時はPATHで指定されたディレクトリ以下が参照されます。 【2】ruby,railsのバージョン確認 whichコマンドでインストール箇所、-vコマンドでバージョンを確認します。 macにはデフォルトでrubyがインストールされていますが、rails開発で利用するrubyは後ほど別にインストールしたものになります。 後ほどpathを編集した際に変更がなされているか確認するため、ここでwhichコマンドを実行した際の参照ディレクトリは覚えておきましょう。 (path変更後はwhichコマンドの実行結果がpath指定のディレクトリになります) % which ruby /usr/bin/ruby % ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.arm64e-darwin20] % which rails /usr/bin/rails % rails -v Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command. 既にrebnvがインストールされていないことも確認しておきます。 % rbenv -v zsh: command not found: rbenv 【3】Command Line Toolsをインストール Command Line Toolsとは Command Line Toolsは、macOSのターミナルでコマンド使うために必要なツールになります。Xcodeというエディタアプリに含まれているため、Appstoreからまとめてインストールすることも可能ですが、Xcode自体はメインのエディタとしては使うことがなく容量も大きいため、ここではCommand Line Tools単体でインストールします。 インストール方法 % xcode-select --install xcode-select: note: install requested for command line developer tools Xcodeをインストールしている人は Xcode自体に「Command Line Tools」が含まれているのでスキップしましょう。 homebrewをインストール 【1】homebrewとは Homebrewはパッケージマネージャー(パッケージ管理システム)といい、macOS上でCUIを用いて必要なアプリケーションがインストールできるようになります。この後使うrbenvもhomebrewを使ってインストールすることになります。 詳細についてはこちらの記事が参考になります。 【2】インストール方法 https://brew.sh/ にアクセスする アクセス後は「Install Homebrew」と記載された直下のコマンドをコピーして実行します。 他記事ではコマンドが記載されているケースが多いですが、公式HPのインストール用コマンドを利用するのが確実です。 インストール後はbrewコマンドが有効かどうか確認するため、-vでバージョン、whichで参照先を確認しておきましょう。 ※brewのバージョンが古い場合はアップデートも同時に行います。 % brew -v Homebrew 3.1.9 (% brew update) % which brew /opt/homebrew/bin/brew rbenvをインストール 【1】rbenvとは railsにて開発を続けていくと、「このアプリはこのrubyバージョンで、このアプリはこのrubyバージョンで...」とプロジェクト毎にrubyのバージョンを変えたいケースも出てくるようです。 rbenvは、複数のRubyのバージョンを管理し、プロジェクトごとにrubyのバージョンを指定して使うことを可能としてくれるツールです。 【2】インストール方法 先ほどインストールしたhomebrewを使ってインストールします。 % brew install ruby-build rbenv インストール後はbrewコマンドが有効かどうか確認するため、-vでバージョンを確認しておきましょう。 % rbenv -v rbenv 1.1.2 【3】パスを通す 無事インストールが確認できたらpathを通します。 pathの設定時は.zshrcファイルを編集します。この「.zshrc」というファイルは”コマンド入力について設定している”ファイルになります。 zshrcファイルについてはこちらの記事が分かりやすいので参考にしてみてください。 コマンドで追加する方法もありますが、現在記述されている内容を見ながら編集するためにvimから直接編集します。 vimの編集方法についてこちらの記事を参考にしました。 vimを開いた直後はenterでの改行や自由なコピペができないため、手こずる場合はescを押してからiを押し、インサートモードにした上での編集をオススメします。 また編集終了後は% source ~/.zshrcで更新することを忘れないでおきましょう。 % vi ~/.zshrc export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" % source ~/.zshrc % cat .zshrc export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" rubyをインストール 【1】インストール方法 rbenvでインストールできるrubyのバージョンを確認しましょう。 % rbenv install -l インストールできるrubyのバージョンを確認したら、その際の安定版バージョンをインストールしましょう。 安定板についてはruby公式ページを確認してください。 記事作成時の安定バージョンは2.7.3か3.0.1になるので、ここでは最新版である3.0.1をインストールしてみます。 % rbenv install 3.0.1 【2】インストールしたrubyバージョンの適応 インストールしたバージョンが追加されているか確認します。3.0.1が追加されていることを確認してください。 *がついている箇所が現在使用設定にあるrubyになります % rbenv versions * system (set by /Users/hanawakazuki/.rbenv/version) 3.0.1 3.0.1をglobalで利用できるようにします。 $ rbenv global 3.0.1 上記コマンドを実行後、rbenvで管理されたrubyに変更されます。(3.0.1に*が移動したことを確認してください) $ rbenv versions system * 3.0.1 (set by /Users/hanawakazuki/.rbenv/version) インストール後はrubyコマンドが有効かどうか確認するため、-vでバージョンを確認しておきましょう。 またwhichコマンドでrubyコマンドの先が.rbenv/下に変更されているか確認します。 % which ruby /Users/kazukihanawa/.rbenv/shims/ruby % which gem /Users/kazukihanawa/.rbenv/shims/gem railsをインストール 【1】インストール方法 % gem install rails % rbenv rehash インストール後はrailsコマンドが有効か確認するために、-vでバージョン、whichで参照先を確認しておきましょう。 % which rails /Users/kazukihanawa/.rbenv/shims/rails % rails -v Rails 6.1.3.2 【2】yarnのインストール Rails 6から webpackerが必須になったのでまずはyarnをインストールします。 % brew install yarn しつこいですが-vでバージョン、whichで参照先を確認しておきましょう。 % which yarn /opt/homebrew/bin/yarn % yarn -v 1.22.10 【3】rails コマンドの実行 rails newでプロジェクトを作成します。 rails new プロジェクト名 rails sでサーバーが立ち上げるか確認しましょう。 rails s 表示されたIPにアクセスし、ローカルサーバーで起動ができているか確認します。 その後はVScodeなどから編集可能です。 まとめ macでの環境開発は初めてだったためかなり手こずりました。 vimやzshrcの知識はおろか、ディレクトリ構造やruby自体の知識も足りていなかったため、エラーに悩まされ勉強し直す事になったのはいい機会だと思います。 躓いたときはwhichや-vを確認し、zshrcからpathの整合性を把握しながら進むことが重要だと学びました。 記事自体に不足、解釈の間違い等あれば是非ご指摘ください。宜しくお願いします。
- 投稿日:2021-06-03T14:37:41+09:00
【Rails】閲覧履歴の降順表示+定時削除を実装する
ポートフォリオ作成時に閲覧履歴機能を実装したのですが、色々詰まったところがあったので整理しておきたいと思います。 この記事では、以下をまとめていきます: 1. 閲覧履歴機能の実装(モデルに記述) 2. 閲覧履歴が降順で表示されるように調整(コントローラーとビューに記述) 3. 1ヶ月以上前の閲覧履歴が定時で削除されるようにする(バッチ処理) 前提 機能としては・・・ 複数のユーザー(会員登録・ログイン済)がレシピを投稿でき、自分や他者が投稿したレシピを閲覧できる ユーザーがログイン済の場合、閲覧履歴が記録され、マイページから自分の閲覧履歴を確認できる 閲覧履歴は降順で表示される(閲覧順:新→古) 1ヶ月経った閲覧履歴は定時削除される モデル & コントローラー 以上の機能を実装するために、以下の構成で進めていきます。 なお、PFでレシピ投稿サイトを作成したのでPostRecipeということにしていますが、ArticleやBookなど別の投稿内容に置き換えていただければと思います。 MODEL User(会員) History(閲覧履歴) PostRecipe(レシピ投稿) CONTROLLER post_recipes バッチ処理には、gemのwheneverを使用しschedule.rbに記述していきます。 Userモデルの作成及びユーザー認証にはdeviseを使用しています。 上記のモデル・コントローラーは作成済として進めていきます。 1. 閲覧履歴機能の実装 では、まず肝心の閲覧履歴機能から作っていきます。 なお実装にあたっては「Railsのcontrollerとmodelのみを使って記事閲覧履歴を作成する」の記事から、多分に勉強させていただきました。 こちらの記事ではコントローラーに記述されていたのですが、コントローラーがややファットになるのが気になったので、私はモデルの方に記述しています。コードの内容としてはほぼ同じです。ぜひ一度上記の記事を確認してください。 1-1. アソシエーション記述 まずは、3つのモデルのアソシエーションから記述していきます。 app/models/post_recipe.rb belongs_to :user has_many :histories, dependent: :destroy app/models/history.rb belongs_to :user belongs_to :post_recipe app/models/user.rb has_many :post_recipes, dependent: :destroy has_many :histories, dependent: :destroy 1-2. browsing_historyメソッド記述 次に、PostRecipeモデルに閲覧履歴を保存する処理を書いていきます。 今回メソッド名は、閲覧履歴を意味するbrowsing_historyにします。 ※コードの内容の解説は?の記事に事細かに説明されているので、ここでは割愛します。 app/models/post_recipe.rb belongs_to :user has_many :histories, dependent: :destroy def browsing_history(user) new_history = histories.new new_history.user_id = user.id # 同じ投稿をcurrent_userが閲覧している場合、古い履歴を削除 if user.histories.exists?(post_recipe_id: id) visited_history = user.histories.find_by(post_recipe_id: id) visited_history.destroy end new_history.save 1-3. post_recipesコントローラーで呼び出し ではbrowsing_historyメソッドを、コントローラーで呼び出します。 閲覧履歴が追加されるのは、ユーザーがレシピを閲覧した時なので、post_recipesコントローラーのshowアクションに記述します。 app/controllers/post_recipes_controller.rb def show @post_recipe = PostRecipe.find(params[:id]) if user_signed_in? @post_recipe.browsing_history(current_user) end end deviseのヘルパーメソッドuser_signed_in?を使って、ユーザーがログインしている場合は閲覧履歴を保存する処理を実行します。 2. 閲覧履歴を降順で表示 閲覧履歴を表示するのはログインユーザーのマイページなので、usersコントローラーのshowアクションとビューに記述を加えます。 2-1. usersコントローラーのshowアクションに記述 このコントローラーの記述が詰まったところで… 最初はこのように書いていました? なお、where(is_draft: false)は下書きでないレシピを表示するために加えています。 app/controllers/users_controller.rb #うまくいかなかった例 def show @browsed_posts = @user.browsed_posts.where(is_draft: false) end app/models/user.rb #うまくいかなかった例 has_many :histories, dependent: :destroy has_many :browsed_posts, through: :histories, source: :post_recipe ところがこれだと、当たり前ですが閲覧履歴が降順で表示されません。 orderメソッドでcreated_at: "DESC"を@browsed_postsの末尾に追記しても、レシピの投稿日時で降順に並び変わるだけで、閲覧履歴そのものが降順にはならない…。 閲覧履歴が古→新の順で表示されるのはだいぶ違和感があります。 さて、どうしよう… といろいろ試行錯誤して行き着いたのがこれでした? app/controllers/users_controller.rb def show @user = User.find(params[:id]) @browsed_posts = PostRecipe.joins(:histories).where(is_draft: false,'histories.user_id': @user.id).order('histories.created_at': "DESC") end joinsメソッドで、historiesテーブルをpost_recipesテーブルとを内部統合します。 これによって、閲覧履歴そのもののcreated_atが取得でき、これを降順に並び替えることができます。 joinsメソッドによるテーブル統合については「【Rails】 joinsメソッドのテーブル結合からネストまでの解説書」がわかりやすかったです。 joinsメソッドの引数には、テーブル名でなく、アソシエーションで定義した関連名が入ります。今回は、冒頭でモデルに記述した通り、userモデルとhistoryモデルは1対多(user has_many histories)なので、引数にはhistoriesが入ります。 (2-1のうまくいかなかった例で、models/user.rbにhas_many :browsed_posts, through ...(以下略)を定義してますが、これは結果的に不要だったので、モデルの記述はSTEP1のままです) 2-2. ビューの記述 ここまで来れば、ビューで閲覧履歴のレコードをeachで取り出してあげればよさそうですね。 app/views/users/show.html.erb <table> <% @browsed_posts.each do |browsed_post| %> <tr> <td><%= link_to (attachment_image_tag browsed_post, :recipe_image, :fill, 100, 100, format: 'jpg', size:'60x60', class:'rounded-circle'), post_recipe_path(browsed_post.id) %></td> <td><%= link_to browsed_post.title, post_recipe_path(browsed_post) %></td> <td><%= link_to browsed_post.user.name, user_path(browsed_post.user) %></td> </tr> <% end %> </table> 3 バッチ処理で一定期間以上前の閲覧履歴を自動削除 最後に、バッチ処理を実装していきます。 閲覧履歴はどんどん増えて蓄積されてしまうので、今回は1ヶ月以上前の閲覧履歴は削除されるようにしておきたいと思います。 3-1. whenever導入 ではまず、gemのwheneverをインストールします。 後ほどcronを使用するのですが、その設定を簡単に書けるようにしてくれるgemですね。 Gemfile gem 'whenever', require: false terminal $ bundle install 3-2. schedule.rbを生成し、バッチ処理を記述 まず、schedule.rbを生成します。 terminal $ wheneverize . 次に、schedule.rbを開いて、処理を記述します。 config/schedule.rb # Use this file to easily define all of your cron jobs. # # It's helpful, but not entirely necessary to understand cron before proceeding. # http://en.wikipedia.org/wiki/Cron # Example: env :PATH, ENV['PATH'] set :output, 'log/cron.log' set :environment, :development every 1.days, at: '0:00 am' do runner 'History.where("created_at < ?", 30.days.ago.beginning_of_day).delete_all' end ?毎日0時に、30日以上前にcreateされたhistoriesテーブルのレコードを全て削除します ちなみに、config/application.rbにconfig.time_zone = 'Tokyo'と記述することで時間を日本時間にしています。 3-3. cronにバッチ処理を反映する さて、wheneverでは、schedule.rbに記述した内容(バッチ処理)をcrontabに反映させることで実行される仕組みになっているので、crontabに反映させてあげましょう。 開発環境の場合 terminal $ bundle exec whenever --update-crontab [write] crontab file updated #このように表示されていれば成功 次に、crontabに反映されているか確認しておきます。 先ほどschedule.rbに記述した内容が反映されていれば、OKです。 terminal $ crontab -l 処理が正常に動いていれば、以下のようにlogファイルが生成されているはずです。 開発環境で正常に動くか確認してみましょう。 log/cron.log Running via Spring preloader in process xxxx Running via Spring preloader in process yyyy 確認できたら、開発環境の方では処理を停止しておきましょう? terminal $ bundle exec whenever --clear-crontab 本番環境の場合 本番環境で実行する場合は、まずschedule.rbのenvironmentをproductionに変更します? config/schedule.rb env :PATH, ENV['PATH'] set :output, 'log/cron.log' set :environment, :production #ここを変更 every 1.days, at: '0:00 am' do runner 'History.where("created_at < ?", 30.days.ago.beginning_of_day).delete_all' end さらに、Rails5以上の場合は、下記を追記します? この追記が必要な背景は「Rails5のproduction環境でlib/配下のクラス読込みがNameErrorになるのはautoloadが無効化されたからだった」に詳しく書かれているので、確認してみてください。 config/application.rb config.paths.add 'lib', eager_load: true 上記のファイルを変更した上で、EC2上で①のコマンドを実行します。 開発環境とはコマンドが違うので注意。 本番環境 # ①定時処理の内容更新 RAILS_ENV=production bundle exec whenever --update-crontab # ②定時処理の停止 RAILS_ENV=production bundle exec whenever --clear-crontab # ③cronのログ確認 RAILS_ENV=production crontab -l 以上です! お疲れさまでした。 参考資料 【Rails】wheneverを本番環境+Capistranoで使う 【Rails】 joinsメソッドのテーブル結合からネストまでの解説書 【Rails】バッチ処理(whenever)で、1日1回メールを自動配信してみる Railsのcontrollerとmodelのみを使って記事閲覧履歴を作成する
- 投稿日:2021-06-03T13:37:02+09:00
rails コメント機能実装
①コメントを記録するモデルを作成しよう rails g model post_comment comment:text user_id:integer book_id:integer ②マイグレートしてテーブルをデータベースに登録する。 rails db:migrate ③アソシエーションで関連付けを行う PostComment.rb class PostComment < ApplicationRecord belongs_to :user belongs_to :book end user.rb class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :active_relationships, class_name: 'Follow', foreign_key: 'user_id' has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_user_id' has_many :followings, through: :active_relationships, source: :target_user has_many :followers, through: :passive_relationships, source: :user has_many :books, dependent: :destroy # コメントの追加、記事の削除と同時に削除する has_many :post_comments, dependent: :destroy attachment :profile_image, destroy: false validates :name, length: {maximum: 20, minimum: 2}, uniqueness: true validates :introduction, length: {maximum: 50} # いいねしたかの判定 def already_favorited?(book) self.favorites.exists?(book_id: book.id) end end class Book < ApplicationRecord belongs_to :user # 多くの良いねを受け取る可能性がある、記事の削除と同時に削除する(いいね) has_many :favorites, dependent: :destroy # コメントの追加、記事の削除と同時に削除する has_many :post_comments, dependent: :destroy validates :title, presence: true validates :body, presence: true, length: {maximum: 200} end ④コントローラーを作成しアクションを書く PostCommentsController.rb class PostCommentsController < ApplicationController def create #本に対してのコメントのためどの本にコメントを書くか決めるためfindする book = Book.find(params[:book_id]) #現在のユーザーが新しい投稿をする(post_comment_paramsでコメントを許可されている) comment = current_user.post_comments.new(post_comment_params) #本のidとコメントのidは一緒 comment.book_id = book.id #コメントを保存する comment.save #投稿後詳細画面へ遷移する redirect_to book_path(book) end def destroy #コメントidとコメントの記述idを削除する PostComment.find_by(id: params[:id], book_id: params[:book_id]).destroy redirect_to book_path(params[:book_id]) end private def post_comment_params params.require(:post_comment).permit(:comment) end end ⑤ネストする routes.rb Rails.application.routes.draw do devise_for :users root 'homes#top' get 'home/about' => 'homes#about' resources :books do resources :post_comments, only: [:create, :destroy] end end ⑥viewに書く index.html.erb <%= link_to "#{ book.post_comments.count} コメント", book_path(book.id) %></td> ・コメント数がわかる 「0 コメント」という感じになる ・リンク先は、ブックのshowへ行く show.html.erb <td><div class="comments"> #@bookでidを取ってきてくれて、どの投稿にコメントが何個になっているかを表示してくれる。本のコメントの数を表示と覚えよう。 <p>コメント件数:<%= @book.post_comments.count %></p> <% @book.post_comments.each do |post_comment| %> <p><%= image_tag('no_image.jpg') %></p> <%= post_comment.user.name %> <%= post_comment.created_at.strftime('%Y/%m/%d') %><%= post_comment.comment %> <% if post_comment.user == current_user %> <div class="comment-delete"> #遷移してpost_commentのデストロイアクションを実行して、コメントのbook=idとコメントのレコードを削除する <%= link_to "削除", book_post_comment_path(post_comment.book, post_comment), method: :delete %> </div> <% end %> <% end %> </div> </td> </tr> </table> <div class=""> <%= form_with model:[@book,@post_comment], local: true do |f| %> <%= f.text_area :comment, size:"90x5" %> <%= f.submit "送信する" %> <% end %> </div>
- 投稿日:2021-06-03T12:57:05+09:00
いいね機能を入れよう rails中心
リレーション関係を確認しよう ①モデル(テーブルを作成しよう) rails g model favorite user_id:integer recipe_id:integer ②マイグレートしてテーブルをデータベースに登録する。 rails db:migrate ③アソシエーション(テーブル同士の関連付け) 上記の図の線を書いていこう favorite.rb class Favorite < ApplicationRecord #1人のユーザーに属している belongs_to :user #1人の投稿に属している belongs_to :book validates_uniqueness_of :book_id, scope: :user_id end class Book < ApplicationRecord belongs_to :user # 多くの良いねを受け取る可能性がある、記事の削除と同時に削除する(いいね) has_many :favorites, dependent: :destroy # コメントの追加、記事の削除と同時に削除する has_many :post_comments, dependent: :destroy validates :title, presence: true validates :body, presence: true, length: {maximum: 200} end class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :active_relationships, class_name: 'Follow', foreign_key: 'user_id' has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_user_id' has_many :followings, through: :active_relationships, source: :target_user has_many :followers, through: :passive_relationships, source: :user has_many :books, dependent: :destroy has_many :favorites, dependent: :destroy # コメントの追加、記事の削除と同時に削除する has_many :post_comments, dependent: :destroy attachment :profile_image, destroy: false validates :name, length: {maximum: 20, minimum: 2}, uniqueness: true validates :introduction, length: {maximum: 50} # いいねしたかの判定 def already_favorited?(book) self.favorites.exists?(book_id: book.id) end end コントローラーに記述する class FavoritesController < ApplicationController def create @favorite = current_user.favorites.create(book_id: params[:book_id]) # その場に遷移させる redirect_back(fallback_location: root_path) end def destroy #主キーに対してのfind @book = Book.find(params[:book_id]) #↓現在のユーザーについているいいねのデータを取ってくる 主キー以外のレコードでのfind_byになる @favorite = current_user.favorites.find_by(book_id:@book.id) @favorite.destroy redirect_back(fallback_location: root_path) end end @favorite = current_user.favorites.create(book_id: params[:book_id]) ・current_user.favorites.createの意味は現在のユーザーに基づいた良いねを作成するよという意味です。 ・(book_id: params[:book_id])これは良いねをした記事はどれなのかを取ってくるもの redirect_back(fallback_location: root_path) ・いいねをしたところのページにそのまま遷移するという意味 findのまとめ ・主キーに対応するレコードを取り出す ・主キーを複数指定することも可能 ・主キーが1つでも見つからない場合ActiveRecord::RecordNotFound例外が発生する ・findで取得したオブジェクトのクラスはBook find_byのまとめ ・与えられた条件にマッチするレコードのうち最初のレコードだけを返す ・条件を複数指定することができる ・与えられた条件がない場合nilを返す ・find_byで取得したオブジェクトのクラスはBook いいねをしたかしてないかの判定 user.rb #Bookというデータにいいねをした? def already_favorited?(book) #selfは現在のユーザーと同じ意味。現在のユーザーがいいねをした?というレコード(book.id)があるかどうかを確認している self.favorites.exists?(book_id: book.id) end ネストしよう routes.rb resources :books do resource :favorites, only: [:create, :destroy] end viewへ books.html.erb <table class="table table-hover table-inverse"> <thead> <tr> <th></th> <th>Title</th> <th>Opinion</th> <th colspan="3"></th> </tr> </thead> <% @books.each do |book| %> <tbody> <td><%= link_to user_path(book.user.id) do %> <%= attachment_image_tag book.user, :profile_image, :fill, 100, 100, format: 'jpeg', fallback: "no_image.jpg", size:'100x100' %> <% end %> </td> <td><%= link_to book.title,book_path(book,title:book.title)%></td> <td><%= book.body %></td> <td> #もし現在のユーザーが投稿にいいねをしていたら <% if current_user.already_favorited?(book) %> #book_favorites_path(book=book_id投稿したというレコードのこと)にリンク して、methodでfavoriteにあるdestoroyアクションを作動することができる。 <%= link_to book_favorites_path(book),method: :delete do %> <i class="far fa-thumbs-up"></i> <% end %> <% else %> <%= link_to book_favorites_path(book),method: :post do%> <i class="far fa-thumbs-up"></i> <% end %> <% end %> #いいねの数をカウントしてくれるメソッド.count <%= book.favorites.count%> </td> <td> <%= link_to "#{ book.post_comments.count} コメント", book_path(book.id) %></td> <% end %> </tbody> </table>
- 投稿日:2021-06-03T11:22:00+09:00
javaにrailsの変数を持たせるには
今回オリジナルアプリの作成で、railsの変数をjavaで使えないかと調べて実装できたのでここに記録。 結論から言うとgemを入れたら簡単に実装できた。 まず、使用するのはgonと言うgem gemfileに gem"gon"と記載 ターミナルにbundle install その後application.heml.erbのhead内に<%= include_gon %>と書くことでgonを読み取れるように 次に使用したいcontrollerの変数をjavaで使えるように @movie = Movie.find(params[:id]) gon.movie = @movie 変数@movieにgonを加える あとはjsで変数を使えるように 今回はアラートを用いてアラートの内容に変数を用いたいため以下の記述 alert(正解は[${gon.movie.phrase}]です);
- 投稿日:2021-06-03T11:16:09+09:00
AWSのEC2上に環境変数を設定する方法
環境 macOS: Big Sur Ver11.2.3 Rails: 6.1.1 Ruby: 2.6.5 インフラ:AWS(EC2) Webサーバ:Unicorn 困っていること AWSのEC2を使ってポートフォリオをデプロイし、さらにACMで証明書取得, Route53でドメイン取得後、SSL化まで行ったところ、Googleマップが表示されなくなった。 ブラウザのコンソールを確認すると「Google Maps JavaScript API error: InvalidKeyMapError」と表示されている。 したがって、APIキーをEC2上で設定できていないのではと推測。 解決方法 ①EC2にSSHで接続し、Vimを開く % cd .ssh % SSH接続のコマンド(ssh -i hoge.pem ec2-user@アプリのIPアドレス) 接続後、 [ec2-user@ip-172-31-0-160 ~]$ sudo vim /etc/environment ②環境変数を記述する 内容を変更するには「i」を押して編集モードにする。 変更し終わったら「esc」を押して編集モードを終了してから「:wq」で保存して終了すること。 export GOOGLE_MAPS_API_KEY='APIキーを貼り付け' ③設定できたか確認 一度EC2から抜けます。 [ec2-user@ip-172-31-0-160 ~]$ exit ログアウトしたらもう一度EC2にログインし、以下のコマンドを実行 [ec2-user@ip-172-31-0-160 ~]$ env | grep 環境変数の名前 これで設定した環境変数が表示されればきちんと設定できています。 ④Webサーバを一度ストップさせ、再起動させる 一度EC2を抜けて、ローカルのターミナル上でアプリケーションのフォルダに移動します。 以下のコマンドを実行し、Unicornをストップさせる。 % bundle exec cap -t production unicorn:stop 次に、もう一度EC2にSSH接続してログインし、アプリのフォルダに移動します。 Unicornの起動状況を確認するとmasterプロセスなどが起動されていないと思います。 [ec2-user@ip-172-31-0-160 take_out_app]$ ps aux | grep unicorn ec2-user 16164 0.0 0.0 119436 916 pts/0 S+ 03:05 0:00 grep --color=auto unicorn ここまで確認できたら、Unicornを再起動しましょう! [ec2-user@ip-172-31-0-160 take_out_app]$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D →特に何も表示はされませんが成功しています。 もう一度プロセスを確認! masterとworkerという名前のプロセスが実行され、再起動できているのがわかります。 [ec2-user@ip-172-31-0-160 take_out_app]$ ps aux | grep unicorn ec2-user 16196 1.0 12.6 486816 127348 ? Sl 03:08 0:01 unicorn_rails master -c config/unicorn.rb -E production -D ec2-user 16204 0.3 12.3 488336 123972 ? Sl 03:08 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D ec2-user 16216 0.0 0.0 119436 948 pts/0 S+ 03:10 0:00 grep --color=auto unicorn これでブラウザでアプリケーションにアクセスしてみると、無事にGoogleマップが表示されていました! おまけ(Vimから抜けるコマンドまとめ) ① :w → 作成・編集したファイルを保存 ② :q → vimをそのまま終了 ③ :q! → 編集した内容を保存しないでvimを終了 ④ :wq → 編集した内容を保存してvimを強制終了
- 投稿日:2021-06-03T07:05:38+09:00
micropostのカウント備忘録
解決したい問題 投稿したmicropostの数をカウントしたいがエラーが起きる。ちなみに、user.controllerでは正常に使えているよう。 コード 全てのコントローラでmicropostのカウントメソッドを使うことができるよう、application.controllerに書いている。 application.controller class ApplicationController < ActionController::Base include SessionsHelper private def require_user_logged_in unless logged_in? redirect_to login_url end end def counts(user) @count_microposts = user.microposts.count end end micropostのindexでmicropostカウントメソッドを使いたいのでindexメソッドにcounts(@user)を追加した。 micropost.controller class MicropostsController < ApplicationController before_action :require_user_logged_in before_action :correct_user, only: [:edit,:update,:destroy,:show] def index @microposts = current_user.microposts.order(id: :desc).page(params[:page]).per(10) counts(@user) end def update @micropost = Micropost.find(params[:id]) if @micropost.update(micropost_params) flash[:success] = '保存されました。' redirect_to @micropost else flash.now[:danger] = '保存されませんでした。' render :edit end end def edit @micropost = Micropost.find(params[:id]) end def show @micropost=Micropost.find(params[:id]) end def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = '記事を投稿しました。' redirect_to root_url else @microposts = current_user.microposts.order(id: :desc).page(params[:page]) flash.now[:danger] = '記事を投稿できませんでした。' render 'toppages/index' end end def destroy @micropost.destroy flash[:success] = '記事を削除しました。' redirect_back(fallback_location: microposts_path) end private def micropost_params params.require(:micropost).permit(:content,:title) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) unless @micropost redirect_to microposts_path end end end class MicropostsController < ApplicationController before_action :require_user_logged_in before_action :correct_user, only: [:edit,:update,:destroy,:show] def index @microposts = current_user.microposts.order(id: :desc).page(params[:page]).per(10) counts(@micropost) end def update @micropost = Micropost.find(params[:id]) if @micropost.update(micropost_params) flash[:success] = '保存されました。' redirect_to @micropost else flash.now[:danger] = '保存されませんでした。' render :edit end end def edit @micropost = Micropost.find(params[:id]) end def show @micropost=Micropost.find(params[:id]) end def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = '記事を投稿しました。' redirect_to root_url else @microposts = current_user.microposts.order(id: :desc).page(params[:page]) flash.now[:danger] = '記事を投稿できませんでした。' render 'toppages/index' end end def destroy @micropost.destroy flash[:success] = '記事を削除しました。' redirect_back(fallback_location: microposts_path) end private def micropost_params params.require(:micropost).permit(:content,:title) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) unless @micropost redirect_to microposts_path end end end microposts/index.html.erb <h3>記事一覧:<span class="badge badge-secondary"><%= @count_microposts %>件</span></h3> <%= render 'microposts/microposts', microposts: @microposts %> 解決した方法 application.controllerの @count_microposts = user.microposts.countを@count_microposts = current_user.microposts.countにしたところ解決した。
- 投稿日:2021-06-03T06:34:31+09:00
お気に入り登録を実装(非同期通信)
現在RubyとJavaScriptを用いて記事投稿サイトを作成しています。 今回は非同期通信のお気に入り登録を実装します。 お気に入り記事一覧ページの実装までを書いていきたいと思います。 ■仕様等 ・お気に入りアイコンをクリックで登録/解除 ・jQueryを使用しない ■完成後の動作 ■お気に入り機能 ①Userモデルにお気に入り操作に関するメソッドを記述 bookmarkモデル、コントローラーを作成した後、 お気に入り操作に関するメソッドをuserモデルに追記していきます。 ※bookmarksテーブルのカラムはuser_idとarticle_idです。 user.rb has_many :bookmarks_articles, through: :bookmarks, source: :article def bookmark(article) bookmarks_articles << article end def unbookmark(article) bookmarks_articles.delete(article) end def bookmark?(article) bookmarks_articles.include?(article) end 1行目 has_many :bookmarks_articles, through: :bookmarks, source: :article ユーザーがお気に入り登録した記事を全て取得します。 2行目〜4行目 bookmark(article) 引数に渡された記事情報を1行目のbookmarks_articlesに追加します。 6行目〜8行目 unbookmark(article) 引数に渡された記事情報を1行目のbookmarks_articlesから削除します。 10行目〜12行目 bookmark?(article) 引数に渡された記事情報が1行目のbookmarks_articlesに存在するかを判定します。 ②ルーティングの設定 routes.rb Rails.application.routes.draw do devise_for :users root to: "articles#index" resources :bookmarks, only: [:create, :destroy] end 3行目 resources :bookmarks, only: [:create, :destroy] お気に入りの登録と解除を実装するので、createとdestroyを指定します。 ③コントローラーの設定 bookmarks_controller.rb class BookmarksController < ApplicationController def create @article = Article.find(params[:article_id]) current_user.bookmark(@article) end def destroy @article = current_user.bookmarks.find_by(id: params[:id]).article current_user.unbookmark(@article) end end 4行目/9行目 current_user.bookmark(@article) current_user.unbookmark(@article) userモデルで設定した各メソッドの引数に、対象の記事情報を渡しています。 ④ビューの記述 index.html <% @articles.each do |article| %> <div class="article-content" id="article_<%= article.id %>"> <%= render 'shared/articles', article: article %> </div> <% end %> _articles.html <% if user_signed_in? %> <% if current_user.bookmark?(article) %> <%= render 'bookmarks/unbookmark', article: article %> <% else %> <%= render 'bookmarks/bookmark', article: article %> <% end %> <% end %> ■非同期処理の設定 _bookmark.html <%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %> <% end %> _unbookmark.html <%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %> <% end %> 各1行目 <%= link_to bookmarks_path(article_id: article.id), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :post, remote: true do %> <%= link_to bookmark_path(current_user.bookmarks.find_by(article_id: article.id)), class: "bookmark", id: "js-bookmark-button-for-article-#{article.id}", method: :delete, remote: true do %> 各末尾のオプションに注目してください。 remote:trueを設定することでJavaScript形式のリクエストが送信されます。 送信後、コントローラーのcreate/destroyアクションが実行されると、 create.js.erb/destroy.js.erbに遷移します。 create.js.erb document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>' destroy.js.erb document.getElementById('article_<%= @article.id %>').innerHTML = '<%= escape_javascript( render 'shared/articles', article: @article ) %>' 対象の記事を取得して、index.htmlにもあるrenderの部分を挿入しています。 escape_javascriptは改行のエスケープ処理を行います。 以上でお気に入り登録を実装することができました。 ■お気に入り一覧表示ページの実装 ①ルーティングの設定 routes.rb Rails.application.routes.draw do devise_for :users root to: "articles#index" resources :articles do collection do get :bookmarks end end resources :bookmarks, only: [:create, :destroy] end 4行目〜8行目 :articlesにネストする形でcollection do :bookmarks endを記述します。 ②コントローラーの設定 articles_controller.rb def bookmarks @bookmarks_articles = current_user.bookmarks_articles.includes(:user).order('created_at DESC') end current_user.bookmarks_articles 現在のユーザーの全てのお気に入り記事を取得しています。(冒頭のUserモデルにて設定) ③ビューを記述 bookmark.html.erb <% @bookmarks_articles.each do |article| %> <div class="article-content" id="article_<%= article.id %>"> <%= render 'shared/articles', article: article %> </div> <% end %> インスタンス変数を受け取って一覧を表示しています。 以上でお気に入り記事一覧表示ページの完成です。 参考記事 https://miiina01220.hatenablog.com/entry/2020/11/20/172739 https://techtechmedia.com/favorite-function-rails/ https://qiita.com/naberina/items/c6b5c8d7756cb882fb20
- 投稿日:2021-06-03T00:59:28+09:00
【備忘録】AWS Cloud9を用いたIDE環境構築
はじめに Ruby on Railsチュートリアルの環境構築編その1です。 AWS Cloud9を用いたIDE環境構築の流れをメモしておきます。 今回はAWSにログインし、チュートリアルを実行するためのワークスペースを作成するまでの手順を記録します。 大まかな流れ AWSのアカウントを作成し、ログインする。 アカウント作成時には、メールアドレスの他に、住所やクレジットカードの情報を入力する必要があります。 アカウント作成後、ルートユーザーでログインします。 チュートリアルを実行するワークスペースを作成する。 AWSマネジメントコンソールの検索窓に"Cloud9"と入力して検索、Cloud9のサービスを選択する。 Cloud9のトップページの右上の"Create environment"をクリックする。 以下の箇所を変更する。各stepで該当箇所を変更したら、"Next step"をクリックする。 ・step1「Name environment」のName欄に、ワークスペースの名前をつける。 ・step2「Configure settings」のPlatformを”Ubuntu Server 18.04 LTS”を選択する。 インデントの設定を変更する。 ワークスペースが作成できたら、インデントの設定を変更する。 変更するためには、まずページの右上の歯車マークをクリックして、「Preferences」タブを開く。 その後、タブ内の「Code Editor」欄のSoft Tabsの値を「2」に変更する。 ※設定の変更はその場で反映されるため、「Save」ボタンをクリックする必要はない。 以上でワークススペースの作成が完了です。 最後に 次は、クラウド環境にRailsをインストールしていきます。 それでは!