- 投稿日:2020-10-22T23:01:24+09:00
ポートフォリオのダミーデータをFakerで作る【備忘録】
エンジニア初学者の方でポートフォリオのダミーデータを手入力で入れていくのは、とても面倒だし、かっこよくダミーデータを入れたいですよね。gem Fakerを使用すれば簡単に実現できます。
実装の流れ
作業時間は初めての方で30分程度になります。
1.gem Fakerの導入
2.fakerの日本語化
3.ターミナルからbundle install
4.seedファイルに記述
5.ダミーデータの作成/rails db:seed
6.DBに反映されているか確認それではやっていきましょう!
1.gem fakerの導入
まずは、GemfileにFakerを導入しましょう!
Gemfilegem 'faker'2.fakerの日本語化
次にfakerの日本語化ファイルを導入します。
下記のリンクから、ja.ymlをダウンロードorコピーしてください、rails内のconfig>locales>ja.ymlに置きます。
https://github.com/faker-ruby/faker/blob/master/lib/locales/ja.yml
次にapplication.rbのモジュール内にconfig.i18n.default_locale = :ja
を記述しますapplication.rbmodule hoge class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. config.i18n.default_locale = :ja #ここを追加 end end※ダウンロードorコピーはrawボタンからできます。(名前を付けてファイルを保存しましょう)
3.bundle install
ターミナルでbundle installしましょう!
4.seedファイルに記述
いよいよseedファイルにテストデータを入れていきます。
下記の場合、modelで作成した、name/character/emailのカラムに対して50個のランダムなダミーデータを入力してくれます。
Faker::のメソッド記述は、READE.meを確認しましょう!seeds.rb50.times do User.create( name: Faker::Name.name, character: Faker::Games::Pokemon.name, email: Faker::Internet.email, ) end #一番左のname:はモデルのカラム名次に、rails db:seedでデータの反映をします。
5. DBに反映されているか確認
コンソールから
rails c
で反映されているか確認しましょう。User.all※rails cはrails consoleの略
日本語化されたデータが入っていれば完了です。
お疲れさまでした。
- 投稿日:2020-10-22T22:36:55+09:00
turbolinksが悪さをして戸惑った話
この記事ではインストールしたRails 6.0.0を使っています
その中のJavaScriptについてです。
Railsでフリマアプリのコピーアプリを作っている中で、販売価格についての実装をJavaScriptで行っていました!
item_price.jswindow.addEventListener('load', () => { const itemPrice = document.getElementById("item-price"); itemPrice.addEventListener("input", () => { const inputValue = itemPrice.value; const addTaxDom = document.getElementById("add-tax-price"); addTaxDom.innerHTML = (Math.floor(inputValue * 0.1)); const saleProfit = document.getElementById("profit"); const value_result = inputValue * 0.1 saleProfit.innerHTML = (Math.floor(inputValue - value_result)); }) });実装には成功し喜んで次の実装に進み、また出品しようと価格をinputするとたまに手数料と利益の計算が作動しない時がありました。
なんでだろう?と感じいろいろ試行錯誤と調べた結果この記述が悪さをしていたみたいです。
require("turbolinks").start()
この記述はapplicaion.jsに定義した記述です。注目して欲しいのはこの中の"turbolinks"です。意味としては『Ajaxによるページ遷移の高速化のためのライブラリ』という感じです。
素晴らしいメリットなのですが、こいつはたまにJava Scriptに悪さをし、小さなバグを発生させたり、readyが呼ばれない、headerが変化しない等というデメリットがあるらしいです。笑
メリットもあるので使いたいですが、どのような時が使いどきなのでしょうか?turbolinksの使いどき
開発コストを上げてでもページのレンダリングを高速化したい場合
Javascriptの記述量が少ないとき
Railsから作られたビューを返す事がメインであるサイトなど逆にページのレンダリングを高速化する必要がない規模のサイトなど
Javascriptの記述量が多い時
ページごとにcssやJavaScriptを分けて記述している場合は使うメリットは減るということです。まとめ
Turbolinks使用の際は挙動をしっかり理解しておく必要がありますね。
メリット、デメリットを十分に考えたうえで使用用途をしっかり考え運用することをおすすめします。
- 投稿日:2020-10-22T22:25:28+09:00
html & rails メモ
はじめに
超ど初心者のメモです
フォーム
新規投稿フォーム
form_tag("url") do
で送った情報はparam[]
に入る<%=form_tag("/posts/create") do%> <div class="form"> <div class="form-body"> <textarea name="content"></textarea> <input type="submit" value="投稿"> </div> </div> <% end %>
param[:content]
とかで参照する。新規ユーザー登録
<%= form_tag("/users/create") do %> <p>ユーザー名</p> <input name="name" value="<%= @user.name %>"> <p>メールアドレス</p> <input name="email" value="<%= @user.email %>"> <input type="submit" value="新規登録"> <% end %>ログインフォーム
<%= form_tag("送りたいURL") do%> <p>メールアドレス</p> <input name="email"> <p>パスワード</p> <input type="password" name="password"> <input type="submit" value="ログイン"> <% end %>
- 投稿日:2020-10-22T22:16:35+09:00
Ruby on Rails チュートリアルの中で覚えておくべきこと・概念
はじめに
私はWeb系の自社開発企業に転職のためにRailsの勉強をしました!
その勉強の一環でRailsのチュートリアル第6版を実施していて、私はこれが大事 & 覚えておくべきと思ったことを記載します。
- Railsのチュートリアルをやったけど、あんまり頭に入ってない方
- Railsのチュートリアルってどんなこと学べるの?と思ってる方
- Railsでアプリを作成するときの基本を復習、学びたい方 は本記事が参考になるかと思います!
※本記事は5章〜14章までしか網羅してません。(5章からQiitaで記事に纏めて、備忘録にしようと思ったので。。。)
気が向いたら、1章〜4章も後で更新します。STAR WARS方式で。RubyとRailsの基礎用語、概念
★gemとは
- RubyGemsが公開してるRubyのパッケージのこと
- それらパッケージを管理するパッケージ管理システムのこと★bundle install
- Gemfileに記載してあるRailsでの開発に必要なものをInstallするコマンド★rails db:migrate
- railsで使用するデータベースの構造(テーブル、カラム)を変更するときに利用する機能
・rails db:migrateの大まかな使用の流れ
1. マイグレーションファイルを作成&内容を記載
2. $ rails db:migrateコマンドでマイグレーションを順番に実行してデータベースに変更を加える第5章 レイアウトを作成する
5.2.1 アセットパイプライン
アセットパイプラインというのは主に(以下の)3つの主要機能に分かれている。
(以下 ※説明 ★簡単に要約)[アセットパイプラインの最大のメリット]
本番アプリケーションで効率的になるよう最適化されたアセットも自動に生成されること。(=本番環境でページの読み込みを早くするのを自動的に行なってくれる。)
そうすることにより、「開発環境ではファイルの可読性」を保ち、「本番環境ではアプリの実行速度をあげる」という2つの異なる環境で各々に最強の環境を提供するという両立ができる。
★要するに、「開発環境」も「本番環境」も最適化されて、最高に効率/利便性が良いということ
アセットディレクトリ
※開発に必要なもの(アセット)を置いておくディレクトリ
- app/assets: 現在のアプリケーション固有のアセット
- lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
- vendor/assets: サードパーティのアセット(デフォルトでは存在しません)
マニフェストファイル
※上記の「アセット」マニフェストファイルを一つのファイルに纏めることができる。
- アセットを纏める処理を行うのはSprocketsというgem
- マニフェストファイルで纏めれるのはCSSとJavaScript。(※画像ファイルには適応されない。)
*= require_tree . #app/assets/stylesheetsの中の全てのCSSファイルを含むようにしてる *= require_self #マニフェストファイル(application.css)自身も含むようにしてるマニフェストファイルの中の上記のようなコメントをSporocketsが読み込んでいる。
3.プリプロセッサエンジン
※Railsはプリプロセッサエンジンを介して、アセットを実行し、ブラウザに配信できるように、マニフェストファイルを用いて結合し、サイト用に準備する★要するにアセットを一つに纏めて、ブラウザで使いやすくしてる
5.3.4 リンクのテスト
assert_select "a[href=?]", help_path,※help_pathが"/help”という定義は行っている前提。
上記のコードは、以下の"/help”パスが存在するか確認するテスト。
"a[href=?]"の?の部分がhelp_pathに置き換わる。(“/help”が存在するか確認)<a href="/help”>...</a>assert_select "a[href=?]", root_path, count: 2のcountはroot_pathへのリンクは2つあり、2つテストすることを指す。
assert_selectには色々な指定の仕方があります。
以下がそのうちのいくつかの代表例です。
第5章のまとめ
- Railsのパーシャルは効率化のために使われ、別ファイルにマークアップを切り出すことができる。
- Bootstrapフレームワークを使うと、レスポンシブで良いデザインを素早く実装できる
- SassとAsset Pipelineは、(開発効率のために切り分けられた)CSSの冗長な部分を圧縮し、本番環境に最適化した結果を出力する
- Railsのルーティングは自由にルールを定義できて、そのときに名前付きルートも使えるようなる。
- 統合テストはページ感遷移を効率的にシュミレーションできる。第6章 ユーザーのモデルを作成する
6.1 Userモデル
Active Record:データベースとやりとりをするデフォルトのRailsライブラリ
モデルを作成するときは、generate modelというコマンドを使います。
例) nameやemailといった属性を付けたUserモデルの作成$ rails generate model User name:string email:string[Railsの命名慣習]
- コントローラ名:複数形(例:Users)
- モデル名:単数形(例:User)
└モデルは一人のユーザを表すから
- テーブル名:複数形
└テーブル(DB)は複数のユーザの情報を持つから6.1.3 ユーザーオブジェクトを作成する
- user = User.new: インスタンスの生成
- user.save: モデルの保存 ※下記の>>はrails console上で実行してることを意味する。
>> user = User.new(name: "Michael Hartl", email: "michael@example.com") >> user.save
- User.create: Active Recordを通じて、モデルの生成と保存を同時に行う方法
>> User.create(name: "Another Sky”, email: "anothersky@example.org")
- モデルへのアクセス (<オブジェクト名>.<キー>) 例)
>> user.name => "Michael Hartl" >> user.email => "mhartl@example.com" >> user.updated_at => Mon, 23 May 2016 19:05:58 UTC +00:00
- Active Recordでデータが形成された順で検索する方法
>> User.find(1) => #<User id: 1, name: "Michael Hartl", email: "michael@example.com", created_at: "2019-08-22 01:51:03", updated_at: "2019-08-22 01:51:03">
- Active Recordで特定の属性(データ)でユーザーを検索する方法
>> User.find_by(email: "michael@example.com") => #<User id: 1, name: "Michael Hartl", email: "michael@example.com", created_at: "2019-08-22 01:51:03", updated_at: "2019-08-22 01:51:03">6.2 ユーザーを検証する
Active Recordでよく使われるValidation(検証)ケース
1. Presence(存在性)
2. Length(長さ)
3. Format(フォーマット)
4. Uniqueness(一意性)テスト駆動開発のテストの進め方
1. 有効なモデルのオブジェクトを作成
2. その属性のうちの1つを有効でない属性に意図的に変更
3. バリデーションで失敗するかどうかをテストする
4. 念のため、最初に作成時の状態に対してもテストを書いておき、最初のモデルが有効であるかどうかも確認
5. 4.のテストすることで、バリデーションのテストが失敗したとき、バリデーションの実装に問題があったのか、オブジェクトそのものに問題があったのかを確認することができるrails test:models: モデルに関するテストだけを走らせるコマンド
$ rails test:models6.3.1 ハッシュ化されたパスワード
マイグレーション名は自由に指定できる。
末尾を指定(to_usersに)しておくことで、usersテーブルにカラムを追加するマイグレーションがRailsによって自動的に作成される。例)add_password_digest_to_usersというマイグレーションファイルを生成するためには、次のコマンドを実行します。
$ rails generate migration add_password_digest_to_users password_digest:string6章のまとめ
- Active Recordを使うと、データモデルを作成したり、操作したりするための多くのメソッドが使える
- Active RecordのValidationを使うと、モデルに対して制限を追加できる
- よくあるValidationは、「存在するか」「長さ」「フォーマット」
- データベースにインデックスを追加すると検索効率が飛躍的に向上するし、データベースレベルでの一意性を保証するためにもインデックスを使える。
第7章 ユーザー登録
7.3.3 エラーメッセージ
「shared」: 複数のビューで使われるパーシャルは専用のディレクトリ
7.3.4 失敗時のテスト
assert_select: テストの対象がCSSの
・クラスの場合→div#CSSのid名assert_select 'div#error_explanation'クラスの場合→div.CSSのクラス名
assert_select 'div.field_with_errors'7.6.1 本章のまとめ
- debugメソッドを使うことで、有意なデバッグ情報を表示できる
- Sassのmixin機能を使うと、CSSのルールをまとめたり、変数のように他の場所でもmixinで指定したCSS情報を使用できる
- Railsでは簡単に標準的なRESTfulなURLを通した、データ管理が可能
- form_withヘルパーは、Active Recordのオブジェクトに対応したフォームを作成する
- flash変数を使うと、一時的なメッセージを表示できる
- 統合テストを使うと、送信フォームの振る舞いを検証したり、バグの発生を検知できる
第8章 基本的なログイン機構
8.1 セッション
HTTPはステートレスなプロトコル
- 状態管理がないプロトコルということ
- 前後のリクエストの情報を全く利用せず、独立したトランザクション(処理)として扱われる
- なのでHTTPプロトコル内「には」、ユーザのID等の情報を保持する「手段」がない
- ユーザ情報などをWebアプリケーション上で管理する際は、「セッション(Session)」で半永続的な接続をクライアントとサーバ間に別途設定する必要がある
8.1.5 フラッシュのテスト
assert_templateとは:assert_template後にあるURLがビューを描画しているかをテストする。
※下記の場合は、sessions/newがビューを描画してるかのテストassert_template 'sessions/new'コラム 8.1. 「||=」とは何か?
Rubyではnilとfalseを覗いて、あらゆるオブジェクトがtrueになるように設計されている。
Rubyでは、||演算子をいくつも連続して式の中で使う場合、項を左から順に評価し、最初にtrueになった時点で処理を終えるように設計されてる。
この評価法は短絡評価(short-circuit evaluation)と呼ぶ。&&演算子も似たような設計になってるが、項を左から評価して最初にfalseになった時点で処理を終了する点が異なる点である。
8.2.4 レイアウトの変更をテストする
.&
safe navigation演算子(または"ぼっち演算子)
Rubyのぼっち演算子を使うと、obj && obj.methodのようなパターンをobj&.methodのように凝縮した形で書けます。
例を上げると、以下のような論理演算子コードがif user && user.authenticate(params[:session][:password])以下のように簡略化できます。
if user&.authenticate(params[:session][:password])
&& user.authenticate
が&.authenticate
と簡略化できています。
Ruby初学者としては簡略化しすぎでは?と少し混乱するのですが、
ぼっち演算子は使用されることが多いようなので、自分で使ってみて覚える努力が必要そうです。8.4.1 本章のまとめ
- Railsの
session
メソッドを使うと、ページ遷移時の状態を保持できる。一時的な状態の保持にはcookiesも使える。(※今後あらゆるブラウザがクロスドメインでのcookies共有を禁止にするため、Rakute○のドメインで取得した、Rakute○IDなどを別ドメイン(Rakute○以外のドメイン)では使用できなくなる。)session
メソッドを使うと、ユーザIDなどをブラウザに一時保存できる- テスト駆動開発(TDD)はレグレッションバグを防ぐときに便利
- 統合でストでは、ルーティング、DBの更新、レイアウトの変更が正常に実施されてるかテストできる
9章 発展的なログイン機構
9.1.1 記憶トークンと暗号化
cookieを盗み出す有名な方法は4つある。
1. 管理の甘いネットワークを通過するネットワークパケットからパケットスニッファという特殊なソフトで直接cookieを取り出す
2. DBから記憶トークンを取り出す
3. クロスサイトスクリプティング(XSS)を使う
4. ユーザがログインしてるPCやスマホを直接操作してアクセスを奪い取る1.の対処は7.5のSSLをサイトに対応すること
2.の対応は本チュートリアルでは、DBには記憶トークンをハッシュ化して保存してること
3.の対応はRailsでは自動的に対策されてる
4.システム側で根本的防衛策を講じることは不可能■attr_accessorの利用用途
読み取りも書き込みもできるオブジェクトの属性を定義したい時
・name, descriptionという属性を持つUserオブジェクトを定義の仕方
Railsの場合class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :description t.timestamps null: false end end endDBを扱わない純粋なRubyコードの場合
class User attr_accessor :name, :description endちなみに
attr_reader
は読み出し専用の属性を定義したいときに使い、
attr_writer
は書き込み専用の属性を定義したいときに使う。コラム 9.2. 10種類の人々
「この世には10種類の人間がいる。2進法を理解できる奴と、2進法を理解できない奴だ」は、この業界に古くから伝わるジョーク(らしい)です。
ローランドみたいですね(笑)もしかしたらローランドはここからパクったのか??■三項演算子
以下のif elseコードがif boolean? var = foo else var = bar end以下の様に短縮できる
var = boolean? ? foo : bar三項演算子をメソッドの戻り値として使うこともよくあります。
※true, falseで関数の実行結果を判断するなど。9.3.2 [Remember me]をテストする
assert_equal
は、<期待>, <実際の値>の順で値を並べる。assert_equal <expected>, <actual>9.4.1 本章のまとめ
- Railsではページ遷移の際に「状態」を保持することができる。ページの状態を長時間保持したいときは、
cookies
メソッドを使って永続的なセッションにしましょう- remember_token と remember_digest をユーザごとに関連付けて、永続的セッションが実現できる
cookies
メソッドを使うと、ユーザのブラウザにcookiesなどを保存できる- (一般的に)セッションとcookieをそれぞれ削除すると、ユーザのログアウトを実現できる
第10章 ユーザーの更新・表示・削除
10.1 ユーザーを更新する
target="_blank"
を使用すると、リンク先を新しいタブ(またはウィンドウ)で開くようになるので、別のWebサイトへリンクするときに便利な要素。
★個人的にリンク先へ飛ぶときは別のタブが嬉しいから、自分が実装するときはこれは絶対実装したいなと思った!
個人的にはPC用(PCが主なクライアントが想定)のサイトは絶対コレ導入すべきやと思う!<a href="https://gravatar.com/emails" target="_blank">change</a>Railsは、
form_with(@user)
を使ってフォームを構成すると、@user.new_record?
がtrue
のときにはPOST
を、false
のときにはPATCH
を使います。10.2.3 フレンドリーフォワーディング
*Tutorialでは@userではなくuserが使われているが、使うとusers_login_testでエラーが発生するので@userを使う。
sessions_controller.rbdef create @user = User.find_by(email: params[:session][:email].downcase) # paramsハッシュで受け取ったemail値を小文字化し、email属性に渡してUserモデルから同じemailの値のUserを探して、user変数に代入 if @user && @user.authenticate(params[:session][:password]) # user変数がデータベースに存在し、なおかつparamsハッシュで受け取ったpassword値と、userのemail値が同じ(パスワードとメールアドレスが同じ値であれば)true log_in @user # sessions_helperのlog_inメソッドを実行し、sessionメソッドのuser_id(ブラウザに一時cookiesとして保存)にidを送る params[:session][:remember_me] == '1' ? remember(@user) : forget(@user) # ログイン時、sessionのremember_me属性が1(チェックボックスがオン)ならセッションを永続的に、それ以外なら永続的セッションを破棄する redirect_back_or @user # userの前のページもしくはdefaultにリダイレクト else flash.now[:danger] = 'Invalid email/password combination' # flashメッセージを表示し、新しいリクエストが発生した時に消す render 'new' # newビューの出力 end end*実際のエラー文
Error: UsersLoginTest#test_login_with_remembering: NoMethodError: undefined method `remember_token' for nil:NilClass test/integration/users_login_test.rb:60:in `block in <class:UsersLoginTest>'10.5.1 本章のまとめ
- ユーザーは、編集フォームからPATCHリクエストをupdateアクションに対して送信し、情報を更新する
- Strong Prameters (params[:foobar])を使うことによって、安全にWeb上から変更させることができる
- beforeフィルタを使うと、特定のアク4が実行される前にメソッドを呼び出すことができる(※めちゃ便利!!色んな活用用途がありそう!!)
- Authorization(認可)のテストでは、特定のHTTPリクエストを直接送信する簡単なテストと、ブラウザの操作をシミュレーションする(ユーザが実際にする操作)難度の高いテスト(統合テスト)の2つを実行した(個人的には、統合テストのシミュレーションに必要なテストを考えるのは、論理的に考える必要があって、建設的に思考をこらせて楽しかった。)
- フレンドリーフォワーディングは実際のアプリを作るときは絶対いるから、覚えて積極的に実装すべき(UX的に基本的にフレンドリーフォワーディングは必須)
rails db:seed
コマンドは、db/seeds.rb
にあるサンプルデータをDBに流し込むrender @users
を実行すると、自動的に_user.html.erb
パーシャルを参照し、各ユーザーをコレクションとして表示する- boolean型のadmin属性をUserモデルに追加すると、admin?という論理オブジェクトを返すメソッドが自動的に追加される
第11章 アカウントの有効化
ユーザActivationの流れ
1. ユーザーの初期状態は「有効化されていない」(unactivated)にしておく。
2. ユーザー登録が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する。
3. 有効化ダイジェストはデータベースに保存しておき、有効化トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく。
4. ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、データベース内に保存しておいた有効化ダイジェストと比較することでトークンを認証する。
5. ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated)に変更する。11.3.3 有効化のテストとリファクタリング
assigns
メソッドを使うと、対応するアクション内のインスタンス変数にアクセスできるようになる。
例えば、Usersコントローラのcreate
アクションでは@user
というインスタンス変数があるが、テストでassigns(:user)
と書くと、userインスタンス変数にアクセスできるようになる。testでエラーが出た。。
こちらのteratailの記事を参考に以下のコードをdef create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) if user.activated? log_in user params[:session][:remember_me] == '1' ? remember(user) : forget(user) redirect_back_or user else message = "Account not activated. " message += "Check your email for the activation link." flash[:warning] = message redirect_to root_url end else flash.now[:danger] = 'Invalid email/password combination' render 'new' end end以下のようにすると成功しました〜
def create @user = User.find_by(email: params[:session][:email].downcase) if @user && @user.authenticate(params[:session][:password]) if @user.activated? log_in @user params[:session][:remember_me] == '1' ? remember(@user) : forget(@user) redirect_back_or @user else message = "Account not activated. " message += "Check your email for the activation link." flash[:warning] = message redirect_to root_url end else flash.now[:danger] = 'Invalid email/password combination' render 'new' end end11.5.1 本章のまとめ
- アカウント有効化はActive Recordオブジェクトではないが、セッションの場合と同様に、リソースでモデル化できる
- Railsはメール送信で扱うAction Mailerのアクションとビューを生成できる
- Action MailerはテキストメールとHTMLメールの両方を利用できる
- Mailer Actionで定義したインスタンス変数は、他のアクションやビューと同様、Mailerのビューから参照できる
- アカウント有効化のために、生成したトークンを使って一意のURLを作る
- SendGridを使うと、production環境からメール送信できる
12章 パスワードの再設定
12.3.3 パスワードの再設定をテストする
・assignsメソッドはコントローラのインスタンス変数をテストするメソッド。
引数にインスタンス変数をシンボル型で渡す。
そうすることでインスタンス変数にアクセスできるようになり、テストができる。@user = assigns(:user)12.5.1 本章のまとめ
- Railsはメール送信で扱うAction Mailerのアクションとビューを生成できる
- より安全なパスワード再設定のために、ハッシュ化したトークン(ダイジェスト)を使う
第13章ユーザーのマイクロポスト
13.4.1 基本的な画像アップロード
■Active Storage
Active Storageを使うことで画像を簡単に扱うことが出来、画像に関連付けるモデルも自由に指定できます。
Active Storageは汎用性が高く、平文のテキストやPDFファイル、音声ファイルなど様々なバイナリファイルを扱えます。Active Storage APIの中で最初に知っておく必要があるのはhas_one_attachedメソッドです。これは、指定のモデルと、アップロードされたファイルを関連付けるのに使います。
has_one_attached は指定のモデルとアップロードされたファイルを関連付けるのに使えます。
has_one_attached の場合、「マイクロソフト1件に付き画像は1件」ですが、
has_many_attached を使えば、「マイクロソフト1件に付き複数の画像」を添付できます。13.5.1 本章のまとめ
- Rails は複数のキーインデックスをサポートしてる
- Userは複数のMicropostsを持っていて(has_many)、Micropostは一人のんUserに依存してる(belongs_to)
- user.microposts.build(...)というコードは引数で与えたユーザに関連付けされたマイクロポストを返す
dependent: :destroy
オプションを使うと、関連付けされたオブジェクトと自分自身を同時に削除する- fixtureは関連付けを使ったオブジェクトの作成もサポートしてる
- パーシャルを呼び出すときに一緒に変数を渡すことができる
where
メソッドを使うとActive Recordを通して選択(部分集合を取り出すこと)ができる ※生のSQL文と同じ様な文章で取得できる第14章ユーザーをフォローする
14.2.2 統計と[Follow]フォーム
@user ||= current_user上記のコードは
@user
がnilではない場合は何もせず、nilの場合には@user
にcurrent_userを代入するというコード14.4.3 本章のまとめ
- has_many :throughを使うと、複雑なデータ関係をモデリングできる
- has_manyメソッドには、クラス名や外部キーなど、いくつものオプションを渡すことができる
- 適切なクラス名と外部キーと一緒にhas_many/has_many :throughを使うことで、能動的関係(フォローする)や受動的関係(フォローされる)がモデリングできた
- ルーティングは、ネストさせて使うことができる
- whereメソッドを使うと、柔軟で強力なデータベースへの問い合わせが作成できる
- Railsは(必要に応じて)低級なSQLクエリを呼び出すことができる
番外編
■assertの一覧表
メソッド 説明 assert_template(expected, message = nil) そのアクションで指定されたテンプレートが描写されているかを確認する assert_not( test, [msg] ) testがfalseかを確認する。 assert_select "div.nav" selector(div)に合致した要素の内容を引数equality(nav)でチェック ※assert_selectは柔軟でパワフルな機能で、多くのオプションがあるが、レイアウト内で頻繁に変更されるHTML要素 (リンクなど) をテストするぐらいに抑えておくとよい。
■「!!」(「バンバン(bang bang)」と読みます)
オブジェクトをBoolean(論理)値に変換できる演算子。
nilはfalseになります。>> !!nil => falseその他のあらゆるRubyのオブジェクトは、ゼロですらtrueです。
>> !!0 => true■! ビックリマーク(感嘆符)について
◆!マークを使うことで、データ(〜〜属性)を直接変更できる。
★!を使わん場合before_save { self.email = email.downcase }★!を使う場合
before_save { email.downcase! }◆!をメソッドにつけることで例外を発生させられる!!
create, saveでの例。
! をつけない場合(create save)
・処理に実行して、レコードの作成/保存に失敗して際、nil
が返される。
! をつける場合(create! save!)
例外(例:ActiveRecord::RecordNotFound ERROR
)を発生させられる。■HTMLのtype= “email”
htmlでtype=“email”にすると、携帯電話から入力フォームをタップすると、メールアドレス用に最適化された特別なキーボードが表示される。■
private
キーワード
- そのファイル(クラス)内でしか使わないメソッドを定義するために使われる
- 他のファイル(クラス)ではprivate内で定義されたメソッドは使用できない。
- 他のファイルでは使われないメソッドをprivateにすることで、想定外のエラーが避けられるprivate def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) endPATCH Method for HTTP
- Putメソッドは更新というよりは置換
- Putsで置き換え先が「空」でも、値を置き換える(置き換えようとしてる値をそのまま代入する)
- Patchメソッドは既存のリソースを更新・変更・修正■<<演算子(Shovel Operator)
<<
で配列の最後に追記することができます。user.following << other_userさいごに
結構時間かかりました。。Cloud9のエラー等も伴いましたが、結果1ヶ月強くらいかかってしまいました。。
結構ボリューミーだったので仕方ないと思ってますが、もっと早く終わらせてる人もいるので、Portfolio作りは早く終わらせようと思います!!読んでいただきありがとうございました!!
- 投稿日:2020-10-22T22:14:52+09:00
【Ruby on Rails】投稿にタグ付け・インクリメンタルサーチ機能を実装する方法(gemなし)
タグ付けを行う前の前がき
本記事の概要
・投稿にタグ付けをできるようにする。
・文字を入力する度に自動検索を行ってくれる機能(インクリメンタルサーチ)をタグにを実装する開発環境
Mac OS Catalina 10.15.4
ruby 2.6系
rails 6.0系
※rails newでアプリケーションは作成済みであることを前提としています。タグ付け機能の完成形イメージ
上図のGifのように、タグを入力し始めるとDBに保存されているタグを元にオススメタグを表示できるようにしています。
今回の記事を元にタグ付け機能を実装できれば、タグ検索なども容易に実装できるかと思います。タグ付け機能実装の流れ
1. Tag,Post,PostTagRelation、Userモデルを作成
2. 各種モデルのmigrationファイルを編集
3. Formオブジェクトを導入
4. ルーティングの設定
5. postsコントローラーを作成、アクション定義
6. ビューファイルの作成
7. インクリメンタルサーチの実装(JavaScript)上記の手順で実装を行ってきます。
1.Tag,Post,PostTagRelation,Userモデルを作成
まずは、各種モデルを導入しましょう。
% rails g model tag % rails g model post % rails g model post_tag_relation % rails g devise userそのまま、導入した各モデルを関連付け(アソシエーション)してバリデーションを記述しましょう。
post.rbclass Post < ApplicationRecord has_many :post_tag_relations has_many :tags, through: :post_tag_relations belongs_to :user endtag.rbclass Tag < ApplicationRecord has_many :post_tag_relations has_many :posts, through: :post_tag_relations validates :name, uniqueness: true end「through: :中間テーブル」とすることで、多対多の関係であるPostモデルとTagモデルのアソシエーションを組んでいます。
注意点としては、throughによる参照前に中間テーブルの紐付けを行う必要があります。
(コードは上から読み込まれるので、 has_many :posts, through: :post_tag_relations → has_many :post_tag_relationsの順で書いてしまうとエラーになります。)post_tag_relationclass PostTagRelation < ApplicationRecord belongs_to :post belongs_to :tag enduser.rbclass User < ApplicationRecord #<省略> has_many :posts, dependent: :destroy validates :name, presence: trueUserモデルのhas_manyのオプションに、dependent: :destroyと付けているのは、親要素であるユーザー情報が削除された時にそのヒトの投稿も併せて削除されるようにするためです。
なお、PostモデルとTagモデルにて空データを保存させないようにするための記述(validates :〇〇, presence: true)に関しては、後ほど作成するフォームオブジェクトでまとめて指定しますので、今は必要ありません。
2.各種モデルのmigrationファイルを編集
続いて、作成したモデルにカラムを追加していきます。
(最低限必要なのは、tagのnameカラムくらいなので、その他はお好みでアレンジされてください。)postのマイグレーションファイルclass CreatePosts < ActiveRecord::Migration[6.0] def change create_table :posts do |t| t.string :title, null: false t.text :content, null: false t.date :date t.time :time_first t.time :time_end t.integer :people t.references :user, foreign_key: true t.timestamps end end endpostのマイグレーションファイルで外部キーとしてuserを参照しているのは、後ほどuser名を投稿一覧で表示するためです。
tagのマイグレーションファイルclass CreateTags < ActiveRecord::Migration[6.0] def change create_table :tags do |t| t.string :name, null: false, uniqueness: true t.timestamps end end end上記のnameカラムにuniqueness: trueを適用しているのは、タグ名の重複を防ぐために導入しています。
(タグは同じ名前のものが何度も使われることが想定されるので、重複を防いだらタグ付け機能として成り立たなくない?と思われるかもですが、既存のタグを投稿に反映させる方法は後ほど登場します。)post_tag_relationのマイグレーションファイルclass CreatePostTagRelations < ActiveRecord::Migration[6.0] def change create_table :post_tag_relations do |t| t.references :post, foreign_key: true t.references :tag, foreign_key: true t.timestamps end end endこのpost_tag_relationモデルが、多対多の関係であるpostモデルとtagモデルの中間テーブルの役割を担っています。
userのマイグレーションファイルclass DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :name, null: false t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" #<省略>ユーザー名を利用したかったので、nameカラムを追加しました。
カラムの編集が終わったら、忘れずに下記コマンドを実行しましょう。
% rails db:migrate※まだDBを作成していないという方は、先にrails db:createを実行する必要があります。
3.Formオブジェクトを導入
今回の実装では投稿フォームからpostsテーブルとtagsテーブルへ同時に入力値を保存させたいので、Formオブジェクトを利用します。
まず、appディレクトリの中にformsディレクトリを作り、その中にposts_tag.rbファイルを作成しましょう。
そして、下記のようにpostsテーブルとtagsテーブルに同時に値を保存するためのsaveメソッドを定義します。posts_tag.rbclass PostsTag include ActiveModel::Model attr_accessor :title, :content, :date, :time_first, :time_end, :people, :name, :user_id with_options presence: true do validates :title validates :content validates :name end def save post = Post.create(title: title, content: content, date: date, time_first: time_first, time_end: time_end, people: people, user_id: user_id) tag = Tag.where(name: name).first_or_initialize tag.save PostTagRelation.create(post_id: post.id, tag_id: tag.id) end end4. ルーティングの設定
続いて、postsコントローラーのindex・new・createアクションを動かすためのルーティングを設定します。
routes.rbresources :posts, only: [:index, :new, :create] do collection do get 'search' end endcollection内で定義しているsearchアクションへのルーティングは、インクリメンタルサーチ機能で利用します。
5. postsコントローラーを作成、アクション定義
ターミナルでコントローラを生成します。
% rails g controller posts生成されたpostsコントローラファイル内のコードは下記のようになります。
posts_controller.rbclass PostsController < ApplicationController before_action :authenticate_user!, only: [:new] def index @posts = Post.all.order(created_at: :desc) end def new @post = PostsTag.new end def create @post = PostsTag.new(posts_params) if @post.valid? @post.save return redirect_to posts_path else render :new end end def search return nil if params[:input] == "" tag = Tag.where(['name LIKE ?', "%#{params[:input]}%"]) render json: {keyword: tag} end private def posts_params params.require(:post).permit(:title, :content, :date, :time_first, :time_end, :people, :name).merge(user_id: current_user.id) end endcreateアクションでは、先程Formオブジェクトで定義したsaveメソッドを使ってPostsモデルとTagsテーブルへposts_paramsで受け取った値を保存しています。
searchアクションでは、JS側で取得したデータ(タグ入力フォームで打ち込まれた文字列)を元に、 where + LIKE句でtagsテーブルからデータを引っ張り出し、reder jsonでJSに返しています。(JSファイルは後ほど登場。)
そういう訳なので、↑のsearchアクションは、インクリメンタルサーチを実装しないのであれば必要ありません。
6.ビューファイルの作成
new.html.erb<%= form_with model: @post, url: posts_path, class: 'registration-main', local: true do |f| %> <div class='form-wrap'> <div class='form-header'> <h2 class='form-header-text'>タイムライン投稿ページ</h2> </div> <%= render "devise/shared/error_messages", resource: @post %> <div class="post-area"> <div class="form-text-area"> <label class="form-text">タイトル</label><br> <span class="indispensable">必須</span> </div> <%= f.text_field :title, class:"post-box" %> </div> <div class="long-post-area"> <div class="form-text-area"> <label class="form-text">概要</label> <span class="indispensable">必須</span> </div> <%= f.text_area :content, class:"input-text" %> </div> <div class="tag-area"> <div class="form-text-area"> <label class="form-text">タグ</label> <span class="indispensable">必須</span> </div> <%= f.text_field :name, class: "text-box", autocomplete: 'off' %> </div> <div>【おすすめタグ】</div> <div id="search-result"> </div> <div class="long-post-area"> <div class="form-text-area"> <label class="form-text">イベント日程</label> <span class="optional">任意</span> </div> <div class="schedule-area"> <div class="date-area"> <label>日付</label> <%= f.date_field :date %> </div> <div class="time-area"> <label>開始時刻</label> <%= f.time_field :time_first %> <label class="end-time">終了時刻</label> <%= f.time_field :time_end %> </div> </div> </div> <div class="register-btn"> <%= f.submit "投稿する",class:"register-blue-btn" %> </div> </div> <% end %>僕のアプリ実装で使っていたビューファイルをベタ貼りしているため、コードが冗長になっていますが要はフォームの内容を@post等でルーティングに送れていれば問題ありません。
index.html.erb<div class="registration-main"> <div class="form-wrap"> <div class='form-header'> <h2 class='form-header-text'>タイムライン一覧ページ</h2> </div> <div class="register-btn"> <%= link_to "タイムライン投稿ページへ移る", new_post_path, class: :register_blue_btn %> </div> <% @posts.each do |post| %> <div class="post-content"> <div class="post-headline"> <div class="post-title"> <span class="under-line"><%= post.title %></span> </div> <div class="more-list"> <%= link_to '編集', edit_post_path(post.id), class: "edit-btn" %> <%= link_to '削除', post_path(post.id), method: :delete, class: "delete-btn" %> </div> </div> <div class="post-text"> <p>■概要</p> <%= post.content %> </div> <div class="post-detail"> <% if post.time_end != nil && post.time_first != nil %> <p>■日程</p> <div class="post-date"> <%= post.date %> <%= post.time_first.strftime("%H時%M分") %> 〜 <%= post.time_end.strftime("%H時%M分") %> </div> <% end %> <div class="post-user-tag"> <div class="post-user"> <% if post.user_id != nil %> ■投稿者: <%= link_to "#{post.user.name}", user_path(post.user_id), class:'user-name' %> <% end %> </div> <div class="post-tag"> <% post.tags.each do |tag| %> #<%= tag.name %> <% end %> </div> </div> </div> </div> <% end %> </div> </div>こちらも同様に冗長なので、適宜必要なところだけ参照ください...
## 7.インクリメンタルサーチの実装(JavaScript)
こちらは、JSファイルをいじります。
tag.jsif (location.pathname.match("posts/new")){ window.addEventListener("load", (e) => { const inputElement = document.getElementById("post_name"); inputElement.addEventListener('keyup', (e) => { const input = document.getElementById("post_name").value; const xhr = new XMLHttpRequest(); xhr.open("GET", `search/?input=${input}`, true); xhr.responseType = "json"; xhr.send(); xhr.onload = () => { const tagName = xhr.response.keyword; const searchResult = document.getElementById('search-result') searchResult.innerHTML = '' tagName.forEach(function(tag){ const parentsElement = document.createElement('div'); const childElement = document.createElement("div"); parentsElement.setAttribute('id', 'parents') childElement.setAttribute('id', tag.id) childElement.setAttribute('class', 'child') parentsElement.appendChild(childElement) childElement.innerHTML = tag.name searchResult.appendChild(parentsElement) const clickElement = document.getElementById(tag.id); clickElement.addEventListener('click', () => { document.getElementById("post_name").value = clickElement.textContent; clickElement.remove(); }) }) } }); }) };location.pathname.matchを使って、postsコントローラのnewアクションが発火した時に、コードが読み込まれるようにしています。
JS内のおおまかな処理としては、
①keyupでイベント発火させて、タグフォームの入力値をコントローラーへ送る(xhr.〇〇辺り)
②xhr.onload以下でコントローラーから返ってきた情報を元に、予測タグをフロントに表示させる。
③予測タグがクリックされたら、そのタグがフォームに反映される。以上で、タグ付け機能の実装とインクリメンタルサーチの実装ができました。
ざっくりとした記事にはなりますが、最後までお読み頂きありがとうございました!
- 投稿日:2020-10-22T22:12:27+09:00
【Rails】投稿時間の日本語化
application.rbを修正
以下を追記。
config.i18n.default_locale = :ja
config.time_zone = 'Tokyo'application.rbrequire_relative 'boot' require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module ChatApp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.0 config.i18n.default_locale = :ja config.time_zone = 'Tokyo' # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. end endja.ymlファイルを作成
config/locales/ja.ymlja: time: formats: default: "%Y/%m/%d %H:%M:%S"lメソッドを用いて時間を日本化
以下は一例
html.erb<div class="message-date"> <!-- 投稿した時刻を出力する --> <%= l message.created_at %> </div>
- 投稿日:2020-10-22T19:55:30+09:00
メソッドについて
- 投稿日:2020-10-22T19:41:59+09:00
文字列の連結方法
文字列の連結
文字列を連結させるには、文字列同士を+(プラス)で繋ぎます。数式と同じような書き方です。
以下の例では、HelloとWorldの文字列が繋がっています。
【例】irb# 文字列を連結 irb(main):001:0> "Hello " + "World" => Hello Worldirbでコードを実行してみましょう!!
文字列を連結してみましょう。
以下の例のようにirbでコードを実行してみて下さい。irb# 文字列を連結 irb(main):001:0> "Good" + " morning" # 続けてこのように表示されれば成功 => "Good morning"そうすることで、上記のように文字列の連結ができます!!
まとめ
irbとは、ターミナルから直接Rubyのプログラムを動かすことができる機能のこと。
文字列とは、プログラミングの中で文字を扱うための値のこと。
文字列は、+(プラス)で連結できる。以上。
- 投稿日:2020-10-22T18:57:07+09:00
【備忘録】MVCモデル まとめ
はじめに
筆者の漁った色々な知識を備忘録としてまとめ、残しています。
MVCとは
モデル(Model)頭文字「M」、ビュー(View)頭文字「V」、コントローラー(Controller)頭文字「C」の略であり、アプリケーション設定を整理するための概念。イメージはユーザーがブラウザで入力(クリック)した内容をWebアプリケーションで処理する流れ。
RubyonRailsは、MVCモデルで構成されている。
Model(モデル)
アプリケーション固有のデータを扱う部分です。Controllerからの依頼を受けて処理する(Modelに直接処理を記入することもある(例:「特定の文字列を探して抽出して」とか、「データベースのこの項目には空白入れないで」 等))。データベースに対して、データの登録や取得、更新、削除などの処理を行う。
View(ビュー)
PCの画面に関わる部分。データベースの情報を表示する場合、Controllerから情報を受け取り、ブラウザに表示させるHTMLを実際に組み立てる。
Controller(コントローラ)
ModelやViewを制御する部分です。ユーザーからのリクエスト(例:TOPから商品一覧(データベースに登録されてる商品)をみたい 等)を受けて、Modelと連携したり、どのView(画面)を表示するのかといったことを制御します。
RubyonRailsでは
プログラムの構造をMVCの役割によって分けることで、プログラムのメンテナンス性を向上させたり、複数人で開発するときに影響する箇所を限定できるようになっています。
抽象化すると
Viewが営業
Controllerが管理者
Modelが事務Veiw(商品一覧)を表示する時
View :問い合わせがあったので、Controllerさんお客さんに見せるプレゼン用のカタログください!
Controller :了解!Viewくん! Modelさん!キャビネットからもってきて!
Model :controllerさん、了解しました。今とってきます。Viewでユーザーが新規会員登録された時
View :Controllerさんお客さんから依頼とってきました!
Controller :ありがとう!Viewくん! Modelさんに渡しとくね!
Model :controllerさん、了解しました。記録残しておきますね。そして各役割ごとに、業務をルーチン化して効率を向上させている。
みたいなイメージを持っています。
- 投稿日:2020-10-22T18:57:07+09:00
【備忘録】優しいMVCモデル まとめ
はじめに
筆者の漁った色々な知識を備忘録としてまとめ、残しています。
MVCとは
モデル(Model)頭文字「M」、ビュー(View)頭文字「V」、コントローラー(Controller)頭文字「C」の略であり、アプリケーション設定を整理するための概念。イメージはユーザーがブラウザで入力(クリック)した内容をWebアプリケーションで処理する流れ。
RubyonRailsは、MVCモデルで構成されている。
Model(モデル)
アプリケーション固有のデータを扱う部分です。Controllerからの依頼を受けて処理する(Modelに直接処理を記入することもある(例:「特定の文字列を探して抽出して」とか、「データベースのこの項目には空白入れないで」 等))。データベースに対して、データの登録や取得、更新、削除などの処理を行う。
View(ビュー)
PCの画面に関わる部分。データベースの情報を表示する場合、Controllerから情報を受け取り、ブラウザに表示させるHTMLを実際に組み立てる。
Controller(コントローラ)
ModelやViewを制御する部分です。ユーザーからのリクエスト(例:TOPから商品一覧(データベースに登録されてる商品)をみたい 等)を受けて、Modelと連携したり、どのView(画面)を表示するのかといったことを制御します。
RubyonRailsでは
プログラムの構造をMVCの役割によって分けることで、プログラムのメンテナンス性を向上させたり、複数人で開発するときに影響する箇所を限定できるようになっています。
抽象化すると
Viewが営業
Controllerが事業推進
Modelが事務Veiw(商品一覧)を表示する時
View :問い合わせがあったので、Controllerさんお客さんに見せるプレゼン用のカタログください!
Controller :了解!Viewさん! Modelさん!キャビネットからもってきて!
Model :controllerさん、了解しました。今とってきます。Viewでユーザーが新規会員登録された時
View :Controllerさんお客さんから依頼とってきました!
Controller :ありがとう!Viewさん! Modelさんに渡しとくね!
Model :controllerさん、了解しました。記録残しておきますね。そして各役割ごとに、業務をルーチン化して効率を向上させている。
※viewとcontrollerには、実は課長(application_controller.rbとapplication_html.erb))がいて、課(フォルダ)内にまとめて命令を出せたりする。みたいなイメージを持っています。
- 投稿日:2020-10-22T18:57:07+09:00
【備忘録】MVCモデル 簡単なまとめ
はじめに
筆者の漁った色々な知識を備忘録としてまとめ、残しています。
MVCとは
モデル(Model)頭文字「M」、ビュー(View)頭文字「V」、コントローラー(Controller)頭文字「C」の略であり、アプリケーション設定を整理するための概念。イメージはユーザーがブラウザで入力(クリック)した内容をWebアプリケーションで処理する流れ。
RubyonRailsは、MVCモデルで構成されている。
Model(モデル)
アプリケーション固有のデータを扱う部分です。Controllerからの依頼を受けて処理する(Modelに直接処理を記入することもある(例:「特定の文字列を探して抽出して」とか、「データベースのこの項目には空白入れないで」 等))。データベースに対して、データの登録や取得、更新、削除などの処理を行う。
View(ビュー)
PCの画面に関わる部分。データベースの情報を表示する場合、Controllerから情報を受け取り、ブラウザに表示させるHTMLを実際に組み立てる。
Controller(コントローラ)
ModelやViewを制御する部分です。ユーザーからのリクエスト(例:TOPから商品一覧(データベースに登録されてる商品)をみたい 等)を受けて、Modelと連携したり、どのView(画面)を表示するのかといったことを制御します。
RubyonRailsでは
プログラムの構造をMVCの役割によって分けることで、プログラムのメンテナンス性を向上させたり、複数人で開発するときに影響する箇所を限定できるようになっています。
抽象化すると
Viewが営業
Controllerが事業推進
Modelが事務Veiw(商品一覧)を表示する時
View :問い合わせがあったので、Controllerさんお客さんに見せるプレゼン用のカタログください!
Controller :了解!Viewさん! Modelさん!キャビネットからもってきて!
Model :controllerさん、了解しました。今とってきます。Viewでユーザーが新規会員登録された時
View :Controllerさんお客さんから依頼とってきました!
Controller :ありがとう!Viewさん! Modelさんに渡しとくね!
Model :controllerさん、了解しました。記録残しておきますね。そして各役割ごとに、業務をルーチン化して効率を向上させている。
※viewとcontrollerには、実は課長(application_controller.rbとapplication_html.erb))がいて、課(フォルダ)内にまとめて命令を出せたりする。みたいなイメージを持っています。
- 投稿日:2020-10-22T18:40:50+09:00
【注意!!】Railsのdeviseとviewファイルをhamlに変換する時の注意点
こんばんは
アロハな男、やすのりです!今日はdeviseのビューファイルを修正している時に遭遇した躓きポイントについてお話していこうと思います。
ただ、前提が当てはまる人は参考にしていってね!!
前提
Railsでアプリケーション作成中にGemの
deviseを使用していて、更にhaml-rails等でhtml.erb
をhtml.haml
に変換している人状況
deviseの新規登録ページのビューファイルに名前の入力欄を追加したいと思いコード修正していました。
views/users/registrations/new.html.haml%h2 Sign up = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render "users/shared/error_messages", resource: resource .field = f.label :name %br/ = f.text_field :name, autofocus: true .field = f.label :email %br/ = f.email_field :email, autofocus: true, autocomplete: "email" .field = f.label :password - if @minimum_password_length %em (#{@minimum_password_length} characters minimum) %br/ = f.password_field :password, autocomplete: "new-password" .field = f.label :password_confirmation %br/ = f.password_field :password_confirmation, autocomplete: "new-password" .actions = f.submit "Sign up" = render "users/shared/links"ということで、:nameの欄を足してこれでバッチリだ!!
と、思っていたら...
...え?
何も変わってなくない??あれ???修正するビューファイル間違えたかな?と思って確認してみても間違いなく registrations って書いてあるし...
あれ!?
なぜか、new.html.erb
を参照してる!?
しかも参照先がapp/views/deviseregistrations/new.html.erb
になってるぞ!?と、思いいろいろ調べて見ると原因が見えてきました。
原因
僕のアプリケーションの構成上、deviseにUserモデルを持たせていたので、deviseのビューファイルを生成する時に
$ rails g devise:views users
とコマンドを打っていたんですが、これが原因みたいです。
そもそもdeviseのモデルがUserモデル
以外に無いのであれば、$ rails g devise:views
だけで良かったみたいです。
※Userモデル以外というよりは、deviseのモデルが1つだけの時と言う方が正しいかも?2つのコマンドの違いは?
生成されるビューファイルのディレクトリ構造が少し変わってしまいます。
具体的には$ rails g devise:views users
とコマンドを打った場合は、
app/views/users/
以下にディレクトリとビューファイルが生成されます。一方、
$ rails g devise:views
とコマンドを打った場合は
app/views/devise
以下にディレクトリとビューファイルが生成されます。deviseディレクトリ...
これだぁぁあああ!!
はい、ということでビューファイルを生成した段階でコミットしていたので、コミット履歴を削除してコマンドを打ち直した後に、新規ビューファイルのコードを修正してみました。
すると...
よしっ!!
名前の入力欄が加わってるぞ!!参照先もちゃんと
new.html.haml
になってる!!結論
deviseのビューファイルを生成する時は、基本
$ rails g devise:views
でOK!!皆さんも気をつけてね!!
- 投稿日:2020-10-22T17:28:46+09:00
【エラー】Mysql2::Error: Specified key was too long; max key length is 767 bytes
エラーの忘備録
エラー内容
rails db:migrate
をした時に、以下のエラー文が出ました。(Mysqlのエラー)Mysql2::Error: Specified key was too long; max key length is 767 bytes
指定したキーが長すぎる
というエラーです。
テーブルに色々カラムを追加すると、キーの制限である767バイトを超えてしまうため
このエラーが起こってしまいます。原因
config/database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sock
encoding: utf8mb4
という記述のためエラーが出ます。解決方法
encoding: utf8
に書き換えます。config/database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sockutf8mb4とutf8の違いはいまいちわかっていないのですが、utf8mb4は1〜4バイト、utf8は1〜3バイトの文字が格納できるようです。そのためutf8だと、絵文字などは保存できませんが、代わりに容量が大きくなるのではないかな?と推測しました。
すでに
rails db:create
でデータベースを作成してしまっている場合は、% rails db:drop % rails db:createか
% rails db:resetで、もう一度データベースを作り直してあげる必要があります。
おわりに
もし間違えている点などございましたら、ご指摘いただけましたら幸いです。
読んでいただきありがとうございました!
- 投稿日:2020-10-22T17:14:11+09:00
【Rails】アソシエーション
中間テーブル
データ型:referencesとforeign_keyをセットで使用する。
referencesはPKを参照する。よって、:user
と記述しているがカラム名はuser_id
となる。
foreign_keyで、他テーブルの情報を参照できる。model.rbclass CreateRoomUsers < ActiveRecord::Migration[6.0] def change create_table :room_users do |t| t.references :user, null: false, foreign_key: true t.references :room, null: false, foreign_key: true t.timestamps end end enddependent: :destroy
親子関係のデータについて、親を削除した際に子も削除されるようにする。
親モデルの
has_manyにdependent: :destroy
を追記。(例)親モデル:user、子モデル:post
class User < ApplicationRecord has_many :posts, dependent: :destroy
- 投稿日:2020-10-22T17:02:36+09:00
[エラー]background-image: image-urlでSassC::SyntaxError
- 投稿日:2020-10-22T16:45:04+09:00
Macを買い換えたので個人的に便利なアプリや開発ツールをまとめてみた(アプリ編)
Macbook Proをクラムシェルモードで運用していたが、あまりにも本体が熱くなりすぎて寿命が心配になってきたのでiMacを購入しました。
なるべくクリーンな状態で必要なアプリ・ツールのみをインストールしたかったので、それらをまとめる個人的なメモ + どこが気に入ってるのか なども併せてご紹介します。まっさらな状態から、neovimを使ってRails開発が行えるところまでをゴールとします。
いつかまたセットアップする機会がきたときに役に立てばいいかな!今回は、「アプリ編」として筆者が仕事で使っているアプリや、よく利用していて便利だなと思っているアプリをまとめました。
よく見かけるものばっかりだと思いますので、あくまで参考程度にご覧ください。導入するもの
名前 用途 Logi Options Logicool用マウスの機能拡張 Karabiner-Elements キーボードのキーバインド変更 The Unarchiver ファイル解凍 BetterSnapTool ウィンドウリサイズ Aspect Ratio Calculator 比率計算 ATOK 日本語入力 Dropbox オンラインストレージ Photoshop 写真編集 Microsoft 365 ドキュメント編集 Notion 情報集約ツール Todoist タスク管理 Trello タスク管理 Logi Options
Logicoolの Mx Master 3 というマウスを使用しており、それに色々な便利機能を追加してくれます。
Mx Master自体はスクロールホイールの機構が特殊で、一定の速度以上でホイールを回すと滑らかに回転し、長いページも一瞬でスクロールできてめちゃくちゃ便利。
また横にスクロールするためのサムホイールなるものもあるので、Excelなんかでは重宝します。
Logi Optionsではアプリ毎にボタンの動作を変更したり、異なるOS同士でマウスカーソルを共有したり、ファイルの転送をおこなえるようになるので、導入必須ですね。ダウンロード先
https://www.logicool.co.jp/ja-jp/product/optionsKarabiner-Elements
キーマップの変更や、マウスなどの操作をキーボードで行えるようにしたり、エディタのショートカットを設定できたりと、俺に任せろといわんばかりに色々できる。
設定が面倒な人でも既存の設定をインポートすることが可能なので、あれこれいじらなくてもすぐに便利さを感じれると思う。
上の画像のように、vimでCtrl+[を押下したときに自動的に半角英数入力にすることができたり、Ctrlキーを押すだけで英数・かなを切り替えられる。
ちなみにhhkbのUS配列を使ってる人は、このCtrlキーでのアクションはめちゃくちゃ便利。ダウンロード先
https://karabiner-elements.pqrs.org/The Unarchiver
Macの解凍ツールといえばこれ。
これ以外知らない
Windows用ファイルも問題なく解凍してくれます。
それ以上でもそれ以下でもないような定番アプリ。ダウンロード先
https://apps.apple.com/jp/app/the-unarchiver/id425424353?mt=12BetterSnapTool
Windowsのように画面隅にアプリ画面を移動することにより、決められた大きさにリサイズしてくれるもの。
もう少し詳しく書くと、Safariのウィンドウバーをモニター画面上部にドラッグすれば自動的に最大化を行ってくれます。
なぜMacのデフォルト機能として存在しないのか不思議です。ちなみにMagnetという似たようなアプリもあって、そちらのほうがAppStoreでは人気みたいです。(設定時の言語がこちらと異なり、日本語だからかな?)
筆者はマルチモニター環境でして、キーショートカットでウィンドウを他のモニターで表示するように移動させたかったので、こちらのアプリにしました。
Magnetでは設定が見当たらなかったので、マルチモニタ環境の人はこちらがおすすめです。ダウンロード先
https://apps.apple.com/jp/app/bettersnaptool/id417375580?mt=12Aspect Ratio Calculator
たぶんほとんどの人には必要ないかもしれない・・・。
アスペクト比を自動計算してくれます。似たようなアプリはたくさんあるのですが、Macの右上に常時表示できて、クリックするとどのウィンドウよりもトップで表示され、なおかつ見やすい。
探してみると意外とこんな表示にこだわったアプリはなかったので、採用しました。ダウンロード先
https://apps.apple.com/jp/app/aspect-ratio-calculator/id955155151?mt=12ATOK
日本語入力に最適なIME。
月額300円ほどのサブスクリプションに登録しています。
Google日本語入力を使っていた時期もありまして、そちらと比べて変換ミスが起きにくいのと、
かな入力時でも半角スペースを入力できるオプションが便利で使っています。(Googleのほうでも、もしかしたらできるかもしれません。)
ダウンロード先
https://mypassport.atok.com/install/install_mac.htmlDropbox
オンラインストレージサービスの先駆け。
その後色々な競合サービスが打ち出されてきましたが、筆者の周りではみんなこれでファイル共有しているので、仕方なく導入しています。
ファイルが保存できるだけではなく、Excelファイルを同時編集している人がいないか確認できたり、色んなサイトに登録したパスワードなどを安全に保管出来るなど、便利な機能があります。ダウンロード先
https://www.dropbox.com/installPhotoshop
仕事で写真編集をする機会が多いので使っています。
特にPhotoshopじゃないと出来ないことをしているわけではないですが、「コンテンツに応じた塗りつぶし」機能は他のアプリより優れていたので使っています。
これは写真から消したいオブジェクトを合成で綺麗に消してくれるものなのですが、最近はどのように消えるかプレビュー画面で確認しながら、
写真のどの部分をサンプリングするのか自分で編集できるようにもなって、どんどん便利になっていってる機能でもあります。撮影時に通行人が写ってしまうっていうときに、大変便利なわけですね。
ただ消したいオブジェクトによって隠れていた箇所を機械的に導きだすわけなので、複雑な模様などはうまく処理できません。
ダウンロード先
https://www.adobe.com/jp/products/photoshop.htmlMicrosoft 365
説明するまでもないOfficeのサブスクリプションサービスですね。
筆者は仕事でもばりばりというか、ただテストのエビデンス画像を貼るだけなのにExcelを使わされているのですが、Web系エンジニアの方も使われているのでしょうか?
VBAマクロが入ったドキュメントを使うことが多いので課金してますが、正直無料のもので十分な気がしています(泣ダウンロード先
https://www.microsoft.com/ja-jp/microsoft-365/tryNotion
最近知りまして、これから使っていきたいと思っている情報集約アプリ。
説明がむずかしいのですが、Excel・Word・PowerPointなどなどをまとめたようなツールです。
Evernoteに似ていますが、デフォルトでMarkdownに対応しており、起動も早く、データテーブルを用いて集計を行ったり、豊富な機能がそろっております。
ただ学習コストが異常に高いので、ミニマルに初めてみて徐々に色々な機能を使ってみてください。じゃないと挫折する可能性が高い。(筆者も一度挫折しています。)
使い始めは、以下の記事を読むとわかりやすいです。
わかる、Notion 徹底入門ダウンロード先
https://www.notion.so/desktopTodoist
タスク管理アプリ。
iPhoneアプリ、デスクトップアプリもあり、特に課金しなくても同期してくれる点が魅力的。
TODOの表示形式もリスト、カンバン両方に対応していますし、なによりタスクの追加までがシンプルで簡単なので億劫にならない。
Googleカレンダーと同期して、相互にタスクが連携されるように設定しています。ダウンロード先
https://todoist.com/jaTrello
こちらもタスク管理アプリ。
仕事で使用していますが、カンバン方式で視覚的にわかりやすく、タスクの状態が変化していくような管理方法に向いていると思います。
個人で使用するときは、追加までのアクションが少ない上記のTodoistになりましたが、タスクのサムネイル画像をつけられたり便利な面があるので、使い分けてもよさそうです。
ダウンロード先
https://todoist.com/jaまとめ
本当によく見かけるものだったと思いますが、そういったサービスはそれだけ便利であるということですね笑
ブラウザ上で利用しているものは省きましたが、とりあえずこれだけあれば問題ないものをまとめてみました。
この中ではNotionの可能性がやばいと思っているので、早く使い慣れたいなと思っております。筆者はまだMac環境になって日が浅く、これまでずっとWindows環境だったんですね。
なのでMacの便利な機能やアプリを探したり、それこそ初期設定が思っていたより大変でした!
同じような方の参考になればうれしい! もしおすすめなどあれば教えてください!次回は、開発ツール編をまとめたいと思います。
- 投稿日:2020-10-22T16:03:57+09:00
Rails 5 コードリーディング 第2回 ~ActionView編~
class RenderedTemplate # :nodoc: attr_reader :body, :template def initialize(body, template) @body = body @template = template end def format template.format end EMPTY_SPACER = Struct.new(:body).new endformatメソッド内の templateが@templateとインスタンス変数でないのはなぜか?
- 投稿日:2020-10-22T15:19:33+09:00
heroku コマンドが使えなくなったときのメモ
ある日bash: heroku: command not foundでherokuコマンドが使えなくなりました。
git push heroku masterは使えました。
heroku open やheroku rake db:migrate が使えなかったです。npm install -g herokuしたら治りました。
- 投稿日:2020-10-22T15:14:52+09:00
Railsで申し込み機能を実装する
Railsを用いた投稿の申し込み機能とキャンセル機能の実装方法について解説します。
※ユーザー機能と投稿機能は実装済みであることを前提とします。実装の流れ
大まかな流れとして、以下の手順で実装していきます。
1. 投稿と申込者の中間テーブルを作成する
2. モデル同士の関連付け(アソシエーション)を行う
3. ルーティングを定義する
4. コントローラーに申し込み機能とキャンセル機能を定義する
5. 申込ボタンのビューを作成する投稿と申込者の中間テーブルを作成する
投稿は複数人の申込者を持つことができ、ユーザーは複数の投稿に申し込むことができます。
そのため、投稿とユーザーは多対多の関係となり、中間テーブルが必要となります。また、ここでは同じユーザーの中でも投稿者と申込者を区別するために、申込者を
taker
と定義することにします。
投稿のモデル名をPost
とすると、post_takersという中間テーブルを作れば良いということになります。中間テーブルを作成するにはまず、ターミナルに以下のコマンドを打ちモデルを作成します。
$ rails g model PostTaker出来上がったマイグレーションファイルを開き、以下のように記述します。
2020XXXXXXXXXX_create_post_takers.rbclass CreatePostTakers < ActiveRecord::Migration[6.0] def change create_table :post_takers do |t| t.references :post, foreign_key: true t.references :taker, foreign_key: {to_table: :users} t.timestamps end end endここでのポイントは、
t.references :taker, foreign_key: {to_table: :users}
の部分です。
このように書くと、中間テーブルの外部キーとして申込者であるtaker
という名前を定義しつつ、usersテーブルを参照できます。
参考記事:https://qiita.com/publichtml/items/1fba15d8071fab66d043モデル同士の関連付け(アソシエーション)を行う
次に、モデルにアソシエーションを記述します。
user.rbclass User < ApplicationRecord has_many :post_takers, foreign_key: "taker_id", dependent: :destroy endpost.rbclass Post < ApplicationRecord has_many :post_takers, dependent: :destroy has_many :takers, through: :post_takers, dependent: :destroy endpost_taker.rbclass PostTaker < ApplicationRecord belongs_to :taker, class_name: 'User', foreign_key: 'taker_id' validates_uniqueness_of :post_id, scope: :taker_id endpost_taker.rbの
belongs_to :taker, class_name: 'User', foreign_key: 'taker_id'
は、
このような書き方をすることでUserモデルに紐付いた申込者と関連付けができます。
参考記事:https://qiita.com/gyu_outputs/items/421cc1cd2eb5b39e20adまた、
validates_uniqueness_of :post_id, scope: :taker_id
は、
投稿と申込者の同じ組み合わせが2つ以上登録されないようにするため、このようなバリデーションを記述しています。ルーティングを定義する
申し込むというアクションを
take
、キャンセルというアクションをcancel
としてルーティングに以下のように記述します。routes.rbRails.application.routes.draw do resources :posts do member do get 'take' get 'cancel' end end end上記では、パスに投稿のidを含めるためmemberを使っています。
参考記事:https://qiita.com/hirokihello/items/fa82863ab10a3052d2ffコントローラーに申し込み機能とキャンセル機能を定義する
コントローラーに以下の記述を追加します。
※ここでは投稿詳細ページに申込ボタンを表示させることを前提として書いていますが、各々の状況に合わせて書き換えてください。posts_controller.rbclass PostsController < ApplicationController before_action :set_post def show @user = @post.user end def take # 該当の投稿とログイン中のユーザーとの中間テーブルのレコードを作成する PostTaker.create(post_id: @post.id, taker_id: current_user.id) # フラッシュメッセージを表示(フラッシュメッセージを表示させない場合は書かなくて大丈夫です) flash[:notice] = '申し込みが完了しました。' # 投稿の詳細ページにリダイレクト redirect_to action: "show" end def cancel # 中間テーブルの中から、該当の投稿とログイン中のユーザーによるレコードを抽出する post_taker = PostTaker.find_by(post_g_id: @post.id, taker_id: current_user.id) post_taker.destroy flash[:notice] = 'キャンセルが完了しました。' redirect_to action: "show" end private def set_post @post = Post.find(params[:id]) end end申込ボタンのビューを作成する
最後に、申込ボタンのビューを作成したら完成です!
以下のコードはhamlで記述しています。show.html.haml-# 投稿者とログイン中のユーザーが一致しない場合のみボタンを表示させる - if @user.id != current_user.id -# 該当の投稿の申込者の中に、ログイン中のユーザーが含まれているか否かで場合分けする - if @post.taker_ids.include?(current_user.id) = link_to "申し込みをキャンセルする", cancel_posts_path(@post.id), data: { confirm: "こちらの投稿の申し込みをキャンセルしますか?" } - else = link_to "申し込む", take_posts_path(@post.id), data: { confirm: "こちらの投稿に申し込みますか?" }
- 投稿日:2020-10-22T15:11:01+09:00
Rails 5 コードリーディング 第一回 ~ActiveRecord newメソッド編~
Railsの使い方はなんとかわかるにようなりました。
でも、コーディング力が足りない。
他の言語に行った時に、応用力がつかないのではないか、、、
と思ったので、
Railsのソースコードを読んでみることにした。
こちらを参考にしてみました。
Railsコードを読んでみたActive record 周りを読んでみることに
まずは、shift command f で
def new を
全文検索# frozen_string_literal: true module ActiveRecord # = Active Record \Relation class Relation MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group, :order, :joins, :left_outer_joins, :references, :extending, :unscope, :optimizer_hints, :annotate] SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading, :reverse_order, :distinct, :create_with, :skip_query_cache] CLAUSE_METHODS = [:where, :having, :from] INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having] VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS include Enumerable include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation attr_reader :table, :klass, :loaded, :predicate_builder attr_accessor :skip_preloading_value alias :model :klass alias :loaded? :loaded alias :locked? :lock_value (と中略) # Initializes new record from relation while maintaining the current # scope. # # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new]. # # users = User.where(name: 'DHH') # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil> # # You can also pass a block to new with the new record as argument: # # user = users.new { |user| user.name = 'Oscar' } # user.name # => Oscar def new(attributes = nil, &block) block = _deprecated_scope_block("new", &block) scoping { klass.new(attributes, &block) } end alias build new (途中略) def _deprecated_spawn(name) spawn.tap { |scope| scope._deprecated_scope_source = name } end def _deprecated_scope_block(name, &block) -> record do klass.current_scope = _deprecated_spawn(name) yield record if block_given? end enddef new(attributes = nil, &block) block = _deprecated_scope_block("new", &block) scoping { klass.new(attributes, &block) } end alias build newまずは漠然と理解する
1.
新しいものレコードを作るから attribute(属性)はnil
2.
ブロック引数を渡しているが、ブロックはどこにあるのか?
3.
_deprecated_scope_block メソッドが下に定義されているが、意味がわからない
4.
klassとは何か?疑問は尽きない
もうちょっとさぐってみる
- 投稿日:2020-10-22T14:49:36+09:00
[Rails]rails-i18nを用いた日本語化
はじめに
rails-i18nというgemを用いることでエラーメッセージなどを日本語化することができます。
目次
- 言語を日本語に設定する
- rails-i18nのインストール
- 日本語に設定するためのファイルを作成
1. 言語を日本語に設定する
application.rbを編集します。
:jaとすることで日本語に設定することができます。config/application.rbmodule CosmeticReview class Application < Rails::Application config.load_defaults 6.0 #以下の記述を追記します config.i18n.default_locale = :ja end end2. rails-i18nのインストール
gemfileに以下を追記します。
その後、コマンドでbundle installを実行します。gemfilegem 'rails-i18n'3. 日本語に設定するためのファイルを作成
localファイルを作成し、その中に日本語へ翻訳するためのYAMLファイルを作成します。
今回はモデルのエラーメッセージを日本語化しました。locales/ja.ymlja: activerecord: attributes: user: nickname: ニックネーム gender: 性別 introduce: 自己紹介 birthday: 生年月日 post: name: 商品名 description: 商品の説明 image: 画像 shop_name: 購入店 evaluation: 商品の評価 price: 購入価格 category_id: カテゴリー time: formats: default: "%Y/%m/%d %H:%M:%S"階層が少しでもずれているとエラーが出るので気をつけましょう。
参考リンク
- 投稿日:2020-10-22T13:49:53+09:00
MySQL 8.0 を 5.7 感覚で触っていたらハマった my.cnf の設定問題
概要
ここではRailsアプリの開発ではありますが、Webアプリを開発しているとこのエラーに引っかかった方はわかると思います。
this is incompatible with sql_mode=only_full_group_by
問題の詳細はいろんな記事で解説されているので以下の記事などを読んでいただければなと。
今回は、MySQL 8.0 を 5.7 感覚で触っていたらハマった内容となります。
- MySQL5.7にアップデートしたらonly_full_group_byでエラーになった « MySQL « 技術ブログ « 株式会社プロネット
- MySQLのsql_mode正しく設定されていますか? – エンジニアリング 入りました!
環境
$ mysql --version mysql Ver 8.0.21 for Linux on x86_64 (MySQL Community Server - GPL)現象
mysqldの設定は、上記の記事を参考に設定した内容です。
そうすると以下のエラーが発生しました。$ mysql --help | grep my.cnf order of preference, my.cnf, $MYSQL_TCP_PORT, /etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf $ sudo vi /etc/my.cnf --- [mysqld] sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION --- $ systemctl restart mysqld.service Failed to restart mysqld.service: The name org.freedesktop.PolicyKit1 was not provided by any .service files $ sudo systemctl status mysqld.service ● mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since 木 2020-10-22 03:50:33 UTC; 27s ago Docs: man:mysqld(8) http://dev.mysql.com/doc/refman/en/using-systemd.html Process: 8909 ExecStart=/usr/sbin/mysqld $MYSQLD_OPTS (code=exited, status=1/FAILURE) Process: 8885 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS) Main PID: 8909 (code=exited, status=1/FAILURE) Status: "Server startup in progress" Error: 2 (No such file or directory) 10月 22 03:50:32 xxx-xxx-xxx-xxx.yyyyy.zzz systemd[1]: Starting MySQL Server... 10月 22 03:50:33 xxx-xxx-xxx-xxx.yyyyy.zzz systemd[1]: mysqld.service: main process exited, code=exited, status=1/FAILURE 10月 22 03:50:33 xxx-xxx-xxx-xxx.yyyyy.zzz systemd[1]: Failed to start MySQL Server. 10月 22 03:50:33 xxx-xxx-xxx-xxx.yyyyy.zzz systemd[1]: Unit mysqld.service entered failed state. 10月 22 03:50:33 xxx-xxx-xxx-xxx.yyyyy.zzz systemd[1]: mysqld.service failed. $ sudo less /var/log/mysqld.log ... 2020-10-22T03:50:33.416784Z 0 [ERROR] [MY-000077] [Server] /usr/sbin/mysqld: Error while setting value 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' to 'sql_mode'. 2020-10-22T03:50:33.416979Z 0 [ERROR] [MY-010119] [Server] Aborting原因
使用していたMySQLは 8.0 で特に意味もなく 5.7 感覚で使用していたことで起きた問題でした汗
MySQL 8.0 では、sql_mode
の内容が 5.7 から一部変更があり、今回はNO_AUTO_CREATE_USER
が削除されていたことでハマりました。
- MySQL :: MySQL 5.7 Reference Manual :: 5.1.7 Server System Variables
- Default Value
- ONLY_FULL_GROUP_BY / STRICT_TRANS_TABLES / NO_ZERO_IN_DATE / NO_ZERO_DATE / ERROR_FOR_DIVISION_BY_ZERO / NO_AUTO_CREATE_USER / NO_ENGINE_SUBSTITUTION
- MySQL :: MySQL 8.0 Reference Manual :: 5.1.8 Server System Variables
- Default Value
- ONLY_FULL_GROUP_BY / STRICT_TRANS_TABLES / NO_ZERO_IN_DATE / NO_ZERO_DATE / ERROR_FOR_DIVISION_BY_ZERO / NO_ENGINE_SUBSTITUTION
対処
NO_AUTO_CREATE_USER
を削除した以下の設定で無事にMySQLが起動して、概要のエラーが解消できました。[mysqld] sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
- 投稿日:2020-10-22T12:33:15+09:00
vagrant上で作成したポートフォリオをdockerの開発環境に移行してみた
はじめに
初めまして、初投稿になりますので自己紹介と記事のきっかけについて書かせていただきます。
自己紹介
2020年9月にDMMWEBCAMPを卒業し、現在転職活動中のエンジニアです。記事のきっかけ
他のプログラミングスクールが同じかはわかりませんが、スクール生で共通の開発環境にするために全てvagrant上で仮想環境を構築しカリキュラムとポートフォリオの作成を行っていました。そのため、0からの自力で環境構築を行ったことがなく実務を意識するのならDockerぐらいは自力で入れておきたいと思い学習し始めたのがきっかけです。
まだまだ勉強中のため、正直かなり怪しいところは多いとは思いますが同じような勉強中の方の参考に少しでもなれば嬉しいです!この記事で扱うこと
Dockerを使用した開発環境の構築
Vagrant上でできていたことがDocker上でもできるようにするイメージですDockerfile、docker-composeなどの表記
参考までに、Vagrantを使用していた時の開発環境は下記になります
- OS:macOS Catalina 10.15.4
- バックエンド: Ruby 2.5.7、Rails 5.2.4.3
- 開発環境: Vagrant 2.2.4、SQLite (※MySQLやPostgreSQLの場合、この後の見本の表記が異なります)
- 本番環境: MySQL2、AWS (EC2、RDS for MySQL、Route53)、Nginx、 Puma、Capistrano
- 0からの開発環境の構築で起こりうるエラーと解決した方法
記事のきっかけにもあるように、ローカル環境の構築自体も初めてのためdocker以前のエラーもいくつかありました。解決のために参考にさせていただいた記事をメインに紹介していきたいと思います。この記事で扱わないこと
Dockerfile、docker-composeなどの基礎知識
Dockerに関する記事やyoutube多くありますがほとんどがコピペでなんとなく進んでしまうので、最初は自分でドキュメントを読みながら手を動かして全体の仕組みをなんとなく先に掴んでおくのが理解しやすいと思いました。
参考までに自分は下記サイトで学習を始めました。各用語調べながらでも半日〜2日ほどでできる内容かつDockerのイメージが掴めるかと思いますのでおすすめです。
- 入門Docker:dockerの基礎知識、操作について
- Quickstart: Compose and Rails:Ruby on Railsで具体的にdockerをどう始めるか
- 学習方法の選定にはKENTA / 雑食系エンジニアTVさんのyoutubeを参考にさせていただきました
ポートフォリオにAWSとDockerとCircleCIを組み込むための学習順序と教材について事前準備
- Dockerのインストール
こちらの公式サイトより行いましょう
https://hub.docker.com/editions/community/docker-ce-desktop-mac
ターミナルでバージョンの確認ができれば大丈夫です$ docker version Client: Docker Engine - Community Cloud integration 0.1.18 Version: 19.03.13 API version: 1.40
- ポートフォリオ(Dockerの開発環境にしたいプロジェクト)をgit cloneしてくる
記事のきっかけにもありますよう、既にポートフォリオを作成している段階のため環境構築したいものをgithubからローカル上に持ってきましょう$ mkdir docker # Desktopからもしくはvagrantを使用していた時のworkディレクトリなどで実行 $ git clone git@github.com:ユーザー名/リポジトリ名.git各ファイルの準備
Dockerとプロジェクトの準備ができたので、環境構築に必要なファイルを作成していきます
①Dockerfile
# 先ほどクローンしたディレクトリ上で $ touch Dockerfile下記内容を記入していきましょう
# 元となるdocker imageを指定。ポートフォリオのrubyのバージョンと合わせましょう FROM ruby:2.5.7 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/* #sqlite3を指定 RUN apt-get update && apt-get install -y sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs # コンテナ起動した時に、コンテナ内でmyappディレクトリを作成 RUN mkdir /myapp # コンテナ内の作業ディレクトリの指定(上のRUNで作成したディレクトリ) WORKDIR /myapp # ホストマシンのgemfileをコピーし、コンテナ内のディレクトリに貼り付け COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock # 貼り付けたgemfileを読み込み。bundlerバージョンが2以降だとこれが必要 ENV BUNDLER_VERSION 2.1.4 RUN gem install bundler RUN bundle install # アプリケーション(カレントディレクトリ)をコピーして、コンテナ内のディレクトリに貼り付け COPY . /myapp # ポートを3000で公開 EXPOSE 3000 # コンテナにてコマンドを実行 CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]
- ENV BUNDLER_VERSION 2.1.4について
Gefile.lockの一番下にこのような表記があるかと思いますので確認してみて下さいGemfile.lockBUNDLED WITH 2.1.4②docker-compose.yml
# 同じディレクトリ上で $ touch docker-compose.yml下記内容を記入していきましょう
docker-compose.ymlversion: '3' services: web: build: context: ./ dockerfile: Dockerfile # 変更を保存するファイルを指定 volumes: - .:/myapp # ポートを指定 ports: - "3000:3000" # コンテナの起動を維持させる tty: true stdin_open: trueMySQLやPostgreSQLを使用している場合だと、web+DBの記述も必要なのですがSQLiteの場合はこちらのみで大丈夫みたいです。まだこの辺りの知識が乏しいため、ゆくゆくはMySQLに変更したいと思っております・・・
コンテナを起動してみましょう
# Dockerfileを元にdocker imageの作成 $ docker-compose build # docker container をバックグランドで起動 $ dokcer-compose up -d # コンテナ内にてmigrationを更新 docker-compose run web rails db:migrateこれで、いつものlocalhost:3000で表示ができればひと段落です!
引っかかったエラーと解決した方法
大体ですが引っかかった時系列順に記載していきます。最初にも記載しておりますが、エラー内容とおそらくの原因の考察がサブで、メインが解決参考にさせていただいた記事の紹介になります。ありがとうございました
①ActiveRecord::PendingMigrationError Migrations are pending
「dokcer-compose up -d で動いているのにっ・・・!」
$ docker-compose up -d Creating network "~~~_default" with the default driver Creating ~~~_web_1 ... donelocalhost:3000のページへアクセス時に起きたエラーです
コンテナの起動後、docker-compose run web rails db:migrate を行いましょう、あるあるな気がします
参考:docker-compose upで立ち上げたRailsでActiveRecord::PendingMigrationError Migrations are pending②To install the missing version, run 'gem install bundler:2.1.4'
「bundleで始まるコマンド(デプロイ・Rspecなど)ができないっ・・・!」
Traceback (most recent call last): 2: from 1: from ~ Could not find 'bundler' (2.1.4) required by your /Users/ユーザー名/ディレクトリ名/Gemfile.lock. (Gem::GemNotFoundException) To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.1.4`Dockerfileを作成した時に確認した、Gemfile.lockの「BUNDLED WITH 2.1.4」がローカル上で見つからないイメージです。
参考:To install the missing version, rungem install bundler:2.1.4
と出たときの対処法エラー文の通りにインストールを試してみましょう、ダメだった場合は③へ
gem install bundler -v 2.1.4③ERROR: While executing gem ... (Gem::FilePermissionError)
ERROR: While executing gem ... (Gem::FilePermissionError) You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.「gemのインストールの権限がありませんっ・・・!」
sudoを頭につけて行うという記事も多かったですが、そのままインストールよりも「rbenv」というバージョン管理のツールを用いて行う方法の方が今後のことを考えるといい気がします。参考1:gem installでpermissionエラーになった時の対応方法
参考2:rbenvによるRubyのインストールからHello, World!までrbenvの用意ができたら、もう一度やってみましょう
gem install bundler -v 2.1.4④bundle installができない
「bundleが・・・まだダメっ・・・!」
gem installができたところで、デプロイなどの「bundle~」系を入力するとまだエラー文がでます
Install missing gem executables with `bundle install`エラー文に従って、bundle installをしてみると
# 長いため、最後の部分だけ抜粋 An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: mysql2mysql周辺のエラーですが、原因は自力ではわかりませんでした。
下記の参考にさせていただいた記事通りで私は解決できました、本当にありがたい。参考:【Rails】MySQL2がbundle installできない時の対応方法
これでデプロイはできるようになると思います。
⑤Could not find a JavaScript runtime ~
「動いているけどっ・・・!」
Rspecを走らせた時のエラーです
$ bundle exec rspec spec/ --format documentation An error occurred while loading ./spec/models/cart_item_spec.rb. Failure/Error: require File.expand_path('../config/environment', __dir__) ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.githubに飛んでみると、ExecJSのどれかをダウンロードするよう勧めています。
gemをいれる方法もありましたが、Node.jsを選択してダウンロードに進めばスムーズに解決できました。これでRspecもできるようになりました。
終わりに
vagrantからdockerへの移行の手順とエラーの紹介をさせていただきました。
まだまだ勉強不足なところも多いため、dockerを十分に活用できていない点が多くあると改めて思いました。
参考にさせていただいた記事が多かったこともあり、当初思っていたよりも短い時間で導入まではできたので挑戦してみて良かったと感じています。以前のvagrant環境と同じことはできるようになったので、次はCircleCIの導入でより良い開発環境にできるよう学習を続けたいと思います。
初投稿ということもあり、わかりにくい部分もあったかとは思いますが最後までみていただきありがとうございました!
- 投稿日:2020-10-22T12:33:15+09:00
Vagrant上で作成したポートフォリオをDockerの開発環境に移行してみた
はじめに
初めまして、初投稿になりますので自己紹介と記事のきっかけについて書かせていただきます。
自己紹介
2020年9月にDMMWEBCAMPを卒業し、現在転職活動中のエンジニアです。記事のきっかけ
他のプログラミングスクールが同じかはわかりませんが、スクール生で共通の開発環境にするために全てvagrant上で仮想環境を構築しカリキュラムとポートフォリオの作成を行っていました。そのため、0からの自力で環境構築を行ったことがなく実務を意識するのならDockerぐらいは自力で入れておきたいと思い学習し始めたのがきっかけです。
まだまだ勉強中のため、正直かなり怪しいところは多いとは思いますが同じような勉強中の方の参考に少しでもなれば嬉しいです!この記事で扱うこと
Dockerを使用した開発環境の構築
Vagrant上でできていたことがDocker上でもできるようにするイメージですDockerfile、docker-composeなどの表記
参考までに、Vagrantを使用していた時の開発環境は下記になります
- OS:macOS Catalina 10.15.4
- バックエンド: Ruby 2.5.7、Rails 5.2.4.3
- 開発環境: Vagrant 2.2.4、SQLite (※MySQLやPostgreSQLの場合、この後の見本の表記が異なります)
- 本番環境: MySQL2、AWS (EC2、RDS for MySQL、Route53)、Nginx、 Puma、Capistrano
- 0からの開発環境の構築で起こりうるエラーと解決した方法
記事のきっかけにもあるように、ローカル環境の構築自体も初めてのためdocker以前のエラーもいくつかありました。解決のために参考にさせていただいた記事をメインに紹介していきたいと思います。この記事で扱わないこと
Dockerfile、docker-composeなどの基礎知識
Dockerに関する記事やyoutube多くありますがほとんどがコピペでなんとなく進んでしまうので、最初は自分でドキュメントを読みながら手を動かして全体の仕組みをなんとなく先に掴んでおくのが理解しやすいと思いました。
参考までに自分は下記サイトで学習を始めました。各用語調べながらでも半日〜2日ほどでできる内容かつDockerのイメージが掴めるかと思いますのでおすすめです。
- 入門Docker:dockerの基礎知識、操作について
- Quickstart: Compose and Rails:Ruby on Railsで具体的にdockerをどう始めるか
- 学習方法の選定にはKENTA / 雑食系エンジニアTVさんのyoutubeを参考にさせていただきました
ポートフォリオにAWSとDockerとCircleCIを組み込むための学習順序と教材について事前準備
- Dockerのインストール
こちらの公式サイトより行いましょう
https://hub.docker.com/editions/community/docker-ce-desktop-mac
ターミナルでバージョンの確認ができれば大丈夫です$ docker version Client: Docker Engine - Community Cloud integration 0.1.18 Version: 19.03.13 API version: 1.40
- ポートフォリオ(Dockerの開発環境にしたいプロジェクト)をgit cloneしてくる
記事のきっかけにもありますよう、既にポートフォリオを作成している段階のため環境構築したいものをgithubからローカル上に持ってきましょう$ mkdir docker # Desktopからもしくはvagrantを使用していた時のworkディレクトリなどで実行 $ git clone git@github.com:ユーザー名/リポジトリ名.git各ファイルの準備
Dockerとプロジェクトの準備ができたので、環境構築に必要なファイルを作成していきます
①Dockerfile
# 先ほどクローンしたディレクトリ上で $ touch Dockerfile下記内容を記入していきましょう
# 元となるdocker imageを指定。ポートフォリオのrubyのバージョンと合わせましょう FROM ruby:2.5.7 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/* #sqlite3を指定 RUN apt-get update && apt-get install -y sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs # コンテナ起動した時に、コンテナ内でmyappディレクトリを作成 RUN mkdir /myapp # コンテナ内の作業ディレクトリの指定(上のRUNで作成したディレクトリ) WORKDIR /myapp # ホストマシンのgemfileをコピーし、コンテナ内のディレクトリに貼り付け COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock # 貼り付けたgemfileを読み込み。bundlerバージョンが2以降だとこれが必要 ENV BUNDLER_VERSION 2.1.4 RUN gem install bundler RUN bundle install # アプリケーション(カレントディレクトリ)をコピーして、コンテナ内のディレクトリに貼り付け COPY . /myapp # ポートを3000で公開 EXPOSE 3000 # コンテナにてコマンドを実行 CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]
- ENV BUNDLER_VERSION 2.1.4について
Gefile.lockの一番下にこのような表記があるかと思いますので確認してみて下さいGemfile.lockBUNDLED WITH 2.1.4②docker-compose.yml
# 同じディレクトリ上で $ touch docker-compose.yml下記内容を記入していきましょう
docker-compose.ymlversion: '3' services: web: build: context: ./ dockerfile: Dockerfile # 変更を保存するファイルを指定 volumes: - .:/myapp # ポートを指定 ports: - "3000:3000" # コンテナの起動を維持させる tty: true stdin_open: trueMySQLやPostgreSQLを使用している場合だと、web+DBの記述も必要なのですがSQLiteの場合はこちらのみで大丈夫みたいです。まだこの辺りの知識が乏しいため、ゆくゆくはMySQLに変更したいと思っております・・・
コンテナを起動してみましょう
# Dockerfileを元にdocker imageの作成 $ docker-compose build # docker container をバックグランドで起動 $ dokcer-compose up -d # コンテナ内にてmigrationを更新 docker-compose run web rails db:migrateこれで、いつものlocalhost:3000で表示ができればひと段落です!
引っかかったエラーと解決した方法
大体ですが引っかかった時系列順に記載していきます。最初にも記載しておりますが、エラー内容とおそらくの原因の考察がサブで、メインが解決参考にさせていただいた記事の紹介になります。ありがとうございました
①ActiveRecord::PendingMigrationError Migrations are pending
「dokcer-compose up -d で動いているのにっ・・・!」
$ docker-compose up -d Creating network "~~~_default" with the default driver Creating ~~~_web_1 ... donelocalhost:3000のページへアクセス時に起きたエラーです
コンテナの起動後、docker-compose run web rails db:migrate を行いましょう、あるあるな気がします
参考:docker-compose upで立ち上げたRailsでActiveRecord::PendingMigrationError Migrations are pending②To install the missing version, run 'gem install bundler:2.1.4'
「bundleで始まるコマンド(デプロイ・Rspecなど)ができないっ・・・!」
Traceback (most recent call last): 2: from 1: from ~ Could not find 'bundler' (2.1.4) required by your /Users/ユーザー名/ディレクトリ名/Gemfile.lock. (Gem::GemNotFoundException) To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.1.4`Dockerfileを作成した時に確認した、Gemfile.lockの「BUNDLED WITH 2.1.4」がローカル上で見つからないイメージです。
参考:To install the missing version, rungem install bundler:2.1.4
と出たときの対処法エラー文の通りにインストールを試してみましょう、ダメだった場合は③へ
gem install bundler -v 2.1.4③ERROR: While executing gem ... (Gem::FilePermissionError)
ERROR: While executing gem ... (Gem::FilePermissionError) You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.「gemのインストールの権限がありませんっ・・・!」
sudoを頭につけて行うという記事も多かったですが、そのままインストールよりも「rbenv」というバージョン管理のツールを用いて行う方法の方が今後のことを考えるといい気がします。参考1:gem installでpermissionエラーになった時の対応方法
参考2:rbenvによるRubyのインストールからHello, World!までrbenvの用意ができたら、もう一度やってみましょう
gem install bundler -v 2.1.4④bundle installができない
「bundleが・・・まだダメっ・・・!」
gem installができたところで、デプロイなどの「bundle~」系を入力するとまだエラー文がでます
Install missing gem executables with `bundle install`エラー文に従って、bundle installをしてみると
# 長いため、最後の部分だけ抜粋 An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: mysql2mysql周辺のエラーですが、原因は自力ではわかりませんでした。
下記の参考にさせていただいた記事通りで私は解決できました、本当にありがたい。参考:【Rails】MySQL2がbundle installできない時の対応方法
これでデプロイはできるようになると思います。
⑤Could not find a JavaScript runtime ~
「動いているけどっ・・・!」
Rspecを走らせた時のエラーです
$ bundle exec rspec spec/ --format documentation An error occurred while loading ./spec/models/cart_item_spec.rb. Failure/Error: require File.expand_path('../config/environment', __dir__) ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.githubに飛んでみると、ExecJSのどれかをダウンロードするよう勧めています。
gemをいれる方法もありましたが、Node.jsを選択してダウンロードに進めばスムーズに解決できました。これでRspecもできるようになりました。
終わりに
vagrantからdockerへの移行の手順とエラーの紹介をさせていただきました。
まだまだ勉強不足なところも多いため、dockerを十分に活用できていない点が多くあると改めて思いました。
参考にさせていただいた記事が多かったこともあり、当初思っていたよりも短い時間で導入まではできたので挑戦してみて良かったと感じています。以前のvagrant環境と同じことはできるようになったので、次はCircleCIの導入でより良い開発環境にできるよう学習を続けたいと思います。
初投稿ということもあり、わかりにくい部分もあったかとは思いますが最後までみていただきありがとうございました!
- 投稿日:2020-10-22T12:17:58+09:00
Rails使うときの細かなTips
残しておかないと後で忘れそうな奴を書いておく。随時追記予定。
Datadogにエンドポイントでの挙動を細かく出したい (grape使用時)
このリファレンスを参照
https://docs.datadoghq.com/ja/tracing/setup/ruby/#grape下記の実装例のように、リファレンスのoptionsにある
analytics_enabled
をtrueにするといい感じになる。
grapeとrails両方指定するのがポイント。実装例
require 'grape' require 'ddtrace' Datadog.configure do |c| c.use :grape, service_name: 'hoge-app', analytics_enabled: true end Datadog.configure do |c| c.use :rails, service_name: 'hoge-app', analytics_enabled: true end
- 投稿日:2020-10-22T11:30:06+09:00
外部キーを持つデータの削除方法
環境
この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。
前提
3つのモデルを作成し、以下の状況となっています。
User / ユーザー
Report / レポート
Comment / ユーザーとレポートの関連付けテーブルコメントがついているレポートの削除を行う際に、以下のエラーが発生しました。
エラー発生
ActiveRecord::InvalidForeignKey in ReportsController#destroy
外部キーエラー
何が原因なのか、その解決策を考えます。
原因
レポートの削除を行う際に、コメントテーブルのユーザーとレポートのidの値がなくなることが原因と判断。(外部キー制約のため)
ユーザーとレポートのidの関連付けがなくなったものは、コメントも削除できるようにしたい。解決策
dependent: :destroy
をUserモデル
とReportモデル
に記述する。
この記述により、親モデル(Report)を削除する際に、その親モデルに紐づく「子モデル(Comment)」も一緒に削除できる」ようになります。検証
app/models/user.rbclass User < ApplicationRecord has_many :reports has_many :comments, dependent: :destroy endapp/models/report.rbclass Report < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy end無事解決しました!
以上です。
同じような悩みや壁にぶつかっている方のお役に立てれば幸いです!参考
下記を参考にしたので、より深く内容を理解したい方は、見てみてください!
https://qiita.com/Ushinji/items/650fa295a3054d2fe582
https://qiita.com/ITmanbow/items/2170ccaceafd5d401df8
https://qiita.com/Tsh-43879562/items/fbc968453a7063776637
- 投稿日:2020-10-22T11:01:58+09:00
【Rails】System Spec 統合テストの同じ記述をまとめる
ディレクトリとファイルを用意
例として、サインインについての記述をまとめる
specディレクトリ配下にsupportディレクトリを作成
モジュールファイルへの記述
sign_in_support.rbmodule SignInSupport def sign_in(user) visit new_user_session_path fill_in 'Email', with: user.email fill_in 'Password', with: user.password find('input[name="commit"]').click expect(current_path).to eq root_path end end作成したファイルを読み込めるように、rails_helper.rbを編集
rails_helper.rbのコメントアウトを外してモジュールを設定する。
SignInSupport = ジュールファイルのsign_in_support
rails_helper.rbDir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }rails_helper.rbRSpec.configure do |config| config.include SignInSupport #SignInSupport = ジュールファイル名のsign_in_support config.fixture_path = "#{::Rails.root}/spec/fixtures" #以下省略テストファイルへの記述
spec/system/tweets_spec.rbrequire 'rails_helper' RSpec.describe 'ツイート投稿', type: :system do before do @user = FactoryBot.create(:user) @tweet_text = Faker::Lorem.sentence @tweet_image = Faker::Lorem.sentence end context 'ツイート投稿ができるとき'do it 'ログインしたユーザーは新規投稿できる' do # ログインする sign_in(@user) # 新規投稿ページへのリンクがあることを確認する expect(page).to have_content('投稿する') # 投稿ページに移動する visit new_tweet_path # フォームに情報を入力する fill_in 'tweet_image', with: @tweet_image fill_in 'tweet_text', with: @tweet_text # 送信するとTweetモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { Tweet.count }.by(1) # 投稿完了ページに遷移することを確認する expect(current_path).to eq tweets_path # 「投稿が完了しました」の文字があることを確認する expect(page).to have_content('投稿が完了しました。') # トップページに遷移する visit root_path # トップページには先ほど投稿した内容のツイートが存在することを確認する(画像) expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet_image});']" # トップページには先ほど投稿した内容のツイートが存在することを確認する(テキスト) expect(page).to have_content(@tweet_text) end end context 'ツイート投稿ができないとき'do it 'ログインしていないと新規投稿ページに遷移できない' do # トップページに遷移する visit root_path # 新規投稿ページへのリンクがないことを確認する expect(page).to have_no_content('投稿する') end end end RSpec.describe 'ツイート編集', type: :system do before do @tweet1 = FactoryBot.create(:tweet) @tweet2 = FactoryBot.create(:tweet) end context 'ツイート編集ができるとき' do it 'ログインしたユーザーは自分が投稿したツイートの編集ができる' do # ツイート1を投稿したユーザーでログインする sign_in(@tweet1.user) # ツイート1に「編集」ボタンがあることを確認する expect( all(".more")[1].hover ).to have_link '編集', href: edit_tweet_path(@tweet1) # 編集ページへ遷移する visit edit_tweet_path(@tweet1) # すでに投稿済みの内容がフォームに入っていることを確認する expect( find('#tweet_image').value ).to eq @tweet1.image expect( find('#tweet_text').value ).to eq @tweet1.text # 投稿内容を編集する fill_in 'tweet_image', with: "#{@tweet1.image}+編集した画像URL" fill_in 'tweet_text', with: "#{@tweet1.text}+編集したテキスト" # 編集してもTweetモデルのカウントは変わらないことを確認する expect{ find('input[name="commit"]').click }.to change { Tweet.count }.by(0) # 編集完了画面に遷移することを確認する expect(current_path).to eq tweet_path(@tweet1) # 「更新が完了しました」の文字があることを確認する expect(page).to have_content('更新が完了しました。') # トップページに遷移する visit root_path # トップページには先ほど変更した内容のツイートが存在することを確認する(画像) expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet1.image}+編集した画像URL);']" # トップページには先ほど変更した内容のツイートが存在することを確認する(テキスト) expect(page).to have_content("#{@tweet1.text}+編集したテキスト") end end context 'ツイート編集ができないとき' do it 'ログインしたユーザーは自分以外が投稿したツイートの編集画面には遷移できない' do # ツイート1を投稿したユーザーでログインする sign_in(@tweet1.user) # ツイート2に「編集」ボタンがないことを確認する expect( all(".more")[0].hover ).to have_no_link '編集', href: edit_tweet_path(@tweet2) end it 'ログインしていないとツイートの編集画面には遷移できない' do # トップページにいる visit root_path # ツイート1に「編集」ボタンがないことを確認する expect( all(".more")[1].hover ).to have_no_link '編集', href: edit_tweet_path(@tweet1) # ツイート2に「編集」ボタンがないことを確認する expect( all(".more")[0].hover ).to have_no_link '編集', href: edit_tweet_path(@tweet2) end end end RSpec.describe 'ツイート削除', type: :system do before do @tweet1 = FactoryBot.create(:tweet) @tweet2 = FactoryBot.create(:tweet) end context 'ツイート削除ができるとき' do it 'ログインしたユーザーは自らが投稿したツイートの削除ができる' do # ツイート1を投稿したユーザーでログインする sign_in(@tweet1.user) # ツイート1に「削除」ボタンがあることを確認する expect( all(".more")[1].hover ).to have_link '削除', href: tweet_path(@tweet1) # 投稿を削除するとレコードの数が1減ることを確認する expect{ all(".more")[1].hover.find_link('削除', href: tweet_path(@tweet1)).click }.to change { Tweet.count }.by(-1) # 削除完了画面に遷移することを確認する expect(current_path).to eq tweet_path(@tweet1) # 「削除が完了しました」の文字があることを確認する expect(page).to have_content('削除が完了しました。') # トップページに遷移する visit root_path # トップページにはツイート1の内容が存在しないことを確認する(画像) expect(page).to have_no_selector ".content_post[style='background-image: url(#{@tweet1.image});']" # トップページにはツイート1の内容が存在しないことを確認する(テキスト) expect(page).to have_no_content("#{@tweet1.text}") end end context 'ツイート削除ができないとき' do it 'ログインしたユーザーは自分以外が投稿したツイートの削除ができない' do # ツイート1を投稿したユーザーでログインする sign_in(@tweet1.user) # ツイート2に「削除」ボタンが無いことを確認する expect( all(".more")[0].hover ).to have_no_link '削除', href: tweet_path(@tweet2) end it 'ログインしていないとツイートの削除ボタンがない' do # トップページに移動する visit root_path # ツイート1に「削除」ボタンが無いことを確認する expect( all(".more")[1].hover ).to have_no_link '削除', href: tweet_path(@tweet1) # ツイート2に「削除」ボタンが無いことを確認する expect( all(".more")[0].hover ).to have_no_link '削除', href: tweet_path(@tweet2) end end end RSpec.describe 'ツイート詳細', type: :system do before do @tweet = FactoryBot.create(:tweet) end it 'ログインしたユーザーはツイート詳細ページに遷移してコメント投稿欄が表示される' do # ログインする sign_in(@tweet.user) # ツイートに「詳細」ボタンがあることを確認する expect( all(".more")[0].hover ).to have_link '詳細', href: tweet_path(@tweet) # 詳細ページに遷移する visit tweet_path(@tweet) # 詳細ページにツイートの内容が含まれていることを確認する expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet.image});']" expect(page).to have_content("#{@tweet.text}") # コメント用のフォームが存在することを確認する expect(page).to have_selector 'form' end it 'ログインしていない状態でツイート詳細ページに遷移できるもののコメント投稿欄が表示されない' do # トップページに移動する visit root_path # ツイートに「詳細」ボタンがあることを確認する expect( all(".more")[0].hover ).to have_link '詳細', href: tweet_path(@tweet) # 詳細ページに遷移する visit tweet_path(@tweet) # 詳細ページにツイートの内容が含まれていることを確認する expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet.image});']" expect(page).to have_content("#{@tweet.text}") # フォームが存在しないことを確認することを確認する expect(page).to have_no_selector 'form' # 「コメントの投稿には新規登録/ログインが必要です」が表示されていることを確認する expect(page).to have_content 'コメントの投稿には新規登録/ログインが必要です' end endspec/system/comments_spec.rbrequire 'rails_helper' RSpec.describe 'コメント投稿', type: :system do before do @tweet = FactoryBot.create(:tweet) @comment = Faker::Lorem.sentence end it 'ログインしたユーザーはツイート詳細ページでコメント投稿できる' do # ログインする sign_in(@tweet.user) # ツイート詳細ページに遷移する visit tweet_path(@tweet) # フォームに情報を入力する fill_in 'comment_text', with: @comment # コメントを送信すると、Commentモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { Comment.count }.by(1) # 詳細ページにリダイレクトされることを確認する expect(current_path).to eq tweet_path(@tweet) # 詳細ページ上に先ほどのコメント内容が含まれていることを確認する expect(page).to have_content @comment end end
- 投稿日:2020-10-22T10:35:03+09:00
Railsコントローラーで同じ記述をprivate以下にメソッドとして切り分け、before_actionで呼び出せるように実装してみた
この記事ではインストールしたRails 6.0.0を使っています。
*初めての投稿です。見づらければ申し訳ないですやること
複数の記述をまとめる
コントローラーを見やすくする
privateメゾットを使用
before_actionを使用初期コード
items_controller.rbdef show @item = Item.find(params[:id]) end def edit @item = Item.find(params[:id]) end def update @item = Item.find(params[:id]) end【見ていていただくとわかるように同じような記述が複数のアクション内で使われています。こちらを切り分けて呼び出したいと思います!】
方法1 〜private以下に切り分けてあげる〜
items_controller.rbprivate def set_item @item = Item.find(params[:id]) endまず初期コードにある@(インスタンス変数)の記述を削除します。
そしてprivateを下部に作ります。メゾット set_itemを作りその中に先ほど削除した記述を入れてあげます。
(メゾット名はわかり易ければなんでも大丈夫です)
こうすることで、メゾットとして切り分けができます。ですがこれだけでは、切り出しただけで呼び出すことができません。方法2 ~before_actionで呼び出してあげる〜
items_controller.rbbefore_action :set_item, only: [:show, :edit, :update]コントローラー上部にこちらを記述します。一つ一つ説明すると
before_action :各アクションが実行される前に呼び出すための記述
set_item : 方法1のprivate内で作ったメゾット名
only: 特定のアクションだけでつかいますよという感じの意味。
反対語としてexpectがあり、それは特定のアクション以外で使うという意味!
そのあとは指定するアクション名を記述!最後に
初期コードの方が分かりやすいしやる意味あるかな?と最初は思うかもしれません。僕もそうでした。笑
ですが例えばこのあと他のアクションにまた同じ記述が必要になった時などに、before_actionにアクション名を追加するだけになるのでとても便利です!
コードを他の方がみても分かりやすく長ったるい記述じゃないように書けます!
これからも他の方が見やすいコードをかけるよう意識していこうと思っています!
- 投稿日:2020-10-22T02:53:15+09:00
文字列
文字列
文字列は、プログラミングの中で文字を扱うための値です。
文字列を生成するには、文字をダブルクォーテーション"
または、シングルクォーテーション'で囲みます。
以下の例を見てください。【例】Rubyファイル"This is string." 'This is string, too.'このように、文字列は簡単に生成できます。
irbで以下のコードを実行してみましょう
irbでRubyの文字列を記述し、実行してみましょう。
ターミナル# irbを起動 % irbirb# 文字列を書いてエンターキーで実行 irb(main):001:0> "Hello! こんにちは!" # 続けてこのように表示されれば成功 => "Hello! こんにちは!"全角スペースや日本語が含まれているとプログラムはエラーが生じますが、
文字列の値として扱う場合は、エラーにはなりません。まとめ
文字列とは、プログラミングの中で文字を扱うための値のこと。
以上。
- 投稿日:2020-10-22T01:55:29+09:00
Heroku上の画像をAWSのS3へ保存する方法
はじめに
AWSはAmazon Web Servisesの略で、Amazonが提供しているクラウドサーバーのサービスです。
多くの人や企業で使用されているサービスであり、使う容量や期間によっては完全に無料で使うことができます。Heroku上の画像をAWSに保存する理由
Herokuに保存した画像は24時間に一度リセットされる仕様になっています。
画像を外部のサーバーに保存することで、画像が消えてしまうという現象を防ぐことができるため、今回は外部サーバーとしてAWSを利用します。S3とは
AWSのサービスの一つで、12ヶ月間、5GBまでのストレージを無料で利用することができるサービスで、画像などを保存しておくことができます。
今回の手順
AWSへの登録はできている前提で進めます。(登録していない方はAWSで検索して、新規登録してください。)
- IAMユーザーを作成する
- S3に画像の保存先を用意する
- ローカル環境で画像の保存先をS3に変更する
- 本番環境で画像の保存先をS3に変更する
IAMユーザーを作成する
IAMとは
IAMとはAWSのサービスの一つです。
AWSで作成したアカウントはすべての権限を持つルートユーザーとなり、万が一、情報漏洩してしまうと悪用されてしまうリスクがあります。
そのため、使える権限を制限したユーザーを作成し、通常の作業はそのユーザーで行うようにします。
その制限された権限を持つユーザーを作成する機能を持つのがIAMです。IAMユーザーの作成
IAMで作成されるユーザーをIAMユーザーと呼びます。
まず、AWSのサービス検索でIAMを検索し、IAMページへ遷移します。
サイドバーのユーザーから「ユーザーを追加」をクリックします。
そして、任意のユーザー名を入力し、プログラムによるアクセスにチェックを入れ、「次のステップ」をクリックします。
既存のポリシーを直接アタッチを選択し、ポリシーのフィルタにamazons3と入力し、、「AmazonS3FullAccess」にチェックを入れ、「次のステップ」をクリックします。
タグの追加はやりたい人のみやって、「次のステップ」をクリックします。
設定の確認画面が出るので、問題なければ「ユーザーの作成」をクリックします。
この時に出てくる「.csvのダウンロード」は忘れずに、行います。(後々、使うため。)IAMユーザーのパスワードを設定
上で作成したAWS上のIAMユーザーの中で、認証情報のタブをクリックすると、コンソールのパスワード欄があります。
ここの管理をクリックした後に出てくるページで、コンソールへのアクセスは有効化、パスワードの設定は自動生成パスワードを選択し、適用します。
そうすると、パスワードが生成されます。
この時に出てくる「.csvのダウンロード」は忘れずに、行います。(後々、使うため。)S3に画像の保存先を用意する
バケットの作成
S3で実際にデータが格納される場所のことをバケットと呼びます。
AWSにログイン後、サービス検索でS3のページに遷移すると、バケットという文字が出てきます。
サイドバーのバケットという項目の中には「バケットを作成する」というボタンが存在するので、そのボタンをクリックします。バケット名とリージョンの設定
バケット名とリージョンの入力画面に遷移するため、自身で考えたバケット名を入力します。
※バケット名はAWSを使う誰かのバケットと同じ名前はつけられません。リージョンは地域という意味ですが、ここではサーバーの場所を表します。
日本人ならアジアパシフィック(東京)を選択で良いと思います。オプションの設定
オプションを設定したい方はここで設定します。
設定が終われば、次へをクリックします。アクセス許可の設定
デフォルトでは「パブリックアクセスをすべてブロック」にチェックが入っています。
このチェックを外すと、パブリックアクセスのお好みの設定ができます。
設定が終われば、次へをクリックします。確認
確認ページではこれまで行った設定の一覧が表示されるので、問題なければ、「バケットを作成」をクリックします。
バケットポリシーの設定
バケットポリシーとは、どのようなアクセスに対してS3への保存やデータの読み取りを許可するか決められる仕組みです。
今回は先ほど作成したIAMユーザーからのアクセスを許可します。まず、IAMユーザーのARNをコピーして、どこかにメモしておきます。
作成したバケットから、アクセス権限をクリックし、バケットポリシーをクリックし、以下を記述します。
{ "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "コピーしたIAMユーザーのARN" }, "Action": "s3:*", "Resource": "arn:aws:s3:::バケット名" } ] }保存すれば、完了です。
ローカル環境で画像の保存先をS3に変更する
Gemのインストール
rubyでS3を使用するためにaws-sdk-smというGemをインストールします。
Gemfile一番下に下記を追記 gem "aws-sdk-s3", require: falseその後、ターミナルでbundle installを入力します。
保存先を指定する
config/environments/development.rbconfig.active_storage.service = :local 上の記述を以下に変更 config.active_storage.service = :amazonconfig/storage.yml以下のコードを追記 amazon: service: S3 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> region: ap-northeast-1 bucket: ご自身のバケット名※regionのap-northease-1はアジアパシフィック(東京)の番号
環境変数の設定
ターミナル% vim ~/.zshrc iを押して、以下を追記 export AWS_ACCESS_KEY_ID="ここにCSVファイルのAccess key IDの値をコピー" export AWS_SECRET_ACCESS_KEY="ここにCSVファイルのSecret access keyの値をコピー" (CSVファイルはIAMユーザー作成時にダウンロードしたファイルのこと。) :wqで保存する
ローカル環境で、アプリケーションから画像を投稿し、問題なくS3に保存されていることが確認できたら、本番環境でも画像の保存先をS3に変更します。
本番環境で画像の保存先をS3に変更する
保存先を指定する
ローカル環境と同様の作業を行います。
config/environments/production.rbconfig.active_storage.service = :local 上の記述を以下に変更 config.active_storage.service = :amazon環境変数の設定
本番環境にはHerokuを用いるため、Heroku上で環境変数を設定します。
heroku config:setコマンドで環境変数の設定をすることができます。ターミナル% heroku config:set AWS_ACCESS_KEY_ID="ここにCSVファイルの「Access key ID」の値をコピー" % heroku config:set AWS_SECRET_ACCESS_KEY="ここにCSVファイルの「Secret access key」の値をコピー" 環境変数が設定できたか確認するときは % heroku config
編集内容をHerokuに反映させる
ターミナル% git push heroku master
参考
テックキャンプのカリキュラム
「AWSに画像をアップロードする」最後に
本投稿が初学者の復習の一助となればと幸いです。