class LIT::Builder::Object

@api private @since 0.1.0

Constants

AST

Public Class Methods

new(ast, target_module) click to toggle source
# File lib/lit/builder/object.rb, line 10
def initialize(ast, target_module)
  @ast = ast
  @target_module = target_module
  @type_checker = TypeChecker.new(target_module)
end

Public Instance Methods

build() click to toggle source
# File lib/lit/builder/object.rb, line 16
def build
  @ast.statements.map { |stmt| build_statement(stmt) }
end

Private Instance Methods

build_endpoint(endpoint) click to toggle source
# File lib/lit/builder/object.rb, line 36
def build_endpoint(endpoint)
  request = make_type(endpoint.request)
  response = make_type(endpoint.response)

  working_module = @target_module
  endpoint_namespace_stack = endpoint.name.split(".").map { |x| Utils.camelize(x) }
  endpoint_namespace_stack.each do |namespace|
    m = Module.new
    Utils.const_reset(working_module, namespace, m)
    working_module = m
  end

  Utils.const_reset(working_module, "Request", request)
  Utils.const_reset(working_module, "Response", response)
  Utils.const_reset(working_module, "DefinedIn", @target_module)
end
build_enum(enum) click to toggle source
# File lib/lit/builder/object.rb, line 58
def build_enum(enum)
  mod = make_enum(enum.variants)
  Utils.const_reset(@target_module, enum.name, mod)
end
build_statement(stmt) click to toggle source
# File lib/lit/builder/object.rb, line 22
def build_statement(stmt)
  if stmt.is_a?(AST::EndpointStatement)
    build_endpoint(stmt)
  elsif stmt.is_a?(AST::TypeDeclarationStatement::Struct)
    build_struct(stmt)
  elsif stmt.is_a?(AST::TypeDeclarationStatement::Enum)
    build_enum(stmt)
  elsif stmt.is_a?(AST::TypeDeclarationStatement::TypeAlias)
    build_type_alias(stmt)
  else
    raise InvalidASTError, "invalid statement: #{stmt}"
  end
end
build_struct(struct) click to toggle source
# File lib/lit/builder/object.rb, line 53
def build_struct(struct)
  klass = make_struct(struct.fields)
  Utils.const_reset(@target_module, struct.name, klass)
end
build_type_alias(type_alias) click to toggle source
# File lib/lit/builder/object.rb, line 63
def build_type_alias(type_alias)
  object = make_type(type_alias.type)
  Utils.const_reset(@target_module, type_alias.name, object)
end
make_enum(variants) click to toggle source

rubocop:disable Metrics/MethodLength

# File lib/lit/builder/object.rb, line 93
def make_enum(variants)
  mod = Module.new do
    include ::LIT::Object::Enum
  end

  variants.each do |(variant_name, variant)|
    variant_name = Utils.camelize(variant_name)

    if variant == AST::EnumVariant::Unit
      Utils.const_reset(mod, variant_name, Module.new do
        include ::LIT::Object::EnumVariant

        define_singleton_method(:__parent__) { mod }
        define_singleton_method(:__kind__) { :unit }
        define_singleton_method(:__name__) { variant_name }
      end)
    elsif variant.is_a?(AST::EnumVariant::Struct)
      klass = make_struct(variant.fields)
      klass.include(::LIT::Object::EnumVariant)

      klass.define_method(:__parent__) { mod }
      klass.define_method(:__kind__) { :struct }
      klass.define_singleton_method(:__kind__) { :struct }
      klass.define_method(:__name__) { variant_name }

      Utils.const_reset(mod, variant_name, klass)
    else
      raise InvalidASTError, "invalid enum variant type: #{variant}"
    end
  end

  mod
end
make_struct(fields) click to toggle source
# File lib/lit/builder/object.rb, line 68
def make_struct(fields)
  type_checker = @type_checker

  Class.new(::LIT::Object::Struct) do
    define_method(:initialize) do |args|
      fields.each do |(field_name, field_type)|
        value = args.fetch(field_name)
        type_checker.check_type!(field_type, value)
        instance_variable_set(:"@#{field_name}", value)
      end
    end

    fields.keys.each do |field_name|
      define_method(field_name) { instance_variable_get(:"@#{field_name}") }
    end

    define_singleton_method(:__fields__) do
      fields.reduce({}) do |acc, (field_name, field_type)|
        acc.merge(field_name => field_type)
      end
    end
  end
end
make_type(type) click to toggle source
# File lib/lit/builder/object.rb, line 127
def make_type(type)
  object =
    if type.is_a?(AST::Type::Primitive::Map)
      Map.new(
        @target_module,
        type.key_type,
        type.value_type,
      ).build
    elsif type.is_a?(AST::Type::Primitive::Option)
      Option.new(@target_module, type.type).build
    elsif type.is_a?(AST::Type::Primitive::Array)
      Array.new(@target_module, type.type).build
    elsif type.is_a?(AST::Type::AnonymousStruct)
      make_struct(type.fields)
    elsif type.is_a?(AST::Type::AnonymousEnum)
      make_enum(type.variants)
    elsif type == AST::Type::Primitive::String ||
          type == AST::Type::Primitive::Integer ||
          type == AST::Type::Primitive::Float ||
          type == AST::Type::Primitive::Boolean ||
          type == AST::Type::Primitive::Unit
      Module.new
    elsif type.is_a?(AST::Type::Alias)
      target_module = @target_module

      Class.new do
        define_singleton_method(:const_missing) do |name|
          target_module.const_get(type.name).const_get(name)
        end

        define_method(:initialize) do |*args|
          @inner = target_module.const_get(type.name).new(*args)
        end

        define_method(:method_missing) do |*args|
          @inner.send(*args)
        end
      end
    else
      raise InvalidASTError, "invalid type: #{type}"
    end

  Utils.const_reset(object, "TYPE", type)

  object
end