0
0

More than 3 years have passed since last update.

【Ruby】なぜrequire 'json'するとto_jsonできるようになるのか

Last updated at Posted at 2020-08-29

TL;DR

  • json module読み込み時にC言語の拡張メソッドを呼び出して関数の追加とmodule追加して、組み込みのクラスに後からMixinしているから。

疑問

Rubyでjsonに変換する場合to_jsonを使用をする場合が多いと思います。
Hash型やArray型のオブジェクトから変換する場合は、以下のようなコードを書きますが、require 'json'をしないとNoMethodErrorでエラーが発生します。

  • require 'json'しない
エラー起こる
irb(main):002:0> some_hash = {a: 1,b: 2}
irb(main):004:0> some_hash.to_json()
Traceback (most recent call last):
        5: from /usr/local/opt/ruby/bin/irb:23:in `<main>'
        4: from /usr/local/opt/ruby/bin/irb:23:in `load'
        3: from /usr/local/Cellar/ruby/2.7.1_2/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
        2: from (irb):3
        1: from (irb):4:in `rescue in irb_binding'
NoMethodError (undefined method `to_json' for {:a=>1, :b=>2}:Hash)
Did you mean?  to_s
  • require 'json'する
エラー起こらない
irb(main):005:0> require 'json'
=> true
irb(main):002:0> some_hash = {a: 1,b: 2}
irb(main):006:0> some_hash.to_json()
=> "{\"a\":1,\"b\":2}"

組み込みのclassに対して後からインスタンスメソッドを追加するのは珍しいなと感じたので、内部の実装を追って見ます。

ソースコード確認

Rubyのドキュメントによると JSON::Generator::GeneratorMethods::Hashというmoduleが存在するようです。

Rubyソースコードの該当部分を検索してみるとおそらくここから各generatorクラス(json/ext/generator)の呼び出しをしているみたいです。

ext.rb
  module Ext
    require 'json/ext/parser'
    require 'json/ext/generator'
    $DEBUG and warn "Using Ext extension for JSON."
    JSON.parser = Parser
    JSON.generator = Generator
  end

さらに以下を確認するとjson/ext/generator.rbではなくc言語から拡張ライブラリとしてコンパイルされていることが分かります。

extconf.rb
require 'mkmf'

$defs << "-DJSON_GENERATOR"
create_makefile 'json/ext/generator'

該当のc言語のファイルを確認するとrb_define_methodによってHashクラスに対してto_jsonのメソッドが登録及びmoduleの登録が行われていることが分かりました。

    mHash = rb_define_module_under(mGeneratorMethods, "Hash");
    rb_define_method(mHash, "to_json", mHash_to_json, -1);

そして上記のmoduleは以下でMixinすることで後からインスタンスメソッドの追加を行なっているようです。

       klass.class_eval do
          instance_methods(false).each do |m|
            m.to_s == 'to_json' and remove_method m
          end
          include modul
        end

まとめ

json module読み込み時にC言語の拡張メソッドを呼び出して関数の追加とmodule追加して、組み込みのクラスに後からMixinしているから、というのが分かりました。

言語によっては(Pythonとか)組み込みクラスに対してメソッドの追加を許可していない言語もあるので少し新鮮でした。良くも悪くも組み込みクラスに対して柔軟にインジェクションできるんですね。
これからも勉強していきたいと思います。

0
0
4

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
0
0