時々とおまわり

プログラミング雑記

Railsのform_withの使い方

Railsのform_withへの理解が曖昧だったので簡単にまとめました。

form_with の特徴

  • Rails5.1以降はform_tagform_for が非推奨になり、替わりにform_with が推奨になる
  • form_for 的な使い方、form_tag 的な使い方の2つの使い方ができる
  • オプションの() は省略可能
  • デフォルトのHTTPメソッドはPOST(他のHTTPメソッドも指定する可能)
  • remote: true がデフォルト(Ajaxでの非同期通信)

form_withまとめ | Rails Ambassador ~Railsの伝道師~

2つの使い方

from_withform_tag 的な使い方、form_for 的な使い方の2つの使い方ができる

  • form_tag 的な使い方: 関連モデル無し
  • form_for 的な使い方: 関連モデル有り

関連モデル無し(form_tag的)

指定したURLに対してパラメータを送信するフォームを作成する(DBにデータを保存しない)。

  • HTTPメソッドはPOST(デフォルト)
  • 送信したいURLはurl: で指定する
  • 送信されたパラメータはparams[:name] のような形で取得できる
# form_tagを使った書き方

<%= form_tag users_path do %>
  <%= text_field_tag :email %>
  <%= submit_tag %>
<% end %>
# form_withで書き換え

<%= form_with(url: users_path) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

関連モデル有り(form_for的)

指定したモデルオブジェクトを元にフォームを作成する(DBに関連した処理を行う)。

# form_forを使った書き方

<%= form_for @user do |form| %>
  <%= form.text_field :email %>
  <%= form.submit %>
<% end %>
# form_withで書き換え

<%= form_with(model: @user) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

指定したモデルオブジェクトのレコードの状態によって対応するコントローラーのアクション(create、update)を自動で切り替えてくれます。

Action View フォームヘルパー - Railsガイド

下記のルーティングが設定されている場合は次のような動作になります。

$ rails routes
         POST   /users(.:format)      users#create
         PATCH  /users/:id(.:format)  users#update
         PUT    /users/:id(.:format)  users#update
         DELETE /users/:id(.:format)  users#destroy
    user GET    /users/:id(.:format)  users#show
   users GET    /users(.:format)      users#index
new_user GET    /users/new(.:format)  users#new

パターン1: 指定したモデルオブジェクトが空の場合

下記のような新たに作成されたモデルオブジェクトを元にフォームを作成した場合はcreateアクション用のフォームが生成されます。

# app/controllers/users_controller.rb

def new
  @user = User.new
end
# app/views/users/new.html.erb

<%= form_with(model: @user) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>
# 生成されるHTML

<form action="/users" method="post">
  ...
</form>
  • 送信されるURL: /users
  • HTTPメソッド: POST
  • 実行されるアクション: users#create

パターン2: 指定したモデルオブジェクトが存在している場合

下記のように既存のモデルオブジェクトを元にフォームを作成した場合はupdateアクション用のフォームが生成されます。

# app/controllers/users_controller.rb

def edit
  @user = User.find(params[:id])
end
# app/views/users/edit.html.erb

<%= form_with(model: @user) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>
# 生成されるHTML

<form action="/users/update対象のid" method="post">
  <input type="hidden" name="_method" value="patch">
  ...
</form>
  • 送信されるURL: /users/update対象のid
  • HTTPメソッド: PATCH
  • 実行されるアクション: users#update
  • type=hidden で隠しinput要素が追加で生成され、value="patch" でデフォルトのHTTPメソッドのPOSTががPATCHに変更される

Action View フォームヘルパー - Railsガイド

まとめ

曖昧だったform_with の使い方が理解できたのでスッキリした。 フォーム周りは複雑なのでガンガン触って覚えていきたい。

参照

form_withまとめ | Rails Ambassador ~Railsの伝道師~

Action View フォームヘルパー - Railsガイド

【Rails】 form_withの使い方を徹底解説! | Pikawaka

gitでpush済みのコミットを取り消す方法

誤ってpushしたコミットを取り消す必要が出たのでやり方を調べました。

結論

push済みのコミットを取り消すにはgit resetgit revert を使う。

  • git reset: コミットを取り消す、強制pushする
  • git revert: 打ち消しコミットを作成する、追加pushする

コミットの指定方法について

git resetgit revert で使用するコミットの指定方法です。

  • HEAD: 最新のコミット
  • HEAD^: 直前のコミット
    • HEAD~も同じ意味
  • HEAD~n: 直前のコミット + n個分のコミットを指定
    • HEAD~3HEAD~{3}HEAD^^^HEAD~~~は同じ意味
  • git log で出力されるコミットIDで直接コミットを指定
  • HEAD@で代用できる
    • HEAD~@~ は同じ意味
    • HEAD~3@~3 は同じ意味
  • ~^で代用できるが微妙に意味が異なる点に注意する
  • zshの場合は^\^ と入力する
# コミットの指定例

# 直前のコミットを指定
$ git reset HEAD~

# 4つ前までのコミットを指定(HEAD + コミット数)
$ git reset HEAD~3

# コミットIDで指定
$ git log
...
commit e96db8f7687a33246509cb05913b5db2ecc9c5b0 # 指定するコミットID
Author: 
Date:   
...
$ git reset コミットID

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法 - Qiita

【やっとわかった!】gitのHEAD^とHEAD~の違い - Qiita

git reset

コミットを取り消すコマンドです。

Git - git-reset Documentation

  • 歴史改変になる点に注意する
  • コミット取り消し後の再push時は強制pushが必要

git resetのオプション

git reset で良く使うオプションです。

  • --soft: コミットのみ取り消し
  • --mixed: コミット、インデックスのみ取り消し(resetコマンドのデフォルト)
  • --hard: コミット、インデックス、ワーキングツリー全てを取り消し

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法 - Qiita

git resetを使ってpush済みのコミットを取り消す

例) push済みのコミットを取り消したい。

  • 直前のコミットのみ取り消したい
  • 作業途中なのでgit add した内容は残したい
# 直前のコミットの取り消し
$ git reset --soft HEAD~

# 強制push
$ git push -f

git resetを取り消す

git reset 自体を取り消したい場合はORIG_HEAD を指定する。

$ git reset --hard ORIG_HEAD

ORIG_HEAD とは最新の一つ手前のコミットに対するハッシュ値です。

GitのHEAD, ORIG_HEAD, FETCH_HEAD, MERGE_HEADとは? - Qiita

git revert

コミットを打ち消すコミットを作成するコマンドです。

Git - git-revert Documentation

  • コミットを修正したことが残るのでreset に比べたら安全
  • 不要なコミットが増えてしまう
  • コミット修正後のpushはコンフリクトしない

git revertを使ってコミットを取り消す

例) push済みのコミットを取り消したい。

  • 直前のコミットのみ取り消したい
  • 取り消しのコミットログを残したい
# 直前のコミットの打ち消しコミットを作成
$ git revert HEAD

# 追加push
$ git push

まとめ

思ったより複雑で理解するのに時間が掛かってしまった。 過去改変系の操作は全然覚えていないのでガンガン使って覚えていきたい。

参照

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法 - Qiita

【やっとわかった!】gitのHEAD^とHEAD~の違い - Qiita

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法 - Qiita

GitのHEAD, ORIG_HEAD, FETCH_HEAD, MERGE_HEADとは? - Qiita

Git - Documentation

フィヨルドブートキャンプに入会して1年経ちました

2022年11月5日でフィヨルドブートキャンプ(以下「FBC」)に入会して1年が経ちました。 今年も残り1ヶ月になったのでちょっと早いですが今年の振り返りをしておきます。

学習時間について

私の1年間の学習時間です。

  • 合計時間: 約950時間
  • 月平均: 約80時間
  • 週平均: 約20時間
  • 日平均: 約2.5時間

FBC入会当時の学習時間の目標が20h/週だったのでなんとかクリアしています。かなりどんぶり勘定ですが笑 一日の平均は今年の前半に比べて30分ぐらい減ってしまいました。 ただ、FBCの受講生の中でも学習時間は少ない方では無さそうです。

正直なところ、もっと学習時間を捻出できると思っていましたがプライベートが影響して学習時間を捻出できなかったことが悔しかったです。

ラクティスについて

今年1年で進めてきたプラクティスです。

今年の後半で進めることがプラクティスは次の3つです。

この3つのプラクティスはどれもとても難しかったですが、合格する度に自分の成長を感じられとても楽しく学習を継続することができました。

データベース

SQLPostgreSQL、データベース設計を学びました。 中でも「達人に学ぶDB設計 徹底指南書」と「楽々ERDレッスン」の2冊の技術書を読み、実際にDB設計を行うプラクティスが一番難しかったです。 なんとか作図したER図を提出できましたが、理解が曖昧な部分が多かったので結果的にメンターの方とのペアプロで指摘点を一緒に修正する中で理解を深めることができました。

コードを書く難しさとは別の難しさを感じましたが、ものごとの考え方など深い部分の学びが多く楽しかったです。

Webアプリケーション

RubyのwebフレームワークSinatraを使って簡単なメモアプリを作成しました。 Sinatraは使ったことが無かったですが、正直なところ「Railsを使う前の準備運動だろう」と軽く考えていましたが痛い目を見ました笑

RESTとマークアップに対する理解の甘さが露呈し、とても苦労しましたが結果的には成長を感じる事ができました。

CSSの苦手意識を克服するためにやったこと - 時々とおまわり

また、ディレクトリ・トラバーサルというセキュリティ問題についてもメンターの方に指摘していただきセキュリティへの理解も深まりました。

この頃からブログの投稿頻度が増え、自分なりの効率的な学習方法が分かってきた気がします。

ディレクトリ・トラバーサルとは - 時々とおまわり

Ruby on Rails

Ruby on Railsは独学で学んだことがあったので割とスムーズに進んだ方だと思います。 ただ、devise gemを使ったプラクティスは吸収する情報量が多くとても難しかったです。

理解したことを整理するためにブログの投稿はさらに増えましたが、ブログを投稿する時間と必要以上に知識を深堀りしてしまったことで学習時間を圧迫してしまったことは失敗だったなと反省しています。

Railsのフォームのsubmitをhelpers.submit.createで翻訳できる理由を調べた - 時々とおまわり

しかし、ポジティブに捉えるとソースコードを読む経験は後々生きてきそうなので結果的には良かったかもしれません。 この頃から公式ドキュメントを効率良く読むスキルの習得と読解力を鍛えたいと考えるようになりました。

良かったこと、悪かったこと

今年1年の学習での良かったこと、悪かったことです。

良かったこと

  • ポモドーロを導入した事による学習効率の向上
  • 技術ブログを継続できた
  • Discordを使ったオンラインコミュニケーションの経験
  • プログラマがどんな人たち、どんな仕事なのかイメージできた

ポモドーロを導入したことで集中力アップと時間的制限を設けて作業を進めるスキルを身に付けたことは個人的にはとても大きい収穫でした。

FBC入会後5ヶ月の振り返りで目標にしていた技術ブログも継続でき、文章を書く苦手意識を払拭できたことも自信に繋がりました。

また、独学の時に経験できなかったチャットツールでの情報交換は今では無くてはならないものになっています。

そして、今年1年FBCでの学習での一番の収穫はメンターの方とのコードレビューやペアプロの中でプログラマがどんな仕事なのかイメージできたことです。 プログラマとしての考え方、ものごとへの姿勢を働く前に学べたことはとても良かったと感じています。

悪かったこと

  • スケジュール通りに学習が進まなかった
  • 時間を意識することの大事さに気付くのが遅かった

FBC入会当初の予定では1年で卒業する予定でしたが大幅に遅れてしまいました。思った通りに進まなかった理由を自分なりに分析しました。

  • 詰まった時に無駄に深掘りし過ぎた
  • ブログ等のアウトプットの時間が増えすぎた
  • 完璧を目指そうとした
  • 質問するまでに時間を掛け過ぎた

特に時間を圧迫していたのは知識の深堀りです。 gemのソースコードを読むことに多くの時間を掛けていた私にメンターの方から次のようなアドバイスをして頂きました。

Railsを使った開発ではたくさんのgemを使うので、必ずしも実装レベルで全ての中身を理解する必要はないですし、実際現実的ではないと思います。それをしないといけないのであれば、そもそもgemを使わずに全て自分で実装するのと変わらないですからね 😅」

当たり前のことかもしれませんが、全てを理解しよう考えていた私にとっては目から鱗でした。 独学の時には曖昧な理解で学習を進めていたことが良くなかったと感じていたので、FBCに入会してからは全てについて深く理解しようとしていたことが逆に良くなかったと気付くことができました。

また、FBC入会当初は自分で解決しようとして質問するまでに時間が掛かってしまっていたことも良くなかったと感じています。 現在は学習で詰まった際は時間的な制限を設けて時間内に解決出来なかった場合はできるだけ早いだんかいで質問するスタンスで取り組んでいます。

これから

今回の振り返りを通して次のことを身に付けていきたいと考えています。

  • 技術書、ドキュメントの効率的な読み方
  • 課題解決を時間内に終わらせる力
  • 手を動かしながら理解する学習スキル
  • 会話力

技術書、ドキュメントの効率的な読み方

技術書やドキュメントを読むことにはだいぶ慣れましたが、読むこと自体に時間が掛かってしまうので短時間で必要な情報に辿り着くスキルが必要だと感じています。 「技術書」の読書術というぴったりな本を知ることができたのでまずはこの本を読もうと思います。

課題解決を時間内に終わらせる力

学習に時間を掛けること自体は悪いことではありませんが、仕事となるとそうも言ってられないので制限時間内にタスクを終わらせるスキルはとても重要だと感じました。

課題解決にかける制限時間を超えてしまった場合の行動を自分の中でしっかり決めて「質問」や「ペアプロ」を活用して良い意味で「周りの人に頼れるスキル」を身に付けていこうと考えています。

手を動かしながら理解する学習スキル

「ソフトウェアは壊れるぐらい触った方が理解できるかも」と最近感じています。 ソフトウェア開発は何度でも作り直すことができるので壊れるぐらい触ってフィードバックを元に理解をしていく方が早いと気付きました。

技術書やドキュメントを読むことももちろん大切ですが、今後は手を動かして触って理解することにも意識を向けていこうと思います。

思考を伝える会話力

FBCに入会しオンラインでのテキストコミュニケーションはだいぶ慣れましたが、ペアプロ等の直接会話するという行為が意外に多いことに気付きました。 これはプログラマとして働く上でも変わらないと思うので、自分の思考をしっかり相手に伝える会話力を身に付けたいと思っています。

FBC内のコミュニティ内での色々なボイスチャンネルへ参加し話す機会を増やすこと、話し方に関する本を探して読むことをやってみます。

まとめ

久しぶりに技術意外のことを書いてみましたが定期的に書くことで気持ちを切り替えられるので今後も続けようと思います。 卒業までもうしばらく掛かりそうですが、来年こそはプログラマとして働けるように今年以上に頑張っていきたいと思います!

Railsでロールバックしてマイグレーションを修正する

FBCRailsを使ったユーザーフォローのプラクティスを進めています。 間違ったマイグレーションロールバックして修正する必要性が出たのでやり方を調べました。

結論

下記手順でDBをロールバックできる。

  1. マイグレーションのバージョン確認
  2. rollback する
  3. マイグレーションファイルを修正
  4. migrate する

実際の手順

上記手順を元にマイグレーションロールバックしてDBの構造を修正する手順です。

1. マイグレーションのバージョン確認

修正前の状態を確認します。

$ rails db:version
Running via Spring preloader in process 65752
Current version: 20221110192153 # ←修正したいマイグレーション

$ rails db:migrate:status
Running via Spring preloader in process 65819

database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   ...
   up     20221110192153  Create friendships # ←修正したいマイグレーション

2. rollbackする

1つ前のマイグレーションrollback します。

$ rails db:rollback
Running via Spring preloader in process 65920
== 20221110192153 CreateFriendships: reverting ================================
# ロールバックの詳細が表示される
== 20221110192153 CreateFriendships: reverted (0.0049s) =======================

rollback したマイグレーションの状態を再度確認します。

$ rails db:version
Running via Spring preloader in process 66000
Current version: 20210531231050 # 1つ前のマイグレーションになっている

$ rails db:migrate:status
Running via Spring preloader in process 66067

database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   ...
   up     20210531231050  Remove omniauth columns from users # 1つ前のマイグレーション
  down    20221110192153  Create friendships # downになっているので正常にロールバックできている

ちなみに1つ以上前のマイグレーションrollback する場合は次のようにバージョンを指定できます。

# 1つ前のバージョンに戻す
$ rails db:rollback

# 任意のバージョンに戻す(number+1 のバージョンまで戻す)
$ rails db:rollback STEP=number

【Rails】$rails db:rollbackしたい時の間違えない手順 - Qiita

3. マイグレーションファイルを修正

下記の方法でマイグレーションファイルを修正します。

今回は作成済みのマイグレーションファイルを修正しました。

4. 再migrateする

修正したマイグレーションファイルを元に再migrate します。

$ rails db:migrate
Running via Spring preloader in process 67368
== 20221110192153 CreateFriendships: migrating ================================
# 新たなマイグレーションの詳細が表示されます
== 20221110192153 CreateFriendships: migrated (0.0026s) =======================

migrate した状態を確認します。 今回は作成済みのマイグレーションファイルを修正してmigrate したのでバージョンは変更されていません。

$ rails db:version
Running via Spring preloader in process 67451
Current version: 20221110192153 # 再migrateしたバージョン、今回はバージョン自体に変更なし

$ rails db:migrate:status
Running via Spring preloader in process 67520

database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   ...
   up     20210531231050  Remove omniauth columns from users # 1つ前のバージョン
   up     20221110192153  Create friendships # 再migrateしたバージョン、upになっていればok

rails db:migrate:statusStatus が全てup になっていれば正しくmigrate できています。

上記手順でRailsロールバックしてマイグレーションを修正することができました。

注意点

Railsロールバックを行う際には次のことに注意した方が良さそうです。

# rollback したままだと発生するエラー
ActiveRecord::PendingMigrationError - Migrations are pending. To resolve this issue, run:

        bin/rails db:migrate RAILS_ENV=development

まとめ

ロールバックについて復習できて良かった。 ロールバックしないように実装前にちゃんと設計する大事さも再確認できました。

参照

【Rails】$rails db:rollbackしたい時の間違えない手順 - Qiita

rails db:rollbackって一つずつしか差し戻せないんだよって話 - Qiita

git stashの使い方

gitで管理している作業を並行して進める事が多くなってきたのでgit stash の使い方をまとめておきます。

git stashとは?

stashを使用すると、コミットしていない変更を退避することができます。 stashで現在の変更を退避して、今すぐやりたい作業をして、退避させていた変更を戻して作業を再開することができます。

【git stash】コミットはせずに変更を退避したいとき - Qiita

ざっくりですがstash コマンドは次の2つができます。 コミット前の段階でブランチを切替えたい時などに便利です。

  • ファイルの変更を一時的に退避する
  • 一時的に退避させた変更を復元する

git stashの使い方

stash で良く使うコマンドです。

  • git stash: インデックスに追加(git add 済み)されたファイルの変更を退避
  • git stash -u: 未追跡のファイル(git add されていない)、無視されたファイルも含めて変更を退避
  • git stash list: 退避させた変更のリストを表示
  • git stash apply stash@{}: 退避した変更を現在のブランチに復元
  • git stash drop stash@{}: 退避させたリストを削除
  • git stash pop stash@{}: 退避から変更を復元、退避リストを削除

git stashの使用例

作業途中で別ブランチに切り替えて作業後、元のブランチに戻って作業を再開したい。

この場合の作業の流れは次のようになります。

  1. 変更を退避
  2. 別ブランチに切替え
  3. 別ブランチで作業
  4. 元ブランチに切替え
  5. 退避した変更リストを確認
  6. 退避した変更リストを元ブランチに復元
  7. 元ブランチでの作業再開
  8. 復元した変更リストを削除

上記作業を行う際のコマンドの流れは下記のようになります。

$ git stash # 変更を退避
$ git switch 別ブランチ名 # 別ブランチに切替え

# 別ブランチでの作業

$ git switch 元ブランチ名 # 元ブランチに切替え
$ git stash list # 退避した変更リストを確認
$ git stash apply stash@{} # 退避した変更リストを元ブランチに復元

# 元ブランチで作業を再開

$ git stash drop stash@{} # 復元した変更リストを削除

上記の流れで複数のブランチを切替えての作業ができるようになります。

まとめ

今後、使う頻度はどんどん上がっていくと思うのでstashの使い方に早めに慣れていきたい。

参照

【git stash】コミットはせずに変更を退避したいとき - Qiita

Git - git-stash Documentation