Skip to content

Instantly share code, notes, and snippets.

@tomoasleep
Created September 7, 2023 07:58
Show Gist options
  • Save tomoasleep/85d121751ee0d77fae9d357527e28884 to your computer and use it in GitHub Desktop.
Save tomoasleep/85d121751ee0d77fae9d357527e28884 to your computer and use it in GitHub Desktop.
Samples of Tapioca DSL compilers
# typed: true
# frozen_string_literal: true
module Tapioca
module Compilers
class RedisObjects < Tapioca::Dsl::Compiler
extend T::Sig
ConstantType = type_member { { fixed: T.all(T::Class[::Redis::Objects], ::Redis::Objects::ClassMethods) } }
sig { override.returns(T::Enumerable[Class]) }
def self.gather_constants
all_classes.select { |c| c < ::Redis::Objects }
end
sig { override.void }
def decorate
root.create_path(constant) do |klass|
T.let(constant.redis_objects, T::Hash[Symbol, T::Hash[Symbol, T.untyped]]).each do |name, options|
case options[:type]
when :lock
klass.create_method(
"#{name}_lock",
return_type: "::Redis::Lock"
)
when :increment, :decrement
klass.create_method(
name.to_s,
return_type: "::Redis::Counter"
)
when :dict
klass.create_method(
name.to_s,
return_type: "::Redis::HashKey"
)
klass.create_method(
"#{name}=",
parameters: [create_param("value", type: "T.untyped")],
return_type: "T.untyped"
)
when :list
klass.create_method(
name.to_s,
return_type: "::Redis::List[T.untyped]"
)
klass.create_method(
"#{name}=",
parameters: [create_param("value", type: "T.untyped")],
return_type: "T.untyped"
)
when :value
klass.create_method(
name.to_s,
return_type: "::Redis::Value[T.untyped]"
)
klass.create_method(
"#{name}=",
parameters: [create_param("value", type: "T.untyped")],
return_type: "T.untyped"
)
when :set
klass.create_method(
name.to_s,
return_type: "::Redis::Set"
)
klass.create_method(
"#{name}=",
parameters: [create_param("value", type: "T.untyped")],
return_type: "T.untyped"
)
when :sorted_set
klass.create_method(
name.to_s,
return_type: "::Redis::SortedSet"
)
else
end
end
end
end
end
end
end
# typed: true
# frozen_string_literal: true
module Tapioca
module Compilers
class Settingslogic < Tapioca::Dsl::Compiler
extend T::Sig
ConstantType = type_member { { fixed: T.class_of(::Settingslogic) } }
sig { override.returns(T::Enumerable[Class]) }
def self.gather_constants
all_classes.select { |c| c < ::Settingslogic }
end
sig { override.void }
def decorate
root.create_path(constant) do |klass|
instance = T.cast(constant.send(:instance), ::Settingslogic)
define_namespace(klass, instance)
# delegate missing class method to instance
public_instance_methods_of(constant).each do |method_name|
next if constant.respond_to?(method_name)
method = constant.instance_method(method_name)
create_method_from_def(klass, method, class_method: true)
end
end
end
sig { params(namespace: RBI::Tree, instance: ::Settingslogic).void }
def define_namespace(namespace, instance)
instance.symbolize_keys.each_key do |key|
define_getter(namespace, instance, key)
end
end
sig do
params(
namespace: RBI::Tree,
object: ::Settingslogic,
key: Symbol,
).void
end
def define_getter(namespace, object, key)
value = object[key]
case value
when ::Settingslogic
class_name = "Internal#{key.to_s.camelcase}"
namespace.create_class(class_name, superclass_name: "::Settingslogic") do |klass|
define_namespace(klass, value)
end
namespace.create_method(key.to_s, return_type: class_name)
namespace.create_method(key.to_s, return_type: class_name, class_method: true)
else
namespace.create_method(key.to_s, return_type: type_of(value))
namespace.create_method(key.to_s, return_type: type_of(value), class_method: true)
end
end
sig { params(value: T.untyped).returns(String) }
def type_of(value)
case value
when Integer
"Integer"
when String
"String"
when Array
types = value.map { |v| type_of(v) }.uniq
types.length > 1 ? "T::Array[T.any(#{types.join(', ')})]" : "T::Array[#{types.first}]"
when NilClass
"NilClass"
else
"T.untyped"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment