LoginSignup
3
3

More than 5 years have passed since last update.

HanamiのEntityは値を書き換えることができない

Posted at

HanamiのEntityは、初期化した後で値を書き換えることができません。イミュータブル!

ちょっと個人的にインパクトが大きかったので記録を残しておきます。

どうやってupdateするのか

Hanamiでデータベースに保存されている値を更新するコードは以下のようになります

repository = UserRepository.new

user = repository.find(id)
repository.update(user.id, name: 'new name')

データの更新って↓のようなイメージだったのですが、Userエンティティの値を書き換えることはできないのでエラーになります。びっくりしてしまいました。

# うごかない
repository = UserRepository.new

user = repository.find(id)
user.name = 'new name' # NoMethodError: undefined method `name='
user.update

Entity内部から書き換えることもできない

オブジェクトの外側から値を書き換えられませんでしたが、内側からも同様です。ユーザーのパスワードを暗号化するために、以下のようなメソッドを作ったらエラーになりました。

lib/myproject/entities/user.rb
class User < Hanami::Entity

  def password=(password)
    # # NoMethodError: undefined method `password_digest='
    self.password_digest = BCrypt::Password.create(password).to_s
  end

  def password
    BCrypt::Password.new(password_digest)
  end
end

やっぱりセッターの password_digest= メソッドが存在しないと言われます。
ソースを読んでみたら、コンストラクタの最後にObject#freezeが実行されていました。どうやっても値を変更することはできないようです。

entity.rb
    def initialize(attributes = nil)
      @attributes = self.class.schema[attributes]
      freeze
    end

irb
irb(main):001:0> User.new.frozen?
=> true

イミュータブルでも大丈夫

Entityがイミュータブルと聞いてショックでしたが、それは「Entityは値の入れものでしかない。Entityにビジネスロジック的なことを書いてはいけない」っていう風に誤解してしまったからで、よーく考えてみるとそんなに慌てることもないような気がしてきました。
値の書き換えができなくても処理は書けるし、データベースから取得したか、あるいはnewした時点の状態と変わってないことが保証されるので、安全性が増す気がします。

ちなみに、ユーザーのパスワードについてどう実装したらいいかググってみたら、Hanamiのチャットで中の人がコンストラクタでセットするといいよって言ってたので、コンストラクタをオーバーライドして以下のようになりました。

lib/myproject/entities/user.rb
class User < Hanami::Entity

  def initialize(attributes = {})
    if attributes.has_key? :password
      super attributes.merge(
        password_digest: BCrypt::Password.create(attributes[:password])
      )
    else
      super attributes
    end
  end

  def password
    BCrypt::Password.new(password_digest)
  end

end
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3