homebrewを使って、nodenvをインストールした時のPermission denied エラーを解決

エラー内容

homebrewで、nodenvのインストール時に以下のようなPermission deniedエラーが発生しました。

$ brew install nodenv
==> Downloading https://ghcr.io/v2/homebrew/core/nodenv/manifests/1.4.1
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/1aaf03245ee4323ce2635f6ee8b7d88d03644c3b89e5067550d4d774c3070ae2--nodenv-1.4.1.bottle_manifest.json
==> Fetching dependencies for nodenv: ca-certificates, openssl@3 and node-build
==> Downloading https://ghcr.io/v2/homebrew/core/ca-certificates/manifests/2023-08-22
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/a8cd32e30cae0b7335779e93a6554f294f09485802fc253a3a8be441337a6115--ca-certificates-2023-08-22.bottle_manifest.json
==> Fetching ca-certificates
==> Downloading https://ghcr.io/v2/homebrew/core/ca-certificates/blobs/sha256:a331e92e7a759571296581f029e5cc2ec7cee70cd92dc0b5f8eb76095f94a21a
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/9be22fdac2e86ffb3bf6c165d2349e6e7fb9ff474a859890bcd63f79a807641e--ca-certificates--2023-08-22.sonoma.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/openssl/3/manifests/3.1.3
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/f038f540886a6a94c000296efb3058dc47e867eef00f0c6836b8ff310bc04daa--openssl@3-3.1.3.bottle_manifest.json
==> Fetching openssl@3
==> Downloading https://ghcr.io/v2/homebrew/core/openssl/3/blobs/sha256:b3c246dae84e6fe84b9c1a896bf7392aa457667c580509c6161d71e440636d1e
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/19a379dd6a9ddeb5f527a01951d160d48d4cbd5db74b4de6f029dd2682f45d23--openssl@3--3.1.3.sonoma.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/node-build/manifests/4.9.126
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/8e9ab330e6e8c2308db6aa79c81ad2a2230635cf9534c7b0e7cc9abd0ed342e3--node-build-4.9.126.bottle_manifest.json
==> Fetching node-build
==> Downloading https://ghcr.io/v2/homebrew/core/node-build/blobs/sha256:0f69e614e07f45a32854fd1afe1a69a13697d6abbc742cbc01f3f0ac24936be7
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/f296d5096dd9f65d61d3720b2bab7a21bcba6e160c3d5c3857c5570167278881--node-build--4.9.126.all.bottle.tar.gz
==> Fetching nodenv
==> Downloading https://ghcr.io/v2/homebrew/core/nodenv/blobs/sha256:a4bbb8b27f85d0d3ee3628e9360a1f3eb42f0b3fef3c26bc23182d15da620e42
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/f955146912b8fe63d53a5c78beb711f3bf5e4a3ed92b19f0464ac2417d10f630--nodenv--1.4.1.sonoma.bottle.tar.gz
==> Installing dependencies for nodenv: ca-certificates, openssl@3 and node-build
==> Installing nodenv dependency: ca-certificates
==> Downloading https://ghcr.io/v2/homebrew/core/ca-certificates/manifests/2023-08-22
Already downloaded: /Users/username/Library/Caches/Homebrew/downloads/a8cd32e30cae0b7335779e93a6554f294f09485802fc253a3a8be441337a6115--ca-certificates-2023-08-22.bottle_manifest.json
==> Pouring ca-certificates--2023-08-22.sonoma.bottle.tar.gz
cp: /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/.brew: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew/ca-certificates.rb: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/.brew: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share/ca-certificates: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates/cacert.pem: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share/ca-certificates: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: utimensat: /usr/local/Cellar/ca-certificates/.: Permission denied
Error: Failure while executing; `/usr/bin/env cp -pR /private/tmp/d20231013-7085-idhfho/ca-certificates/. /usr/local/Cellar/ca-certificates` exited with 1. Here’s the output:
cp: /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/.brew: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew/ca-certificates.rb: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/.brew: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/.brew: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share/ca-certificates: unable to copy extended attributes to /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates/cacert.pem: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share/ca-certificates: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/share/ca-certificates: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22/share: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22/share: No such file or directory
cp: utimensat: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chown: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chmod: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: chflags: /usr/local/Cellar/ca-certificates/./2023-08-22: No such file or directory
cp: /private/tmp/d20231013-7085-idhfho/ca-certificates/./2023-08-22: unable to copy ACL to /usr/local/Cellar/ca-certificates/./2023-08-22: Permission denied
cp: utimensat: /usr/local/Cellar/ca-certificates/.: Permission denied
Error: Failure while executing .....Permission denied

エラー原因

nodenvのインストールの際に/usr/local/Cellar/ディレクトリ配下にあるファイルにnodenvに関する内容を書き込もうとしたが、/usr/local/Cellar/ディレクトリ配下に書き換えの権限が与えられておらず、書き込めませんという権限エラーが発生したということでした。

通常、/usr/local/Cellar/ディレクトリは、homebrewなどのパッケージ管理システムを使用してインストールしたパッケージの実行ファイルやライブラリ、設定ファイル等が保存されるディレクトリになります。

構造としては、以下のようになっており、パッケージごとにディレクトリを作成し、各パッケージディレクトリ内には、バージョンごとにサブディレクトリを作成することで、異なるバージョンのパッケージを競合なく管理することができます。さらにバージョン変更も簡単に行うことができます。

/usr/local/Cellar/
  ├─ nodenv (package1)/
  │    ├─ Node.js version1/
  │    │    └─ ... (パッケージ1のバージョン1のファイル)
  │    └─ Node.js version2/
  │         └─ ... (パッケージ1のバージョン2のファイル)
  ├─ package2/
  │    └─ ... (パッケージ2のファイル)
  └─ ...

これで、homebrewなどのパッケージ管理システムが、ディレクトリを分けることによって、複数のパッケージを競合なく管理できる仕組みを持っていることが分かりました。

解決法

下記のコマンドで、/usr/local/Cellar/ディレクトリ配下のディレクトリ・ファイル全ての所有者を現在のユーザー (${USER}$(whoami)) に変更し、permission errorを解決します。

$(brew --prefix)/*は、homebrewのインストール先のすべてのディレクトリ、ファイルことなので、/usr/local/Cellar/ディレクトリ内のすべてのディレクトリ、ファイルと言い換えることができます。

# /usr/local/Cellar/ディレクトリ配下のファイル全ての所有者を${USER}で環境変数を使って取得した現在のユーザー名に変更する
$ sudo chown -R ${USER}:staff /usr/local/Cellar/
または、
# Homebrewでインストールしたパッケージのルートディレクトリ配下全ての所有者を$(whoami)で取得した現在のユーザー名に変更する
$ sudo chown -R $(whoami) $(brew --prefix)/*

しかし、また新たなエラーが発生!!

$ brew install nodenv
・・・・
==> Pouring [email protected]
Error: Cannot link openssl@3
Another version is already linked: /usr/local/Cellar/openssl@3/3.1.3

原因

brewが、openssl@3をインストールしようとした時に既存のopenssl@3との競合を解決できず、openssl@3とリンクできません。というエラーが発生したようです。

brewを使って、インストールしたopensslの一覧を表示すると、openssl@3/3.1.3がインストールされていました。

$ brew list openssl
/usr/local/Cellar/openssl@3/3.1.3/.bottle/etc/ (7 files)
/usr/local/Cellar/openssl@3/3.1.3/bin/c_rehash
/usr/local/Cellar/openssl@3/3.1.3/bin/openssl
/usr/local/Cellar/openssl@3/3.1.3/include/openssl/ (135 files)
/usr/local/Cellar/openssl@3/3.1.3/lib/libcrypto.3.dylib
/usr/local/Cellar/openssl@3/3.1.3/lib/libssl.3.dylib
/usr/local/Cellar/openssl@3/3.1.3/lib/engines-3/ (3 files)
/usr/local/Cellar/openssl@3/3.1.3/lib/ossl-modules/legacy.dylib
/usr/local/Cellar/openssl@3/3.1.3/lib/pkgconfig/ (3 files)
/usr/local/Cellar/openssl@3/3.1.3/lib/ (4 other files)
/usr/local/Cellar/openssl@3/3.1.3/share/doc/ (798 files)
/usr/local/Cellar/openssl@3/3.1.3/share/man/ (5533 files)

解決法

一度、既存のopenssl@3のリンクを解除して、インストール再設定をすることで解決しました。

# 既存のopenssl@3のリンクを解除
$ brew unlink openssl@3

# openssl@3を再度インストール
$ brew install openssl@3

# openssl@3のリンクを再設定
$ brew link openssl@3

参考

https://superuser.com/questions/1425577/brew-install-r-permission-denied

https://gist.github.com/irazasyed/7732946

2022/10/13(木) Railsのルーティング&アソシエーション

Railsにおけるバリデーションの設定方法

バリデーションとは、正しいデータのみがデータベースに保存されるようにモデルクラスにて、そのデータが正しいかどうかをチェックするシステムのことを言います。バリデーションに引っかかり、データが不正だと判断された場合は、データベースに保存されません。

バリデーションは、正しいデータだけをデータベースに保存するために行われます。正しいデータだけをデータベースに保存するのであれば、モデルレベルでバリデーションを実行するのが最適です。モデルレベルでのバリデーションは、データベースに依存せず、エンドユーザーがバイパスすることもできず、テストもメンテナンスもやりやすいためです。Railsではバリデーションを簡単に利用できるよう、一般に利用可能なビルトインヘルパーが用意されており、独自のバリデーションメソッドも作成できるようになっています。

Railsガイド

バリデーションの設定方法

validates :カラム名, presence: true
validates :カラム名, length: { maximum: 30 }
validates :カラム名, numericality: { greater_than: 0 }
  • presence: trueは、カラムのデータは、必ず存在する(空のデータでは、保存できないように制限)ように設定したバリデーションです。
  • length: { maximum: 30 }は、カラムの文字数を最大30文字に制限するバリデーションです。(30文字以上のデータは、DBに保存されない)
  • numericality: { greater_than: 0 }は、0以上の数値のみの入力を許可するバリデーションです。
numericalityのオプション 概要
only_integer integerのみ
equal_to 指定された値と等しいか
greater_than_or_equal_to 指定された値以上
less_than_or_equal_to 指定された値以下
greater_than 指定された値よりも大きいか
less_than 指定された値よりも小さいか
odd trueに設定した場合、奇数か
even trueに設定した場合、偶数か

has_many :through関連付け

has_many :through関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。 この関連付けでは、2つのモデルの間に「第3のモデル」(joinモデル)が介在し、それによって相手のモデルの「0個以上」のインスタンスとマッチします。

Railsガイド

class Restaurant < ApplicationRecord
  has_many :line_foods, through: :foods
end
  • 1つのRestaurantは中間テーブルFoodsを経由して複数のLine_foodを持っています。
  • has_many :throughを使うと、1つのレストランにつき仮注文は、複数作成できますが、その仮注文は、foodテーブルを参照していますよという意味になります。

belongs_toのoptionalオプション

optional: trueとは、アソシエーションによって紐づけられた外部キーの値が存在しない値やnilの場合でも、データベースに保存することができるオプションです。

【Rails】外部キーのバリデーションチェックを回避するoptional: trueについて簡単にまとめてみた

class LineFood < ApplicationRecord
  belongs_to :order, optional: true
end
  • 上記の例で言うと、仮注文テーブルの外部キーにorder_idがセットされていない場合でもバリデーションチェックが行われることなくDBに保存できるようになります。
  • optional: trueを設定すると、belongs_toの外部キーnil(存在しないこと)を許可します。

インスタンスメソッドとは?

インスタンスメソッドとは、クラスから作成されたオブジェクトに対して、呼び出せるメソッドのことです。 インスタンスメソッドをコントローラーではなく、モデルに記述すると、様々な箇所からオブジェクトに対して呼び出すことができます。

  • 特定のデータ(インスタンス)に対する操作は、インスタンスメソッドが使われます。
  • 対して、クラスメソッドは、クラス全体に対する共通の操作を行いたい時に使われます。
  • クラスメソッドの定義は、self.メソッド名のようにメソッド名の前にselfをつける必要があります。

ルーティングの名前空間について

namespace: 名前空間名名前空間をつけられます。名前空間をつけることのメリットは、コントローラーを名前空間名のディレクトリで、クループ化するとともに、URLにも名前空間の情報をつけることが出来ることです。 つまり、名前空間というディレクトリを作成することにより、分かりやすくグループ化したルーティングを作成できるようになります。

namespace :a do
  namespace :b do
    ...
  end
end
  • コントローラーをapp/controllers/a/b/というディレクトに分類し、それをURLにも適用するためにnamespaceが使われています。namespaceであるabが入ったURLでルーティングを作成できます。

ルーティングを作成するresourcesメソッドとresourceメソッド

どちらもルーティングを簡単に作成するためのメソッドです。基本的なアクション(index、show、new、create、edit、update、destroy)に対応するURLの組み合わせ(ルーティング)を作成します。

resorcesメソッド

「resourcesメソッド」は、7つのアクション(index、show、new、create、edit、update、destroy)のidを付与したルーティングを一括生成してくれるメソッドです。

【Rails】resourcesメソッドとresourceメソッドの違いについて簡単に解説!

resourceメソッド

「resourceメソッド」は6つのアクション(show、new、create、edit、update、destroy)のルーティングがidなしで生成されます。加えて、「resourceメソッド」はindexアクションのルーティングが生成されません。

【Rails】resourcesメソッドとresourceメソッドの違いについて簡単に解説!

resourcesとresourceの使い分け

resources :restaurants do
  resources :foods, only: %i[index]
end
  • resourcesは、リソースが複数ある場合に使用します。ルーティングに付与されるidを使って、複数あるリソースを識別します。
  • 上記の例で、レストランは、あちらこちらにたくさん存在するので、resourcesを使用しています。
resources :users do
  resource :profile
end
  • resourceは、リソースが1つしかない時に使用します。リソースが複数ではないため、リソースの一覧ページを表示する必要性がありません。よって、resourceでは、indexアクションに対するルーティングが作成されないのです。
  • 上記の例では、特定のユーザーに対するプロフィール情報は、1つなのでresourceを使用しています。

saveとsave!メソッド

  • saveメソッドは、生成したモデルオブジェクトをデータベースに保存します。データベースに保存できなかった場合は、falseを返します。
  • 一方save!メソッドは、同じく生成したモデルオブジェクトをデータベースに保存しますが、保存に失敗した場合は、例外を発生させます。
  • 例外を発生すると、例外が起きた場所で処理を停止します。

参考

Railsガイド-バリデーション

Railsガイド-has many throughについて

【Rails】外部キーのバリデーションチェックを回避するoptional: trueについて簡単にまとめてみた

【Rails】resourcesメソッドとresourceメソッドの違いについて簡単に解説!

2022/10/11(火) DB関連のまとめ

RailsにおけるDBとMigrationファイルModelの役割とその関係

  • どんなデータベースを作って欲しいかを指示するデータベースの設計図Migrationファイルです。
    • あるテーブルにこんなカラムが必要、そのカラムに対する型や制約を指示するもので、Migrationファイルを元にDBが作成されます。
  • Railsのmodelは、DBのテーブルに対応するRubyのクラスです。 テーブル内のデータがどんなもので、テーブル同士の関係がどうかというアソシエーション関係を定義できます。また、データベースとの間で情報のやり取り(データの取得・保存・検索)を行います。
  • モデルおよびモデル内のデータを表現する
  • モデル同士の関連付け(アソシエーション)を表現する
  • 関連付けられているモデル間の継承階層を表現する
  • データをデータベースで永続化する前にバリデーション(検証)を行なう
  • データベースをオブジェクト指向スタイルで操作する

Railsガイド

  • 実際にデータが表のように保管されている箱のようなものがデータベースです。
    • データベースとのやりとりを行うのは、モデルです。

RailsプロジェクトをAPIモードで作成する

  • --apiというオプションをつけるとAPIモードでRailsプロジェクトを作成できます。
$ rails new プロジェクト名 --api

APIモードとは?

Rails APIモードとは、API作成に特化したモードのことです。

APIモードではMVCのV(ビュー)が存在しないため、rails newを実行した際にビューに関するファイルやGemが生成されません。

また、通常のRailsアプリケーションではerbファイルをレスポンスとして返しますが、APIモードではJSONをレスポンスに返します。

【Rails】Rails5から追加された機能「APIモード」について初心者向けにまとめてみた

つまり、Reactなどを使って、非同期的にAPIから外部のデータを取得する場合、JSONデータをレスポンスとして返すRailsAPIモードを使います。

外部キーの役割

外部キーとはモデル間のリレーションを辿る際の基準となるキーのことです。外部キーを使って、テーブル間のアソシエーション関係を表すことが出来ます。あるテーブルに紐づいたデータを作成したいときに外部キーを使うと自動で紐づいたデータがDBに作成されます。

参考

Railsガイド

【Rails】Rails5から追加された機能「APIモード」について初心者向けにまとめてみた

2022/10/3(月) 基礎から学ぶReact/React Hooks実践入門 Chapter1-1~5

インクリメント演算子とデクリメント演算子

インクリメント演算子は、値を1加算し、デクリメント演算子は、1減算します。 - 前置の場合は、加算・減算後の値を返すのに対し、後置の場合は、計算する前の値が返ります。

演算子 演算子 説明
++EX1 インクリメント(前置) EX1の値に+1する
- -EX1 デクリメント(前置) EX1の値に-1する
EX1++ インクリメント(後置) EX1の値に+1する
EX1- - デクリメント(後置) EX1の値に-1する

比較演算子

比較演算子は、左オペランドと右オペランドの大きさを比較し、その結果について真偽値(true/false)で返す演算子です。

厳密等価演算子(===)と厳密不等価演算子(! ==)について

厳密等価演算子厳密不等価演算子は、どちらも2つのオペランド型と値を比較します。厳密等価演算子は、2つのオペランドの値が合っていても型が合わない場合は、trueが返りません。

3 === 3 //=> true
3 === "3" //=> false

しかし、等価演算子(==)を使うと、型が異なる場合でも同じ値であれば、自動的に暗黙の型変換が行われ、以下のように文字列の3と数値の3は同じとみなされてしまいます。

3 == 3 //=> true
3 == "3" //=> true

厳密等価演算子厳密不等価演算子を使うメリットは、暗黙的な型変換を防ぎ、型の安全性を保証してくれるという部分です。

等価演算子(==)を利用するケース

nullまたは、undefined」を一度に判定したい場合は、例外的に等価演算子(==)が使われます。これは、厳密等価演算子がnullとundefinedを比較するとfalseと評価されるのに対し、等価演算子と使うとtrueと評価されるからです。

null === undefined //=> false
null == undefined //=> true
  • 厳密等価演算子を使って、「nullまたは、undefined」を判定する場合は、2回演算子を使った判定を行う必要があります。

参考

基礎から学ぶ React/React Hooks

2022/10/2(日) 現場Rails Chapter1_1 オブジェクトを理解してみよう

クラスとは

全てのオブジェクトが持つそのオブジェクトの原型のことです。

"おはよう".class
=> String
  • 「おはよう」という文字列のクラスを調べると、Stringクラスということが分かります。
  • classというメソッドを使って、どのクラスに属するのかということを調べます。

オブジェクトに対して、どんな機能を持たせるかは、そのオブジェクトがどのクラスに属するかによって決まります。クラスによって予め持っているメソッドなどが異なるためです。

"おはよう".length
=> 4

4.length
(irb):4:in `<main>': undefined method `length' for 4:Integer (NoMethodError)
  • 「おはよう」という文字列オブジェクトは、Stringクラスに属するため文字数を調べることができるlengthメソッドを使用することができます。
  • 一方で、integerクラスに属する数値の4は、lengthメソッドを使用することができません。これは、integerクラスがlengthメソッドを持っていないからです。

今まで出てきたStringクラスやIntegerクラスのようにRubyが標準的に用意しているクラスの他にも、自分のやりたいことに合わせてクラスを作成することもできます。

クラスとインスタンス

クラスは、オブジェクトの原型であると説明しました。代わって、インスタンスは、クラスを元にして作られたオブジェクトのことです。 1クラスからは、インスタンスを生み出すことができ、作成されたインスタンスは、クラスが持つ属性やメソッドを持っています。

例) 分かりやすく例えると、クラスはたい焼きの金型であると言えます。金型を元にして、実際に作られるたい焼きがインスタンスになります。

クラスであるたい焼きの金型があることで、小倉あんのたい焼きや抹茶味のたい焼き、クリーム味のたい焼きなどの様々なたい焼きインスタンスを作り出すことができるのです。

変数

変数とは、コンピュータプログラムのソースコードなどで、データを一時的に記憶しておくための領域に固有の名前を付けたもの。
e-Words

label = "おはよう"
=> "おはよう"

p label
=> "おはよう"     
  • あるオブジェクトを参照したい時にそのオブジェクトの参照口を指し示してアクセスできるように名前をつけたものです。
  • labelという変数に文字列「おはよう」を代入すると、変数を使うだけで、文字列「おはよう」を参照できるようになります。

変数には、プログラム中の一定の処理の範囲で使われ、その範囲が終わったら捨てられるローカル変数とオブジェクトの内部に保持されてオブジェクトが存在する限り一緒に存在するインスタンス変数があります。

  • ローカル変数とは、プログラム中のブロック(関数やメソッドなど)の内部で宣言され、その範囲内でのみ有効な変数のことです。
  • インスタンス変数とは、個々のインスタンスごとの固有の変数です。インスタンスの情報が入った変数のことで、メンバ変数、フィールドとも言われます。

メソッド

オブジェクトの振る舞いをメソッドと言います。色々な処理をひとつにまとめてメソッドとして定義することで、何度も再利用(実行)ができるようになります。

インスタンスに対して、呼び出せるメソッドはインスタンスメソッドと呼ばれ、クラス内に定義します。インスタンスメソッドをクラス内に定義することで、別のインスタンスにも同じような振る舞いをさせることができるのです。

class Human
    # メソッドの定義
    def walk
        puts "歩きます"
    end
end

# Humanクラスからインスタンスを生成
humanA = Human.new()

# インスタンスに対して、インスタンスメソッドを呼び出す
humanA.walk # => 歩きます

参考

現場で使える Ruby on Rails 5速習実践ガイド

e-Words

2022/10/1(土) SPA, SSR, SSGの深堀り

MPAとは

ブラウザから「こういうページがほしい」とHTTPリクエストをもらうと、Webサーバーは内部でHTMLを組み上げ、JavaScriptCSSと一緒にHTTPレスポンスを返します。重要なのは、この処理がページを遷移するごとに毎回行われるということです。
SPA, SSR, SSGって結局なんなんだっけ?

  • SPAと対照的にMPAでは、 差分検出をすることなく、リクエストごとに毎回サーバー側でHTMLの作成が行われページ全体が更新されます。その結果、ページの更新に時間がかかるなどパフォーマンスが悪くなるデメリットがあります。
  • MPAでのパフォーマンスの問題を解決するひとつとしてSPAがあります。

SPAとは

初回リクエスト時にページの基本となるHTMLやCSSJavaScriptをサーバーから受け取り、その後は必要なデータだけをサーバーに要求し、その変更差分を表示する方式で、ページを更新します。 SPA, SSR, SSGの違いについて図解でまとめてみた

  • ページ全体を読み込むのは、初回リクエスト時のみで、ページ更新時はサーバーからデータのみを受け取り変更差分を更新します。
  • ページの更新時にサーバーから受け取るデータは、JSON形式(JavaScript)で非同期に受け取ります。(Ajax通信)
  • ブラウザ更新時にJSON形式で受け取ったデータをもとにクライアント側(ブラウザ)で、HTMLを構築し、変更差分を反映させたページを作成します。
  • SPAは、クライアント側でページを生成するため、CSR(client side rendering)とも呼ばれます。
  • SPAは、更新時に非同期で必要なデータのみを受け取るため、更新時間を素早くできるなどのメリットがあります。
  • 一方で、SPAは、SEOに弱いなどのデメリットもあります。

SPA(シングルページアプリケーション)サイトはGoogleに正しく認識されないため、SEO的には最悪だと言われてきました。そのため、SSR(サーバサイドレンダリング)をし、初回画面表示時は本来ReactやAngularが生成するhtmlを、サーバサイドでレンダリングしてクライアント側はそれを表示するだけ、という手法を用います。
SSR無しのReact・Angular製のSPAサイトはGooglebotにどれくらい認識されるのか?

SSRとは

ブラウザ上で初期データをレンダリングするのではなく、サーバー側でAPIを叩き、レンダリングまでを行ってからHTMLファイルを返却する。

SPA, SSR, SSGの違いについて図解でまとめてみた

SSRでは、初回リクエスト時にサーバーにリクエストが走ります。初回リクエストを受け取ったサーバーは、APIと連携をして初回データを反映したHTMLを構築し、そのHTMLをブラウザに返すレンダリングまでを行います。つまり、SSRでは、サーバー側でページを構築し、ページを返すレンダリングまでが行われるため、Server Side Renderingと呼ばれます。

2回目以降のリクエストは、直接ブラウザからAPIにアクセスしデータを取得しに行きブラウザ上でデータを反映したページを生成してレンダリングを行います。

SSRでは、SPAで挙げられる問題点を解決するためにSPAと組み合わせて使われるアーキテクチャです。 - 初回ページ表示までの時間が早い - SEOに強い

しかし、サーバー側の負担が大きくなるなどのデメリットもあります。

SSGとは

アプリケーションのビルド時(アプリケーションがサーバーにデプロイされるタイミング)にサーバーがAPIから必要な初期データ等を取得し、そのデータを反映したHTMLを作成します。

重要なのは、HTMLの作成が行われるタイミングがサーバーへのリクエスト時ではなく、ビルド時であることです。

よって、サーバーへリクエストがあると、ビルド時に作成済みのHTMLファイルを返却するだけなので、素早くページの更新ができるようになります。 SPA, SSR, SSGの違いについて図解でまとめてみた

  • SSGでは、事前にビルドしたタイミングで、サーバーがデータ取得を行いレンダリングが行われるので、そのことをプリレンダリング事前描画)と言います。
    • レンダリングとは、表示用のデータをもとに、内容を整形して表示することです。
  • SSGは、リクエスト時にかかるサーバーへの負荷がかからないのがメリットです。
  • ただし、ページ量が多いWebサイトなどだと、ビルド時にかかるサーバーへの負担が大きくなります。

まとめ

  • データを取得するには、APIへアクセスを行う
  • SPAでは、ブラウザ側(クライアント)でページを生成するのに対し、SSR、SSGでは、サーバー側でページの生成を行う。
  • SPAでは、ブラウザ側(クライアント)でページの生成が行われるため、CSRと呼ばれる
  • SPA、SSR、SSGは、それぞれデータ取得のタイミングが異なるのと、ページ生成の場所が異なるだけです。

参考

SPAの基本と3大JavaScriptフレームワーク(Angular、React、Vue.js)の最新動向 ~2020年版~

SSR無しのReact・Angular製のSPAサイトはGooglebotにどれくらい認識されるのか?

SPA, SSR, SSGの違いについて図解でまとめてみた

SPA, SSR, SSGって結局なんなんだっけ?

Gitでブランチを切るのを忘れて間違えてmasterにコミットしてしまったときの対処法

誤ってmasterブランチでコミットしてしまっても、そのコミットの変更内容を別のブランチに移すことで対処できます

① どこのコミットまで変更内容を移したいのか確認する

# masterブランチのコミット履歴を1行で表示させます
$ git log --oneline master
813c118 (HEAD -> master) Add:  # 移動したい変更3
a7ba267 Add: 〇〇〇〇 # 移動したい変更2
3dfd56a Add: 〇〇〇〇 # 移動したい変更1
7dd0b34 (origin/master) Merge pull request 
2a3701c (origin/development) 
ede71f6 Add:〇〇〇〇
8a05884 Add: 〇〇〇〇
a1f10f4 first-commit

上記より、7dd0b34から上の3つのコミットが誤ってコミットしてしまった部分だと分かります。

② 変更の移動先となるブランチがまだ作成されていない場合は、移動先となるブランチを作成します

# ブランチ名を作成し移動
$ git checkout -b ブランチ名

# ブランチ一覧表示と今いるブランチの確認
$ git branch

git reset --hardコマンドでmasterブランチから移動したい変更のコミットを削除します

今回は、3つのコミットを移動させたいので、masterブランチから最新の3つ分のコミット履歴を削除します。ただし、git reset --hardを使う場合は、プッシュ前のコミットに対してのみ、使うようにしましょう。

  • HEADは、今自分がいるブランチの先頭のことで、自分が作業している場所を示すポインタです。
# masterブランチにいることを確認する
$ git branch
* master
  posts

# masterブランチで、git reset --hardを使います。
# masterブランチの指定した時点まで「HEAD(今自分がいるブランチの先頭)・ステージングエリア・作業ディレクトリ」のすべてが巻き戻されます。(今回は3つ前まで)
$ git reset --hard HEAD~3
HEAD is now at 7dd0b34 Merge pull request #1 from name/development

# masterブランチのコミット履歴を1行で表示
$ git log --oneline master 
7dd0b34 (HEAD -> master, origin/master) Merge pull request 
2a3701c (origin/development) Add:〇〇〇〇
ede71f6 Add:〇〇〇〇
8a05884 Add: 〇〇〇〇
a1f10f4 first-commit

これで、今いるmasterブランチの先頭がリモートリポジトリの内容をプルした部分まで巻き戻りました。

④ 移動したいブランチで作業の続きを行います

masterブランチのコミットは削除しましたが、移動したブランチ内ではコミット履歴が残っていることが確認できました。

# masterからpostブランチへ移動
$ git checkout posts

# ブランチ確認
$ git branch
* master
  posts

# postsブランチのコミット履歴を表示
$ git log --oneline posts 
813c118 (HEAD -> posts) Add: 〇〇〇〇 # 移動した変更3
a7ba267 Add: 〇〇〇〇 # 移動した変更2
3dfd56a Add: 〇〇〇〇 # 移動した変更1
7dd0b34 (origin/master, master) Merge pull request 
2a3701c (origin/development) Add:〇〇〇〇
ede71f6 Add:〇〇〇〇
8a05884 Add: 〇〇〇〇
a1f10f4 first-commit

参考

[Git]誤ったブランチで実施した変更を正しいブランチに移動する | DevelopersIO

第6話 git reset 3種類をどこよりもわかりやすい図解で解説!【連載】マンガでわかるGit ~コマンド編~ - itstaffing エンジニアスタイル