Ich habe hunger

あふりかエンジニア、アフリカ向けのB2BのSaaSを開発する

rubyで初心者が間違いそうな間違いしてしまった(まぁ、初心者みたいなもんだけど)

あるあるなのかもしれないけどやってしまった。

RailsでUserモデルがあって、そこにenumでrolesみたいなのを定義している。
しかし、roles内にあるadmin権限は我々スーパー管理者しか触れないようにしたい。
なので、一般ユーザーには目に触れもしないようにしたいため、こうしていた。

    %tr
      - User.roles.delete(:admin)
      %th= f.label :role, "Role"
      %td= f.select :role, User.roles.map{|r| [r[0], r[0]]}

が、しかしこれをするとUserモデルのrolesから:adminから消し去ってしまって、二度と出て来ない。
つまり、スーパー管理者の編集画面ですら:adminの設定が見えなくなってしまって、admin権限を持つユーザーを作成することができなくなってしまった。


さすがにこれはまずい、と以下のように修正した。

    %tr
      - roles = User.roles
      - roles.delete(:admin)
      %th= f.label :role, "Role"
      %td= f.select :role, roles.map{|r| [r[0], r[0]]}


が、問題は解決されない。
で、なんだろうと思ったがこれにも問題があった。

User.roles.object_id
roles.object_id


と調べてみると同じobject_idを保有している。ので、deleteをrolesにかけたところでUser.rolesから:adminが消え去る現象は改善されない。
つまり同じオブジェクトに操作していることになっているので、cloneする必要があるので以下のようにした。

    %tr
      - roles = User.roles.dup
      - roles.delete(:admin)
      %th= f.label :role, "Role"
      %td= f.select :role, roles.map{|r| [r[0], r[0]]}


が、実はこれにも問題(以下略

結論から言うと、今回はこれでOKなんだけども、

User.roles[:admin].object_id
roles[:admin].object_id

は同じなので、実はまだヤバい。
rolesの中から(インデックスとして)消し去るだけなら良いんだけど、:adminの持つ値を自体を変えるとUser.rolesの中の:adminの値まで変わってしまうことになるっぽい。

    %tr
      - roles = Marshal.load(Marshal.dump(User.roles))
      - roles.delete(:admin)
      %th= f.label :role, "Role"
      %td= f.select :role, roles.map{|r| [r[0], r[0]]}

じゃあ、どうすんだよ!って話なんだけど、こうすると良いっぽい。


参考はこちら

qiita.com