CircleCI Checkoutができない

Circle CI上でテストが動かなくなったので備忘録として。

エラーメッセージ

Directory (/home/circleci/project) you are trying to checkout to is not empty and not a git repository

原因

原因に関しては、CircleCIのissueを読んでみたのですがいまいちわからず...

対応策

issueでも記載されてある通り、- checkoutの位置をcircleci/browser-tools@1.4.0より上にもってくるかorbのバージョンを1.4.1にあげたらよいみたいでした。

circleci/browser-toolsのバージョンを上げる

config.yml

version: 2.1

orbs:
  ruby: circleci/ruby@2.0.0
  node: circleci/node@5.0.3
- browser-tools: circleci/browser-tools@1.4.0
+ browser-tools: circleci/browser-tools@1.4.1
jobs:
  ・・・省略

checkoutの位置を変更する

config.yml

version: 2.1

orbs:
  ruby: circleci/ruby@2.0.0
  node: circleci/node@5.0.3
  browser-tools: circleci/browser-tools@1.4.0
  ・・・省略
  test:
    ・・・省略
    steps:
   + - checkout
      - browser-tools/install-chrome
      - browser-tools/install-chromedriver
   - - checkout
      - ruby/install-deps

仕事をしながら、フィヨルドブートキャンプに参加して思ったこと

この記事はフィヨルドブートキャンプ Part 2 Advent Calendar 2022の9日目の記事になります。
part1もありますので、興味のある方は是非😄
昨日は、@rosh_1228の「Railwayの紹介」という記事でした。

この記事に関して

仕事をしながらスクールに通うメリット、デメリットに関しての内容になります。

仕事をしながらスクールに通えるのか?と不安に思っている人たちの参考になればと思います。

メリット

1. スクールで学んだことを実際の仕事に生かすことができる

フィヨルドブートキャンプでは、Linuxの基礎からWebアプリの仕組み、RailsJavaScript etc...と非常に幅広く学ぶことができます。その中でも実際に自分が実際の仕事で生かすことができたことを何点か紹介してみます。

  • 良いコードを仕事で書けるようになった

  • デイリースクラム(仮)を仕事に導入し、チーム開発を円滑に進めることができた

  • 自分の考えていることを言語化できるようになった

  • 考察し、自分の考えを持てるようになった

2. 読書する習慣が付いた

FBCでは各プラクティス毎に必読本があります。 世の中にはたくさんの技術書があり、どれを読めばいいかわからない中これは絶対に読むべき!と具体的に教えてくれるのは大変ありがたかったです。
自分はよく通勤時間に必読本を読んでいたこともあり、今では通勤時間は読書をするルーティンができあがったのは非常に良かったと思います。
最近読んだ中で断トツで面白かったのは、FBCビブリオバトル@goruchan32が紹介していました情熱プログラマーでした。

3. 自走力が付いた

他のスクールはわかりませんが、FBCでは良くも悪くも一から教えてはもらえません。
もちろんどうしても分からない場合などはメンターや、Q&A掲示板で質問することができますが決して答えは教えてれないので自分自身で考えて考えて考え抜く必要がでてくるため、自然と自走力が付いたな~と思います。

デメリット

1. 各種イベント(輪読会etc)に参加しずらい

輪読会などのイベントが平日の9:00~17:00くらいの時間帯に集まっていたため、なかなか参加することができませんでした。
じゃ、土日に輪読会開催しちゃえばいいじゃん!と思いながらもなかなか実行に移すことができなかった事を後悔しています...

2. 自分の時間が取れなくなる

起床 → FBC→ 仕事 → 帰宅 → 家族との時間 → FBC → 就寝というサイクルだったので、なかなか息抜きの時間を作るのは難しかったな~と思います。
自分は上手に息抜きすることができなかったので、プラクティスの途中で何度も力尽きたことがありました。 (その度日報の更新がとまったり...)
何事にも全力で取り組むことは大切ですが、息抜きしないと絶対にどこかで力尽きてしまうと思うのでみなさんは息抜きもしっかりしていきましょう(戒め)

おわりに

個人的に感じたメリット、デメリットを書きましたが自分は仕事をしながらフィヨルドブートキャンプに参加して良かったと思います。
技術的なことを学べたことはもちろんですが、原因追求の方法や考え方を学べたことが一番大きかったように思います。
この記事が仕事に通いながらスクールに通えるかな?と不安に思っている人の助けに少しでもなれればと思います。

明日はnicole 2525さんが記事を書いてくださいます。

Fly.ioでProcfile的なことをする方法

自作サービスで、Railsとは別にDiscord Botを常時起動させておく必要があったが、Fly.ioにはProcfileなんてものは存在しなかった😢
いろいろ調べてみたところ、Fly.ioではfly.tomlなるものを使用すればいいということが分かったので備忘として。

The Fly platform uses fly.toml to configure applications for deployment. Configuration of builds, environment variables, internet-exposed services, disk mounts and release commands go here. TOML is a simple configuration file format. Here's a useful introduction on its syntax. You don't need to create a fly.toml file by hand. Running flyctl launch will create a fly.toml file for you. You can also generate one from an existing app by running flyctl config save. VSCode users: Install the Even Better TOML extension for automatic fly.toml validation and hints drawn from this documentation. App Configuration (fly.toml)

公式ドキュメントを確認したところ、どうやらfly.tomlはデプロイする際のアプリケーション設定を記述することができるらしい。
Questions / Helpも確認したところ、自分の探していることのスレッドを発見した。
どうやら、 [processes]セッションに記述すればよいみたい。

修正前のfly.toml

・・・省略・・・
[[services]]
  http_checks = []
  internal_port = 8080
  processes = ["app"]
  protocol = "tcp"
  script_checks = []

修正後のfly.toml

[[services]]
  http_checks = []
  internal_port = 8080
  processes = ["web"]
  protocol = "tcp"
  script_checks = []

[processes]
web = "bundle exec rails server -b [::] -p 8080"
bot = "rake discord_bot:start"

[processes]セクション内にRailsを起動するコマンド、rakeタスクを実行するコマンドを記述し、processes = ["app"]processes = ["web"]に置き換える。

相手と自分の認識をすり合わせる

やっと...スクラム開発に到達することができました🤗
スクラム開発では誰かが起票したissueに取り組んでいくことになるのですが、一番大切だなと思う事を実体験を元に書こうと思います。

タイトルにも書いていますが、自分が一番大切なことは相手(issueの起票者)と自分の認識をすり合わせることだと思います。
読解力の高い人だと、相手の言いたい事、実装したいことを理解できるのかもしれませんが自分はあまり読解力が高くないので...😅

good first issueの実体験

スクラム開発のはじめにgood frist issueという比較的簡単なissueを割り振って頂きました。

github.com

下記がissueのコードになるのですが、1行目の- title @practice.title- title "#{@practice.title}の提出物"に書き換えると
それに引きずられ、4行目のtitle(h1タグ)も変更されてしまいます。
今回のissueはtitleタグの変更なので4行目は変更しなくてよさそうだな〜と思いましたもしかするとh1タグも変更してくれという暗黙的なお願いがあるかもしれないのでissue起票者に確認をしました。 確認したところ、やはりtitleタグの変更だけでよい!ということでした。

1 - title @practice.title
2 - category = @practice.category(current_user.course)
3
4 = render '/practices/page_header', title: title, category: category
5 = render 'page_tabs', resource: @practice

もし、暗黙的なお願いが含まれていたとすると出戻り作業が発生していました。
今回のissueは簡単だったので、出戻り作業が発生してもあまりタイムロスにはなりませんがこれが難しいissue(作業に1~2日かかる)だと出戻り作業で発生するタイムロスは深刻なものになってしまいます!
一番初めに読解力の高い人は...と書きましたが、相手の思考を完全に理解できる人なんてまずこの世にいないと思います。 もし、何かの仕事を依頼されたとき相手と認識を合わせずに着手する人がいれば、まず一旦相手と自分の認識のすり合わせを絶対にするべきだと思います。

余談ですが、その後のissueで調子にのった自分は相手と自分の認識をすり合わせず着手したので、あとあと出戻り作業が発生しました(小声)
ちゃんと相手と自分の認識のすり合わせをしてから作業をしましょう!(戒め)

npmをリリースしました

フィヨルドブートキャンプで自作npmを作成する課題があり、先日リリースしました。
初めて0から1を作成し、どんなnpmを作成するかなどすごく悩みました...

npmを作成した経緯

漠然とAPIを使ってみたいな〜と思い無料で使用できるAPIを探したところ、
さけのわというところが日本酒に関するAPIを提供しているとの事だったため、日本酒に関するnpmを作成することにしました。

ターゲット層

居酒屋にMacbookを持っていって、今日どんな日本酒を飲もうかな〜と言いながらnpmを叩く変人向けです。
上記は冗談ですが、居酒屋などに行った際日本酒の銘柄などたくさんありどんな日本酒が人気なのか分からない人をターゲットにしました。
ゆくゆくはLINEBotなどと紐づけて、居酒屋などで気軽に利用できるようにしたいと思います。

npmの紹介

www.npmjs.com

使用方法

  1. npmのインストール

    npm install jpn-sake
    
  2. npx jpn-sakeで実行

今後

まだまだソースコードリファクタリングなどが残っているので、メンターから指摘された点を改善していくと同時に、
上記でも挙げたように、npmのままではイマイチ使い勝手がよくないのでLINEBotと紐づけてもっと気軽に日本酒を探せるようにしたいと思います。 今まで自分でアプリなどを作成したこともなかったため、今回自分で作成し、リリースまでできた事がうれしいです。 少しは成長できたかな?

【Ruby on Rails】N+1問題

N+1問題が発生したので、原因と対応策を備忘としてまとめた。

N+1問題とは🤔

ループ処理の中で都度SQLを発行してしまった結果、大量のSQLが発行されパフォーマンスが低下してしまう問題

ソースコード

# app/controllers/users_controller.rb
# frozen_string_literal: true

class UsersController < ApplicationController
  def index
    @users = User.order(:id).page(params[:page])
  end

  def show
    @user = User.find(params[:id])
  end
end
# app/views/users/index.html.erb
<h1><%= User.model_name.human %></h1>

<table>
  <thead>
    <tr>
      <th><%= User.human_attribute_name(:avatar) %></th>
      <th><%= User.human_attribute_name(:email) %></th>
      <th><%= User.human_attribute_name(:name) %></th>
      <th><%= User.human_attribute_name(:postal_code) %></th>
      <th><%= User.human_attribute_name(:address) %></th>
      <th></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= image_tag user.avatar.variant(resize:'50x50').processed if user.avatar.attached? %></td>
        <td><%= user.email %></td>
        <td><%= user.name %></td>
        <td><%= user.postal_code %></td>
        <td><%= user.address %></td>
        <td><%= link_to t('views.common.show'), user %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<%= paginate @users %>

N+1発生箇所

今回、ユーザー一覧(アイコンも表示)を表示するページでN+1問題が発生した。

# app/controllers/users_controller.rb
  def index
    @users = User.order(:id).page(params[:page])
  end

# app/views/users/index.html.erb
    <% @users.each do |user| %>
      <tr>
        <td><%= image_tag user.avatar.variant(resize:'50x50').processed if user.avatar.attached? %></td>
        <td><%= user.email %></td>
        <td><%= user.name %></td>
        <td><%= user.postal_code %></td>
        <td><%= user.address %></td>
        <td><%= link_to t('views.common.show'), user %></td>
      </tr>
    <% end %>
# rails.log
Started GET "/users" for ::1 at 2022-05-19 12:53:28 +0900
Processing by UsersController#index as HTML
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 51], ["LIMIT", 1]]
  Rendering layout layouts/application.html.erb
  Rendering users/index.html.erb within layouts/application
  User Load (0.3ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 25], ["OFFSET", 0]]
  ↳ app/views/users/index.html.erb:16
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 1], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 2], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 3], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 4], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 5], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 6], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 7], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 8], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 9], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 10], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 11], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 12], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 13], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 14], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 15], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 16], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 17], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 18], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 19], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.2ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 20], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 21], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 22], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 23], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 24], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 25], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
  ↳ app/views/users/index.html.erb:18
   (0.1ms)  SELECT COUNT(*) FROM "users"
  ↳ app/views/users/index.html.erb:29
  Rendered users/index.html.erb within layouts/application (Duration: 74.7ms | Allocations: 30676)
[Webpacker] Everything's up-to-date. Nothing to do
  Rendered layout layouts/application.html.erb (Duration: 86.1ms | Allocations: 34506)
Completed 200 OK in 92ms (Views: 83.2ms | ActiveRecord: 3.8ms | Allocations: 36494)

上記のログを見るとSQLを下記のタイミングで発行していることがわかる。
1. controller側でのUser.order(:id).page(params[:page])で呼び出し1回
1. view側でのuser毎の呼び出しN回
計26件のSQLが発行されていることが分かる。

解決方法

  1. includesを使用する
    事前にデータを全件取得しておけば、<% @users.each do |user| %>の中で都度SQLを発行しなくて済む。

    # app/controllers/users_controller.rb
     def index
       @users= User.all.includes(:avatar_blob).order(:id).page(params[:page])
     end
    
    # rails.log
    Started GET "/users" for ::1 at 2022-05-19 12:54:50 +0900
    (1.0ms)  SELECT sqlite_version(*)
    (0.6ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
    Processing by UsersController#index as HTML
    User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 51], ["LIMIT", 1]]
    Rendering layout layouts/application.html.erb
    Rendering users/index.html.erb within layouts/application
    User Load (1.1ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 25], ["OFFSET", 0]]
    ↳ app/views/users/index.html.erb:16
    ActiveStorage::Attachment Load (0.7ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? AND "active_storage_attachments"."record_id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["record_type", "User"], ["name", "avatar"], ["record_id", 1], ["record_id", 2], ["record_id", 3], ["record_id", 4], ["record_id", 5], ["record_id", 6], ["record_id", 7], ["record_id", 8], ["record_id", 9], ["record_id", 10], ["record_id", 11], ["record_id", 12], ["record_id", 13], ["record_id", 14], ["record_id", 15], ["record_id", 16], ["record_id", 17], ["record_id", 18], ["record_id", 19], ["record_id", 20], ["record_id", 21], ["record_id", 22], ["record_id", 23], ["record_id", 24], ["record_id", 25]]
    ↳ app/views/users/index.html.erb:16
    (0.5ms)  SELECT COUNT(*) FROM "users"
    ↳ app/views/users/index.html.erb:29
    Rendered users/index.html.erb within layouts/application (Duration: 443.2ms | Allocations: 304422)
    [Webpacker] Everything's up-to-date. Nothing to do
    Rendered layout layouts/application.html.erb (Duration: 473.5ms | Allocations: 312669)
    Completed 200 OK in 532ms (Views: 477.1ms | ActiveRecord: 5.3ms | Allocations: 325554)
    
  2. with_attached_avatarを使用する
    ActiveStorageでN+1問題を解決する際は、上記のメソッドを使用すればOK

    scope :“with_attached#{name}”, -> { includes(“#{name}attachments”: :blob) }

    メソッド内でも同じようにincludesされているみたい。

参考サイト

【Ruby on Rails】deviseでつまづいたところ

deviseを用いてユーザー認証を実装する際に躓いたポイントを素人目線でまとめてみた。

deviseとは🤔

deviseとはRuby on Railsで作成したアプリケーションに簡単にユーザー認証機能を実装することができるgemである。 github.com

前提

  • rails generate devise MODELrails generate devise usersで実行

deviseのインストールとセットアップ

READMEにインストール手順が記載されているので、基本的にその指示通りにインストールすればOK

  1. Gemfileにgem 'devise'を追加し、bundle installを実行
    ※READMEに記載のあるように、Rails 4.1以上からしか使用できないので注意が必要

    Devise 4.0 works with Rails 4.1 onwards. Add the following line to your Gemfile:

  2. rails generate devise:installを実行

  3. config/environments/development.rbに下記の行を追加
    config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

  4. userモデルを作成するため下記コマンドを実行

    rails g devise users
    rails db:migrate
    
  5. サーバーを起動した後、http://localhost:3000/users/sign_inにアクセスして、ユーザーが作成できるか確認してみる

deviseのViewとController

現在の各種ファイルを確認したところsign_inやsign_upで表示された画面に対応するViewファイルが見当たらない...
上記のセットアップの中でViewやControllerが一緒に作成されると思い込んでいたが、作成されなかったので作成する。

# Viewファイルの作成
rails g devise:views users
# Controllerファイルの作成
rails g devise:controllers users

ただし、このままでは作成されたViewやControllerをごにょごにょしても実際には反映されない。
config/routes.rbを変更する前だと、各種viewなどはgem自体のviewファイルなどを参照していることがログからわかる。

  Rendering /Users/choco/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application
  Rendered /Users/choco/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views/devise/shared/_error_messages.html.erb (Duration: 1.2ms | Allocations: 295)
  Rendered /Users/choco/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views/devise/shared/_links.html.erb (Duration: 1.5ms | Allocations: 617)
  Rendered /Users/choco/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application (Duration: 411.3ms | Allocations: 2799

config/routes.rbに記載されているdevise_for :usersを下記に置き換えることにより、app/views/users配下にある各種Viewを参照するようになる。
※ 下記の場合だとregistrationssessionsのみusers配下を参照するようになる (パスワード再設定ページなどはusers配下を参照しない)

  devise_for :users, controllers: {
    registrations: 'users/registrations',
    sessions: "users/sessions",
  }
  Rendering users/registrations/new.html.erb within layouts/application
  Rendered users/shared/_error_messages.html.erb (Duration: 1.4ms | Allocations: 289)
  Rendered users/shared/_links.html.erb (Duration: 2.0ms | Allocations: 611)
  Rendered users/registrations/new.html.erb within layouts/application (Duration: 327.1ms | Allocations: 279730)

全view、controllerをuser配下のものを参照させたいときは、config/initialize/devise.rbに記載されているconfig.scoped_views = falseconfig.scoped_views = tureに置き換えてあげるとよい。

参考サイト

github.com

zenn.dev

qiita.com