DEV Community

Yuta Goto
Yuta Goto

Posted on

hash keys to method names

これは .ごっ!のアドベントカレンダー の19日目の記事です。

RubyのHashのキー名をメソッド名として扱うときの備忘録です。
例えば、 h = {num: 42} があったときに h.num42を取得したいといったときです。

Rubyの Hash#fetchmethod_missing をいい具合に利用します。

class Hash
  def method_missing(m)
    fetch(m) { fetch(m.to_s) { super } }
  end
end

h = {num: 42, str: "string", first_hash: { second_hash: "second_hash_str" }}
p h.num # => 42
p h.str # => "string"
p h.first_hash.second_hash # => "second_hash_str"
p h.string # => undefined method 'string' for {....}:Hash (NoMethodError)
Enter fullscreen mode Exit fullscreen mode

この method_missing()Hashにそのメソッドが定義されていなかった時に呼び出されるメソッドで、引数の m は呼び出そうとしたメソッド名のSymbolが渡ります。

fetch() はキーに関連付けられた値を返すメソッドです。キーがない場合はブロックの実行結果が返されます。

それぞれの詳しい内容はリファレンスマニュアルを読んでください。

https://docs.ruby-lang.org/ja/3.1/method/BasicObject/i/method_missing.html
https://docs.ruby-lang.org/ja/3.1/method/Hash/i/fetch.html

上記にあるコードを順番に読み解きます。

  • h.num の場合
    1. method_missingm = :num が渡る。
    2. fetch(:num)h には :num のキーがあるのでその値を返す。
      • h.string の場合
      • method_missingm = :string が渡る。
      • fetch(:string)h には :string のキーが存在しないのでブロックの中身が実行される。
      • m.to_s した結果が fetch されるので、 fetch("string")となるが h には "string" のキーが存在しないのでブロックの中身が実行される。
      • super で実際の method_missing が実行される。

良さげな使い方としては、 APIリクエストしてJSONを受け取った時にメソッドっぽく値を取り出すことができ、きれいなコードを書いているように見えることです。

以下サンプルコードです。

require 'faraday'

class Hash
  def method_missing(m)
    fetch(m) { fetch(m.to_s) { super } }
  end
end

conn = Faraday::new({url: "https://api.nasa.gov"}) do |c|
  c.request :json
  c.response :json
  c.adapter Faraday.default_adapter
end

res = conn.send(:get, "/planetary/apod", { api_key: "DEMO_KEY" }, nil)
p res.body.copyright # => "Craig Stocks"
p res.body.title # => "The Tadpole Nebula in Gas and Dust"
Enter fullscreen mode Exit fullscreen mode

このコードでは faraday を使用していますが、httpリクエストの方法はなんでもよいです。

GitHub logo lostisland / faraday

Simple, but flexible HTTP client library, with support for multiple backends.

Faraday

Gem Version GitHub Actions CI GitHub Discussions

Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle You probably don't want to use Faraday directly in your project, as it will lack an actual client library to perform requests. Instead, you probably want to have a look at Awesome Faraday for a list of available adapters.

Getting Started

The best starting point is the Faraday Website, with its introduction and explanation Need more details? See the Faraday API Documentation to see how it works internally.

Supported Ruby versions

This library aims to support and is tested against the currently officially supported Ruby implementations. This means that, even without a major release, we could add or drop support for Ruby versions following their EOL. Currently that means we support Ruby 2.6+

If something doesn't work…

レスポンスbodyの中身をまるでメソッドかのように値を取得しています。


上記のコードは万能ではありません。 Hash のインスタンスメソッドにあるメソッド名がキー名と同じだった場合、インスタンスメソッドが優先されます。({dig: "dig_string"} とあった場合は、method_missingではないので Hash#dig が優先されるということです。)

小さい個人ツールやgemとして使う分には便利ではありますが、大きいプロダクトに適用させる場合には注意しましょう。

Top comments (0)