aws-sdk-ruby に cloud_formation なナニを発見

色々確認している中で

  • json なナニを作るの面倒
  • テスツ大変

な話がごろごろ転がっている模様。むむむ、と言いつつ aws-sdk-ruby なソースを見てみるに lib/aws/cloud_formation なるディレクトリがあることを確認。
lib/aws/cloud_formation/client.rb を見てみたところ、validate_template なるメソドに関するコメント記述を確認。

      # Calls the ValidateTemplate API operation.

てことなのでこれを試験に使えないかな、という事で検討着手。

と言いつつ

client.rb が非常にメタな記述になっているのでこっち方面も掘削。定義は以下な形になってます。

      define_client_methods('2010-05-15')

こちら、lib/aws/api_config/CloudFormation-2010-05-15.yml を指しているものと思われます。つうかこのメソドはどこにあるんだ、ということで確認。とりあえず、AWS::CloudFormation::Client は Core::QueryClient を継承している模様。

    # Client class for AWS CloudFormation.
    class Client < Core::QueryClient

Core::QueryClient に定義はありませんでしたが、Core::Client を継承している事が判明。

module AWS
  module Core
    class QueryClient < Core::Client

AWS::Core::Client に定義されていることを確認。

        # Defines one method for each service operation described in
        # the API configuration.
        # @param [String] api_version
        def define_client_methods api_version

          const_set(:API_VERSION, api_version)

          api_config = load_api_config(api_version)

          api_config[:operations].each do |operation|

            builder = request_builder_for(api_config, operation)
            parser = response_parser_for(api_config, operation)

            define_client_method(operation[:method], builder, parser)

          end
        end

む、load_api_config メソドは直上で定義されてます。

        def load_api_config api_version
          lib = File.dirname(File.dirname(__FILE__))
          path = "#{lib}/api_config/#{service_name}-#{api_version}.yml"
          YAML.load(File.read(path))
        end

なんとなく当たってますね。つうかこないだの年末に aws-sdk な spec 読んでますね。
それは良いとして以下の記述なソレなんですが

            builder = request_builder_for(api_config, operation)
            parser = response_parser_for(api_config, operation)

superclass な記述を確認しておこう、ということで AWS::Core::Client の reqest_builder_for やら response_parse_for の定義が以下。

        # Define this in sub-classes (e.g. QueryClient, RESTClient, etc)
        def request_builder_for api_config, operation
          raise NotImplementedError
        end

        # Define this in sub-classes (e.g. QueryClient, RESTClient, etc)
        def response_parser_for api_config, operation
          raise NotImplementedError
        end

コメントの通り、subclass で定義せよ、ということか。そして AWS::Core::QueryClient における request_builder_for とか response_parse_for の定義が以下。

      def self.request_builder_for api_config, operation
        QueryRequestBuilder.new(api_config[:api_version], operation)
      end

      def self.response_parser_for api_config, operation
        QueryResponseParser.new(operation[:outputs])
      end

例えば RESTClient とかだとまた違うオブジェクトを生成して云々、な風になってるのだろうな。

嗚呼抽象化

AWS::Core::Client#define_client_method から add_client_request_method を呼び出してるんですが、してる事は

  • operation という配列 (?) に引数の method_name を追加
  • ClientRequestMethodBuilder.new
  • method_name な手続き定義な文字列の method_def を作って module_eval 呼び出し
    • 基本的には options を作って client_request 手続き呼び出し

というあたりな模様。つうか抽象度が高すぎてイメージできてなかったりしてます。つうかナチュラル爆発させてそうな気がしてるんですが validates って意味てきにアレですね。うーん困った。