ベルリンのITスタートアップで働くジャバ・ザ・ハットリの日記

日本→シンガポール→ベルリンへと流れ着いたソフトウェアエンジニアのブログ

Rubyのクラスで定義されたattr_accessor一覧をそのクラスのインスタンス変数から取り出す方法

移転しました。

一応題名の通りで「Rubyのクラスで定義されたattr_accessor一覧をそのクラスのインスタンス変数から取り出す方法」なのだが、何を言ってるのか書いた本人でも「?」となりがちなのでまずはやりたかったことから説明する。

例えばBookクラスがあって、そこに attr_accessorが定義されている。

class Book
  attr_accessor :id, :title, :author

end

Bookクラスを作ってインスタンス変数を作る。

@book = Book.new

attr_accessorに定義されているように以下のように変数にアクセスできる。

> @book.title
"STAR WARS"

やりたかったのはこの@bookを使って中で定義されているattr_accessorの一覧を取り出すこと。
こんな感じ。

> @book.something_something
[:id, :title, :author]

結果的にできた方法はこれ

class BaseModel
  
  def self.attr_accessor(*vars)
    @@attributes ||= []
    @@attributes.concat vars
    super
  end

  def attributes
    @@attributes
  end
end

class Book < BaseModel
  attr_accessor :id, :title, :author
end

こうしておくと以下のように一覧が出る。やってみれば単純だけど、最初ちょっと悩んだ。

> @book.attributes
[:id, :title, :author]

<解説>

クラスメソッドのattr_accessorの処理を上書きし、クラス変数として@@attributesを定義して、そこに一覧を格納しておくようにする。

  def self.attr_accessor(*vars)
    @@attributes ||= []
    @@attributes.concat vars
    super
  end

インスタンス変数@bookからインスタンスメソッドのattributesが呼ばれたら、クラス変数を返す。

  def attributes
    @@attributes
  end

もっといい方法をご存知でしたらぜひコメントください。

なんでこんなことをやっているのかというと個人プロジェクトで作っているRailにGoogle Cloud Datastoreをつなげようとしたのがことの始まり。Google Cloud PlatformはJavaやGo、Pythonなんかには初期から対応していたのにRubyはかなり後回し。最近になってやっと対応してくれたと思ったら、その公式サイトから出ているコードがなんとも微妙。

例えばGoogle Cloud DatastoreをつなげたRailsのModelのコード例がこんな感じ。

class Book

  attr_accessor :id, :title, :author, :published_on, :description

  # [START to_entity]
  # ...
  def to_entity
    entity                 = Google::Cloud::Datastore::Entity.new
    entity.key             = Google::Cloud::Datastore::Key.new "Book", id
    entity["title"]        = title
    entity["author"]       = author       if author
    entity["published_on"] = published_on if published_on
    entity["description"]  = description  if description
    entity
  end
  # [END to_entity]
end

Bookクラスが1つだけならいいけど、後から作るモデルクラスにも全部いちいちentity["なんやら"]って定義する訳にいかない。
初心者向けチュートリアルでわかりやすいといえばわかりやすいけど。

tango-ruby.hatenablog.com

tango-ruby.hatenablog.com

tango-ruby.hatenablog.com