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 Take a look at Awesome Faraday for a list of available adapters and middleware.

Why use Faraday?

Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses, making it easier to build sophisticated API clients or web service libraries that abstract away the details of how HTTP requests are made.

Faraday comes with a lot of features out of the box, such as:

  • Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more)
  • Persistent connections (keep-alive)
  • Parallel requests
  • Automatic response parsing (JSON, XML, YAML)
  • Customization of the request/response cycle with middleware
  • Support for streaming responses
  • Support for uploading files
  • And much more!

Getting Started

The best starting point is the Faraday Website, with…

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


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

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

Top comments (0)