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

Railsのscssで画像のパスを指定できていなかった話

やりたかったこと li要素の「・」を自前の画像に変えてサイズを調整したかった うまくいかなかったこと いくら画像を指定してもliの・が自前の画像に変更されない。 試したこと cssでliの・を画像に変更する方法をググって以下の方法を試した background-image:url("画像ファイルの相対パス") background-repeat: no-repeat; background-size: 20px; background-position:left center; liの・を画像に変えることは可能だが、それだと画像サイズの変更ができないためliの背景画像として表示させた。(かった) 解決 原因は画像ファイルをCSSのurl関数にscssファイルからの相対パスで指定していたこと。 scssはそのままcssとして読み込まれるのではなくコンパイルされてcssになる様なのでこの書き方だと画像が見つからない(エラーも出なかったので気づかなかった) 正しい書き方はこう railsのヘルパーメソッドimage-urlを使う background-image: image-url("ファイル名"); image-urlはrailsでsample_pjというプロジェクトであれば sample_pj/app/assest/image配下のファイルを探してくれる。 images直下のsample1.pngという画像を出したい場合はこう background-image: image-url("sample1.png"); images直下にsharedというディレクトリがありその中のsample2.pngを表示させたい場合はこう background-image: image-url("shared/sample2.png"); 最後に cssとscssの違いをなんとなーくいけるだろでやっていたためパスが変わることなど全く頭になかったので長時間のつまづき。よく考えてみたらhtml.erbで表示させる時もimage_tagというrailsのメソッド使ってたなーと。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]layoutsファイルと7つのアクション

はじめに 本記事は、本日私が理解できていなかった 「layouts」ファイルの中身について書きました。 今回は、これに何時間もかかりましたので、備忘録として投稿します。  layoutsファイルには 具体例 <!DOCTYPE html> <html> <head> 省略 </head> <body> <header class="header"> <div class="inner"> <div class="nav"> <div class="nav__left"> <%= link_to image_tag("logo.png", class: :logo), root_path %> </div> <% if user_signed_in? %> <div class="nav__right"> <%= link_to "ログアウト", destroy_user_session_path, method: delete, class: :nav__logout %> <%= link_to "New Proto", root_path, class: :nav__btn %> </div> <% else %> <div class="nav__right"> <%= link_to "ログイン", new_user_session_path, class: :nav__btn %> <%= link_to "新規登録", new_user_registration_path, class: :nav__btn %> </div> <% end %> </div> </div> </header> <%= yield %> <footer class="footer"> <p class="copyright">Copyright © ~~~ All rights reserved.</p> </footer> </body> </html> このように、 <html> <head></head> <body> <header></header> <footer></footer> </body> </html> という記述になるファイルになります。 つまり、ブラウザ上に表示されるものに直結すると考えても良いと思います。 勘違いしていたこと 当時、私は、「新規登録ボタン」からユーザー登録ができるようコードを書いておりましたが、 一向に新規登録画面に遷移することはありませんでした。 理由としては、 createアクションで、app>views>registrations>newのファイルにユーザーが情報登録するためのURLをコードに記述しただけだったためです。 <div class="main"> <div class="inner"> <div class="form__wrapper"> <h2 class="page-heading">ユーザー新規登録</h2> <%= form_with model: @user, url: user_registration_path, local: true do |f| %> 省略 </div> </div> </div> 原因 完全に理解していたと思われた 7つのアクションの役割を正確に理解していなかったことから、 このようなミスに繋がったと考えます。 改善策 ・再度、7つのアクションについての役割を分析する。 ・今後もこのようなことが起きないように注意する。 ・注意の方法として、7つのアクションそれぞれに関連深いものも確認しておく。 この3つに焦点を当てて、復習します。 終わりに 初心者がまず失敗してしまうミスという感じがします。 ここからは理解したことをさらに深く、 知識の肉付けをどんどん実施していきたいと思いました。 以上です。 引き続き頑張ります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

meta-tagsについて

Gem meta-tags について 今回SEO対策として何か実装したいと考えて調べてみた所、meta-tagsというGemを知りましたので、実際に使ったので備忘録として記入したいと思います。 meta-tagsの導入 routes.rb Gem 'meta-tags' bundlerでインストール bundle exec これで導入完了です 各種設定 上記の作業を正常に完了してればconfig/initializers/meta_tags.rbという感じにファイルが生成されています。 meta-tagsの内容は下記の通りです。 meta_tags.rb # Use this setup block to configure all options available in MetaTags. MetaTags.configure do |config| # How many characters should the title meta tag have at most. Default is 70. # Set to nil or 0 to remove limits. # config.title_limit = 70 # When true, site title will be truncated instead of title. Default is false. # config.truncate_site_title_first = false # Maximum length of the page description. Default is 160. # Set to nil or 0 to remove limits. # config.description_limit = 160 # Maxumum length of the keywords meta tag. Default is 255. # config.keywords_limit = 255 # Default separator for keywords meta tag (used when an Array passed with # the list of keywords). Default is ", ". # config.keywords_separator = ', ' # When true, keywords will be converted to lowercase, otherwise they will # appear on the page as is. Default is true. # config.keywords_lowercase = true # List of additional meta tags that should use "property" attribute instead # of "name" attribute in tags. # config.property_tags.push( # 'x-hearthstone:deck', # ) end こちらの記述は「タイトルでも文字数制限などを設定できますが、デフォルトでも大丈夫らしいので、今回はこのままです。 各種サイトの条件に合わせて設定してください。 metaタグの初期設定をおこなうため、「app/helpers/application_helper.rb」にdefault_meta_tagsメソッドを実装します。 application_helper.rb module ApplicationHelper def default_meta_tags { site: 'サイト名', title: 'タイトル', reverse: true, charset: 'utf-8', description: 'description', keywords: 'キーワード', canonical: request.original_url, separator: '|', icon: [ { href: image_url('favicon.ico') }, { href: image_url('icon.jpg'), rel: 'apple-touch-icon', sizes: '180x180', type: 'image/jpg' }, ], og: { site_name: 'サイト名', # もしくは site_name: :site title: 'タイトル', # もしくは title: :title description: 'description', # もしくは description: :description type: 'website', url: request.original_url, image: image_url('ogp.png'), locale: 'ja_JP', }, twitter: { card: 'summary', site: '@ツイッターのアカウント名', } } end end これまでの作業が終わればあとは、application.html.erbのhead内に<%= display_meta_tags(default_meta_tags) %>を記述すればviewに表示できます。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storageの導入

Active Storageとは? ファイルアップロード機能を簡単に実装できるGemです。 ActiveStorageを導入しただけではファイルをアップロードできるようになるだけです。 保存した画像を画像を加工してから(サイズや色など)表示できるようにするには以下の画像加工ツールとgemが必要になります。 ImageMagick コマンドラインから画像に処理を加えることができるツールです。 処理としては、画像の作成やサイズ変更、保存形式の変更などがあります。 ImageMagickはGemではなく、ソフトウェアです。 そのため、Homebrewからインストールします。 GemではないImageMagickをRubyやRailsで扱うには、MiniMagickというGemが必要となります。 MiniMagick ImageMagickの機能をRubyで扱えるようにしてくれるGemです。 RailsでImageMagickを扱うために必要となります。 MiniMagickによって、ImageMagickの機能がRailsで使用できるようになりますが、画像サイズの変更には、もう1つImageProcessingというGemを追加する必要があります。 ImageProcessing MiniMagickでは提供できない、画像サイズを調整する機能を提供するGemです。 たくさんのツールとGemを紹介しましたが、それぞれの関係性は把握しておきましょう。 それでは、これらのツールとGemを導入します。 導入手順 まずはImageMagickを Homebrew からインストールします。 ターミナル % brew install imagemagick 次にMiniMagickとImageProcessingをインストールします。 gemfile    # gemfileの一番下に記述する % gem 'mini_magick' % gem 'image_processing', '~> 1.2'      続いて、ターミナルにてbundle installします。 ターミナル % bundle install gemを新しくインストールしたらローカルサーバーを再起動しましょう。 ターミナル % rails s # サーバー開いている場合は一度「 control + c 」で閉じてから再起動するように 以上でActive Storageを使用する為に必要なツールのインストールは完了です。 続いて、Active Storageをアプリケーション内で使用する準備を行います。 ターミナル % rails active_storage:install rails active_storage:installコマンドを実行すると、Active Storageに関連したマイグレーションが作成されます。続けてマイグレートしましょう。 ターミナル % rails db:migrate Sequel Proを確認し、active_storage_attachmentsとactive_storage_blobsの二つのテーブルが追加されていれば成功です。 これで、Active Storageと画像加工ツールの導入は完了です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ポートフォリオ

E-tripp ?はじめに  今回のこのポートフォリオは転職を目的として作成しました。ただ作成するのではなく、現場を意識してdevelopからブランチを切り最後にmasterでデプロイをする開発工程で行いました。  また、だらだら作っていてはいつまで経っても転職活動ができないと考え、自分で納期日(完成予定日)と、工程をAプラン(終わったら転職活動スタート)・Bプラン(転職活動中)と分けて取り組むようにしました。以下はAプランの実装で、制作期間は約1ヶ月です。 ?制作背景 課題 旅行プランを作るのに、色々なサイトを探して決めるのが大変とパートナーが不満にしていました。そして簡単に計画を作りたいという要望を叶えたいと考えました。 解決方法 時間や場所をクリックで選択、登録し簡単スケジュール化 目的 簡単に旅行プランを立てる パートナーの要望を叶える、笑顔にさせる 継続的なサービス展開(ビジネス) この課題の解決として誕生したのが『E-tripp』です。 サイトURL https://arcane-forest-90597.herokuapp.com/ ?アプリ概要 クリックするだけで簡単に旅行プランを制作できるアプリです。 ?開発環境 Ruby 2.7.3 Ruby on Rails 6.1.3 PostgreSQL 13.2(DB) Github Visual Studio Code JavaScript jQuery bootstrap4.5.1 Heroku ✈️実装機能 機能 Gem ① ログイン機能 devise ② ゲストログイン機能 ✖︎ ③ プラン作成機能(CRUD) cocoon ④ いいね機能(Ajax) ✖︎ ⑤ いいねカウント機能(Ajax) ✖︎ ⑥ コメント機能(Ajax) ✖︎ ⑦ 検索機能 ransack ⑧ ページネーション機能 kaminari ⑨ 画像アップロード機能   carriewave ⑩ 管理者機能 activeadmin ✏️ER図 苦労した点 多対多のアソシエーション 今回一番苦労しました。1対多のアソシエーションはすぐに理解する事ができたのですが、多対多のアソシエーションに一番時間を費やしました。中間テーブルによってどっちが複数持てるのか、外部キーはどこが所有するのか等、非常に迷いました。解決策としてやりたいことを図や絵や言葉で表現(例;plan→schedule→spot)しながら進めていくことで、無事に解決する事ができました。 ?トップページ 工夫した点 ①ページ切り替え ログインの有無で、ページにボタンを追加していろいろなページに移動できるようにしました。 ②シンプル ユーザーが見やすいようにGIF画像を取り入れました。最初は動画を取り入れたのですが、読み込みに時間がかかる、ユーザーがクリックする手間といった課題が出たので、それを解決するためにGIF画像を採用しました。 苦労した点 デプロイ先で画像が表示されない ローカル環境では表示されていた画像が、デプロイ先では表示されませんでした。原因は、自動デプロイの際に画像がコンパイルされ、パスが変わるとのことで、assets_pathを記述したことで無事に表示されました? ?プラン作成画面 工夫した点 ①プランに複数のスポットを追加可能に ユーザーが複数のスポットを登録できたら良いと思い、『スポットを追加』のボタンを押すことで、スポットのフォームを追加できるよう実装しました。時間も一緒に登録できます。 ②既存のスポットをセレクトボックスで選択 ユーザーの入力負担を減らそうと思い、スポットをセレクトボックスで選択できるよう実装しました。 今後は画像をクリックしたらフォームに登録できるような実装をしていきたいと考えています。? 苦労した点 中間テーブルの追加したカラムに時間を登録 多対多のアソシエーションにおいて、いいね機能等は中間テーブルにFKのみで実装できたが、今回はFKと時間のカラムが追加されたことで中間テーブルにデータが中々保存できませんでした。時間がかかりましたが、form_wtihの中にfields_forを追加したことで無事に解決できました? ?プラン詳細画面 工夫した点 ①プランが見やすいようタイムライン化 ユーザーがすぐに時間と場所を確認できたらいいと思い、タイムライン化で実装しました。 編集機能で順番を入れ替えることもできます。 ②画像をクリックでスポット詳細ページへ ユーザーはすぐに場所の情報を知りたいと思い、行く場所をクリックすることで詳細ページに移動できるよう実装しました。 苦労した点 plan詳細ページからspot詳細ページに移動 plan詳細ページからspot詳細ページに移動したかったのですが、解決に時間がかかりました。初めはplanからuserにリンクをつけての移動ができたので、同じようにすればいいと考えましたが、前者は多対多で後者が1対多の関係だったので、やりたいことが違うとわかりました。原因は引数の数でした。引数を2つ設定したことで無事に解決することができました? ?スポット詳細画面 工夫した点 いいねボタンとコメント機能をAjax化 いいねボタンとコメント機能をAjax化したことで、ページが切り替わる時のユーザーの待ち時間と、サーバーへの負担を減すようにしました。 苦労した点 Ajaxが機能しない コメント機能の作成と削除をAjax化にしたかったのですが、リロードしないと動かない現象が起こりました。原因はjqueryが正しく読み込まれていないことでした。読み込む場所が違っており正しい位置にしたことで無事に解決できました? ?今後実装予定(Bプラン) テスト(Rspec) N+1問題 AWSへデプロイ タグ機能 無限スクロール機能 ページネーションのAjax化 画像をクリックしたらフォームに登録できる機能 さいごに パートナーにこのアプリを使ってもらい、喜んでもらい、笑顔にすることができました。また、もっとこうした方がいいといったアドバイス(要望)を頂いたので、ユーザー側からの意見として参考に修正していきたいと思います。 制作にあたっては、納期日(完成予定日)意識したことで、予定日の週は追い込まれることを経験しました。余裕を持った計画の逆算が必要だと感じました。 またエラーに対しては仮説を立て調べ上げ、8割は自力で解決することができました。詰まっていたところが自力で解決できたときはとても嬉しかったです。 今後は継続的なサービス展開ができるように開発をしていきたいと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails6】カスタムフォントの導入方法

開発中のRailsアプリでストップウォッチのフォントが普通すぎてダサいな〜と思ったのでカスタムフォントを導入しました。 知識定着のためにアウトプットします。 環境 Rails 6.0.4 Ruby 2.6.5 好きなフォントをインストールして配置します。 私は「DSEG」というデジタル時計をイメージしたカスタムフォントをインストールしました。 ▼こんな感じです ファイルインストール後に、app/assetsにfontsというフォルダを作成します。 その後、fontsフォルダにインストールしたファイルを配置します。 application.scssにインストールしたファイル名などを記述してCSSで参照できるようにします。 /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * *= require_tree . *= require_self */ // ↓下記追加↓ @font-face { font-family: "DSEG"; //名前を命名(何でも良い) src: asset_url('DSEG7Classic-Bold.ttf'); //ファイル名を指定 } 【注意】 application.cssになっている場合は、application.scssにしましょう! .cssだとうまく読み込めないみたいです、、、。 CSSに記載する HTMLは以下のようになってます。 show.html.erb <h1>ストップウォッチ</h1> <div id="container"> <div id="stopwatch"> <div id="display">0.000</div> </div> </div> CSSにさっきの命名した名前を書きます。 show.css #display { font-family: "DSEG"; } 完成! これが、、、 ↓ こうじゃ~~ フォントが適用されましたね。 なんだかストップウォッチくんも嬉しそうですね。 (あとで他の装飾もしてあげます。笑) まとめ かんたんにできましたね〜〜 フォント変えるだけで、結構雰囲気変わるので意外とフォントって重要なんですね〜。 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで架空のCafeのHPを作ってみよう!【12日目】『特定のデータの取得(active:hashの属性値)』編

概要 基本Railsの記法に則り書いていきます! 1から全ての説明ではなく その中であれ?どうやるの?と 疑問に思った点や実装に困った箇所を ピックアップして紹介していきます♩ 設定と準備 ・Rails ・HTML ・CSS ・Javascript(jQuery) ↑上記の言語とフレームワークを使い 架空(自分で考えたテキトーなもの)のCafeの HPを作っていこうと思います! 12日目の作業内容 ・ビューの作成(トップページに特定の情報を表示) 12日目の気になった箇所 特定のデータの取得でカラムごとの取得はできたが 属性まで指定できるのだろうか。 仮説 カラム別に取得する場合は (testテーブルのoptionカラムだけ取得したい場合とする) samples_controller.rb @test_option = Test.select('option') このような形で取得することができる。 (取得の仕方は複数存在します) 似たような形で属性まで指定できないか試してみる。 Testテーブルのoption_idカラムのname属性だけを 取得したい場合とする。今回はname属性の属性値2を取得したい。 (_idはactive:hashの使用時の記法です) samples_controller.rb @test_option = Test.select('option.name.2') このように記述してみたがうまくいかなかった。 結論 複数やり方があると思いますが今回は私がうまくいった方法を紹介します。 上記と同じ条件で Testテーブルのoption_idカラムのname属性だけを 取得したい場合とします。今回はname属性の属性値2を取得するとします。 samples_controller.rb @test_option = Test.where(:option_id name='2') 私の場合このような記述の仕方でうまくいきました。 簡単に説明すると 取得したいテーブル(モデル)に対してwhereメソッドを使います。 samples_controller.rb @test_option = Test.where whereメソッドは条件に一致したレコードを配列で取得してくれます。 そして、 whereに取得したい条件を入れていきます。 まずはカラム名です。 samples_controller.rb @test_option = Test.where(:option_id) この状態だとoption_idカラムの全てを取得する条件になります。 なので、さらに属性まで絞りたいので、 samples_controller.rb @test_option = Test.where(:option_id name) このように続けて記述することでその属性まで条件まで絞ることができます。 同じように続けて、 samples_controller.rb @test_option = Test.where(:option_id name='2') と、することで、属性の属性値まで条件を絞ることができました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]文字列(String)のtrue、falseをBoolean(TrueClass、FalseClass)へ変換

rubyでture、falseの入力を文字列(Stirng)として受け付けたとき、Boolean型のtrue、falseとして扱うために変換するコードを紹介します。 前提 Rubyではnillかfalse以外をtrueとして扱います。 そのため、文字列のfalseを条件文に利用すると、論理値としてはtrueになってしまいます。 false.rb bool = gets p bool.class if bool p "Trueです!" else p "Falseです!" end =====入力・出力結果===== false String "Trueです!" String→Booleanの変換コード、動作の紹介 文字列のtrue、falseをBooleanに変換する方法を早速紹介します。 string→boolean変換.rb class String def to_bool # 関数の呼び出し元文字列がtureだったらBoolean型のtrueを返す if self =~ /(true|True|TRUE)/ return true # 関数の呼び出し元文字列がfalseだったらBoolean型のfalseを返す elsif self =~ /(false|False|FALSE)/ return false # 関数の呼び出し元文字列がtrue、false以外だったら元の文字列を返す else return self end end end コードの要点をひとつだけ補足すると、class String部分はオープンクラスというRubyの概念を利用して、元々存在していないメソッドを追加しています。 オープンクラスについてはページ下部で紹介しますので、一旦置いておいて、動作確認のコードも合わせて紹介します。 動作確認.rb class String def to_bool # 関数の呼び出し元文字列がtureだったらBoolean型のtrueを返す if self =~ /(true|True|TRUE)/ return true # 関数の呼び出し元文字列がfalseだったらBoolean型のfalseを返す elsif self =~ /(false|False|FALSE)/ return false # 関数の呼び出し元文字列がtrue、false以外だったら元の文字列を返す else return self end end end str = gets.to_bool if str.class == String p 'Stringです!' else if str p 'Trueです!' else p 'Falseです!' end end =====入力・出力結果===== 例① false "Falseです!" 例② true "Trueです!" 例③ a ”Stringです!” オープンクラスの補足 オープンクラスはざっくりいうと、クラスの再定義、メソッドの上書き、メソッドの追加を可能にしてくれる概念です。 メソッドの上書きかどうかはメソッド名で判別されるので、名前被りが発生していないかの確認はとても大切になります。 名前が被っていないかの確認は以下二つが簡単かと思います。 コードを書いて検証(No methodがエラーが出るかどうかを確認) リファレンスを見る(Stringクラスはこちら) Boolean→Stringの補足 String→Booleanは自らメソッドを追加しなければなりませんが、逆のBoolean(TrueClass、FalseClass)→Stringは用意されています。 Boolean→String.rb bool = false str = bool.to_s p bool.class p str.class =====出力結果===== FalseClass String
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2にRailsアプリをデプロイ④ ~EC2でRailsを起動させる設定~

はじめに これは前回の記事の続きです。 今回はEC2でRailsアプリを起動させるための設定をしていきます。 よろしくお願いします。 ssh鍵をGitHubに登録 アプリケーションのコードをGitHubを使ってEC2サーバにクローンします。 しかしながらEc2サーバにアプリをクローンしようとすると、GitHubは誰のEc2インスタンスかわからないのでssh公開鍵をGitHubに登録し、それを認証として利用してクローンwp許可してくれるようになります。 それでは下記のコードでssh鍵のペアを作成し、GitHubにssh鍵を登録していきます。 EC2にログインした状態で実行します。 ターミナル [ec2-user@ip-172-31-23-189 ~]$ ssh-keygen -t rsa -b 4096 Generating public/private rsa key pair. Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/ec2-user/.ssh/id_rsa. Your public key has been saved in /home/ec2-user/.ssh/id_rsa.pub. The key fingerprint is: 3a:8c:1d:d1:a9:22:c7:6e:6b:43:22:31:0f:ca:63:fa ec2-user@ip-172-31-23-189 The key's randomart image is: +--[ RSA 4096]----+ | + | | . . = | | = . o . | | * o . o | |= * S | |.* + . | | * + | | .E+ . | | .o | +-----------------+ 次にcatコマンドで表示されたssh公開鍵の値をコピーします。 下記のコマンドを実行します。 ターミナル [ec2-user@ip-172-31-23-189 ~]$ cat ~/.ssh/id_rsa.pub すると公開鍵の情報が表示されるので全てコピーします。 ターミナル(例) ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLwt...... # 表示された内容すべてをコピー 以下をクリックしてGitHubにアクセスしてください。 SSH鍵登録リンク 遷移先で「New SSH key」をクリックします。 先ほどコピーした公開鍵を貼り付けて「Add SSH key」をクリックして保存します。 登録が完了したら、下記のコマンドで確認します。 ターミナル [ec2-user@ip-172-31-23-189 ~]$ ssh -T git@github.com Hi <Githubユーザー名>! You've successfully authenticated, but GitHub does not provide shell access. yesかnoの質問があればyesでenterキーを押します。 Unicornをインストール 次にアプリケーションサーバーの設定をします。 Unicornというアプリケーションサーバーを使います。 Gemfileのgroup :production do ~ endに下記を記述します。 Gemfile group :production do gem 'unicorn', '5.4.1' end unicorn.rbを作成 configの配下にunicorn.rbを作成します。「config/unicorn.rb」に下記の記述をします。 config/unicorn.rb #サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく app_path = File.expand_path('../../', __FILE__) #アプリケーションサーバの性能を決定する worker_processes 1 #アプリケーションの設置されているディレクトリを指定 working_directory app_path #Unicornの起動に必要なファイルの設置場所を指定 pid "#{app_path}/tmp/pids/unicorn.pid" #ポート番号を指定 listen 3000 #エラーのログを記録するファイルを指定 stderr_path "#{app_path}/log/unicorn.stderr.log" #通常のログを記録するファイルを指定 stdout_path "#{app_path}/log/unicorn.stdout.log" #Railsアプリケーションの応答を待つ上限時間を設定 timeout 60 #以下は応用的な設定なので説明は割愛 preload_app true GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true check_client_connection false run_once = true before_fork do |server, worker| defined?(ActiveRecord::Base) && ActiveRecord::Base.connection.disconnect! if run_once run_once = false # prevent from firing again end old_pid = "#{server.config[:pid]}.oldbin" if File.exist?(old_pid) && server.pid != old_pid begin sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU Process.kill(sig, File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH => e logger.error e end end end after_fork do |_server, _worker| defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection end 編集したらリモートリポジトリにコミット、プッシュします。 完了したら、EC2にローカルの全内容を反映させます。 下記のコマンドで権限を付与します。 ターミナル #mkdirコマンドで新たにディレクトリを作成 [ec2-user@ip-172-31-23-189 ~]$ sudo mkdir /var/www/ #作成したwwwディレクトリの権限をec2-userに変更 [ec2-user@ip-172-31-23-189 ~]$ sudo chown ec2-user /var/www/ 次にリポジトリURLをGitHubから取得してクローンします。 デプロイするGitHubのページにいって緑色の「Code」というボタンをクリックしてURLをコピーします。 下記のコマンドでクローンします。 ターミナル [ec2-user@ip-172-31-23-189 ~]$ cd /var/www/ [ec2-user@ip-172-31-23-189 www]$ git clone コピーしたURLを貼り付ける 次回は本番環境での設定を行います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでサイトを作成する下準備までの大まかな手順

 記事の目的  Cloud9にてECサイトを作るにあたって「何から始めればいいんだっけ?」と思いQiitaを含めて何回か参照していましたが、流れがまとまらなかったので自分用の備忘録として記載します。参考までに。  本当に大まかにまとめたので、詳細箇所については別途追記するかもしれません。 ECサイト作成前に準備すること。  個人製作の場合は必ず必要というわけではないですが、作成する前に作っておくと後々サイトを作りやすいです。 Cacooというサイトであれば両方作成できるので、おすすめです。 ・ER図 ・ワイヤーフレーム ・GitHubの連携 ・ImageMagickのインストール ファイル・モデル・コントローラーの作成 ①ファイルの作成 ※Cloud9にて諸々の初期設定が終わっていること。これに関しては個人の設定に左右される。 ターミナルを開いて、下記を入力しファイルを作成する。 (例)として、ファイル名を「Illusters」というアプリを作るものとします。 【ターミナル】 ~$ rails new ファイル名 (例)~$ rails new Illusters ※ファイル名は"頭文字:大文字" ②モデルの作成 作りたいモデルを作成する。 モデルの中にカラムとデータ型を一緒に作成すれば後々マイグレーションファイルがごちゃごちゃしないのでオススメ。 user_idといったモデルに直結するidは記載不要。 【ターミナル】 ~$ rails g model モデル名 カラム1:データ型1 カラム2:データ型2 ... (例)~$ rails g model user name:string profile_image:string introduction:text ※モデル名は"頭文字:小文字,単数形" 必要なモデルを作成した後に必ず下記の文を実行しマイグレーションすること! 【ターミナル】 ~$ rails db:migrate ↑を行わないとrailsの実行時にエラーが発生する。 モデルを削除したい場合...「モデルを間違えて作ってしまった」「後々いらなくなってしまった」など 【ターミナル】   ~$ rails d model モデル名 (例)~$ rails d model user を実行すれば削除される。 もちろん、削除した後でも~$ rails db:migrateを行って、マイグレーションファイルを更新する必要がある。 【補足】 よく使うデータ型 データ型 入力される値 使用例 integer 整数 id, 単価, 個数 など string 文字列(256文字以内) 名前, 住所, 画像, 電話番号(0から始まる) など text 文字列(制限なし) 自己紹介, キャプション など boolean true / false 男女設定, はい/いいえの質問, 表示/非表示 など float 小数点以下を含んだ数値 数量, パーセンテージ, datatime 日時 現在時刻, 登録日時 など ③コントローラの作成 ②で作成したモデルに付随するアクションコントローラーを作成する。 【ターミナル】 ~$ rails g controller コントローラ(モデルの複数形) アクション1 アクション2 ... (例)~$ rails g controller users new show edit ※コントローラ名は"頭文字:小文字,複数形" アクション名はターミナルで記載すると、下記が自動で行われるため、時短できる。 また、任意で作成したアクションも記載することはできる。 ・Cloud9内で自動で「△△-controller.html.erb」内にアクション名が追加される ・Cloud9内にフォルダが追加されその中に「△△.html.erb」が作成される コントローラを削除したい場合... モデル同様、コントローラも削除できる。 【ターミナル】   ~$ rails d controller モデル名 (例)~$ rails d controller users を実行すれば削除される。 route.rbファイルにアクション名を追加 route.rb内のファイルにコントローラを記載する。 RESTfulなアクションと任意のアクションで対応が異なるのでそれぞれ説明する。 【A. RESTfulなアクションのみ使用する場合】 【routes.rbファイル内】 resources :コントローラ名 (例) resources :users RESTfulなアクションはよく使うため、「resources :△△」の一文のみで下記7つのアクションができる。 アクション名 使用用途 メソッド new 新規作成画面 GET create 新規作成 POST index 一覧表示 GET show 詳細画面の表示 GET edit 編集画面の表示 GET update 編集 POST/PATCH destroy 削除 DELTE なお、メソッドについて端的に言うと  ・GET : 画面に表示されるもの  ・POST,PATCH : 専用の画面はなくコントローラ内のみで使用される  ・DELETE : POST,PATCH同様専用画面はない。削除するメソッド 【B. 任意のアクションを設定する場合】 RESTfulのものとは一致しないアクションを作成したいときはroutes.rbファイル内に任意のアクション名を追加する。 【routes.rbファイル内】 メソッド 'アクション名' => '保存するファイルの場所' (例)get 'download' => 'item_images#download' (例)post 'download' => 'item_images#download' ※'保存するファイルの場所'に指定がなければ "=> '~~~~'"以降は不要 以上!下準備はこれで完了です。 下準備はこれで終わりです、ここから先はコードをご自由に書いてみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

config gem と dotenv-rails gem を共存させるときの ポイント

Rails での開発では、以下の2つのgemを使い分けたり、併用して設定情報を管理する方が多いかと思います。 config gem yaml 管理 dotenv-rails gem .env(及び環境変数) での管理 ですが、「設定情報を、 yaml に記載したのか、.env(及び環境変数)に記載したのか?」と分からなくなり手が止まる瞬間があります。 なので、最近は config gem を介して .env を参照するようにして、設定情報の参照先を一本化しています。 その中で、以下の 2 つのポイントのどちらかを押さえておかないとエラーになることを確認したので、共有します。 実行環境 Ruby 3.0.0 Rails 6.1.4 前提 読み込みの対象になる yaml と、.env ファイルは以下のものになります。 # config\settings.yml comment: これはテスト用アプリ # .envに記載した環境変数 SAMPLE_API_KEY の内容を読み込む sample_api_key: <%= ENV.fetch('SAMPLE_API_KEY') %> # .env # config\settings.ymlから参照される SAMPLE_API_KEY=sample_api_key 注意するポイント 次に示す 2 つプランのうちどちらかを守らないと、以下のようなエラーになります。 # エラー内容 (erb):2:in `fetch': key not found: "SAMPLE_API_KEY" (KeyError) プラン A:Gemfile で dotenv-rails gem を先に記述する Gemfileにて、dotenv-rails gem を読み込んでから config gem を読み込むようにします。これだけでエラーを回避できます。 # NG gem 'config' gem 'dotenv-rails' # OK gem 'dotenv-rails' gem 'config' プラン B:yaml ファイルでロードメソッドの実行する gemfile での、記述の順番に配慮しなくても yaml 内で、Dotenv::Railtie.load を実行することで、エラーを回避して読み込みできます。 プランAと比べて、明示的に .env が参照されていることがわかるという利点があります。 # <% Dotenv::Railtie.load %> を追加する <% Dotenv::Railtie.load %> comment: これはテスト用アプリ sample_api_key: <%= ENV.fetch('SAMPLE_API_KEY') %> それでは皆さん、安全で便利に設定情報を管理していきましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Twitterシェアボタンは超かんたんに実装できる

色んなサイトで見かけるTwitterのシェアボタン実は、超かんたんに実装できます。 まじですぐ実装できるので、記事が短くなりすぎてしまいそうで不安ですw こんな感じ シェアボタンを押すとこんな感じで、Twitterに飛びます。 かんたんにシェアできるので便利ですね〜 ポートフォリオサイトのせておきますので、どんどんシェアしてみてくださいww 開発環境 ruby 2.6.3 Rails 5.2.6 さっそく実装 実装方法はビューで、TwitterのシェアためのURLにリンクを実装するだけです。 実装 <%= link_to "https://twitter.com/share?url=#{ request.url }&text=【テスト】%0a%0a#{ post.content }、シェアさいこう〜", target: '_blank' do %> <i class="fab fa-twitter" style='color: #3399FF;'></i>シェア <% end %> これだけで、実装完了です。 これだけでは、少しわかりにくいので、解説していきます。 URL まず、リンクの遷移先とツイートに入ってるURL部分を見ていきます。 ツイート内のURL部分はurl=#{ request.url } # request.urlにシェアボタンを設置したページのURLが入る link_to "https://twitter.com/share?url=#{ request.url } ツイート本文 次にツイートの本文部分を見ていきます。 ここは、ツイートしたい内容によって、任意の文字列、カラムなんかを指定してアレンジができます。 ツイート本文は&text= 今回は、 文字列  改行 post.content 文字列 でツイートを構成しています。 # %0a%0aが改行 # カラムを使う場合は#{ }の中に入れる &text=【テスト】%0a%0a#{ post.content }、シェアさいこう〜" target: '_blank' これはlink_toのオプションでつけることにより、リンク先を別タブで開けるようになります。 シェアリンク 最後に表示されているリンク部分のデザインです。 Twitterなので、FontAwesomeのアイコンを使って、青くしています。 <i class="fab fa-twitter" style='color: #3399FF;'></i>シェア まとめ 思ったとおり、短い記事になりましたw 実際この記事読んでもらったら、変更点はツイートの本文ぐらいなので、実装は5分以内にできるんじゃないでしょうか。 さくっと、Twitterシェアボタン実装してみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RSpec】リクエストスペック実装(備忘録)

はじめに PFで基本実装がある程度進んだので、そろそろテストの実装もやってみようと実装開始した際に、かなり苦戦して、メンターさんにアドバイス頂きながら実装したので、備忘録として残します。(初歩的な内容です。) 開発開発 Mac Ruby 2.7.2 Rails 6.1.3.2 前提条件 RSpec・factory_bot_rails・ Fakerは導入済み (わからない方は、参考記事→RailsでのRSpecの設定やら書き方とかいろいろ) エクササイズの情報(exercise:外部キー)をもつ、記事(article)投稿アプリ(Twitterのような投稿アプリにカテゴリーが選択できるイメージです。) 現状 新規投稿(create)で、「パラメーターが正常な時、リクエストが成功する」テスト実行中。テストでは、「302を予想していたけど、200です。」と弾かれ、実際の挙動では、投稿した際のログでは、「302」となっている現状(⭐の部分)。 テストの結果 1) Articles Logind POST #create パラメータが正常なとき リクエストが成功する Failure/Error: expect(response).to have_http_status(302) expected the response to have status code 302 but it was 200 # ./spec/requests/articles_spec.rb:110:in `block (5 levels) in <main>' 実際に投稿した際のログ Started POST "/articles" for ::1 at 2021-06-10 08:04:32 +0900 Processing by ArticlesController#create as HTML Parameters: {"authenticity_token"=>"[FILTERED]", "article"=>{"day"=>"2021-06-10", "exercise_id"=>"1", "minutes"=>"5", "body"=>"OK"}, "commit"=>"投稿"} User Load (2.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]] TRANSACTION (1.3ms) BEGIN ↳ app/controllers/articles_controller.rb:14:in `create' Exercise Load (2.6ms) SELECT "exercises".* FROM "exercises" WHERE "exercises"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ app/controllers/articles_controller.rb:14:in `create' Article Create (5.7ms) INSERT INTO "articles" ("day", "minutes", "body", "user_id", "created_at", "updated_at", "exercise_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["day", "2021-06-10"], ["minutes", 5], ["body", "OK"], ["user_id", 1], ["created_at", "2021-06-09 23:04:32.648795"], ["updated_at", "2021-06-09 23:04:32.648795"], ["exercise_id", 1]] ↳ app/controllers/articles_controller.rb:14:in `create' TRANSACTION (1.4ms) COMMIT ↳ app/controllers/articles_controller.rb:14:in `create' Redirected to http://localhost:3000/articles ⭐Completed 302 Found in 65ms (ActiveRecord: 13.7ms | Allocations: 8864) 原因 今回の原因は、下記の2つです。 ① テスト用のデータを上手く作成出来ていなかった。(Fakerの書き方の間違い。) ② テスト用データをコントローラーに渡せていなかった。(上手く作成出来ていなかったので、わたせていない。) 解決までの考え方とか手順を色々と記載しているので、「最終コードだけみたい。」という方は、スキップして下さい。 上記の①と②が原因で、createアクションが呼ばれず、saveが失敗(データが作成されない) = ステータスコード302ではなく200となっていました。 解決した手順 ①create アクションでデバック挟んで、save が成功してるか失敗してるかの確認。 デバックツールを導入していない方は、下記の方法で導入して下さい。 # Gemfile group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] # ***** 以下を追加 ***** gem 'pry-byebug' # ***** 以上を追加 ***** end # ターミナル bundle install デバックツールの導入が完了したら、コントローラーに binding.pry を入れて確認しましょう。 articles_controller.rb class ArticlesController < ApplicationController before_action :set_article, only: %i[edit update destroy] def index @articles = Article.includes(:user, :likes).order(created_at: :desc) end def new @article = Article.new end def create @article = current_user.articles.build(article_params)  # ***** 以下を追加 *****   ⭐binding.pry # ***** 以上を追加 ***** if @article.save redirect_to articles_path, notice: '投稿しました' else flash.now[:alert] = '投稿に失敗しました' render :new end end def show @article = Article.find(params[:id]) @comments = @article.comments.includes(:user).order(created_at: :desc) @comment = Comment.new end def edit end def update if @article.update(article_params) redirect_to articles_path, notice: '更新しました' else flash.now[:alert] = "更新に失敗しました" render :edit end end def destroy @article.destroy! redirect_to articles_path, alert: '削除しました' end private def article_params params.require(:article).permit(:day, :minutes, :body, :exercise_id) end def set_article # 「自分の投稿」の中から URL の :id に対応する投稿を探し、「他人の投稿」の場合はエラー @article = current_user.articles.find(params[:id]) end end binding.pry を挟んで、サーバー起動し、新規投稿してみます。 #投稿した際のログ Started POST "/articles" for ::1 at 2021-06-10 10:55:28 +0900 Processing by ArticlesController#create as HTML Parameters: {"authenticity_token"=>"[FILTERED]", "article"=>{"day"=>"", "exercise_id"=>"1", "minutes"=>"", "body"=>""}, "commit"=>"投稿"} User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]] From: /Users/miya/Desktop/tumieku_app/app/controllers/articles_controller.rb:15 ArticlesController#create: 13: def create 14: binding.pry => 15: @article = current_user.articles.new(article_params) 16: if @article.save 17: redirect_to articles_path, notice: '投稿しました' 18: else 19: flash.now[:alert] = '投稿に失敗しました' 20: render :new 21: end 22: end [1] pry(#<ArticlesController>)> @article => nil [2] pry(#<ArticlesController>)> @article = current_user.articles.new(article_params) => #<Article:0x00007fac7d34f1a8 id: nil, day: nil, minutes: nil, body: "", user_id: 1, created_at: nil, updated_at: nil, exercise_id: 1, likes_count: 0> [3] pry(#<ArticlesController>)> params[:article] => #<ActionController::Parameters {"day"=>"", "exercise_id"=>"1", "minutes"=>"", "body"=>""} permitted: false> [4] pry(#<ArticlesController>)> @article.save TRANSACTION (1.6ms) BEGIN ↳ (pry):four:in `create' Exercise Load (1.7ms) SELECT "exercises".* FROM "exercises" WHERE "exercises"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ (pry):four:in `create' TRANSACTION (1.2ms) ROLLBACK ↳ (pry):four:in `create' => ⭐false 私は、14行目にデバックを挟んでいますが、15行目の下にデバックを挟んで、その時点の @article を確認した方がベターだそうです! @article.save を確認した際に、false となっているので、保存に失敗しています。(通常はtrue になります。) ②article(記事) がどういうい状態になれば保存が出来るかを考える。 保存出来る条件 感想  (空でないこと) 投稿日 (空でないこと) エクササイズの時間 (空でないこと・整数・0〜300以内) 登録されているエクササイズを選択しているか(exercise_id) 保存出来ない条件 下記のデータがきちんと入力できていない場合 感想 投稿日 エクササイズの時間 登録されているエクササイズを選択しているか 保存できる条件を満たしていると、save が成功します。 実際に入力した後に、デバックで確認すると下記のようになります↓ [1] pry(#<ArticlesController>)> @article => #<Article:0x00007f889c6b6670 id: nil, day: Thu, 10 Jun 2021, minutes: 1, body: "s", user_id: 1, created_at: nil, updated_at: nil, exercise_id: 1, likes_count: 0> [2] pry(#<ArticlesController>)> @article.save TRANSACTION (1.9ms) BEGIN ↳ (pry):two:in `create' Exercise Load (1.5ms) SELECT "exercises".* FROM "exercises" WHERE "exercises"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ (pry):two:in `create' Article Create (1.8ms) INSERT INTO "articles" ("day", "minutes", "body", "user_id", "created_at", "updated_at", "exercise_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["day", "2021-06-10"], ["minutes", 1], ["body", "s"], ["user_id", 1], ["created_at", "2021-06-10 10:44:14.444612"], ["updated_at", "2021-06-10 10:44:14.444612"], ["exercise_id", 1]] ↳ (pry):two:in `create' TRANSACTION (1.4ms) COMMIT ↳ (pry):two:in `create' => ⭐true これは、実際にサーバーを起動して確認したので、「テストで、この処理の流れが上手くいっているか?」を確認する必要があります。(テストのデータが、実際の投稿と同じようにコントローラーに値を渡して、保存ができればOKということです。) ③テスト用のデータの設定 spec/factories/articles.rb # 修正前 FactoryBot.define do factory :article do day { Faker::Date } minutes { Faker::Number } body { Faker::Lorem.paragraph } user exercise end trait :invalid do body { nil } end end articlres_spec.rb it 'リクエストが成功する' do binding.pry post(articles_path, params: { article: attributes_for(:article) }) expect(response).to have_http_status(302) end [4] pry(#<RSpec::ExampleGroups::Articles::Logind::POSTCreate::Nested>)> params = { article: attributes_for(:article) } => {:article=>{:day=>Mon, 26 Jul 2021, :minutes=>208, :body=>"ぐんほんらいたいやく。ちょさくけん俵はんそう。しょうりゃく撃つうえる。"}} テスト側のコードの params = { article: attributes_for(:article) } では、値が代入しているのになあ〜。と思っていましたが、このパラメーターが、コントローラー側の⭐article_params(ストロングパラメーター)に、渡っているかが重要なことに気づいていませんでした。 articles_controller.rb def create @article = current_user.articles.build(⭐article_params) if @article.save redirect_to articles_path, notice: '投稿しました' else flash.now[:alert] = '投稿に失敗しました' render :new end end この時点で、コントローラー側にデバッグ挟んで、パラメーターを確認しても、パラメーターがコントローラー側に渡っていません。 Parameters: {"authenticity_token"=>"[FILTERED]", "article"=>{"day"=>"", "exercise_id"=>"1", "minutes"=>"", "body"=>""}, "commit"=>"投稿"} FactoryBotの修正  dayとminutesを修正。「Faker day」とか「Faker 日付」とかで検索して、下記のように修正しました。 spec/factories/articles.rb # 1回目の修正 FactoryBot.define do factory :article do ⭐day { Faker::Date.in_date_period } ⭐minutes { Faker::Number.within(range: 1..300) } body { Faker::Lorem.paragraph } user exercise end trait :invalid do body { nil } end en コントローラーにデバックを挟んで、パラメーターが渡っているか確認。 pry(#<ArticlesController>)> article_params => #<ActionController::Parameters {"day"=>"2021-04-04", "minutes"=>"109", "body"=>"たくす魔法しょうかする。不思議とめる太る。誤用問題約する。"} permitted: true> pry(#<ArticlesController>)> @article => #<Article:0x00007fc7c3c959b8 id: nil, day: Fri, 12 Feb 2021, minutes: 179, body: "不健康退くきょうき。割り箸ちらかすたいこうする。〜系出かける入江。", user_id: 40969, created_at: nil, updated_at: nil, ⭐exercise_id: nil, likes_count: 0> pry(#<ArticlesController>)> @article.save => false pry(#<ArticlesController>)> @article.save! ActiveRecord::RecordInvalid: バリデーションに失敗しました: Exerciseを入力してください パラメーターは、渡っていますが、@article.save が false となり保存に失敗しています。ちなみに @article.save! とすると、バリデーションに失敗するので、どこが失敗しているのかわかりやすいです。(⭐の所で、exercise_id: nil でもおかしいと気づけます。) save! (ActiveRecord::Persistence) データベースへの保存に成功した場合は true が戻り値になります。バリデーションに失敗した場合は、例外が発生します。 ④コントローラー側に、exercise_id のデータを渡す spec/factories/articles.rb # 2回目の修正 FactoryBot.define do factory :article do day { Faker::Date.in_date_period } minutes { Faker::Number.within(range: 1..300) } body { Faker::Lorem.paragraph } user ⭐exercise_id { FactoryBot.create(:exercise).id } end trait :invalid do body { nil } end end コントローラーにデバックを挟んで、パラメーターが渡っているか確認します。 [2] pry(#<ArticlesController>)> @article = current_user.articles.build(article_params) => #<Article:0x00007fdc2c316428 id: nil, day: Mon, 08 Mar 2021, minutes: 156, body: "たいこうする味噌閉じる。迷子馬こいぬ。よぼうかんそくしょくん。", user_id: 43941, created_at: nil, updated_at: nil, ⭐exercise_id: 15346, likes_count: 0> [3] pry(#<ArticlesController>)> @article.save => true 今度は、⭐の所にもパラメーターが渡っていて、@article.save が true になって保存に成功しています! 今回の場合は、exercise_id は、外部キーで、このarticle(記事)は、exercise(エクササイズ)に紐づいて作成される。そのため、このようなexercise_id { FactoryBot.create(:exercise).id }書き方で、紐づけるexerciseのデータを作成する必要があったという事でした! 学び メンターさんとのやり取りで、学んだこと エラーになると、「なんで?どこが原因?」と原因を探すことばかりに集中して、「本来の処理の流れは?」←ココを意識することが大事だと学びました どういう状況の時に、レスポンスが302になるか?(通常の処理の流れを確認【目指すべきゴール】) 新規投稿・更新・削除は、一時的なリダイレクトになるので、ステータスコードは200ではなく、302となる。今回の場合は、createアクションが呼ばれ、saveが成功(データが作成された) = ステータスコード302 この処理の流れが、出来ていなかったので、テストが通らなかった。 エラー解決は、仮説→検証の繰り返し 間違っているいない関係なく、自分なりの仮説を立てたら、検証をすること。 article がどういうい状態になれば保存が出来るかを考える(通常の処理の流れを確認【目指すべきゴール】) 保存出来る条件 保存出来ない条件 現役のエンジニアの方は、問題の切り分け方が違うなと感じました。 Railsはデフォルトで 開発環境 本番環境 テスト環境 それぞれ存在していて、それぞれが異なるデータベース紐づいていること。 そして、テストの時は「テスト環境」で動かしてるため、当然データベースも「テスト環境用のデータベース」を確認すること。 私が、行っていたチェック方法は、「テスト環境のデータベース」をチェックしなければいけないのに、「開発環境のデータベース」をチェックしてたので、解決までに時間がかかりました。 最終コード(テストのみ) 練習も含めて、かなり長いコードになっています。いろんな書き方があると思いますので、ご参考までに。 spec/factories/articles.rb FactoryBot.define do factory :article do day { Faker::Date.in_date_period } minutes { Faker::Number.within(range: 1..300) } body { Faker::Lorem.paragraph } user exercise_id { FactoryBot.create(:exercise).id } end trait :invalid do body { nil } end end articles_spec.rb require 'rails_helper' RSpec.describe "Articles", type: :request do describe "Logind" do let(:user) { create(:user) } before do sign_in user end describe 'GET #index' do subject { get(articles_path) } context '投稿が存在するとき' do let(:article) { create(:article, user_id: user.id) } before do create(:like, user_id: user.id, article_id: article.id) end it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end it 'article.day が表示されている' do subject expect(response.body).to include article.day.to_s end it 'user.name が表示されている' do subject expect(response.body).to include article.user.name end it 'article.exercise.name が表示されている' do subject expect(response.body).to include article.exercise.name end it 'article.minutes が表示されている' do subject expect(response.body).to include article.minutes.to_s end it 'article.body が表示されている' do subject expect(response.body).to include article.body end end end describe 'GET #show' do subject { get(article_path(article_id)) } context '投稿が存在するとき' do let(:article) { create(:article) } let(:article_id) { article.id } it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end it 'article.day が表示されている' do subject expect(response.body).to include article.day.to_s end it 'article.exercise.name が表示されている' do subject expect(response.body).to include article.exercise.name end it 'article.minutes が表示されている' do subject expect(response.body).to include article.minutes.to_s end it 'article.body が表示されている' do subject expect(response.body).to include article.body end context ':idに対応するユーザーが存在しないとき' do let(:article_id) { 1 } it 'エラーが発生する' do expect { subject }.to raise_error ActiveRecord::RecordNotFound end end end end describe 'GET #new' do subject { get(new_article_path) } it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end end describe 'POST #create' do subject { post(articles_path, params: params) } context 'パラメータが正常なとき' do let(:params) { { article: attributes_for(:article) } } it 'リクエストが成功する' do subject expect(response).to have_http_status(302) end it '投稿が保存される' do expect { subject }.to change { Article.count }.by(1) end it '一覧ページにリダイレクトされる' do subject expect(response).to redirect_to articles_path end end context 'パラメータが異常なとき' do let(:params) { { article: attributes_for(:article, :invalid) } } it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end it '投稿が保存されない' do expect { subject }.not_to change(Article, :count) end it '投稿ページがレンダリングされる' do subject expect(response.body).to include '投稿' end end end describe 'GET #edit' do subject { get(edit_article_path(article_id)) } context '投稿が存在するとき' do let(:article) { create(:article, user_id: user.id) } let(:article_id) { article.id } it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end it 'article.day が表示されている' do subject expect(response.body).to include article.day.to_s end it 'article.exercise.name が表示されている' do subject expect(response.body).to include article.exercise.name end it 'article.minutes が表示されている' do subject expect(response.body).to include article.minutes.to_s end it 'article.body が表示されている' do subject expect(response.body).to include article.body end context ':idに対応するユーザーが存在しないとき' do let(:article_id) { 1 } it 'エラーが発生する' do expect { subject }.to raise_error ActiveRecord::RecordNotFound end end end end describe 'PATCH #update' do subject { patch(article_path(article.id), params: params) } let(:article) { create(:article, user_id: user.id) } context 'パラメータが正常な場合' do let(:params) { { article: attributes_for(:article) } } it 'リクエストが成功する' do subject expect(response).to have_http_status(302) end it 'article.day が更新される' do origin_day = article.day new_day = params[:article][:day] expect { subject }.to change { article.reload.day }.from(origin_day).to(new_day) end it 'article.minutes が更新される' do origin_minutes = article.minutes new_minutes = params[:article][:minutes] expect { subject }.to change { article.reload.minutes }.from(origin_minutes).to(new_minutes) end it 'article.body が表示されている' do origin_body = article.body new_body = params[:article][:body] expect { subject }.to change { article.reload.body }.from(origin_body).to(new_body) end it '一覧ページにリダイレクトされる' do subject expect(response).to redirect_to articles_path end end context 'パラメータが異常なとき' do let(:params) { { article: attributes_for(:article, :invalid) } } it 'リクエストが成功する' do subject expect(response).to have_http_status(200) end it 'article.day が更新されない' do expect { subject }.not_to change(article.reload, :day) end it 'article.minutes が更新されない' do expect { subject }.not_to change(article.reload, :minutes) end it 'article.body が更新されない' do expect { subject }.not_to change(article.reload, :body) end it '編集ページがレンダリングされる' do subject expect(response.body).to include '編集' end end describe 'DELETE #destroy' do subject { delete(article_path(article.id)) } let!(:article) { create(:article, user_id: user.id) } context 'パラメータが正常な場合' do it 'リクエストが成功する' do subject expect(response).to have_http_status(302) end it '投稿が削除される' do expect { subject }.to change(Article, :count).by(-1) end it '投稿一覧にリダイレクトすること' do subject expect(response).to redirect_to(articles_path) end end end end end end まとめ 本来の処理の流れを理解していると、不具合の解消もよりスムーズになるのかなと思います。個人的にnil=ミスしている と思ってしまうので、本来の処理の流れを理解していたら「ここは、nilでいいよね。」と判断が出来ます。やっぱり処理の流れは大事だと感じました! 同じように、「テストに失敗する?」「どう考えればいいか。わかんない!」と悩んでいる方への参考になると幸いです。 メンターさんより、わかりやすい記事を紹介して頂いたので、参考記事に入れておきます! 参考記事 RailsでのRSpecの設定やら書き方とかいろいろ RailsのCRUD系メソッドの戻り値 Railsのdevelopment/productionについて FactoryBotのassociationとは
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

過去7日間の投稿をグラフ表示 Chartkick Turbolinks無効化

やりたいこと 過去7日間の投稿数を取得し、折れ線グラフで表示する. グラフが表示できなかったり、12日分表示してしまったり・・・ととにかく大変でした。 そもそもJavascriptの知識が浅いため、、難しかったです。 7日間の投稿データ取得 前回作成した記事と同様です。グラフ化が大変だったのでここはサクッと行きます 違う点は、、 コントローラーに記述しなくても、モデルで定義した値をviewで使用できるということです。 7日間の投稿データ取得 models/book.rb   scope :created_today, -> { where(created_at: Time.zone.now.all_day) } scope :created_yesterday, -> { where(created_at: 1.day.ago.all_day) } scope :created_2days, -> { where(created_at: 2.days.ago.all_day) } scope :created_3days, -> { where(created_at: 3.days.ago.all_day) } scope :created_4days, -> { where(created_at: 4.days.ago.all_day) } scope :created_5days, -> { where(created_at: 5.days.ago.all_day) } scope :created_6days, -> { where(created_at: 6.days.ago.all_day) } users_controller.rb def show @user=User.find(params[:id]) @books = @user.books.page(params[:page]).reverse_order @today_book = @books.created_today @yesterday_book = @books.created_yesterday @this_week_book = @books.created_this_week @last_week_book = @books.created_last_week @book=Book.new end users/show.html.erb <table class="table"> <thead> <tr> <th>6日前</th> <th>5日前</th> <th>4日前</th> <th>3日前</th> <th>2日前</th> <th>昨日</th> <th>今日</th> </tr> </thead> <tbody> <tr> <th><%= @books.created_6days.count %></th> <th><%= @books.created_5days.count %></th> <th><%= @books.created_4days.count %></th> <th><%= @books.created_3days.count %></th> <th><%= @books.created_2days.count %></th> <th><%= @yesterday_book.count %></th> <th><%= @today_book.count %></th> </tr> </tbody> </table> グラフ化①・・・chart.js ? chartkick をいれる chart.jsで検索するとchartkickが出てきます。 もう何を入れるのか分からなくなりました 以下の記事で別物であるということに気づきました。多くの記事で、chartkickを使用しているので、chartkickでやっていきたいと思います。 Gemインストール 以下の記事の通り行いました。groupdateもインストールしてしまいましたが、今回使わなかったです。 (正しくは、理解できず、うまく使えなかったです・・・) Turbolinksの無効化 無効化にしないと、グラフが表示されないのではないかと思います。 私は、とにかくTurbolinksの記述を全削除してしまいましたが、個別削除がベストですね。 以下の記事で全削除できます。 Viewの記述 Turbolinks全削除済みの場合 users/show.html.erb <canvas id="myChart" width="300" height="100"> </canvas> <script> var ctx = document.getElementById("myChart").getContext('2d'); var myChart = new Chart(ctx, { type: 'line', data: { labels: ['6日前', '5日前', '4日前', '3日前', '2日前', '1日前', '今日'], datasets: [{ label: "投稿数", data: [<%= @books.created_6days.count %>, <%= @books.created_5days.count %>, <%= @books.created_4days.count %>, <%= @books.created_3days.count %>, <%= @books.created_2days.count %>, <%= @yesterday_book.count %>, <%= @today_book.count%>], backgroundColor: 'rgba(255, 80, 120, 1.0)', borderColor: 'rgba(255, 80, 120, 1.0)', fill: false }] }, }); </script> Turbolinksを個別で無効化 ``` これで、折れ線グラフの表示ができました。 参考にさせていただいた記事 以下の記事でも、グラフができましたが12日分表示されてしまいました。 どうやって7日間分に抑えれば良いのだろう?? 今回、groupdate使えこなせてないですが、使えたら便利
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsのコントローラーでAPIの必須項目の入力チェックする

railsのコントローラーでAPIの必須項目が入力されているかチェックする方法です。 どんなケースで使うのか具体的な使い方を書いていきます。 rails guideには以下の説明がされています。 In addition, parameters can be marked as required and will flow through a predefined raise/rescue flow that will result in a 400 Bad Request being returned if not all required parameters are passed in. サンプルコード class UserController < ApplicationController rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing def index @users = User.search(user_search_params) end private def user_search_params params.require(:name, :email) params.permit(:name, :email) end def handle_parameter_missing(e) render json: { message: e.original_message }, status: 400 end end コードの説明 indexアクションではユーザの入力値を使ってユーザ情報の検索を行う。 ユーザの入力値はrequireメソッドを使用して必須項目の存在確認を行う。 必須項目が存在しない場合は例外ActionController::ParameterMissingが発生するのでrescue_fromで捕捉する。 rescue_fromで例外を捕捉した後handle_parameter_missing内でエラーメッセージを抽出しレスポンスのjsonを返却する。 コードの詳細な説明 1. actionの定義 ユーザの検索アクションです。User.search引数にユーザの入力値を検証したパラメーターを指定します。 def index @users = User.search(user_search_params) end 2. 入力値の検証 def user_search_params params.require(:name, :email) params.permit(:name, :email) end requireメソッドを使ってパラメータを検証します。 requireメソッドは引数に指定したkeyが存在しない場合、例外ActionController::ParameterMissingを発生させます。 3. 例外ActionController::ParameterMissingの捕捉 rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing def handle_parameter_missing(e) render json: { message: e.original_message }, status: 400 end rescue_fromでActionController::ParameterMissingを捕捉してエラー処理を行います。 エラー処理ではmessagekeyにoriginal_messageで取り出したエラーメッセージを格納します。 また、status codeに400を指定します。 エラーメッセージの抽出について original_messageメソッドを使用して簡潔なエラーメッセージのみを取り出す事ができます。 $ e.original_message => "param is missing or the value is empty: name" message メソッドだとkeyのサジェストが出てしまいます。 e.message => "param is missing or the value is empty: name\nDid you mean? target_date\n rate\n controller\n format" e.full_messageだとスタックとレースが表示されます。 参考 rails guide
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]値オブジェクトとは?〜使い方と注意点を押さえる〜

はじめに 開発の規模が大きくなればなるほど、モデルが肥大化するFat Modelという問題が発生しやすくなります。そこで、値(Value)オブジェクトという設計手法を利用することでこの問題が解決出来ますので、利用方法と注意点をしっかり押さえて導入を検討してみましょう! 値オブジェクトとは? 値オブジェクトとは、読んで字の如く、「値」を表すオブジェクトなのですが、この値とはドメイン駆動設計という設計手法に登場する概念で、同一性を判断できない情報のことを指します。 値オブジェクトを利用することで、コードを綺麗に保ち見通しが良くなることで、テストが書きやすくなり、Fat Modelの問題を解消できます。また、オープン・クローズドの原則という、あるクラスを修正したとき、他のクラスに影響が出ない設計を実装できます。 例 山田さんと田中さんがいるとします。この2人の血液型はどちらもA型です。血液型が同じだからといって、同一人物となるわけではありません。 こういった、値が同じでも同一だと断定できない情報のことを値オブジェクトと呼びます。 ※住所や名前なども同様です 前提条件 ユーザー(User)、管理者(Admin)という2つのクラスがあるとする それぞれ名前として姓と名を持つ 姓と名からフルネームを取得できる 使い方 それぞれのユーザーの名前を値オブジェクトにします。 1.名前を切り出したNameクラスを実装する。 app/values/name.rb class Name attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end def full_name "#{first_name} #{last_name}" end end 2.それぞれのユーザーのクラスに値オブジェクトを使えるように設定する。 app/models/user.rb class User < ApplicationRecord # 第一引数に値オブジェクト名、第二引数には属性のマッピング composed_of :name, mapping: [%w(first_name first_name), %w(last_name last_name)] end app/models/admin.rb class Admin < ApplicationRecord # 第一引数に値オブジェクト名、第二引数には属性のマッピング composed_of :name, mapping: [%w(first_name first_name), %w(last_name last_name)] end 利用例 Valueオブジェクトを用いた検索 users = User.where(name: Name.new('Taro', 'Yamada')) ユースケース 複数の値を組み合わせてひとつの値を算出するもの 名前(姓・名・ミドルネーム) 住所(国や県・市区町村など) 値の表現や比較を行うもの 日付 通過 気温 商品レビューにおける星の数 注意点 導入タイミングは、値オブジェクトを複数のクラスから利用することになったとき app/values下に配置 ファイル名はValueオブジェクトの名前にする name.rb address.rb 必要最低限のインタフェースのみを公開する 公開したインタフェースに対するユニットテストを書く 終わりに 上記の通り、何もかも切り出して値オブジェクトとしても良いわけではないようですね。注意点をしっかり押さえることで、見通しが良いコードを実装出来るので、これからタイミングを見て使っていきましょう! 参考 Rails: Value Objectを検討してみよう(翻訳) Railsのデザインパターン: Valueオブジェクト composed_of を使って Rails で値オブジェクトを扱う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いいね機能実装 [Ruby on Rails]

完成品 投稿にいいね機能を実装する。 テーブルは、Users,Booksに加えFavoritesを追加する。 実装の手順 ①モデルの作成 ②ルーティングを追加 ③それぞれのモデルに関連付けを追加 ④コントローラを作成 ⑤Viewを作成する ①モデルの作成 terminal $rails g model Favorite user_id:integer book_id:integer user_id:userとの紐づけ book_id:book投稿との紐づけ integer:データ型の整数 ②ルーティングを追加 routes.rb resources :books do resource :favorites , only: [:create , :destroy] end favoritesのshowページが不要で、idの受け渡しも必要ないので、resourceとなっています。 今回は、resources :booksにネストします。 resources :booksの中にresource :favorites , only: [:create , :destroy]を入れるという意味です。 ネストする理由は、ルートパスにあります。 routes.rb #ネストしない場合 resources :books resource :favorites , only: [:create , :destroy] ネストしないとルートパスが、下図になります。実はこのままだと、どの投稿(book_id)にいいねを付けたか特定することができません。 ネストすることでルートパスが下図になります。投稿(book_id)を特定することができますね。 ③それぞれのモデルに関連付けを追加 app/model/user.rb has_many :favorites, dependent: :destroy app/model/book.rb has_many :favorites, dependent: :destroy def favorited_by?(user) favorites.where(user_id: user.id).exists? end app/model/favorite.rb belongs_to :user belongs_to :book アソリエーション ※ user : favorite = 1 : 多  ※ book : favorite = 1 : 多 1つのuserからみて、いいねは多数あるのでhas_many 1つのbook(投稿)からみて、いいねは多数あるのでhas_many 1つのいいねからみて、user・book(投稿)は1つしかないのでbelongs_to has_manyの後にはdependent: :destroyを付けましょう。 favorited_by?メソッドを作成します。 このメソッドで、引数で渡されたユーザidがFavoritesテーブル内に存在(exists?)するかどうかを調べます。 ④コントローラを作成 terminal rails g controller Favorites app/controllers/favorites_controller.rb class FavoritesController < ApplicationController def create   book = Book.find(params[:book_id]) favorite = current_user.favorites.new(book_id: book.id) favorite.save redirect_to request.referer end def destroy book = Book.find(params[:book_id]) favorite = current_user.favorites.find_by(book_id: book.id) favorite.destroy redirect_to request.referer end end #必要に応じてインスタンス変数を付けましょう。(book => @book) current_userはログイン中のユーザーです。 request.refererは、元のviewに戻ってきます。いいねをクリック→いいねされたviewに戻ります。 なぜparamsの中が:book_idなのか?恐らくusr_idと混同するからと推測 ⑤Viewを作成する terminal #<% books.each do |book| %> <% if book.favorited_by?(current_user) %> <%= link_to book_favorites_path(book), method: :delete do %>♥<%= book.favorites.count %> <% end %> <% else %> <%= link_to book_favorites_path(book), method: :post do %>♡<%= book.favorites.count %> <% end %> <% end %> #<% end %> #book/indexで投稿を全て表示しているので今回はインスタンス変数なし <% if book.favorited_by?(current_user) %> いいねがクリックされている状態と、されていない状態で表示を変えます。 <%= book.favorites.count %> いいねされた数を表示しています。 以上で実装完了となります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む