Open1

whereは配列じゃない!concatを使用する際の注意点 & URL直打ち禁止の難易度高めver

あおさんあおさん

Your ManagerのURL直打ちを禁止する記述が非常に難しかった。

もっとも難しかったのはtasks_controllerへの記述

 def ensure_correct_user
    # parmasでuser_idを取得(URLで遷移しようとしているユーザーのid)
    # URLに入力されたユーザーが所属しているグループ一覧を取得するために
    # group_usersテーブルよりparamsで取得したuser_idをもつグループを配列で取得しjoin_groupsへ格納
    # join_groupsを展開してgroup_usersテーブルよりjoin_groupのgroup_idをもつユーザーを配列で取得しcolleague_usersへ格納 (このとき、重複するものがあればまとめる)
    # colleague_usersの中にcurrent_userがいるか判定する

    unless User.find_by(id: params[:user_id]).nil?

      user = User.find(params[:user_id])
      join_groups = GroupUser.where(user_id: user.id)
      @colleague_users = []
      join_groups.each do |join_group|
        @colleague_users.concat GroupUser.where(group_id: join_group.group_id).to_a
      end
      @users = []
      @colleague_users.each do |colleague_user|
        @users.concat User.where(id: colleague_user.user_id).to_a
      end
      @users.uniq!

      unless @users.include?(current_user)
        redirect_to tasks_path(user_id: current_user.id)
      end
    else
      redirect_to tasks_path(user_id: current_user)
    end

  end

かなり複雑になってしまったけどポイントは2つ

  1. where文を使用したあとのデータは配列ではない

ここで

GroupUser.where(user_id: user.id)

の戻り値は配列ではなく、配列ライクなものであることに注意
実際にはActiveRecord::Relationというオブジェクトで、ほぼ配列のようなもの。
これがあるおかげでwhereにはメソッドチェーンを使用することができる。

  1. concatメソッド
    配列 other を自身の末尾に破壊的に連結します。
    今回のようにeach文の中で配列の中身を蓄積したいときなどに使える。
array = [1, 2]
a     = [3, 4]
array.concat a
p array          # => [1, 2, 3, 4]
p a              # => [3, 4]       # こちらは変わらない

参考:
https://docs.ruby-lang.org/ja/latest/method/Array/i/concat.html

ただし、このconcatというメソッドは配列にしか使うことができない。
whereの戻り値は厳密には配列ではないので、wehreでは使用できない。
そのため、

.to_aで配列の形式にしてあげる

変数 = [] でなにもデータがはいっていないときでもエラーにならないようにしてあげる

これらを行う必要がある。

@users.uniq!

では、@usersに格納した配列データで重複するものを覗いている。