【Rails】コメント機能の実装

テックキャンプ74日目、やっと実装課題終わりました。
でもそこからまた数日経ち、いつの間にか残り100日です。
焦る。

今日は実装課題のコメント機能で躓いていたことを思い出しながら記事を書いてみようと思います。

今回の実装課題

ある投稿に対してコメント機能があるサイトの作成。
コメントは投稿の詳細ページから閲覧、投稿が可能。

コメントが成功した場合は、投稿の詳細ページにリダイレクトする。
コメントが失敗した場合は、投稿の詳細ページに戻る(viewファイルにそのまま戻る)。

実装手順

投稿(prototype)の詳細ページを見るために、showアクションで定義。 prototypes_controller.rb

def show

    @prototype = Prototype.find(params[:id])
    @comment = Comment.new
    @comments = @prototype.comments.includes(:prototype)

end
  1. 詳細を表示したい投稿内容をビューに渡すために@prototype に代入。
  2. @comment = Comment.newという空のインスタンスを生成。そうすると、form_withを使用してcommentsコントローラーのcreateアクションにパラメーターとして送られる。
  3. @commentsに@prototypeに結びついた全てのコメント情報とそれに結びつくユーザー情報を代入。

prototypes/show.html.erb

<div class="prototype__comments">
        <% if user_signed_in? %>
          <%= form_with model: [@prototype, @comment],local: true do |f|%>
            <div class="field">
              <%= f.label :content, "コメント" %>
              <%= f.text_field :content, id:"comment_content" %>
            </div>
            <div class="actions">
              <%= f.submit "送信する", class: :form__btn  %>
            </div>
          <% end %>
        <% end %>
        <ul class="comments_lists">
          <% @comments.each do |comment| %>
          <li class="comments_list">
            <%= link_to comment.user.name, user_path(comment.user_id), class: :comment_user %>
            <%= comment.content %>
          </li>
          <% end %>
        </ul>
      </div>
ここでなんでform_withでモデルを2つ渡すの?という疑問。

commentsコントローラーのルーティングを確認すると、

routes.rb

resources :prototypes do
    resources :comments, only: :create

rails routes (ターミナル)

prototype_comments POST   /prototypes/:prototype_id/comments(.:format)                                                      comments#create

上記のように、プロトタイプのidがないとcommentsコントローラーのcreateアクションにパラーメーターが送られないよ〜とわかる。
これは、プロトタイプとコメントのルーティングをネストしているためであり、 コメントを保存したい場合、form_withに2つの引数(親クラスと子クラスの順)を指定する必要があります。

子クラスが空かご健在かでcreateかupdateに飛ぶという感じでしょうか。(今回はcreateのみ実装)

@commentsの表示方法

@commentsをそのまま

<%= @comments %>

と記載すると、@commentsの中身が複数あるからダメだよ。ってなりました。
複数あるものはeachでひとつずつ取り出そうと思い、コメント一覧にeachを使用しました。

コメントが成功した場合/失敗した場合

comments_controller.rb

def create
    @comment = Comment.new(comment_params)

    if @comment.save
      redirect_to prototype_path(@comment.prototype)
    else
      @prototype = @comment.prototype
      @comments = @prototype.comments
      render "prototypes/show"
    end

private

def comment_params
    params.require(:comment).permit(:content).merge(user_id: current_user.id, prototype_id: params[:prototype_id])
end
  1. @commentにストロングパラメーターを用いて、送られてきたパラメーターを代入。
  2. もし保存できた場合は、プロトタイプの詳細ページへいく。(リダイレクト)
  3. もし保存できなかった場合は、詳細ページのviewに戻る。 (render)

renderメソッド

あるビューをユーザーに提供するために使うメソッド。
renderメソッドは特定のアクションに対応したビューをユーザーに提供するだけであり、指定したビューに対応するアクションは実行しない。
元のインスタンス変数の値が上書きされず、何か入力していた場合は、それらを保持したままビューに戻る。

redirect_toメソッド

ブラウザに別のURLでHTTPリクエストを送って欲しいと指示するメソッド。
新たなリクエストを送信されたときと同じ動きになるので、再度コントローラーを経由してビューが表示される。
元のインスタンス変数の値が上書きされる。

使い分け

ビューを返すだけならrenderメソッドでよい。
アクション終了後にあるアクションを実行したい場合や、他のサイトへ接続したい場合にはredirect_toメソッドを使用。

なぜインスタンス変数@prototypeと@commentsを定義するのかという疑問

renderだとviewファイルにそのまま戻るので、戻るときにインスタンス変数を使わないと情報を渡すことができない。
そのため、ここでも行き先のviewファイルに渡すインスタンス変数@を定義する必要がある。
@prototype →@commentが存在するprototype
@comments→@prototypeにされたcommentsを全て取得
⚠️アソシエーションの定義によって単数形、複数形に注意。

ストロングパラメーターのmergeのところがよくわからんから深掘りしてみた

mergeメソッド
フォーム外のデータを安全に追加するために使用。
ちなみに、permitはフォームからのデータを許可するために使用。

今回のcurrent_user.idはdeviseのGemを導入しているため使用できます。
prototype_id: params[:prototype_id]で値を入れることができているのは、ルーティングでcommentsをネストしているためです。
コメントを入力するときに自分のidやそのコメント先のプロトタイプ名を直接入力することはないけど、
パラメーターに含めてDBに保存したいな。というときに、
mergeメソッドを使って、パラメーターに含めたいキーと値を記述できます。

まとめ

まとまりのない記事になってしまいましたが。。。

ネストしているときの注意点や、renderを使用した場合に再度変数を渡してあげる必要があることなどなど。
コメント機能1つで改めて学習できたなと感じました。
間違ったことも書いてあるかもしれませんので、何かあれば教えてください!

【Rails】deviseでユーザー登録ができない...「encrypted_password」の正体を暴いた!

朝活でカリキュラムを少しずつ進めておりますが、
実装課題でのユーザー登録のところで 、フォームに入力してもデータベースに保存されないエラーで躓きました。
自分の実装と解決後の学びをまとめようと思います。

やったこと

①deviseをインストール
②Userモデルを作成
マイグレーションファイルにカラムを追加
④form_withでf.labelとf.text_fieldにusersテーブルのカラム名を入れてフォームを作成
⑤deviseのコントローラーにストロングパラメーターを反映

ここまで実装して動かしてみると、なぜかユーザー登録ができない。

Processing by Devise::RegistrationsController#create as TURBO_STREAM
  Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録"}
Unpermitted parameter: :encrypted_password. Context: { controller: Devise::RegistrationsController, action: create, request: #<ActionDispatch::Request:0x0000000107068350>, params: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録", "controller"=>"devise/registrations", "action"=>"create"} }
  TRANSACTION (0.2ms)  BEGIN
  User Exists? (1.2ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'ddd@ddd' LIMIT 1
  TRANSACTION (0.2ms)  ROLLBACK

emailの後にROLLBACKされている様子。
"encrypted_password"=>"[FILTERED]"となっており、パスワードあたりが怪しいのかな?と疑う。

確認したこと

モデルで変な制約をかけていないか

設計書通り記載。

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name,  presence: true
  validates :profile, presence: true
  validates :occupation,  presence: true
  validates :position, presence: true
end

migrationファイルの制約

設計書通り記載。

2024XXXXXXXXXX_devise_create_users

class DeviseCreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :name,               null: false
      t.string :profile,            null: false
      t.text :occupation,           null: false
      t.text :position,             null: false
# 省略

ストロングパラメーター

deviseはストロングパラメーターが使用できないので、代わりにdevise_parameter_sanitizerを使用。

自分で新しく追加したカラムを指定。
emailとpasswordについてはdeviseのデフォルトであるため記載不要。

application_controller.rb

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile, :occupation, :position])
  end
end

passwordのカラム名

new.html.erb

<%= form_with model: @user, url: user_registration_path, local: true do |f| %>

#省略

<div class="field">
<%= f.label :encrypted_password, "パスワード(6文字以上)" %><br />
<%= f.password_field :encrypted_password, id:"user_password", autocomplete: "new-password" %>
</div>

<div class="field">
<%= f.label :password_confirmation, "パスワード再入力" %><br />
<%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %>
</div>

#省略

<% end %>

初めはストロングパラメーターで「:encrypted_password」も許可したらいいのか?
とかも思ったのですが、emailとpasswordについては元々deviseの機能として渡す機能はあるし、なんか気持ち悪いと思い・・・
deviseにおける新規登録やログイン機能について少し深掘りしてみることにしました。

encrypted_password

そもそも、encrypted_passwordとは、暗号化されたパスワードを意味しているらしい。
encrypted_passwordは、deviseを用いて作られるusersテーブルにあるカラムですが、
このencrypted_passwordはあくまで暗号化されたパスワードなので、暗号化されるまでは使えないカラム。

新規登録やログイン画面でパスワードはpasswordとして入力された値を受け取りdeviseへ送信する必要があるらしい。

passwordとpassword_confimationが同じかどうかをチェックし、
同じ(true)ならばencryped_passwordに暗号化した文字列を返すという機能がdeviseに実装されているそう。

なので、自分のコードを下記に書き換えてみました。

new.html.erb

<%= form_with model: @user, url: user_registration_path, local: true do |f| %>

#省略

<div class="field">
<%= f.label :password, "パスワード(6文字以上)" %><br />
<%= f.password_field :password, id:"user_password", autocomplete: "new-password" %>
</div>

<div class="field">
<%= f.label :password_confirmation, "パスワード再入力" %><br />
<%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %>
</div>

#省略

そうすると、データーベースにしっかりと保存ができるようになりました。

まとめ

上記より、今回私がdeviseを用いてユーザー登録の実装をする際にエラーになってしまった原因は、

deviseを用いて作られるカラム、「encrypted_password」が、暗号化されたパスワードであり、
formで入力したパスワードが暗号化されていなかったことが原因でした。
パスワードは新規登録やログイン画面でpasswordとして入力された値を受け取りdeviseへ送信します。
deviseには passwordとpassword_confimationが同じかどうかをチェックし、同じならばencryped_passwordに暗号化した文字列を返すという機能が実装されているので、
この機能を利用し、encrypted_passwordが暗号化され使用できるようになった感じと理解しました。

簡単に書きすぎてしまいましたが、
devise独特な?ルールやメソッドなどもあるので、しっかりと復習しようと思います。

【Ruby】2つの文字列の一致・不一致問題

今日のRubyドリルでの自分の脳内処理を残したくなったので、記事を書いてみることにしました。

練習問題

2つの文字列の末尾の文字を比較して、一致する場合はTrue、一致しない場合はFalseを出力するプログラムの実装。
【条件】
・メソッドの引数に、任意の2つの文字列を指定する。
・引数に指定された2つの文字列のうち、どちらかがもう一方の文字列の末尾にある場合は、Trueを出力する
・上記を満たせていない場合は、Falseを出力する
・入力された文字が大文字でも小文字でも、同一の文字として処理を行う

私の考え

①文字を大文字か小文字どちから一方に揃える

まず条件を見て、大文字でも小文字でも同一の文字として処理するために、文字を大文字か小文字どちから一方に揃える必要があると考えました。
今回が2つの文字列を全て小文字に揃えることとし、downcaseメソッドを使用しました。

downcaseメソッド

文字列に含まれる大文字を小文字に変換したいときに使用するメソッド。

使い方

文字列.downcase

str = "Hello World"
puts str.downcase

出力結果
=> hello world

ちなみにupcaseメソッドで大文字に揃える方法でもよいのかなと思いました。(使い方はdowncaseメソッドと同じ)

2つの文字列を a,bとし、小文字に変換した文字列を下記のように変数に代入しました。

a_down = a.downcase 
b_down = b.downcase

②どちらか一方がもう一方の文字列の末尾に含まれるかを確認する

次に、どちから一方がどちらか一方の文字列の末尾に含まれるかを確認するために、
2つの文字列の長さを比較して、長い方の末尾から短い方の文字数分文字を切り取り、
その切り取った末尾の文字列と、短い方の文字列が一致するかの判定をしたらいいのかなと思いました。

※初めはinclude?メソッドを使おうかと思ったのですが、
含まれることを確認できても、「末尾に含まれる」という部分が難しくなるのかなと思い却下。

文字数の比較

2つの文字列がそれぞれ何文字あるのか数えるためにlengthメソッドを使用しました。

lengthメソッド

配列に対して使用すると、配列の要素の数を返す。
文字列対して使用すると、文字の数を返す。

使い方

配列or文字列.length

array  = ["red","blue","yellow"]
str = "たろぽよた"

array.length
=> 3

str.length
=> 5

2つの文字列を a,bとし、それぞれの文字数を下記のように変数に代入しました。

a_length = a.length
b_length = b.length

2つの文字列の長さを比較して条件分離を作ってみました。

if a_length >= b_length
 
else
  
end

a >= b、それ以外はa<bって感じです。
a = bはどっちに含めてもいいかなと思い、1つ目の条件式に入れてみました。

文字の末尾を切り取る

文字列の末尾を切り取るために、sliceメソッドを使いました。

sliceメソッド

配列や文字列から指定した要素を取り出すことができるメソッド。
要素を指定する際は数値で指定。
⚠️先頭は0からはじまる!

使い方

配列or文字列.slice(指定したい要素の番号)

array = [0, 1, 2, 3, 4, 5]
array.slice(1)

出力結果
=>1

sliceは範囲演算子(..)で取り出したい範囲を指定することができます。

今回の問題の場合、
a_length >= b_length の場合は、aの末尾からbの文字数分切り取りたいので、

a_down.slice(-(b_length)..-1)

それ以外(a_length < b_length)の場合は、bの末尾からaの文字数分切りたいので、

b_down.slice(-(a_length)..-1)

と、記載してみました。

長い方の文字列の末尾から切り取った文字列と、短い方の文字列が一致するかの判定をする

先ほどの条件式に、上記に書いたものをあてはめてみました。

if a_length >= b_length
    b_down == (a_down.slice(-(b_length)..-1))
  else
    a_down == (b_down.slice(-(a_length)..-1))
  end

完成形

def end_other(a, b)
  # 処理を記述
  a_down = a.downcase
  b_down = b.downcase
  a_length = a.length
  b_length = b.length

  if a_length >= b_length
    puts b_down == (a_down.slice(-(b_length)..-1))
  else
    puts a_down == (b_down.slice(-(a_length)..-1))
  end
end

# 呼び出し例
end_other('POYOTA', 'poyo') 
end_other('TaRo', 'poYotAro') 
end_other("KoromAn", "KoRo") 

出力結果
=>
false
true
false

想定の結果がでました。

でも細かいことを言うと、問題文は「True」「False」の表記になっているため、
指示通り忠実に。ということを考慮し、下記のように付け加えてみました。

def end_other(a, b)
  a_down = a.downcase
  b_down = b.downcase
  a_length = a.length
  b_length = b.length

  if a_length >= b_length
    result = b_down == (a_down.slice(-(b_length)..-1))
  else
    result = a_down == (b_down.slice(-(a_length)..-1))
  end

  if result
    puts "True"
  else
    puts "False"
  end
end

# 呼び出し例
end_other('POYOTA', 'poyo') 
end_other('TaRo', 'poYotAro') 
end_other("KoromAn", "KoRo") 

出力結果
=>
False
True
False

大事なことは、問題文の意図をしっかり理解することだとは思うで、
判定出せたらそれでよいのかなと思いつつも...
問題をよく読む練習として実装してみました。

模範回答

def end_other(a, b)
  a_down = a.downcase
  b_down = b.downcase
  a_len = a_down.length
  b_len = b_down.length
  if b_down.slice(-(a_len)..- 1) == a_down || a_down.slice(-(b_len)..- 1) == b_down
    puts "True"
  else
    puts "False"
  end
end

とってもシンプル!! またもや自分のコードはややこしいと思いました。

自分のコードのいいところ1個でもないかな... と考えてみました。
ちょっと強引かもしれませんが、
if文の中に||演算子を使用すると、真偽判定のために左右両方の処理をしなければいけないのに対し、
if文の中を比較演算子のみにすることで、不必要な計算がされず、処理が速くなるのかな ?と思いました。

おまけ

本日キャッチアップゼミに参加し、
他の受講者さんの貴重なご意見をたくさん聞けて、モチベーションが上がりました!
私も積極的にコミュニティを活用して、皆様のアウトプットをもっと吸収したいと思いました。

【Ruby】メソッドを機能別に分解することのメリットとは

今日は本日の朝活で考えたことについて書こうと思います☕️

練習問題

ECサイトのポイント付与サービスを考える問題。

1000円以上でポイント還元率が変化(3%→5%)し、お誕生日の場合はポイントが5倍になるというプログラム。
お誕生日の場合はtrue, そうでない場合はfalseを指定してメソッドを呼び出す。
全てのポイントの計算が終わったら、小数点以下は切り捨てる。

私の考え

ここで私は

① ポイント還元率の条件式メソッド
② ①を利用してお誕生日かどうかで最終的なポイントを計算するメソッド

この2つが必要と考えました。

上記を踏まえて、
商品が10,000円で、お誕生日ではなかった場合の実装をしてみました。

def point_return_rate(amount)
  if amount <= 999
    point = amount * 0.03
  else
    point = amount * 0.05
  end
  return point 
end

def calculate_points(amount, is_birthday)
  if is_birthday
    puts "ポイントは#{(point_return_rate(amount) * 5).floor}点です"
  else
    puts "ポイントは#{(point_return_rate(amount)).floor}点です"
  end
end

# 呼び出し例
calculate_points(10000, false)

まず、「999円以下の場合は、商品のお値段に対して3%、1000円以上は5%のポイントが付与されるよ〜」という「ポイント還元率メソッド(point_return_rate)」を定義しました。
ここでは1,000円以上という部分をelseでまとめてます。
メソッドの命名がちょっと分かりにくいのも気になりますが、一旦このまま書きます。
※一瞬、ここをamount <= 999と1000 < amountと分岐しようとしてしまい、ハッとなりました・・・!

この問題では、「最終的に何ポイント獲得できるか」を出力したいので、
先ほど定義したポイント「ポイント還元率メソッド(point_return_rate)」を、「お誕生日かどうか判定して計算するメソッド(calculate_points)」に埋め込みました。

しかし、この問題の模範回答では
calculate_pointsというメソッド内に、私が思っていた①②の条件式を2つ入れて実装していました。

def calculate_points(amount, is_birthday)
  if amount <= 999
    point = amount * 0.03
  else
    point = amount * 0.05
  end
  if is_birthday
    point = point * 5
  end
  puts "ポイントは#{point.floor}点です"
end

とてもシンプル!
でも私が実装したプログラムもちゃんと動くし、頑張って考えて実装したからメリット1つでも見つけてあげたい...

と思い、メソッドを機能別に分解することのメリットを考えてみることにしました。

メソッドを機能別に分解することのメリット

今回の問題で考えてみた場合、
お誕生日だけではなく、別の条件でもポイントをサービスしたい!みたいなのがどんどん思いついた時に、
「ポイント還元率メソッド(point_return_rate)」をいろんな所で使いまわせるのかなあと思いました。
また、定義しておけば「これなんだ?」って思った際に、定義されたメソッドのところに戻れば中身を確認しやすいのかなとも思いました。

素人が絞り出したメリットなので、考え方として当っているかは分かりません...
色々な人から意見をいただけたら嬉しいです!

おまけ

今週はじめにお子がRS肺炎で入院。
日中は面会(夜間付き添い不可)、仕事で日中の面会が厳しい時は仕事の前後で面会。
朝活と面会後体力があれば夜に学習を進めておりました。
学習できて2時間/日くらい?(お子が心配すぎてあんまり頭が働いてませんでしたが...)

そんな不安たっぷりな1週間でしたが明日退院が決まり安心👏
辛い入院生活と治療を耐えたお子、よく頑張った!
母も学習頑張ろう!と思いました。

【Ruby】include?メソッドとall?メソッドの組み合わせ

毎日こつこつ取り組んでいるRubyドリル(全70問)も 気がついたら2周目中盤です。

今日は本日の朝活で発見した 「include?メソッドとall?メソッドを組み合わせた実装」 について書こうと思います。

include?メソッド

文字列や配列の要素に含まれる文字列に対して、指定した文字列が存在するか確認するときに使用する。

指定した文字列が

存在した場合はtrue

存在しない場合はfalse

をそれぞれ返す。

include?の書き方

オブジェクト名.include?("検索文字列")

⚠️Rubyには文字列を検索するinclude?の他にモジュールを呼び出すincludeがありますが、

本日は文字列を検索する方について書こうと思います。

本日の練習問題

include?を使って配列内に指定の数字が全て含まれているか調べるプログラムの実装。

  • 配列内に1,2,3が全て入っている場合は、「True」と出力

  • 配列内に1,2,3の全てが入っていない場合は「False」と出力

ここで私、下記のようにinclude?の()内に複数の要素を指定。

def array123(nums)
  if nums.include?(1, 2, 3)
    puts "True"
  else
    puts "False"
  end
end

array123([1, 1, 2, 3, 1])

すると、下記のエラーが返ってきました。

`include?': wrong number of arguments (given 3, expected 1) (ArgumentError)

このエラー文について読み解くと、

ArgumentError

これは「引数の数が合わないよー」というメッセージ。

エラーのカリキュラムで学習していたので、ここは理解。

wrong number of arguments (given 3, expected 1)

引数で求めている数が本来は1個(expected)なのに、3個与えられている(given)よ〜。

ここもエラーのカリキュラムで学習済み。

どうやらinclude?は「1つの要素」しか指定することができず、要素を複数指定することはできないらしい。

今回の問題の場合は、1, 2, 3全ての要素を指定する必要があるため、

include?のみを使用する場合は、&&演算子を複数使用する必要があるよう。

def array123(nums)
  if nums.include?(1) && nums.include?(2) && nums.include?(3)
    puts "True"
  else
    puts "False"
  end
end

array123([1, 1, 2, 3, 1])

私個人の感想としては、

「&&演算子を複数使用すると、見にくい…めんどくさい…」

この感想をなんとか解消するために、なんかまとめて打ち込んで返してもらえる方法ないかな〜と探していたら…

all?メソッドに遭遇!

all?メソッド

配列の要素を繰り返し実行し、ブロックの返り値が全て真の場合にtrueを返すメソッド。

all?の書き方

オブジェクト.all? { |要素| ブロック処理}

今回の練習問題みたいに3つの要素だけならまだ演算子を複数使うのでもいいと思うのですが、

存在を調べたい要素がたくさん合った場合とか、

配列の中に指定した要素より大きい値が存在するかとか、

そういう時にはこっちの方がスッキリしそうじゃない? これ使ってみよう!

ということで、書いてみました。

def array123(nums)
  array = [1, 2, 3]
  if array.all?{
      |n| nums.include?(n)
    }
    puts "True"
  else
    puts "False"
  end
end

array123([1, 1, 2, 3, 1])

探したい要素をarrayにお渡しして、

array.all?でarrayの中身全部入ってますか〜?という感じです。

ブロックの引数nに配列(array)の要素([1, 2, 3])を順番に格納しながら、

繰り返し条件式(nums.include?)を実行し、条件式が偽になった時点で処理を中断してfalseを返す仕組みです。

挙動を確認したら、しっかり動いてくれました。

1周目のRubyドリルでは模範回答を見て、こんなもん。って思ってましたが、

カリキュラムを進めながら、2周目のRubyドリルに取り組むと、

自分だったらこうした方が楽だなとか、

他の人が見る時こっちの方が見やすいのでは?とか、

いろんな視点を持って取り組むことができている気がします。

もちろん未経験の立場での意見なので、 これが実は良くないコーディング…とかもあるかもしれません。

どんどん質問して添削してもらおうと思います!

プログラミング初心者の挑戦記

はじめまして。

未経験からプログラミング勉強を始めるにあたり、

アウトプットや勉強している時の気持ちを記録に残せたらいいなと思い、

はてなブログを始めてみることにしました。

 

自己紹介

・プログラミング開始時年齢 31歳

・看護師7年(腫瘍内科→オペ室/救急)

 →医療関連の会社員1年

 →クリニックに転職(オペ看/フルタイム/現在2年目)

・1歳半育児中

 

プログラミングの勉強を始めたきっかけ

夫がフルリモートのSE。

夫のフルリモートに頼りきっており、自分が保育園の送迎ができず、自分が仕事から帰宅すると、夫と息子は夕飯を食べ始めている日々。

土曜日も自分が働いており、家族の時間は日曜日のみ。

自分も在宅でできる仕事をして、家族の帰宅を迎えたいな…と悩んでいた時に、

未経験でもエンジニア転職にチャレンジしている人たくさんいるよ!と、

夫に教えてもらったことがきっかけです。

 

看護師もやりがいがあり楽しい仕事。

エンジニアが楽だとは思っていない。

そもそもプログラミングが自分に合っているかもわからない。

エラー対応をしている夫を見て、エンジニアって大変。

などなど。。。

挙げたらキリがありませんが、未経験の業界にチャレンジすることに不安もありました。

 

しかし、今は生活はできているだけで、

本当に働きたい形なのかな?とか考えた時に、

ワークライフバランス」、「貢献性」が自分の軸なのでは?と思うようになってきました。

看護師という職でその条件を叶えてくれる仕事を探すことも手段だったとは思います。

しかし、看護師は出勤が必須。

家族との時間も大切にするならば、少しでも在宅ワークができた方が、自分の中で理想の働き形になるのでは?と考えました。

 

そんな時、医療従事者の経験を活かしてエンジニアサイドから医療に貢献できる仕事もあるという経験談を読みました。

私生活では在宅ワークで通勤時間を削減することで、家族との時間を少しでも長くしつつ、

仕事の現場では医療従事者経験の視点から、こういうのあったらいいなと考えられる。

私がやりたいのはこれなのかもしれない…!と思い、

プログラミング学習を始めることに決めました。

 

エンジニアになれたらすぐに理想の働き方ができるわけではなく、

未経験エンジニアから経験エンジニアになるための経験を積む必要があり、

大変な時期もあることは重々承知です。

それくらい覚悟を決めてのチャレンジです!

 

プログラミングを始めてから〜現在まで

2023年08月末〜

現役エンジニアの夫は独学でプログラミング学習をしたとのことで、

自分もProgate、Udemyなどを使って学習を開始しました。

しかし、書いてある通りに書いて、わかった気になってしまっているような日々が続き、

これでどうやって自分の作りたいものを作るのかな?

なかなか点と点がつながらない。。。

と、モチベーションが上がらない日々。

その後、数ヶ月学習をやめてしまった時期が続きました。(挫折!)

 

でもこのままでいいのか?と諦めきれない自分もおり、学習方法を見直しました。

子育てや仕事もあり、無知な自分では質の良い学習ができないと思い、

短期間集中して学習の道筋を立ててくれるサービスが自分にあっていると感じました。

 

2024年01月末〜

カウンセリング等を受けたのちに、TECH CAMP受講開始。

現在2ヶ月(6ヶ月中)経過しました。

正直、独学で悩んでいる期間があったなら、もっと早く受講すればよかった!

と、思ってしまうくらい、とっても素敵なプログラミングスクールだと思います。

ターミナルって何?プログラミング言語って何?と、予備知識皆無の私でも、

受講開始2ヶ月経った今、少しずつコードが書けるようになり、

自分で思い通りの挙動になるコードが書けるととても達成感を感じます。

もちろんエラーに出会ってむしゃくしゃすることもたくさんあります。

そういう時はこの経験も自分の学習のため!と言い聞かせて日々頑張ってます。

 

1週間の学習スケジュール

休日1日

学習日(TECH CAMPの時間割学習日)8時間

平日1日休みがあるので、お子が保育園に行っている間が勝負です!

勤務日

お子が起床するまでの朝2時間程度(5:00〜7:00)

TECH CAMPのドリル学習やカリキュラムを少しずつ進めています。

夜はお子の就寝時間にもよりますが、少しできたらいいなくらいでとどめてます。

朝活の方が集中力もあるので、夜更かしはしないようにしています。

 

こんな感じの生活を2ヶ月ほど続けてますが、

週1回のライフコーチさんとの面談や、他の受講者さんのSlackでのアウトプットのおかげで、自分も頑張ろう!とモチベーションも保てています。

こんなに勉強毎日するのいつぶりかな?もしかして初めてかも。

という感じですが、苦痛なく無理なく取り組めています。

日常的に夫に言葉でoutputするようにして、自分の理解度の確認をしています。

毎日聞いてくれる夫には感謝でいっぱいです。

 

最後に

長くなってしまいましたが、

今後このはてなブログには学習のアウトプット、

時々学習中の気持ちを記録していこうと思います。

同じような気持ちの方の役に立てたらいいな〜