ウェブエンジニア珍道中

日々の技術的に関する経験を書いていきます。脱線もしますが助けになれば幸いです。

immutableなクラスでto_hashを作るまで

Rubyにてセッタをもたないimmutableなクラスを作り、to_hashで値を取ってくるようにしたのでまとめます。

今回は例として本(Book)クラスを作ります。

完成品

class Book
  ATTRIBUTE_NAMES = [:name, :author, :price].freeze
  attr_reader *ATTRIBUTE_NAMES

  def initialize(name, author, price)
    @name = name
    @author = author
    @price = price
  end

  def to_hash
    ATTRIBUTE_NAMES.inject({}) do |hash, attribute_name|
      hash.merge({ attribute_name => send(attribute_name) })
    end
  end

  alias_method :to_h, :to_hash
end

puts Book.new("こころ", "夏目漱石", 1980).to_hash
# {:name=>"こころ", :author=>"夏目漱石", :price=>100}

attr_readerでゲッタをまとめて定義しています。attr_accessorとの違いはセッタの有無です。セッタを無くしimmutableに近づけています。

本来は

attr_reader :name, :author, :price

のように指定するのですが、直接指定すると後ほど指定した:name, :author, :priceの3つの名前を手軽に取ってくることができなかったため、まずクラスの定数に入れています。

to_hashメソッドでは先程のクラスの定数ATTRIBUTE_NAMESを元にハッシュを作成しています。

def to_hash
  ATTRIBUTE_NAMES.inject({}) do |hash, attribute_name|
    hash.merge({ attribute_name => send(attribute_name) })
  end
end

keyを:nameなどの属性名のシンボルで、valueにnameなどのゲッタメソッドを叩いて取ってきた値を入れたハッシュを作って返しています。sendを使わない場合は以下でも動きます。

def to_hash
  ATTRIBUTE_NAMES.inject({}) do |hash, attribute_name|
    hash.merge({
      attribute_name => instance_variable_get("@#{attribute_name}") 
    })
  end
end

あと一応to_hでも大丈夫なようにエイリアスを貼りました。

DDDにおけるドメインオブジェクトとかはこんな感じで作っていったらなと思いました。また何かあればアップデートしていきます。

injectを初めてまともに使えた気がする…。