虚無庵

徒然なるままに

microsoft_graph のコードを読む(3)

microsoft_graph のコードを読む - 虚無庵 microsoft_graph のコードを読む(2) - 虚無庵

前回は OData::Service#initialize について読みました。

今回は再度 MicrosoftGraph#initialize に戻ります。

class MicrosoftGraph
  attr_reader :service
  BASE_URL = "https://graph.microsoft.com/v1.0/"

  def initialize(options = {}, &auth_callback)
    @service = OData::Service.new(
      api_version: options[:api_version],
      auth_callback: auth_callback,
      base_url: BASE_URL,
      metadata_file: options[:cached_metadata_file]
    )
    @association_collections = {}
    unless MicrosoftGraph::ClassBuilder.loaded?
      MicrosoftGraph::ClassBuilder.load!(service)
    end

  end

  def containing_navigation_property(type_name)
    navigation_properties.values.find do |navigation_property|
      navigation_property.collection? && navigation_property.type.name == "Collection(#{type_name})"
    end
  end

  def path; end
end

MicrosoftGraph::ClassBuilder

ひとまず @association_collections は置いておいて、MicrosoftGraph::ClassBuilder を読む

github.com

MicrosoftGraph::ClassBuilder.loaded?

def self.loaded?
  @@loaded
end

クラス変数 @@loaded を返すのみです。@@loaded はデフォルトで false が代入されています。

MicrosoftGraph::ClassBuilder.load!(service)

def self.load!(service)
  if !@@loaded
    @service_namespace = service.namespace
    service.entity_types.each do |entity_type|
      create_class! entity_type
    end

    service.complex_types.each do |complex_type|
      create_class! complex_type
    end

    service.entity_sets.each do |entity_set|
      add_graph_association! entity_set
    end

    service.actions.each do |action|
      add_action_method! action
    end

    service.functions.each do |function|
      add_function_method! function
    end

    service.singletons.each do |singleton|
      class_name = classify(singleton.type_name)
      MicrosoftGraph.instance_eval do
        resource_name = singleton.name
        define_method(OData.convert_to_snake_case(resource_name)) do
          MicrosoftGraph
            .const_get(class_name)
            .new(
              graph:         self,
              resource_name: resource_name,
              parent:        self
            ).tap(&:fetch)
        end
      end
    end

    MicrosoftGraph.instance_eval do
      define_method(:navigation_properties) do
        service.entity_sets
          .concat(service.singletons)
          .map { |navigation_property|
            [navigation_property.name.to_sym, navigation_property]
          }.to_h
      end
    end

    @@loaded = true
  end
end

はい、もう勘の良い方はお気づきですね。コードジェネレーターを使わずに機能を実現できているのは、Rubyメタプログラミング技法を使っているからです。

これを読むのが本当にねぇ…。という訳で、限られた昼休みではなくちゃんと時間を取って書きます。

(続く?)