- 投稿日:2020-10-22T23:21:45+09:00
少しずつ正規表現を覚えていく①
- 投稿日: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-22T21:49:24+09:00
[プログラミング大全] §4 変数と定数
目的
「プログラミングを学ぼう!!」と意気込んだものの、
プログラミングって難しそうだし、英語も読めないし、PC操作自体苦手だし、
昔に挫折したし、というあなたに向けて、
この記事を読み・PCを動かすだけで「なんだ!プログラミングって面白いじゃん!」
って思ってもらうのが目的です。プログラミング大全というタイトルで連載化していきたいと考えています。
開発環境
- Mac:macOS10.15.7(メインPC)
- ブラウザ:Google Chrome
- テキストエディタ:VSCode(Visual Studio Code)
変数と定数
今日は変数と定数について書いていきたいと思います。
中学数学で聞き覚えがあると思いますがプログラミング上ではニュアンスが少し異なります。変数とは
変数値は、文字列や数値といったオブジェクトを認識するための手法です。
変数を利用することで、再利用したいオブジェクトを変数の名前で呼び出すことが可能です。言葉だけだとイメージしにくいので実際にプログラムを動かしながら確認していきましょう!
sample.rbword = "Hello World" #この場合wordが変数名になり"Hello world"が収納したい値になります。 puts word #putsの後に変数名を打ち込みますターミナルで確認します。
$ ruby sample.rb #以下の様に表示されれば成功です。 Hello World変数の特徴としてはプログラム中に再代入が可能なことです。
sample.rbword = "Hello World" word = "このリンゴは200円です。" #上の行で変数化したものを変更します。 puts wordターミナルで確認します。
$ ruby sample.rb #以下の様に表示されれば成功です。 このリンゴは200円です。上記の様に後から代入したものに書き変わっていきます。
Rubyにおいては変数にはどんなオブジェクトを代入でき文字列を代入した後に
数値を代入することも可能です。変数にメソッドを使うことも可能です。変数を扱う練習
イメージしやすくするために、もう少し変数を扱っていきましょう。
「底辺が3、高さが8の三角形の面積」を求めるプログラムを書きます。
samole.rbbottom = 3 #底辺を変数としています。 puts bottom * 8 / 2ターミナルで確認します。
12
ではこの三角形の底辺が5になった場合はどうすれば良いでしょうか?
samole.rbbottom = 5 #ここの値を5に帰るだけです puts bottom * 8 / 2ターミナルで確認します。
20
この様に変数を使ってわかりやすいコードを書くことを心がけましょう!
定数
最後に定数ですが、定数は基本的に変数と同様に値を格納する方法です。
変数との違いは再代入が不可です。
定数を定義することは、最初の文字を大文字にづることです。
*慣習としては全て大文字にし、再代入はしません。定数を再代入しようとすると以下の様にエラー文が出ます。
sample.rbWORD = "Hello world" WORD = "このリンゴは200円です。" puts WORD$ ruby sample.rb sample.rb:2: warning: already initialized constant WORD sample.rb:1: warning: previous definition of WORD was here このリンゴは200円です。定数に関しては円周率など変わることがない値に使いましょう!
sample.rbPI = 3.14 puts 5 * 5 * PI$ ruby sample.rb #以下の様に表示されれば成功です、 78.5以上です!
備考
───────────────────────────────
■著者おすすめの本
───────────────────────────────「プロになるためのWeb技術入門」
「転職の思考法」
「ハイパワーマーケティング」
「嫌われる勇気」
「アウトプット大全」───────────────────────────────
■著者おすすめの映画
───────────────────────────────「マイ・インターン」
「シン・ゴジラ」
「ドラゴンボール超 ブロリー」
「School of Roc」
- 投稿日:2020-10-22T20:18:43+09:00
【Ruby on Rails】Rubocop-airbnbを使用して、コードチェック
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS CatalinaRuboCopとは
RuboCopとは、rubyコードが「コーディング規約どおりに書かれているか」をチェックする静的コード解析ツールです。
rubocop−airbnbとは
airbnb社が作成した解析となり、今回はこちらを使用しコードチェックを行います。
gitは下記の通りです。
https://github.com/airbnb/ruby/tree/master/rubocop-airbnbRubocop-airbnb」のインストール
下記の通りに追加します。
Gemfilegroup :development do ... gem 'rubocop-airbnb' endターミナル$ bundle install設定ファイルの作成
アプリケーションフォルダ配下に
「.rubocop.yml」と「.rubocop_airbnb.yml」ファイルを作成します。
これらを作成することによりrubocopでコード解析したくないファイルやフォルダを指定します。
※ファイル名の「.」は必要なため忘れず記載してください。.rubocop.ymlinherit_from: - .rubocop_airbnb.yml AllCops: Exclude: - 'db/**/*' - 'bin/*' - 'config/environments/*' - 'config/application.rb' - 'config/initializers/*' - 'config/spring.rb' - 'lib/tasks/*' - 'vendor/**/*' - 'path/ruby'.rubocop_airbnb.ymlrequire: - rubocop-airbnbRubocopでコードのチェック
ターミナルでアプリケーションフォルダに移動し、下記を実行。
ターミナル$ bundle exec rubocop --require rubocop-airbnb ... 50 files inspected, 150 offenses detectedこのように表示されます。
50ファイルをチェックして150箇所のコーディング違反があったことを意味しています。
このように数が膨大になると一つ一つチェックは難しいため、下記を実行。ターミナル$ bundle exec rubocop --require rubocop-airbnb -a ... 50 files inspected, 150 offenses detected, 150 offenses correctedこのように150個すべてを修正してくれました。
これでも修正が出来ていない場合は、目視での確認になります。まとめ
このようにコードの自動修正が簡単にできます。
すごく便利なのでぜひ使ってみてください。またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork
- 投稿日: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さん、了解しました。記録残しておきますね。そして各役割ごとに、業務をルーチン化して効率を向上させている。
※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-22T18:22:05+09:00
【Ruby】twitter-adsの中身を叩き切る
業務でtwitter-adsを使ってTwitterの広告配信管理画面のAPIを叩くことがあったのですが、
Ruby初心者過ぎてかなりしんどかったのでメモしておきます。twitter-adsバージョンは7です。
クライアントを生成する
これを作らないことには始まりません。
require 'twitter-ads' # initialize the client client = TwitterAds::Client.new( CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET )アカウントを取得する
githubのquick startをみると。
# load the advertiser account instance account = client.accounts('c3won9gy')とありますが、そこに渡すID?はなんやねん。という話になります。
色々と調べていった結果、client.accounts.each do |v| p v.id endこういう感じでやったらそれらしいのが1件でてきたのでそこででてきたIDを渡してあげればいけました。
アカウントが保持しているキャンペーン一覧をだす
上記で取得した、
account
を使います。account.campaigns.each do |v| p "=====================" p v.id p v.reasons_not_servable p v.servable p v.deleted p v.created_at p v.updated_at p v.name p v.funding_instrument_id p v.end_time p v.start_time p v.entity_status p v.currency p v.standard_delivery p v.daily_budget_amount_local_micro p v.total_budget_amount_local_micro p v.to_delete p "=====================" endこうやってやるとズラーっとキャンペーン一覧がでてきます。
(こうやって一個一個pせずに一行で全部の値を出す方法ご存じの方いたら教えて下さい・・・)※ステータスが「削除済み」となっているキャンペーンについては出力されません。
特定のキャンペーンのみのオブジェクトを作りたい場合は、
campaign = account.campaigns('1m5bbbb')こういう感じで生成してあげて、(渡すIDは上記の
v.id
で出てきた値です)campaign.nameという感じでキャンペーン名が取得できます。
campaign.name = "MADOKA" campaign.saveってやればキャンペーン名を変更できたりもします。幸せいっぱいですね。
広告グループを出力する
account.line_items()こちらも
account
が保持する広告グループを出力するので、account
を使います。これで広告グループの一覧が取得できます。
※ステータスが、「実行中」、「停止中」、「予約済み」、「おすすめ」のものがでてきます。テイラードオーディエンス一覧をだす。
これも
account
を使います。account.tailored_audiences.each do |v| p "===========" p v.id p v.name p v.list_type p v.audience_size p v.audience_type p v.metadata p v.partner_source p v.reasons_not_targetable p v.targetable p v.targetable_types p "===========" endここまででなんとなく取得系の雰囲気はつかめてきました。
ここから更に実践的な使い方を書いていきます。
広告グループに紐付いているテイラードオーディエンスを出す。
広告グループは、
line_item = account.line_items('1abcd')で取得できます。
で、この取得した
line_item
で、line_item.targeting_criteria.each do |target| if target.targeting_type == 'TAILORED_AUDIENCE' then p account.tailored_audiences(target.targeting_value).name end endこんな感じになります。
広告グループに紐付いているテイラードオーディエンスの紐づきを解除する
line_item.targeting_criteria.each do |target| if target.targeting_type == 'TAILORED_AUDIENCE' then target.delete! end end特定のテイラードオーディエンスを指定した広告グループに紐付ける
これがかなり手こずったのですが、
tc = TwitterAds::TargetingCriteria.new(account) tc.line_item_id = '紐付け対象広告グループのID' tc.targeting_value = '紐付けたいテイラードオーディエンスID(value)' tc.targeting_type = 'TAILORED_AUDIENCE' ここは固定でこれ入れればOK tc.saveといった感じでした。
以上です
紐付けに関して。
TA(テイラードオーディエンス)を作成してもすぐには「準備完了」ステータスにならないので、
作成後、定期的にそのTAの様子をバッチで見に行って、ステータスが「準備完了」(targetable)になったのを確認して、紐付けっていうロジックを組む必要があります。このSDKの使い方を理解するまでがくそしんどかったのですが、理解さえできれば後は書くだけなので楽しかったです。
今日はそんな感じです。
- 投稿日: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: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-22T12:35:05+09:00
Ruby アンパサンドを用いた安全参照演算子(&.)とは
はじめに
変数が
nil
の時、メソッドによってはエラーを起こすものがあったりします。例えば、なんかの処理で値を配列に詰めたものを
each
で回したりするときに、もしnil
が入ってたりすると、処理が止まってエラーが出る。。なんてことが起こると困りますよね。そんなときはガード節(関数や繰り返し処理の最初に例外を取り除くように実装する方法)を使って
next if [変数].nil?
とやって、nil
を回避できたりするんですが、論理演算子を応用した別の方法があるときいて、メモしていきたいと思います。アンパサンド(&)を用いた安全参照演算子
最初に結論から申しますと、
[オブジェクト]&.[メソッド]
というかたちで書きます。例えば、
#&を使わないと ary = [[1,2,3],nil,[5,6]] ary.each do |element_ary| p element_ary.length end #=> #3 #Traceback (most recent call last): # 2: from test.rb:3:in `<main>' # 1: from test.rb:3:in `each' #test.rb:4:in `block in <main>': undefined method `length' for #nil:NilClass (NoMethodError) #&を使うと ary = [[1,2,3],nil,[5,6]] ary.each do |element_ary| puts element_ary&.length end #=> #3 #nil #2このように、エラーがおきません。
原理
実はこれ、論理演算子
&&
が関係しています。一般的に、論理演算子は以下の特徴を持ってます。
・左から順に評価される
・論理式の真偽が決まると、残りの式は評価されない
・最後に評価された式の値が論理全体の値となるつまり、
[条件1] && [条件2]
とあったとき、[条件2]
は[条件1]
が真のときにのみ、評価されます。[条件1]
がnil
もしくはfalse
であった場合、条件2を評価するまでもなく、論理演算子&&
の結果はnil
もしくはfalse
になるからです。これを応用して、上記の例を用いて説明すると、以下のように書けます。
ary = [[1,2,3],nil,[5,6]] ary.each do |element_ary| p element_ary && element_ary.length end
element_ary
にnil
が入ると[条件1]
の時点で、論理演算子の処理が終わり、nil
を返すので、[条件2]
のlength
メソッドにnil
が渡ることはありません。これを省略して以下のように、オブジェクトの後に
&
をつけることでも表現できるのが、安全参照演算子と呼ばれるものだったのです。ary = [[1,2,3],nil,[5,6]] ary.each do |element_ary| p element_ary&.length endおわりに
論理演算子の応用例ということでしたが、あたまいいなあと思いました。
- 投稿日: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-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:03:42+09:00
備忘録
- 投稿日: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-22T02:00:21+09:00
【ruby】文字列末尾への挿入、置換、破壊【b021】
某スキルチェックの際にググったメソッド忘備録 兼 初投稿
1.出力された値の取得
ループ処理の中に任意の数、入力された文字を配列として取得qiita.rbhoge #入力される文字 huga #入力される文字 piga #入力される文字 lines = [] #予め配列の変数を定義 lines << gets.chomp #改行がないようにchomp・複数行にわたって文字列がくるので配列に追加していく
qiita.rblines = ["hoge", "fuga", "piga"] puts lines[0] #インデックス番号(添字)を指定して任意の文字を出力 >> hoge・インデックス番号で任意の文字を呼び出し条件によって必要な変更を行い出力
2.文字末尾へ挿入
文字列末尾の種類によって挿入するか置換するかの条件分岐を考えます。
qiita.rb#条件分岐 文字列.end_with?('探したい値') #任意の文字末尾に探したい値があるか puts hoge.end_with?('e', 'ge', 'ch') #複数の値を一度に参照可能(どれか一つでもあればおk) >> ture #挿入メソッド 文字列.insert(挿入先のインデックス番号,"挿入する値") #['h','o','g','e']のインデックス番号は先頭からだと[0,1,2,3]、末尾からだと[-4,-3,-2,-1] puts hoge.insert(-1,"s") #末尾へ挿入 >> hoges puts hoge.insert(0,"s") #先頭へ挿入 >> shoge #破壊的メソッド 文字列.delete_suffix("消す値") #末尾の消す値を消去 puts gehoge.delete_suffix("ge") >> geho #先頭は消えない #置換(末尾"ge"を"ver"へ変換) puts gehoge.delete_suffix("ge").insert(-1,"ver") >> hehover参考
https://qiita.com/uuchan/items/a4e9382440bdc4d2ac75
https://qiita.com/prgseek/items/92b49fe6b0a579f9cdd8さくっと置換できれば良かったが難しかったので合わせ技でゴリ押した
もっと良い方法を知っている方、コメントいただければ幸いです!
- 投稿日:2020-10-22T01:36:50+09:00
RubyのKernelモジュールについて
まとめようと思ったきっかけ
普段しれっとputsとかprintとかとか使ってるけど、これって何者?と思ったので調べてみると、カーネルモジュールに含まれているメソッドらしい。「Kernelモジュール?」となったので調べてみた。
Kernelモジュール
全てのクラスから参照できるメソッドを定義しているモジュール。 Object クラスはこのモジュールをインクルードしています。
Object クラスのメソッドは実際にはこのモジュールで定義されています。これはトップレベルでのメソッドの再定義に対応するためです。まず、Objectクラスという全てのスーパークラスがあって、ここで定義されたメソッドは全てのオブジェクトに使える。
そしてKarnelモジュールはこのObjectクラスに含まれているので、結果、どこでもメソッドが使えるということ。
(ちなみにモジュールとは、クラスのようにインスタンス化ができないが、定数やメソッドを定義できる機能。)こんな感じ。
talk.rb#この部分がトップレベル(クラスやモジュールの外側) def say puts "hello" end #この部分がトップレベル #Humanクラスは親クラスのObjectを継承しているのでsayメソッドが使える。 class Human def talk say end end human = Human.new() human.talk #=>hello最後に
自分なりに調べたのですが、もし間違いがあったらご指摘いただけると幸いです。
参考文献
オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル
https://docs.ruby-lang.org/ja/latest/class/Kernel.htmlRubyにおけるトップレベル
https://www.javadrive.jp/ruby/method/index1.htmlRuby のトップレベルメソッドって結局なんなの
https://qiita.com/pink_bangbi/items/c08ec7b32fc6dd20baad