TL;DR
--Since the C language extension method is called when the json module is loaded, the function is added and the module is added, and the built-in class is mixed later.
When converting to json in Ruby, I think that you often use to_json
.
When converting from an object of type Hash
or ʻArray, write the following code, but if you do not do
require'json', an error will occur with
NoMethodError`.
--require'json'
not
Error occurs
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'
No error occurs
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}"
I found it unusual to add instance methods to the built-in class later, so I'll follow the internal implementation.
According to the Ruby documentation, there seems to be a module called JSON :: Generator :: GeneratorMethods :: Hash
.
https://docs.ruby-lang.org/ja/latest/class/JSON=3a=3aGenerator=3a=3aGeneratorMethods=3a=3aHash.html
If you search the relevant part of the Ruby source code, it seems that you are probably calling each generator class (json / ext / generator
) from here.
https://github.com/ruby/ruby/blob/v2_7_1/ext/json/lib/json/ext.rb#L8
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
If you check the following further, you can see that json / ext / generator
is compiled as an extension library from C language instead of .rb
.
https://github.com/ruby/ruby/blob/v2_7_1/ext/json/generator/extconf.rb#L4
extconf.rb
require 'mkmf'
$defs << "-DJSON_GENERATOR"
create_makefile 'json/ext/generator'
When I checked the corresponding c language file, I found that rb_define_method
registered the to_json
method and module for the Hash
class.
https://github.com/ruby/ruby/blob/68d7e93b3baf91ac7d7cc100b75bab81ba7dee76/ext/json/generator/generator.c#L1511
mHash = rb_define_module_under(mGeneratorMethods, "Hash");
rb_define_method(mHash, "to_json", mHash_to_json, -1);
And it seems that the above module is adding an instance method later by mixing it below.
https://github.com/ruby/ruby/blob/68d7e93b3baf91ac7d7cc100b75bab81ba7dee76/ext/json/lib/json/common.rb#L67
klass.class_eval do
instance_methods(false).each do |m|
m.to_s == 'to_json' and remove_method m
end
include modul
end
I found out that when I read the json module, I called the C language extension method to add a function and a module, and later mixed it into the built-in class.
It was a little refreshing because some languages (such as Python) do not allow the addition of methods to built-in classes. You can flexibly inject into embedded classes, for better or for worse. I would like to continue studying.