Skip to content

Commit

Permalink
Merge pull request #1336 from bdewater/kredis-compiler
Browse files Browse the repository at this point in the history
Add Kredis compiler
  • Loading branch information
KaanOzkan authored Jan 11, 2023
2 parents 9018a04 + 9c3008d commit 7eaa3fa
Show file tree
Hide file tree
Showing 11 changed files with 5,717 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ group(:development, :test) do
gem("aasm", require: false)
gem("bcrypt", require: false)
gem("xpath", require: false)
gem("kredis", require: false)

# net-smtp was removed from default gems in Ruby 3.1, but is used by the `mail` gem.
# So we need to add it as a dependency until `mail` is fixed:
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ GEM
json (2.6.3)
kramdown (2.4.0)
rexml
kredis (1.3.0)
activesupport (>= 6.0.0)
redis (>= 4.2, < 6)
language_server-protocol (3.17.0.2)
loofah (2.19.1)
crass (~> 1.0.2)
Expand Down Expand Up @@ -260,6 +263,8 @@ GEM
parser (>= 2.6.4.0)
sorbet-runtime (>= 0.5.9204)
unparser
redis (5.0.5)
redis-client (>= 0.9.0)
redis-client (0.11.2)
connection_pool
regexp_parser (2.6.1)
Expand Down Expand Up @@ -369,6 +374,7 @@ DEPENDENCIES
graphql
identity_cache
kramdown (~> 2.4)
kredis
minitest
minitest-hooks
minitest-reporters
Expand Down
1 change: 1 addition & 0 deletions gemfiles/Gemfile-rails-6-1
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ group(:development, :test) do
gem("aasm", require: false)
gem("bcrypt", require: false)
gem("xpath", require: false)
gem("kredis", require: false)

# net-smtp was removed from default gems in Ruby 3.1, but is used by the `mail` gem.
# So we need to add it as a dependency until `mail` is fixed:
Expand Down
1 change: 1 addition & 0 deletions gemfiles/Gemfile-rails-main
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ group(:development, :test) do
gem("aasm", require: false)
gem("bcrypt", require: false)
gem("xpath", require: false)
gem("kredis", require: false)

# net-smtp was removed from default gems in Ruby 3.1, but is used by the `mail` gem.
# So we need to add it as a dependency until `mail` is fixed:
Expand Down
130 changes: 130 additions & 0 deletions lib/tapioca/dsl/compilers/kredis.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# typed: strict
# frozen_string_literal: true

begin
require "kredis"
rescue LoadError
return
end

module Tapioca
module Dsl
module Compilers
# `Tapioca::Dsl::Compilers::Kredis` decorates RBI files for all
# classes that include [`Kredis::Attributes`](https://github.com/rails/kredis/blob/main/lib/kredis/attributes.rb).
#
# For example, with the following class:
#
# ~~~rb
# class Person < ApplicationRecord
# kredis_list :names
# kredis_flag :awesome
# kredis_counter :steps, expires_in: 1.hour
# kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
# end
# ~~~
#
# this compiler will produce an RBI file with the following content:
# ~~~rbi
# # typed: true
#
# class Person
# module GeneratedKredisAttributeMethods
# sig { returns(Kredis::Types::Flag) }
# def awesome; end
#
# sig { returns(T::Boolean) }
# def awesome?; end
#
# sig { returns(PrivateEnumMorning) }
# def morning; end
#
# sig { returns(Kredis::Types::List) }
# def names; end
#
# sig { returns(Kredis::Types::Counter) }
# def steps; end
#
# class PrivateEnumMorning < Kredis::Types::Enum
# sig { void }
# def black!; end
#
# sig { returns(T::Boolean) }
# def black?; end
#
# sig { void }
# def blue!; end
#
# sig { returns(T::Boolean) }
# def blue?; end
#
# sig { void }
# def bright!; end
#
# sig { returns(T::Boolean) }
# def bright?; end
# end
# end
# end
# ~~~
class Kredis < Compiler
extend T::Sig

ConstantType = type_member { { fixed: T.all(Class, ::Kredis::Attributes::ClassMethods, Extensions::Kredis) } }

sig { override.void }
def decorate
return if constant.__tapioca_kredis_types.nil?

module_name = "GeneratedKredisAttributeMethods"

root.create_path(constant) do |model|
model.create_module(module_name) do |mod|
constant.__tapioca_kredis_types.each do |method, data|
generate_methods(mod, method, data)
end
end
model.create_include(module_name)
end
end

class << self
extend T::Sig

sig { override.returns(T::Enumerable[Module]) }
def gather_constants
all_classes
.grep(::Kredis::Attributes::ClassMethods)
.reject { |klass| klass.to_s == "ActiveRecord::Base" || klass.try(:abstract_class?) }
end
end

private

sig { params(mod: RBI::Scope, method: String, data: T::Hash[Symbol, T.untyped]).void }
def generate_methods(mod, method, data)
return_type = data.fetch(:type)
case return_type
when "Kredis::Types::Enum"
klass_name = "PrivateEnum#{method.split("_").map(&:capitalize).join}"
create_enum_class(mod, klass_name, data.fetch(:values))
return_type = klass_name
when "Kredis::Types::Flag"
mod.create_method("#{method}?", return_type: "T::Boolean")
end

mod.create_method(method, return_type: return_type)
end

sig { params(mod: RBI::Scope, klass_name: String, values: T::Array[T.untyped]).void }
def create_enum_class(mod, klass_name, values)
klass = mod.create_class(klass_name, superclass_name: "Kredis::Types::Enum")
values.each do |value|
klass.create_method("#{value}!", return_type: "void")
klass.create_method("#{value}?", return_type: "T::Boolean")
end
end
end
end
end
end
114 changes: 114 additions & 0 deletions lib/tapioca/dsl/extensions/kredis.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# typed: true
# frozen_string_literal: true

begin
require "kredis"
rescue LoadError
return
end

module Tapioca
module Dsl
module Compilers
module Extensions
module Kredis
attr_reader :__tapioca_kredis_types

def kredis_proxy(name, key: nil, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Proxy")
super
end

def kredis_string(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_integer(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_decimal(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_datetime(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_flag(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Flag")
super
end

def kredis_float(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Enum", values: values)
super
end

def kredis_json(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

def kredis_list(name, key: nil, typed: :string, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::List")
super
end

def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::UniqueList")
super
end

def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Set")
super
end

def kredis_slot(name, key: nil, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Slots")
super
end

def kredis_slots(name, available:, key: nil, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Slots")
super
end

def kredis_counter(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Counter")
super
end

def kredis_hash(name, key: nil, typed: :string, config: :shared, after_change: nil)
collect_kredis_type(name, "Kredis::Types::Hash")
super
end

def kredis_boolean(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
collect_kredis_type(name, "Kredis::Types::Scalar")
super
end

private

def collect_kredis_type(method, type, values: nil)
@__tapioca_kredis_types ||= {}
@__tapioca_kredis_types[method.to_s] = { type: type, values: values }
end

::Kredis::Attributes::ClassMethods.prepend(self)
end
end
end
end
end
59 changes: 59 additions & 0 deletions manual/compiler_kredis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Kredis

`Tapioca::Dsl::Compilers::Kredis` decorates RBI files for all
classes that include [`Kredis::Attributes`](https://github.com/rails/kredis/blob/main/lib/kredis/attributes.rb).

For example, with the following class:

~~~rb
class Person < ApplicationRecord
kredis_list :names
kredis_flag :awesome
kredis_counter :steps, expires_in: 1.hour
kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
end
~~~

this compiler will produce an RBI file with the following content:
~~~rbi
# typed: true

class Person
module GeneratedKredisAttributeMethods
sig { returns(Kredis::Types::Flag) }
def awesome; end

sig { returns(T::Boolean) }
def awesome?; end

sig { returns(PrivateEnumMorning) }
def morning; end

sig { returns(Kredis::Types::List) }
def names; end

sig { returns(Kredis::Types::Counter) }
def steps; end

class PrivateEnumMorning < Kredis::Types::Enum
sig { void }
def black!; end

sig { returns(T::Boolean) }
def black?; end

sig { void }
def blue!; end

sig { returns(T::Boolean) }
def blue?; end

sig { void }
def bright!; end

sig { returns(T::Boolean) }
def bright?; end
end
end
end
~~~
1 change: 1 addition & 0 deletions manual/compilers.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ In the following section you will find all available DSL compilers:
* [GraphqlInputObject](compiler_graphqlinputobject.md)
* [GraphqlMutation](compiler_graphqlmutation.md)
* [IdentityCache](compiler_identitycache.md)
* [Kredis](compiler_kredis.md)
* [MixedInClassAttributes](compiler_mixedinclassattributes.md)
* [Protobuf](compiler_protobuf.md)
* [RailsGenerators](compiler_railsgenerators.md)
Expand Down
Loading

0 comments on commit 7eaa3fa

Please sign in to comment.