Skip to content

Commit

Permalink
Generate Kaminari scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
rzane committed Nov 28, 2024
1 parent 873be64 commit 4fb93fd
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
83 changes: 83 additions & 0 deletions lib/tapioca/dsl/compilers/kaminari.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# typed: strict
# frozen_string_literal: true

return unless defined?(Kaminari)

require "tapioca/dsl/helpers/active_record_constants_helper"

module Tapioca
module Dsl
module Compilers
# `Tapioca::Dsl::Compilers::Kaminari` decorates RBI files for models
# using Kaminari.
#
# For example, with Kaminari installed and the following `ActiveRecord::Base` subclass:
#
# ~~~rb
# class Post < ApplicationRecord
# end
# ~~~
#
# This compiler will produce the RBI file `post.rbi` with the following content:
#
# ~~~rbi
# # post.rbi
# # typed: true
# class Post
# extend GeneratedRelationMethods
#
# module GeneratedRelationMethods
# sig do
# params(
# num: T.any(Integer, String)
# ).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods))
# end
# def page(num = nil); end
# end
# end
# ~~~
class Kaminari < Compiler
extend T::Sig
include Helpers::ActiveRecordConstantsHelper

ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } }

sig { override.void }
def decorate
root.create_path(constant) do |model|
target_modules.each do |module_name, return_type|
model.create_module(module_name).create_method(
::Kaminari.config.page_method_name.to_s,
parameters: [create_opt_param("num", type: "T.any(Integer, String)", default: "nil")],
return_type: "T.all(#{return_type}, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)",
)
end

model.create_extend(RelationMethodsModuleName)
end
end

class << self
sig { override.returns(T::Enumerable[Module]) }
def gather_constants
descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
end
end

private

sig { returns(T::Array[[String, String]]) }
def target_modules
if compiler_enabled?("ActiveRecordRelations")
[
[RelationMethodsModuleName, RelationClassName],
[AssociationRelationMethodsModuleName, AssociationRelationClassName],
]
else
[[RelationMethodsModuleName, "T.untyped"]]
end
end
end
end
end
end
102 changes: 102 additions & 0 deletions spec/tapioca/dsl/compilers/kaminari_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# typed: strict
# frozen_string_literal: true

require "spec_helper"

module Tapioca
module Dsl
module Compilers
class KaminariSpec < ::DslSpec
describe "Tapioca::Dsl::Compilers::Kaminari" do
sig { void }
def before_setup
require "active_record"
require "kaminari/activerecord"
end

describe "initialize" do
it "gathers no constants if there are no ActiveRecord classes" do
assert_empty(gathered_constants)
end

it "gathers only ActiveRecord constants with no abstract classes" do
add_ruby_file("post.rb", <<~RUBY)
class Post < ActiveRecord::Base
end
class Product < ActiveRecord::Base
self.abstract_class = true
end
class User
end
RUBY

assert_equal(["Post"], gathered_constants)
end
end

describe "decorate" do
describe "with relations enabled" do
before do
require "tapioca/dsl/compilers/active_record_relations"
activate_other_dsl_compilers(ActiveRecordRelations)
end

it "generates an RBI file" do
add_ruby_file("post.rb", <<~RUBY)
class Post < ActiveRecord::Base
end
RUBY

expected = <<~RBI
# typed: strong
class Post
extend GeneratedRelationMethods
module GeneratedAssociationRelationMethods
sig { params(num: T.any(Integer, String)).returns(T.all(PrivateAssociationRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) }
def page(num = nil); end
end
module GeneratedRelationMethods
sig { params(num: T.any(Integer, String)).returns(T.all(PrivateRelation, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) }
def page(num = nil); end
end
end
RBI

assert_equal(expected, rbi_for(:Post))
end
end

describe "without relations enabled" do
it "generates an RBI file" do
add_ruby_file("post.rb", <<~RUBY)
class Post < ActiveRecord::Base
end
RUBY

expected = <<~RBI
# typed: strong
class Post
extend GeneratedRelationMethods
module GeneratedRelationMethods
sig { params(num: T.any(Integer, String)).returns(T.all(T.untyped, Kaminari::PageScopeMethods, Kaminari::ActiveRecordRelationMethods)) }
def page(num = nil); end
end
end
RBI

assert_equal(expected, rbi_for(:Post))
end
end
end
end
end
end
end
end

0 comments on commit 4fb93fd

Please sign in to comment.