20200712のRailsに関する記事は22件です。

【Rails】rails g migrationコマンドのオプションで注意したいこと

ターミナルでrails g migrationコマンドを実行する際に、オプションとして「カラム名:データ型」を付け加える際には必ず:(コロン)の直後にはスペースを入れないようにする!

terminal
rails g migration add_user_id_to_posts user_id: integer
                                               ⬆️
                                              半角スペースを開けない!

上記のように「user_id:」の直後に半角のスペースを入れてしまうと、余計なカラムが作成されてしまうエラーが発生する為。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveAdminのactionsボタンのitemについて

はじめに

ActiveAdminは管理画面のCRUDを簡単に実装できるGemです。
ActiveAdminは便利なのですが、細かいところをイジろうとした際にどのように実装すれば良いのか詰まることが頻繁にあります。
今回は、条件によって表示するactionsを分けたいと思いこちらの記事を参考にさせていただいて実装をしました。
実装の中で以下のitemという記述が何をしているのかよくわからなかったため調べました。

app/admin/users.rb
actions defaults: false do |user|
      item I18n.t('active_admin.view'), admin_user_path(user), class: 'view_link member_link'
      item I18n.t('active_admin.edit'), edit_admin_user_path(user), class: 'edit_link member_link'
      item I18n.t('active_admin.delete'), admin_user_path(user), class: 'delete_link member_link', method: :delete, data: { confirm: I18n.t('active_admin.delete_confirmation') } unless user.admin?
    end

何をしているのか?

itemを消してみるとSyntaxErrorになります。

app/admin/users.rb
actions defaults: false do |user|
      I18n.t('active_admin.view'), admin_user_path(user), class: 'view_link member_link'
      item I18n.t('active_admin.edit'), edit_admin_user_path(user), class: 'edit_link member_link'
      item I18n.t('active_admin.delete'), admin_user_path(user), class: 'delete_link member_link', method: :delete, data: { confirm: I18n.t('active_admin.delete_confirmation') } unless user.admin?
    end

スクリーンショット 2020-07-12 22.58.22.png

itemが必要なのはわかりましたが、itemの中身はどのようになっているのかpryで見てみます。

app/admin/users.rb
actions defaults: false do |user|
      binding.pry
      item I18n.t('active_admin.view'), admin_user_path(user), class: 'view_link member_link'
      item I18n.t('active_admin.edit'), edit_admin_user_path(user), class: 'edit_link member_link'
      item I18n.t('active_admin.delete'), admin_user_path(user), class: 'delete_link member_link', method: :delete if user.id == 1
    end

スクリーンショット_2020-07-12_23_02_53.png

なるほどitemはaタグを生成してくれていることがわかりました。

itemを使ってみる

itemには引数を渡すことができます。

app/admin/users.rb
 actions defaults: false do |user|
   item('hoge')
 end

上記は次のアクションを生成します。
スクリーンショット_2020-07-12_23_18_34.png
スクリーンショット_2020-07-12_23_19_53.png

hogeという名前で/admin/usersへのリンクが生成されました。
また、itemに第2引数を渡すこともできます。

app/admin/users.rb
actions defaults: false do |user|
  item('hoge', '/admin/hoge')
end

こちらはhogeという名前で/admin/hogeへのリンクを生成します。
スクリーンショット_2020-07-12_23_25_58.png

最後に

actionsをカスタマイズする際にitemに引数を渡すと、様々なリンクを生成できることがわかりました。
actionsについて書かれているソースコードの箇所はこちらになりますので、気になられた方は参照ください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TECH CAMP学習 個人アプリ④git ignore

5Kって何だ!?

個人アプリを作成中に、Git Hubにプルリクエストをしてない事をユーザー管理作成中の時に気づいてしまいました。
かなりデータが溜まっていって、ワークスペースになんか『5K』と表示されてしまい全てをプルリクエストできません、と注意で書かれてありました。

Git Hubデスクトップにもchangesが500と表示されてしまい500!!??となりました。5Kの意味はこれの事なのかな??

そしてやはり遅かったのか、どうすればプルリクエストできるのか、ファイルのどれかを消さないといけないのかと思い何となく一部のファイルを消去。

ただそれだけでは全くうまくいかず、少なくならず、ひたすらにググってググって、やっと見つけたのがgit ignore!!!
VS code 内で.git ignoreファイルを作り、以下のコードを入れました。
image.png

これで一気にchangesが91まで縮小され、プルリクエストができるようになりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails チュートリアル(第4版) 第8章

8.1.1 演習

1.GET login_pathとPOST login_pathとの違いを説明できますか? 少し考えてみましょう。
 GETはlogin_pathの内容を取得、POSTはlogin_pathに送信する。
 違うのは分かる、でもうまく説明出来ない感じ。
 GETとPOSTの違いについて参考にさせていただきました。

2.ターミナルのパイプ機能を使ってrails routesの実行結果とgrepコマンドを繋ぐことで、Usersリソースに関するルーティングだけを表示させることができます。同様にして、Sessionsリソースに関する結果だけを表示させてみましょう。現在、いくつのSessionsリソースがあるでしょうか? ヒント: パイプやgrepの使い方が分からない場合は Learn Enough Command Line to Be Dangerousの Section on Grep (英語) を参考にしてみてください。

$ rails routes | grep sessions
    login GET    /login(.:format)          sessions#new
          POST   /login(.:format)          sessions#create
   logout DELETE /logout(.:format)         sessions#destroy

8.1.2 演習

1.リスト 8.4で定義したフォームで送信すると、Sessionsコントローラのcreateアクションに到達します。Railsはこれをどうやって実現しているでしょうか? 考えてみてください。ヒント:表 8.1とリスト 8.5の1行目に注目してください。
 action="/login" method="post"
 POST /loginはcreateアクションだから。

8.1.3 演習

1.Railsコンソールを使って、表 8.2のそれぞれの式が合っているか確かめてみましょう. まずはuser = nilの場合を、次にuser = User.firstとした場合を確かめてみてください。ヒント: 必ず論理値オブジェクトとなるように、4.2.3で紹介した!!のテクニックを使ってみましょう。例: !!(user && user.authenticate('foobar'))

>> user = nil
=> nil
>> !!(user && user.authenticate('foobar'))
=> false
>> user = User.first
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2020-07-10 15:10:18", updated_at: "2020-07-10 15:10:18", password_digest: "$2a$10$VICUYjma4CkWdl2WFBg.FenXS3mnUPnWYT8YmGUOYS2...">
>> !!(user && user.authenticate('foobar'))
=> true
>> !!(user && user.authenticate('password'))
=> false

8.1.4 演習

1.8.1.4の処理の流れが正しく動いているかどうか、ブラウザで確認してみてください。特に、flashがうまく機能しているかどうか、フラッシュメッセージの表示後に違うページに移動することを忘れないでください。
 省略

8.2.1 演習

1.有効なユーザーで実際にログインし、ブラウザからcookiesの情報を調べてみてください。このとき、sessionの値はどうなっているでしょうか? ヒント: ブラウザでcookiesを調べる方法が分からない? 今こそググってみるときです! (コラム 1.1)
 省略

2.先ほどの演習課題と同様に、Expiresの値について調べてみてください。
 省略

8.2.2 演習

1.Railsコンソールを使って、User.find_by(id: ...)で対応するユーザーが検索に引っかからなかったとき、nilを返すことを確認してみましょう。

>> user=User.find_by(id:3)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
=> nil

2.先ほどと同様に、今度は:user_idキーを持つsessionハッシュを作成してみましょう。リスト 8.17に記したステップに従って、||=演算子がうまく動くことも確認してみましょう。

>> session = {}
=> {}
>> session[:user_id] = nil
=> nil
>> @current_user ||= User.find_by(id: session[:user_id])
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
=> nil
>> session[:user_id]= User.first.id
  User Load (0.1ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> 1
>> @current_user ||= User.find_by(id: session[:user_id])
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2020-07-10 15:10:18", updated_at: "2020-07-10 15:10:18", password_digest: "$2a$10$VICUYjma4CkWdl2WFBg.FenXS3mnUPnWYT8YmGUOYS2...">
>> @current_user ||= User.find_by(id: session[:user_id])
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2020-07-10 15:10:18", updated_at: "2020-07-10 15:10:18", password_digest: "$2a$10$VICUYjma4CkWdl2WFBg.FenXS3mnUPnWYT8YmGUOYS2...">
>> 

8.2.3 演習

1.ブラウザのcookieインスペクタ機能を使って (8.2.1.1)、セッション用のcookieを削除してみてください。ヘッダー部分にあるリンクは非ログイン状態のものになっているでしょうか? 確認してみましょう。
 省略

2.もう一度ログインしてみて、ヘッダーのレイアウトが変わったことを確認してみましょう。その後、ブラウザを再起動させ、再び非ログイン状態に戻ったことも確認してみてください。注意: もしブラウザの [閉じたときの状態に戻す] 機能をオンにしていると、セッション情報も復元される可能性があります。もしその機能をオンにしている場合、忘れずにオフにしておきましょう (コラム 1.1)。
 省略

8.2.4 演習

1.試しにSessionヘルパーのlogged_in?メソッドから!を削除してみて、リスト 8.23が redになることを確認してみましょう。
 省略

2.先ほど削除した部分 (!) を元に戻して、テストが greenに戻ることを確認してみましょう。
 省略

8.2.5 演習

1.リスト 8.25のlog_inの行をコメントアウトすると、テストスイートは red になるでしょうか? それとも green になるでしょうか? 確認してみましょう。
 REDになる。

2.現在使っているテキストエディタの機能を使って、リスト 8.25をまとめてコメントアウトできないか調べてみましょう。また、コメントアウトの前後でテストスイートを実行し、コメントアウトすると red に、コメントアウトを元に戻すと green になることを確認してみましょう。ヒント: コメントアウト後にファイルを保存することを忘れないようにしましょう。また、テキストエディタのコメントアウト機能については Test Editor Tutorial の Commenting Out (英語) などを参照してみてください。
 Ctrl + /

8.3 演習

1.ブラウザから [Log out] リンクをクリックし、どんな変化が起こるか確認してみましょう。また、リスト 8.31で定義した3つのステップを実行してみて、うまく動いているかどうか確認してみましょう。
 省略
2.cookiesの内容を調べてみて、ログアウト後にはsessionが正常に削除されていることを確認してみましょう。
 省略

メモ

  • rails routesコマンドで現状のルーティングを確認することが出来る。
  • flash.nowのメッセージはその後リクエストが発生したときに消滅する。
  • @foo = @foo || "bar"@foo ||= "bar"と等価である。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ターミナル】ターミナル強制終了後、rails sしたらA server is already runningとなった時の対処法【Rails】

「controll+z」でサーバーを切ったら、rails sが効かなくなった。どうしよう・・という問題。

【バージョン】

Rails 5.2.4.3
Ruby 2.5.1
macOS Catalina 10.15.4

【経緯】

ローカルで作業中、rails sで立ち上げたlocalhost:3000を「controllキー + c」でサーバを切り、再度「rails s」で起動させようとしたらうんともすんともいわず・・・ほかの切り方がないか調べたところ「controllキー + z」で強制終了できることが判明。

しかし、「controllキー + z」で強制終了後、
再度、rails sで立ち上げたところ、localhost:3000が立ち上がらず、下記のエラーが出た。

ターミナル
A server is already running. Check /Users/名前/XXX/XXXXXX/tmp/pids/server.pid.

「サーバーはすでに起動しています」と言われた。

【解決方法】

なので、

ターミナル
% lsof -i:3000

でどんなプロセスが動いているのかを確認してみる。

lsofコマンド
…「LiSt Open Files」(開いているファイル群を列挙する)という言葉に由来するようです。その名の通り、「プロセスが開いているファイル」を表示するコマンドらしいです。



ターミナル
COMMAND  PID            USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    6792 satokokinoshita   11u  IPv4 0x95b341c28591923f      0t0  TCP localhost:hbci (LISTEN)
ruby    6792 satokokinoshita   22u  IPv6 0x95b341c27813ce6f      0t0  TCP localhost:hbci (LISTEN)



実際の画面はこんな感じだった
quiita.png

ここで、COMMANDがrubyとなっているPIDの番号 (ここだと6792、下の画面だと3405) を、○に入れる。

ターミナル
% kill -9 〇〇〇〇



これで、rails sで再度起動できるようになりました。

※ちなみにkillコマンドについてはkill -9(強制終了)以外は試しておりません。

localhost:3000のサーバーを切った時に消えるはずのプロセスが、強制終了したことで一部残ってしまっていたのだと思います。

「PID(プロセスアイディー)」は、プロセスを識別するための一意の数字になりますが、
PIDを指定してkillコマンドで切ることで、サーバーを切った状態に戻ったということになったのだと思います。

【参考にさせていただいたサイト】

このポートで実行中のプロセスはどれ? lsofコマンドの使い方

プロセスを止める最終手段killコマンドの種類・シグナルの使い方



【当該エラーに直面した時の感想的な】

Railsの画面に表示されるエラーは慣れたけど、ターミナルのエラーってあまりないので変な汗がでました?
ターミナルコマンドはどんどん慣れていきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人アプリ作成#2

一覧の作成

スクリーンショット 2020-07-12 21.47.19.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブランチに最新のマスターの情報を取り込む方法

初投稿!

非常に感動した内容だった為、備忘録!

ブランチを切って作業をしていると、マージするまでの間にmasterに追加された更新で表示が変わってしまったり、コンフリクトするようになってしまいますよね。
それを防止するためには定期的にブランチにマスターの内容を反映させる必要があります。
その方法をメモ。

  1. 反映したいブランチに切り替える
作業してるディレクトリ% git checkout ブランチ名

2. マスターの内容をブランチに反映させる

作業してるディレクトリ% git merge master

以上

参考にさせていただいたサイトhttps://hacknote.jp/archives/11191/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionTextのデータ構成はどうなってる?

個人的にActionTextを導入する機会があったのでメモ。
blogサービスのようなものを開発する際に、ArticleモデルにどのようにActionTextを導入すればいいのか悩んだので、誰かの役に立てればと思います。

ActionTextとは

rails6から使えるようになった機能の1つ。
Railsにリッチテキストコンテンツと編集機能を導入できる。

今回やりたいこと

記事投稿サービスを作りたいと思います。
タイトル、説明文、本文の3つのデータをもつArticleモデルを作成し、本文の編集にActionTextを導入します。

データ構成

ActionTextを導入するモデル作成

Articleモデルを生成します。
titleは記事のタイトル、descriptionは記事の説明文を表します。本文のデータを格納するカラムは作成しないことに注意してください。

rails g model Article title:string description:text

ActionTextを導入する

ActionTextは以下のコマンドでインストールできます。

rails action_text:install

ActionTextをインストールすると、2つのmigrationファイルが生成されます。
1つはActive Storageに関わるファイルで、もう一つがActionTextのテーブルを作成するファイルです。
後者のテーブルがこの記事で説明したいデータ構成の鍵となります。

リッチテキストコンテンツは独自のRichTextモデルに保存され、このモデルはアプリケーションの既存のあらゆるActive Recordモデルと関連付けられます。 あらゆる埋め込み画像(およびその他の添付ファイル)は自動的にActive Storageに保存され、includeされたRichTextモデルに関連付けられます。

https://railsguides.jp/action_text_overview.html#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB

class CreateActionTextTables < ActiveRecord::Migration[5.2]
  def change
    create_table :action_text_rich_texts do |t|
      t.string     :name, null: false
      t.text       :body, limit: 16777215
      t.references :record, null: false, polymorphic: true, index: false

      t.datetime :created_at, null: false
      t.datetime :updated_at, null: false

      t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
    end
  end
end

上記のデータを、Articleモデルと紐づけることで、記事の内容をリッチテキストで編集することが可能になります。
データの紐付けは簡単で、以下のようにモデルに追加してください。

article.rb
class Article < ApplicationRecord
  has_rich_text :content
end

どのようなデータ構造になっているのか

上記で説明した通り、Articleテーブルでtitledescriptionを保持し、記事の内容(content)は、ActionText::RichTextというテーブルに保持されています。以下のコンソールの結果を見るとイメージをしやすいと思います。

# Articleモデルのデータを取得
irb(main):001:0> Article.all.first
=> #<Article id: 1, created_at: "2020-07-11 13:19:00", updated_at: "2020-07-11 13:19:00", title: "テスト", description: "テストです">

# Articleモデルに紐づいているActionText::RichTextのデータを取得
irb(main):002:0> Article.all.first.content
=> #<ActionText::RichText id: 1, name: "content", body: #<ActionText::Content "<div class=\"trix-conte...">, record_type: "Article", record_id: 1, created_at: "2020-07-11 13:19:00", updated_at: "2020-07-11 13:19:00">

# 記事の内容を取得するには?
irb(main):003:0> Article.all.first.content.body
=> #<ActionText::Content "<div class=\"trix-conte...">

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Action Textのデータ構成について

個人的にActionTextを導入する機会があったのでメモ。
blogサービスのようなものを開発する際に、ArticleモデルにどのようにActionTextを導入すればいいのか悩んだので、誰かの役に立てればと思います。

ActionTextとは

rails6から使えるようになった機能の1つ。
Railsにリッチテキストコンテンツと編集機能を導入できる。

データ構成

ActionTextを導入するモデル作成

Articleモデルを生成します。
titleは記事のタイトル、descriptionは記事の説明文を表します。

rails g model Article title:string description:text

ActionTextを導入する

ActionTextは以下のコマンドでインストールできます。

rails action_text:install

ActionTextをインストールすると、2つのmigrationファイルが生成されます。1つはActive Storageに関わるファイルで、もう一つがActionTextのテーブルを作成するファイルです。後者のテーブルがこの記事ではターゲットとなります。ActionTextで編集された内容は、このテーブルに格納されるからです。

class CreateActionTextTables < ActiveRecord::Migration[5.2]
  def change
    create_table :action_text_rich_texts do |t|
      t.string     :name, null: false
      t.text       :body, limit: 16777215
      t.references :record, null: false, polymorphic: true, index: false

      t.datetime :created_at, null: false
      t.datetime :updated_at, null: false

      t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
    end
  end
end

上記のデータを、Articleモデルと紐づけることで、記事の内容をリッチテキストで編集することが可能になります。
データの紐付けは簡単で、以下のようにモデルに追加してください。

article.rb
class Article < ApplicationRecord
  has_rich_text :content
end

どのようなデータ構造になっているのか

上記で説明した通り、Articleテーブルでtitledescriptionを保持し、記事の内容(content)は、ActionText::RichTextというテーブルに保持されています。以下のコンソールの結果を見るとイメージをしやすいと思います。

# Articleモデルのデータを取得
irb(main):001:0> Article.all.first
=> #<Article id: 1, created_at: "2020-07-11 13:19:00", updated_at: "2020-07-11 13:19:00", title: "テスト", description: "テストです">

# Articleモデルに紐づいているActionText::RichTextのデータを取得
irb(main):002:0> Article.all.first.content
=> #<ActionText::RichText id: 1, name: "content", body: #<ActionText::Content "<div class=\"trix-conte...">, record_type: "Article", record_id: 1, created_at: "2020-07-11 13:19:00", updated_at: "2020-07-11 13:19:00">

# 記事の内容を取得するには?
irb(main):003:0> Article.all.first.content.body
=> #<ActionText::Content "<div class=\"trix-conte...">

結論

ActionTextは本当に簡単に導入できますが、データの仕組みが少し複雑なので注意してください。
また、私自身railsを勉強し始めて半年の未熟者ですので、間違いや不適切な表現があればご指摘ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails5でECサイトを作る⑧ ~Productモデル編、親子ともに条件つきで絞り込み表示~

はじめに

架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る⑦の続きです。
今回は商品の一覧、詳細を表示するProductモデルの実装です。商品の管理はadminサイト側で行うので、customerサイト側では画面を表示する処理のみになります。

ただし、商品を束ねるGenreモデル、その下に属するProductモデルの両方に「販売中・停止」のステータスが存在します。商品ごとの販売中・停止を設定するほかに、ジャンルごとでも同様の設定を行えるということです。customerサイトで表示するのはGenre、Product両方で「有効」のものだけなので、その絞り込みが少々難しめです。

ソースコード

https://github.com/Sn16799/bakeryFUMIZUKI

Modelのアソシエーション

association.jpg

controller

app/controllers/products_controller.rb
class ProductsController < ApplicationController

  # サイドバー表示用
  before_action :set_genres

  def show
    @product = Product.find(params[:id])
    @cart = @product.cart_items.build
  end

  def index
    # ジャンルが有効 かつ 商品ステータスが有効 な商品を絞り込み
    @products = Product.includes(:genre).where(genres: {validity: true}).is_active.page(params[:page]).per(9)
  end

  private

  def set_genres
    @genre = Genre.is_valid
  end

  def product_params
    params.require(:product).permit(:name,:price,:image_id, :genre_id)
  end
end

indexにおいて、Genreモデル(親)のvalidityカラムと、Productモデル(子)のstatusカラムに条件を設定して絞り込みをかけています。これで、「ジャンルが有効」かつ「商品ステータスが有効」の商品群を取り出すことができました。また、ページャ(kaminari)を用意して、1ページにつき9個の商品が表示されるようにしています。

app/models/product.rb
scope :is_active, -> { where(status: true) }
app/models/genre.rb
scope :is_valid, -> { where(validity: true) }

controller内にあるis_activeとis_validは、いずれも商品、ジャンルの有効・無効を絞り込むスコープです。今回のアプリでは何回も行う処理ではないので別に必須ではないのですが、スコープを定義しておくことで後から修正しやすくなったり、コードソース上で何の処理をしているのか、視覚的に分かりやすくなったりします。

view

index画面

app/views/products/index.html.erb
<div class="col-lg-10 space">
  <div class="container">
  <h2>
    <span style="display: inline-block;">全商品一覧</span>
    <span style="display: inline-block;"><%= @products.count %>種)</span>
  </h2>
</div>
  <div class="container">
    <div class="row">
    <% @products.each do |gp| %>
      <%= render 'products/box', product: gp %>
    <% end %>
  </div>
  </div>
  <%= paginate @products %>
</div>
<%= render 'genres/index', genres: @genres %>

部分テンプレートは、'products/box'が商品1個ごとに写真・名前・値段などが載ったち小さなdivで、'genres/index'はジャンルごとに商品を見られるリンクを集めたサイドバーです。HTMLソースは前回の記事で公開しています。

show画面

app/views/products/show.html.erb
<div class="col-lg-10 space">
  <div class="container">
    <div class="row">
      <div class="col-lg-4 offset-lg-2">
        <%= attachment_image_tag(@product, :image, :fill, 560, 420, fallback: "no_img.jpg") %>
      </div>
      <div class="col-lg-4">
        <h3>
          <%= @product.name %>
        </h3>
        <h4>商品説明</h4>
        <%= @product.introduction %>
        <h4>
          <%= price_include_tax(@product.price) %>
        </h4>
      </div>
    </div>
  </div>

  <div class="container">
    <div class="w-50 offset-lg-6">
    <%= form_with(model: [@product, @cart], local: true, url: {controller: 'cart_items', action: 'create'}) do |f| %>
    <%= f.label :ご購入個数 %>
    <%= f.number_field :quantity, value: 1 ,min:1, max:99 %><%= f.hidden_field :product_id, value: @product.id  %>
    <%= f.submit "カートに入れる", class: "btn btn-danger" %>
    <% end %>
  </div>
  </div>
</div>

<%= render'genres/index', genres: @genres %>

show画面では、商品の説明を見て、気に入ったものをカートに入れることができます。

空欄のフォームを表示するようにはなっていますが、CartItemControllerに何も書いていないため、現時点で「カートに入れる」ボタンを押すとエラーになります。ご注意を。

後記

商品がたくさん並んだ画面を作ったことで、見た目にはパン屋さんのサイトになりました。相変わらず機能面は皆無ですが。また、ページャや商品のボックスにCSSの加工を一切施しておらず、少し殺風景な感じは拭えません。装飾は最後にまとめて行う予定なので、まずは機能の完成を急ぎたいところ。

このECサイトの原型となっているのはスクールのチーム実装で作ったアプリなのですが、今見るとバリデーションなど意外と抜け落ちているところがあります。3人がかりで取り組んでも見逃す部分はあるのだなあと思いましたが、もしかしたら単にそこまで細かく要件定義された課題ではなかっただけかも知れません。

今回は2回目の開発なので、その辺りもこだわって実装していきたいと思います。次回へ続く!

参考

Rails小モデルから親モデルの情報で検索したい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt/Rails】formDataで画像をPOSTしてCarrierWaveで保存した

Nuxt.jsからformDataで画像をPOSTして、Railsで画像を保存する(CarrierWaveを使ってます)際に地味に詰まってしまったので備忘録を残します。

Rails

Google Cloud Storageに画像を保存していまして、設定諸々は以下の備忘録に書いてます。

https://qiita.com/arthur_foreign/items/43da529ab3beb760ba4b

※そのため、CarrierWaveの設定周りは省略しました。

また、実装としては記事のサムネイルを保存することとします。

model

article.rb
class Article < ApplicationRecord
  belongs_to :user
  mount_uploader :thumbnail, ImageUploader
  serialize :thumbnail
end

controller

articles_controller.rb
class ArticlesController < ApplicationController
  def create
    article_form = ArticleForm.new(article_form_params)
    if article_form.invalid?
      render_422(article_form)
    else
      @article = article_form.save!
      render json: @article, status: :ok, serializer: ArticleSerializer
    end
  end
end

serializer

article_serializer.rb
class ArticleSerializer < ActiveModel::Serializer
  attributes :thumbnail
  belongs_to :user, serializer: UserSerializer
end

form

article_form.rb
class ArticleForm
  include ActiveModel::Model
  include Virtus.model

  attr_reader :thumbnail, :user_id

  attribute :thumbnail, String
  attribute :user_id, User

  def save!
    Article.create!(
      thumbnail: thumbnail,
      user_id: user_id
    )
  end
end

Vue/Nuxt

画像を送信する時のformDataのみ書いてます。

<script>
  methods: {
    async handleSubmit() {
      const formData = new FormData()
      const blob = new Blob([this.image], { type: this.thumbnailType })
      formData.append('article[thumbnail]', blob, this.thumbnailName)
      await this.postArticle(formData)
    },
  },
</script>

※画像の設定は別の備忘録に書こうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

hamlでYouTube動画をDBから呼び出して埋め込み表示させてみた

自作アプリでYouTubeの動画を埋め込み表示させてみた時に見付けた方法

自作アプリの作成でお気に入りの動画を一旦DBに保存して、呼び出す際に埋め込み表示させたかったので、色々模索したやり方を紹介します。

一般的な埋め込みの方法

好きなYouTube動画の共有ってところを選ぶ

スクリーンショット 2020-07-12 16.16.00.png

さらにそこから埋め込みってやつを選ぶ
スクリーンショット 2020-07-12 16.16.33.png

動画の埋め込み用のURLが出るので、コピーをクリックする
スクリーンショット 2020-07-12 16.16.59.png

これをHTML(haml)に直接貼り付ければ完成

出来上がりがこんな感じ
スクリーンショット 2020-07-12 16.21.46.png

ただ、これでDBに投稿して、hamlファイルから呼び出そうとすると文字列で出てしまったので、それを解消する方法です。

実際に行ったやり方

%iframe#player{frameborder: "0", height:"390", src: (movie.url), type: "text/html", width: "640"}
※今回はMovieモデルと言うのを作って、カラム名をurlにしてます

簡単に言うと、
・外枠の部分はhamlファイルにしておく(サイズ等はお好みで変えられるはずです)
・DBにはURL部分のみ保存→呼び出す記述にしてます
・タイトルや内容なども別のカラムで保存

こうする事で、DBに投稿→呼び出しの際も埋め込み表示に出来ました。

実際の画像(まだ開発中ですが)

スクリーンショット 2020-07-12 16.36.11.png
↑無事に全部投稿機能で実装が出来ました(パチパチ)

1つ注意ポイント

私が使ったURLですが、この青下線の部分です(ダブルクォーテーションは要らないです)
スクリーンショット 2020-07-12 16.38.35.png

スクリーンショット 2020-07-12 16.16.33.png

こっちの青下線のURLではダメでした。
おそらく埋め込み用のURLでは無いからだと思います。

以上、同じ様な実装をされたい方の参考になれば幸いです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] enumを使って下書き機能を作った

はじめに

下書き機能を作ろうと思った時にあまり記事がなかったので記録として残しておこうと思いました。

開発環境

ubuntu(WSL)
Ruby 2,5.1
Rails 6.0.2

事前準備

以下の機能は作成済みとします。

  • 投稿機能・詳細・削除・編集(Post)
  • ユーザーの作成・詳細(User)
models/post.rb
  belongs_to :user
models/user.rb
  has_many :microposts, dependent: :destroy

Postテーブルにカラムを追加する

まずはPostモデルにstatusカラムを追加しboolean型にします。
booleanでなくintergerでも可能です。
以下のコマンドを入力します。

bin/rails g migration AddStatusToPost status:boolean

マイグレーションファイルを編集します。

migrationfile.rb
  def change
    add_column :microposts, :status, :boolean, default: true, null: false
  end

編集したらマイグレートします。

Postモデルにenumを追加する。

models/post.rb
  enum status: { draft: false, published: true }

statusカラムのdraft(下書き)をfalseに指定し、statusカラムのpublished(公開)をtrueに指定します

ユーザーごとの情報を取得

@userでuserのidを取得。

users_controller.rb
  #下書き用
  def confirm
    @user = User.find(params[:user_id])
    @microposts = @user.microposts.draft.page(params[:page])
  end

  #公開用
  def show
    @user = User.find(params[:id])
    @microposts = @user.microposts.published.page(params[:page])
  end

ルーティングを追加

collection をつけるとURLにidが付かなくなります

routes.rb
resources :users do
  get 'confirm'
end

Viewの設定

関係あるところだけを抜粋しました。

view/users/show.html.slim
//ユーザーの詳細画面
= link_to "投稿一覧", @user
= link_to  "お気に入り", user_likes_path(current_user)
= link_to "下書き一覧", user_confirm_path(current_user)

//自分の投稿を表示する
- if @microposts.present?
  = render "microposts/list", microposts: @microposts
- else
  h4 投稿はありません

下書き一覧は自分の好みに合わせて書いてください。

view/users/confirm.html.slim
h4 下書き一覧

table.table.table-hover
  thead.thead-default
    tr
      th = Micropost.human_attribute_name(:title)
      th = Micropost.human_attribute_name(:content)
      th = Micropost.human_attribute_name(:created_at)
      th
  tbody
    - @microposts.each do |micropost|
      tr
        td = link_to micropost.title, micropost
        td = link_to micropost.content, micropost
        td

icroposts/listの中身は上記と同じです。

終わりに

間違いがありましたら編集リクエスト、コメントをお願いします。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gitでコミットを取り消したい(特定のコミット番号まで戻したい時)

こんな人におすすめ

『あああコミットしたけど元にもどしたいなー』『マージしたけどブランチ内で昔の履歴みたいなー』
なんて時、履歴を見て、ささっと取り消してしまいましょう。

環境

ruby 2.5.1
Rails 5.0.7.2

手順

1)自分がいるブランチのコミットログを調べる

git log

こういうのが出てきます。

ユーザー名@ユーザー名noMacBook-Pro アプリケーション名 % git log
commit 8af1dXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (HEAD -> 今いるブランチ名, origin/今いるブランチ名)
Author: ユーザー名 <メールアドレス>
Date:   Sun Jul 12 15:28:51 2020 +0900

    コミットした時のコメント

commit 239b0XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: ユーザー名 <メールアドレス>
Date:   Thu Jun 25 23:36:58 2020 +0900

    コミットした時のコメント

commit ed8ccXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: ユーザー名 <メールアドレス>
Date:   Thu Jun 25 17:31:52 2020 +0900

    コミットした時のコメント

2)戻したい場所のハッシュ値を確認

commit ed8ccXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

3)リセットする

git reset --hard ed8ccXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

完了done!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで表示時間を日本時間に設定する方法

やりたいこと

投稿時間を国際標準時間から日本時間の表示に変更したい

変更前(GMT標準時間)▼
Screenshot from Gyazo
変更後(日本時間)▼
Screenshot from Gyazo

設定の方法

  • config/application.rb 内に config.time_zone = 'Tokyo' の記述を追加
application.rb
# ↑これ以前のコードは割愛
module App
  class Application < Rails::Application
    config.time_zone = 'Tokyo'
  end
end
  • サーバーが立ち上がっている場合は一度シャットアウトして再度立ち上げ直す
    ※dockerを起動している場合は、下記でコンテナを立ち上げ直す(ターミナルで下記コマンドを実行)
    docker-compose stop
    docker-compose up -d

これで日本時間で表示されるが、フォーマットを別途変更する必要あり

  • strftimeメソッド を使って、表示フォーマットを変えたい箇所にメソッドを当てる
sample.html.erb
# 下記はサンプル
<td><%= @tweet.created_at.strftime('%Y年%m月%d日 %H時%M分') %></td>

変換のメソッドを定義する方法

やること:Initializeにフォーマット変換を定義する

  • config/initializers配下time_formats.rbというファイルを作成する
  • Time::DATE_FORMATS[:datetime_jp] = '%Y年%m月%d日 %H時%M分' と記述する
time_formats.rb
Time::DATE_FORMATS[:datetime_jp] = '%Y年%m月%d日 %H時%M分'

※ [:datetime_jp] は任意の命名でOK
※ '%Y年%m月%d日 %H時%M分' には変換したいフォーマットを記述する

  • 使いたい箇所(viewファイル内)で .to_s(:datetime_jp]) と記述して使う
sample.html.erb
# 下記はサンプル
<td><%= @tweet.created_at.to_s(:datetime_jp) %></td>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】プロフィールに自己紹介機能を実装するシンプルな方法

プロフィールに自己紹介機能を実装する

ポートフォリオ完成後、追加実装したので順序を備忘録として残します。
ユーザー名・email・パスワードなどの既存カラムに、introductionカラムを新たに追加します。
なるべくシンプルに記します!

どうやって?

以下の順序です。

  1. git checkout -b introduction (ブランチ切る)
  2. usersテーブルにintroductionカラムを追加する
  3. データベースに反映させる
  4. usersコントローラーに必要コードの追記
  5. userモデルに必要コードの追記
  6. ユーザー詳細ページ(MYPAGE)に表示させる
  7. ユーザー編集ページ(EDIT)に表示させる
  8. 完成!

以下順番に記します!

1. git checkout -b introduction (ブランチ切る)

新たな機能を実装するため、ブランチを切リます。

2. usersテーブルにintroductionカラムを追加する

ターミナル
$ rails generate migration AddIntroductionToUsers introduction:text
      invoke  active_record
      create    db/migrate/20200712005652_add_introduction_to_users.rb

上記rails gコマンドでintroductionカラムを追加します。

3. データベースに反映させる

ターミナル
$ docker-compose run  web rails db:migrate
== 2020~~~~ AddIntroductionToUsers: migrating ===========================
-- add_column(:users, :introduction, :text)
   -> 0.0518s
== 2020~~~~ AddIntroductionToUsers: migrated (0.0519s) ==================

4. usersコントローラーに必要コードの追記

private methodに追記
introduction属性を付与する

users_controller.rb
   def user_params_update
      params.require(:user).permit(:name, :email, :image, :introduction) # introdution追加
   end

これでintroductionをupdate可能になります

5. userモデルに必要コードの追記

バリデーションを追加します。自己紹介は50文字以内で入力させます。
文字数は自由に設定してください。
※presenseですが、falseにしないと新規登録時に作用してintroductionがnilとなって新規登録できなくなりますので注意してください。新規登録時に自己紹介も入力必要な場合とするならtrueでOKです。

user.rb
validates :introduction, presence: false, length: { maximum: 50 } # 自己紹介の最高文字数は50文字

6. ユーザー詳細ページ(MYPAGE)に表示させる

show.html.slim
 = @user.introduction

SCSSなどで適宜修正してください

7. ユーザー編集ページ(EDIT)に表示させる

ユーザー編集はユーザー名・emailのみでしたがそこにintroductionを追記します。

edit.html.slim
.form-group
  = f.label :introduction
  = f.text_area :introduction, class: 'form-control', id: 'user_introduction'

完成

以上で自己紹介をusersテーブルに追加し、introduction属性を追加完了しました。
途中でintroductionをintroduceと書いていたりしたので、皆様も注意してください!

スクリーンショット 2020-07-12 13.33.19.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

undefined method `user_signed_in?'と出たときの対処法

日々学んだことやつまずいたことについてまとめていきます。
記載に誤りがありましたら、ご指摘していただけると助かります!
いつも他のかたの記事に助けられているので、少しでもお役に立てればと思います。

どういうエラーなのか

deviseで使えるようになる、 user_signed_in ヘルパーメソッドが定義されていないというエラー。

どんなときに起こるのか

1.deviseがインストールされていないとき。
2.routes.rbにdevise_for :users などの記述がないとき。

対処法

1.
Gemfileに以下を記述。

gem 'devise'

ターミナルで以下を実行。

gemをインストール

bundle install

設定ファイルを作成

rails g  devise:install

ログイン機能に対応したモデルを作成

rails g devise user

ログイン機能に関連するテーブルを作成

bundle exec rake db:migrate

2.
routes.rbに以下を記述

devise_for :users
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【1対多】の関連付け

個人的備忘録

ターミナルでモデルの関連付けをするときにいつも忘れるのでメモ。
Attendanceモデルをuserモデルに紐付けたいときに使う。
重要なのは最後の user:references 。

$ rails g model Attendance worked_on:date started_at:datetime finished_at:datetime note:string user:references

以下のt.references :user, foreign_key: true が表示されていればおk

class CreateAttendances < ActiveRecord::Migration[5.1]
  def change
    create_table :attendances do |t|
      t.date :worked_on
      t.datetime :started_at
      t.datetime :finished_at
      t.string :note
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end
$ rails db:migrate

結果。

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20190523135500) do

  create_table "attendances", force: :cascade do |t|
    t.date "worked_on"
    t.datetime "started_at"
    t.datetime "finished_at"
    t.string "note"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_attendances_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "password_digest"
    t.string "remember_digest"
    t.boolean "admin", default: false
    t.string "department"
    t.datetime "basic_time", default: "2019-05-22 23:00:00"
    t.datetime "work_time", default: "2019-05-22 22:30:00"
    t.index ["email"], name: "index_users_on_email", unique: true
  end

end

最後に格モデルに対してコードを記述するのを忘れずに。

class User < ApplicationRecord
  has_many :attendances, dependent: :destroy
class Attendance < ApplicationRecord
  belongs_to :user

これで関連付けができました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】RspecでCapybaraを使用した際に「undefined method `visit'」というエラーが出た場合の解決方法

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・rspec-rails: 4.0.1
・Capybara: 3.32.2
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

下記実装済み。

Slim導入
Bootstrap3導入
Font Awesome導入
ログイン機能実装
投稿機能実装

原因

Capybaraが読み込めていない。

解決方法

1.requireで読み込む

spec_helper.rb
require 'capybara/rspec' # 追記
RSpec.configure do |config|
end

2. 解決方法1で解決しない場合はDSLで強制的に読み込む

spec_helper.rb
require 'capybara/rspec'
RSpec.configure do |config|
  config.include Capybara::DSL # 追記
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

booleanのvalidateについて

booleanのカラムについて、バリデーションをかけたがvalid?がtrueにならず、期待していた実行とは違うものになってしまった!

そこで、booleanのカラムについて調べたことを備忘録としてまとめておく!

ダメなパターン:Notnull制約でfalseが入っているので、presence: trueをすれば良いと思い下記のようにした!

validates :check, presence: true

しかし、これだとcheck=falseの時にエラーが出てしまう!

調べてみると、下記のようにする必要があることがわかった!

validates :check, inclusion: {in: [true, false]}

参考資料
https://qiita.com/mktakuya/items/a13c2175f0f0d9871038

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]No route matches [GET] "/users/sign_out”の対処法

はじめに

ログアウトを実行するとルーティングエラーが発生したのでアウトプット

環境

Rails 5.0.7.2
ruby 2.5.1
devise

問題点

deviseのアカウント編集機能を実装し、ログアウトをリクエストすると、
No route matches [GET] "/users/sign_out”とエラー発生

原因

sign_out時のメソッドがDELETEからGETに変更されているので(いつなったのかは把握できていない)、変更すれば解決しそう。

対処方法

config/initializers/devise.rbを編集▼

devise.rb
#省略
  config.sign_out_via = :delete
#省略

上記の記述をgetに変更▼

devise.rb
#省略
  config.sign_out_via = :get
#省略

サーバーを再起動して、rake routesをターミナルにて実行すると▼

 destroy_user_session GET    /users/sign_out(.:format)      devise/sessions#destroy

getメソッドに変化しているので再度ログアウトを実行すると想定通りの動作をするはず!
参考にしてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

all_month が気になって ActiveSupport DateAndTime::Calculations を読んでみた

これ何

先輩にレビュー中に、 all_month 使った方がいいんじゃない?と言われたので、気になって調べてみた。
ActiveSupport の DateAndTime::Calculations に定義されていたので、周りのmethodも読んでみる

範囲指定のmethod

all_monthの内部実装を眺めてみた。rails/railsの内部実装だが、読めるコードでわかる。

def all_month
  beginning_of_month..end_of_month
end

ref: activesupport/lib/active_support/core_ext/date_and_time/calculations.rb

同じファイルの中を見てみるとall_month 以外にも all_xxx methodが存在した。対象範囲が変わるだけで、使えそうなmethodが多い。

  • all_day
  • all_month
  • all_quarter:3ヶ月ごとの日時情報が取れる
  • all_week:週の開始日を決めることができる
  • all_year

気づき 1. future?, past?

未来・過去かを判定するmethodが存在する!再定義しなくても、rails/railsであるものを使える!!気づき。

Date.yesterday.past?
=> true
Date.yesterday.future?
=> false
Date.tomorrow.future?
=> true
Date.tomorrow.past?
=> false

気づき 2. all_xxxと同様にnext_xxx, prev_xxxも存在する

next_xxx, prev_xxx が定義されている。 prev_xxx は last_xxx にaliasされている。

Date.current
=> Sun, 11 Jul 2020
Date.current.next_week
=> Mon, 13 Jul 2020
Date.current.prev_week
=> Mon, 29 Jun 2020
Date.current.last_week
=> Mon, 29 Jun 2020

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む