[Ruby] Pourquoi require'json'autorisera to_json

TL;DR

Question

Lors de la conversion en json en Ruby, je pense que vous utilisez souvent to_json. Lors de la conversion à partir d'un objet de type Hash ou ʻArray, écrivez le code suivant, mais si vous ne faites pas require'json', une erreur se produira avec NoMethodError`.

--require'json' pas

Erreur se produit


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'

Aucune erreur ne se produit


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}"

J'ai trouvé inhabituel d'ajouter des méthodes d'instance à la classe intégrée plus tard, je vais donc suivre l'implémentation interne.

Vérification du code source

D'après la documentation Ruby, il semble y avoir un module appelé JSON :: Generator :: GeneratorMethods :: Hash.

https://docs.ruby-lang.org/ja/latest/class/JSON=3a=3aGenerator=3a=3aGeneratorMethods=3a=3aHash.html

Si vous recherchez la partie appropriée du code source de Ruby, il semble que vous appelez probablement chaque classe de générateur (json / ext / generator) à partir d'ici.

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

De plus, si vous vérifiez ce qui suit, vous pouvez voir que json / ext / generator est compilé comme une bibliothèque d'extension à partir du langage c au lieu de .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'

Quand j'ai vérifié le fichier de langage c correspondant, j'ai trouvé que la méthode to_json était enregistrée et que le module était enregistré pour la classe Hash par rb_define_method.

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);

Et il semble que le module ci-dessus ajoute une méthode d'instance plus tard en la mélangeant ci-dessous.

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

Résumé

J'ai découvert que c'était parce que j'avais appelé la méthode d'extension du langage C lors de la lecture du module json, ajouté la fonction et ajouté le module, puis l'ai mélangé dans la classe intégrée.

C'était un peu rafraîchissant car certains langages (comme Python) ne permettent pas l'ajout de méthodes aux classes intégrées. Vous pouvez injecter de manière flexible dans les classes intégrées, pour le meilleur ou pour le pire. J'aimerais continuer à étudier.

Recommended Posts

[Ruby] Pourquoi require'json'autorisera to_json
Pourquoi les écoles apprennent Ruby
Divisez par Ruby! Pourquoi est-ce 0?