TECH EXPER 25日目

TECH::EXPERT

今日は新しい方の建物で勉強。
ガラス張りなので窓際は暑い。
応用カリキュラム以降はこっちで勉強するように促されたのもあって異常に人が多い。アジアビルの方が少ないので楽だ。
私はフォンティスが近いし、このビルで勉強する意味はないな。
土足だし長時間戦う場所じゃない。

ruby

attr_accessor, attr_reader, attr_writer

attr_accessor ゲッターとセッター
attr_reader ゲッターのみ
attr_writer セッターのみ

rails

buildとnew

同じ機能。昔は違ったようだがver4以降では特に差はない。
かつてbuildはアソシエーションありのモデルに使われていたみたい。

form_with

Rails5.1からはform_tagとform_forはform_withメソッドに統合

collection_check_boxes

APIドックによると以下の引数を持つ。

collection_check_boxes (ActionView::Helpers::FormOptionsHelper) - APIdock
collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)

具体的には以下のように用いる。

collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
   b.label(:"data-value" => b.value) { b.check_box + b.text }
end

なおform_for等のインスタンスをオブジェクトに持つ場合には1つ目の引数は省略できる。
例 f.collection_check_boxes

第2引数のmethodの意味がわからない?
後ろの3つの引数だけで動きそうに思うが、第1,2引数を指定することで内部で何をしているのでしょうか?

以下のように定義してpryでparamsの内容を確認してみた

= form_for @group do |f|
  = f.text_field :name, placeholder: "グループ名を入力してください", class: "chat-group-form__input", id: "chat_group_name"
  = f.collection_check_boxes :user_ids, User.all, :id, :name
  = f.submit :commit, value: "登録する", class: "chat-group-form__action-btn", "data-disable-with": '送信中'

paramsの:groupに ”user_ids”=>[“”, “1”, “2”] と書いてあるのがcheckboxで選択したID。
もしかしてcheckboxの第2引数がparamsのキーに入る???
確認するとやっぱりそうでした。ただ、第2引数を闇雲に指定してもundefinedと言われるので、第1引数のオブジェクトに関連する変数じゃないとダメみたい。

pry> params
  => <ActionController::Parameters {
    "utf8"=>"✓", 
    "authenticity_token"=>"wmjtg==", 
    "group"=>{
        "name"=>"hogehoge", 
        "user_ids"=>["", "1", "2"]
        }, 
    "commit"=>"登録する", 
    "controller"=>"groups", 
    "action"=>"create"
    } 
    permitted: false>

ここでpermitする項目を抜粋 permit(:name, { user_ids: [] })

pry> params.require(:group).permit(:name, { user_ids: [] })
=> <ActionController::Parameters {
  "name"=>"e3333333333", 
  "user_ids"=>["", "1", "2"]
  } 
  permitted: true>

{ user_ids: [] }の部分は:user_idsでもいいんじゃないかと思ったけど、
unpermitted parameterと言われてしまいました。
配列には配列であることを明示しないと受け付けてくれない感じ。

pry> params.require(:group).permit(:name, :user_ids)    
Unpermitted parameter: user_ids
=> <ActionController::Parameters {"name"=>"e3333333333"} permitted: true>

user_ids:[]として見たら通りました。
user_ids: []と半角スペースでも問題なくOK。

pry> params.require(:group).permit(:name, user_ids:[])
=> <ActionController::Parameters {
"name"=>"e3333333333", 
"user_ids"=>["", "1", "2"]
} permitted: true>

ここまで書いて気づいたけど、railsはハッシュの{}は省略できるんだったわ。

https://railsguides.jp/action_controller_overview.html#ネストしたパラメータ

params.require(:group).permit( {user_ids: []}, :name)
これは通る

params.require(:group).permit( user_ids: [], :name)
これは通らない
syntaxエラー unexpected ')' で止まる。

params.require(:group).permit(:name, user_ids:[])
これが通るのだから通りそうなものなのに。
配列指定の後に続けて書く場合は{}で囲わないとエラーになっちゃいますね。

has_many throughのsave時の挙動

def new
  @group = Group.new
  @group.users << current_user
end

def create
  @group = Group.new(create_params)
  @group.save
end

private
def create_params
  params.require(:group).permit(:name, user_ids: [])
end

group.saveする時にgroupのテーブルのみならず、group_userテーブルにも保存される。
save直前でpryで止めて確認すると、

@group
=> #<Group:0x00007ffb6f749268 id: nil, name: "sgggg", created_at: nil, updated_at: nil>

@group.users
=> [#<User id: 1, email: "aaa@aaa", name: "hogehoge">,
 #<User id: 2, email: "fdg@dfg", name: "fugafufa">]
@product = Product.new
@product.save
とする際に、
Productモデルのインスタンスであるため、そのモデルが管理するテーブル(products)だけが更新されると思っていました。

ところがgroupとuserを中間テーブルを用いてアソシエーションを組んだ場合に、
@group.saveを行うと、groupテーブルのみならず中間テーブルも更新されます。
さらに@group.users[1][:name] = “hoge”のような処理を加えて
@group.saveを行うとuserテーブルも更新されます。

つまり標準ではアソシエーションを組むと自動的に連携するすべてのテーブルを更新するのだと思います。
rails guideのautosaveの項目に同様の記述があります。
Active Record Associations — Ruby on Rails Guides
ActiveRecordAssociationsThisguidecoverstheassociationfeaturesofActiveRecord.Afterreadingthisguide,youwillknowhowto:DeclareassociationsbetweenActiveRecordmodels....

.new(class)に外部キーっぽい配列を引き渡した場合の処理

has_many throughでuserとgroupモデルが繋がっているときに

def create
  @group = Group.new(create_params)
  @group.save
end

private
def create_params
  params.require(:group).permit(:name, user_ids: [])
end

“user_ids”=>[“”, “1”, “2”]を引き渡したところ、

@group
=> #<Group:0x00007ffb6f749268 id: nil, name: "sgggg", created_at: nil, updated_at: nil>

@group.users
=> [#<User id: 1, email: "aaa@aaa", name: "hogehoge">,
 #<User id: 2, email: "fdg@dfg", name: "fugafufa">]

が返った。.newに配列を渡すと複数生成されるのか?
そもそも配列を渡してよいのか?
initializeメソッドでの処理だと思うが、その実態はどのように確認したらよいのか?

flashメッセージ

一時的に表示させるメッセージ
:notice :alert :flashの3つが使える。

Action Controller の概要 - Railsガイド
本ガイドでは、コントローラの動作と、アプリケーションのリクエストサイクルでコントローラがどのように使われるかについて説明します。セッション、フィルタ、cookie、データストリーミング、リクエストによって発生する例外、その他多くの話題を取り扱っています。
  def create
    @group = Group.new(create_params)
    if @group.save
      redirect_to controller: :messages, action: :index
      flash[:notice] = 'グループを作成しました'
    else
      flash[:alert] = 'グループ名を入力してください'
      render :new
    end

redirect_toには直接引き渡すこともできる。

redirect_to :root, notice: "グループ作成"

errorsメソッド

validationで失敗した場合、保存しようとしたインスタンスはnilではなく、エラー情報を含んだインスタンスになる。(情報が追加される)
これをrenderで再度同じページに飛ばすことで、エラー情報を持つ場合の分岐が作れる。

.full_messages

エラーメッセージを配列で取得できる。

- if @group.errors.any?
  .chat-group-form__errors
     %h2= "#{@group.errors.full_messages.count}件のエラーが発生しました。"
     %ul
       - @group.errors.full_messages.each do |message|
         %li= message

.any? {|item| block }

配列に対して処理する。block内での処理でtrueが返ればtrueを。
配列が空であったり、処理結果が全てfalseだった場合にfalseを返す。

haml

form_for

f.text_field等は1段インデントする。
また属性はカンマ区切りで続けてかける。

= form_for @group do |f|
  = f.text_field :name, placeholder: "グループ名を入力してください", class: "chat-group-form__input", id: "chat_group_name"

data-disable-with

多重送信を防ぐためにrails5から実装。
連続でsubmitをクリックできなくなるが、代わりに表示する名称を設定できる。

f.submit "Send", "data-disable-with": "送信中..."

GIT

Reset, Checkout, Revert

戻す際に使うコマンド。
それぞれ違うが詳細は勉強中

コメント