From 4c6c4fee0125dc4cfdd3d763188a13b271021b52 Mon Sep 17 00:00:00 2001 From: Ray Zane Date: Thu, 28 Nov 2024 13:19:32 -0500 Subject: [PATCH 1/4] Remove unnecessary shims --- sorbet/rbi/shims/tapioca.rbi | 42 ------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 sorbet/rbi/shims/tapioca.rbi diff --git a/sorbet/rbi/shims/tapioca.rbi b/sorbet/rbi/shims/tapioca.rbi deleted file mode 100644 index 8db6cae..0000000 --- a/sorbet/rbi/shims/tapioca.rbi +++ /dev/null @@ -1,42 +0,0 @@ -# typed: true -# frozen_string_literal: true - -module Tapioca - module Dsl - module Compilers - class ActiveRecordRelations < Compiler - ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } } - end - end - - module Helpers - module ActiveRecordColumnTypeHelper - class ColumnTypeOption < T::Enum - class << self - sig do - params( - options: T::Hash[String, T.untyped], - block: T.proc.params(value: String, default_column_type_option: ColumnTypeOption).void, - ).returns(ColumnTypeOption) - end - def from_options(options, &block); end - end - - sig { returns(T::Boolean) } - def persisted?; end - sig { returns(T::Boolean) } - def untyped?; end - end - end - - module ActiveRecordConstantsHelper - RelationMethodsModuleName = T.let(T.unsafe(nil), String) - AssociationRelationMethodsModuleName = T.let(T.unsafe(nil), String) - - RelationClassName = T.let(T.unsafe(nil), String) - AssociationRelationClassName = T.let(T.unsafe(nil), String) - AssociationsCollectionProxyClassName = T.let(T.unsafe(nil), String) - end - end - end -end From c39b359e648c3fdee94e2b98fc5271002e30c022 Mon Sep 17 00:00:00 2001 From: Ray Zane Date: Thu, 28 Nov 2024 13:36:36 -0500 Subject: [PATCH 2/4] Add Kaminari support --- Gemfile | 1 + Gemfile.lock | 5 + lib/tapioca/dsl/compilers/kaminari.rb | 85 +++ manual/compiler_kaminari.md | 30 + manual/compilers.md | 1 + sorbet/rbi/gems/actionmailbox@7.2.1.rbi | 6 + sorbet/rbi/gems/actiontext@7.2.1.rbi | 6 + sorbet/rbi/gems/activerecord@7.2.1.rbi | 2 + sorbet/rbi/gems/activestorage@7.2.1.rbi | 6 + .../rbi/gems/kaminari-activerecord@1.2.2.rbi | 106 +++ sorbet/rbi/gems/kaminari-core@1.2.2.rbi | 657 ++++++++++++++++++ sorbet/rbi/gems/tapioca@0.16.5.rbi | 337 ++++++++- sorbet/tapioca/require.rb | 1 + spec/tapioca/dsl/compilers/kaminari_spec.rb | 102 +++ 14 files changed, 1344 insertions(+), 1 deletion(-) create mode 100644 lib/tapioca/dsl/compilers/kaminari.rb create mode 100644 manual/compiler_kaminari.md create mode 100644 sorbet/rbi/gems/kaminari-activerecord@1.2.2.rbi create mode 100644 sorbet/rbi/gems/kaminari-core@1.2.2.rbi create mode 100644 spec/tapioca/dsl/compilers/kaminari_spec.rb diff --git a/Gemfile b/Gemfile index 30a9bf6..d0a97f8 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ group :development do gem "minitest-reporters" gem "attr_json" + gem "kaminari-activerecord" gem "money-rails" gem "paperclip" gem "rails", "~> 7.2" diff --git a/Gemfile.lock b/Gemfile.lock index 808f4e1..4af16be 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,6 +101,10 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.7.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) language_server-protocol (3.17.0.3) logger (1.6.1) loofah (2.22.0) @@ -289,6 +293,7 @@ PLATFORMS DEPENDENCIES attr_json boba! + kaminari-activerecord minitest minitest-hooks minitest-reporters diff --git a/lib/tapioca/dsl/compilers/kaminari.rb b/lib/tapioca/dsl/compilers/kaminari.rb new file mode 100644 index 0000000..d1b84fa --- /dev/null +++ b/lib/tapioca/dsl/compilers/kaminari.rb @@ -0,0 +1,85 @@ +# 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 < Tapioca::Dsl::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 + extend T::Sig + + 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 diff --git a/manual/compiler_kaminari.md b/manual/compiler_kaminari.md new file mode 100644 index 0000000..1b9f31c --- /dev/null +++ b/manual/compiler_kaminari.md @@ -0,0 +1,30 @@ +## Kaminari + +`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 +~~~ diff --git a/manual/compilers.md b/manual/compilers.md index e9e0fa4..967b4d4 100644 --- a/manual/compilers.md +++ b/manual/compilers.md @@ -8,6 +8,7 @@ This list is an evergeen list of currently available compilers. * [ActiveRecordAssociationsPersisted](compiler_activerecordassociationspersisted.md) * [ActiveRecordColumnsPersisted](compiler_activerecordcolumnspersisted.md) * [AttrJson](compiler_attrjson.md) +* [Kaminari](compiler_kaminari.md) * [MoneyRails](compiler_moneyrails.md) * [Paperclip](compiler_paperclip.md) diff --git a/sorbet/rbi/gems/actionmailbox@7.2.1.rbi b/sorbet/rbi/gems/actionmailbox@7.2.1.rbi index 7429160..213f169 100644 --- a/sorbet/rbi/gems/actionmailbox@7.2.1.rbi +++ b/sorbet/rbi/gems/actionmailbox@7.2.1.rbi @@ -695,6 +695,9 @@ end class ActionMailbox::Record < ::ActiveRecord::Base include ::ActionMailbox::Record::GeneratedAttributeMethods include ::ActionMailbox::Record::GeneratedAssociationMethods + include ::Kaminari::ActiveRecordModelExtension + include ::Kaminari::ConfigurationMethods + extend ::Kaminari::ConfigurationMethods::ClassMethods class << self # source://activemodel/7.2.1/lib/active_model/validations.rb#71 @@ -702,6 +705,9 @@ class ActionMailbox::Record < ::ActiveRecord::Base # source://activerecord/7.2.1/lib/active_record/enum.rb#167 def defined_enums; end + + # source://kaminari-activerecord/1.2.2/lib/kaminari/activerecord/active_record_model_extension.rb#15 + def page(num = T.unsafe(nil)); end end end diff --git a/sorbet/rbi/gems/actiontext@7.2.1.rbi b/sorbet/rbi/gems/actiontext@7.2.1.rbi index d48aecf..44ea6b6 100644 --- a/sorbet/rbi/gems/actiontext@7.2.1.rbi +++ b/sorbet/rbi/gems/actiontext@7.2.1.rbi @@ -1163,6 +1163,9 @@ end class ActionText::Record < ::ActiveRecord::Base include ::ActionText::Record::GeneratedAttributeMethods include ::ActionText::Record::GeneratedAssociationMethods + include ::Kaminari::ActiveRecordModelExtension + include ::Kaminari::ConfigurationMethods + extend ::Kaminari::ConfigurationMethods::ClassMethods class << self # source://activemodel/7.2.1/lib/active_model/validations.rb#71 @@ -1170,6 +1173,9 @@ class ActionText::Record < ::ActiveRecord::Base # source://activerecord/7.2.1/lib/active_record/enum.rb#167 def defined_enums; end + + # source://kaminari-activerecord/1.2.2/lib/kaminari/activerecord/active_record_model_extension.rb#15 + def page(num = T.unsafe(nil)); end end end diff --git a/sorbet/rbi/gems/activerecord@7.2.1.rbi b/sorbet/rbi/gems/activerecord@7.2.1.rbi index 56f4561..fe9b49f 100644 --- a/sorbet/rbi/gems/activerecord@7.2.1.rbi +++ b/sorbet/rbi/gems/activerecord@7.2.1.rbi @@ -8221,6 +8221,7 @@ class ActiveRecord::Base include ::ActiveRecord::Suppressor include ::ActiveRecord::Normalization include ::ActiveRecord::Marshalling::Methods + include ::Kaminari::ActiveRecordExtension include ::ActiveStorage::Attached::Model include ::ActiveStorage::Reflection::ActiveRecordExtensions include ::ActionText::Attribute @@ -8283,6 +8284,7 @@ class ActiveRecord::Base extend ::ActiveRecord::SignedId::ClassMethods extend ::ActiveRecord::Suppressor::ClassMethods extend ::ActiveRecord::Normalization::ClassMethods + extend ::Kaminari::ActiveRecordExtension::ClassMethods extend ::ActiveStorage::Attached::Model::ClassMethods extend ::ActiveStorage::Reflection::ActiveRecordExtensions::ClassMethods extend ::ActionText::Attribute::ClassMethods diff --git a/sorbet/rbi/gems/activestorage@7.2.1.rbi b/sorbet/rbi/gems/activestorage@7.2.1.rbi index ac104c3..5e06b6d 100644 --- a/sorbet/rbi/gems/activestorage@7.2.1.rbi +++ b/sorbet/rbi/gems/activestorage@7.2.1.rbi @@ -2615,6 +2615,9 @@ end class ActiveStorage::Record < ::ActiveRecord::Base include ::ActiveStorage::Record::GeneratedAttributeMethods include ::ActiveStorage::Record::GeneratedAssociationMethods + include ::Kaminari::ActiveRecordModelExtension + include ::Kaminari::ConfigurationMethods + extend ::Kaminari::ConfigurationMethods::ClassMethods class << self # source://activemodel/7.2.1/lib/active_model/validations.rb#71 @@ -2622,6 +2625,9 @@ class ActiveStorage::Record < ::ActiveRecord::Base # source://activerecord/7.2.1/lib/active_record/enum.rb#167 def defined_enums; end + + # source://kaminari-activerecord/1.2.2/lib/kaminari/activerecord/active_record_model_extension.rb#15 + def page(num = T.unsafe(nil)); end end end diff --git a/sorbet/rbi/gems/kaminari-activerecord@1.2.2.rbi b/sorbet/rbi/gems/kaminari-activerecord@1.2.2.rbi new file mode 100644 index 0000000..413c481 --- /dev/null +++ b/sorbet/rbi/gems/kaminari-activerecord@1.2.2.rbi @@ -0,0 +1,106 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for types exported from the `kaminari-activerecord` gem. +# Please instead update this file by running `bin/tapioca gem kaminari-activerecord`. + + +# source://kaminari-activerecord//lib/kaminari/activerecord/version.rb#3 +module Kaminari + class << self + # source://kaminari-core/1.2.2/lib/kaminari/config.rb#13 + def config; end + + # source://kaminari-core/1.2.2/lib/kaminari/config.rb#9 + def configure; end + + # source://kaminari-core/1.2.2/lib/kaminari/models/array_extension.rb#70 + def paginate_array(array, limit: T.unsafe(nil), offset: T.unsafe(nil), total_count: T.unsafe(nil), padding: T.unsafe(nil)); end + end +end + +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_extension.rb#6 +module Kaminari::ActiveRecordExtension + extend ::ActiveSupport::Concern + + mixes_in_class_methods ::Kaminari::ActiveRecordExtension::ClassMethods +end + +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_extension.rb#9 +module Kaminari::ActiveRecordExtension::ClassMethods + # Future subclasses will pick up the model extension + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_extension.rb#11 + def inherited(kls); end +end + +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_model_extension.rb#6 +module Kaminari::ActiveRecordModelExtension + extend ::ActiveSupport::Concern + include ::Kaminari::ConfigurationMethods + + mixes_in_class_methods ::Kaminari::ConfigurationMethods::ClassMethods +end + +# Active Record specific page scope methods implementations +# +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#5 +module Kaminari::ActiveRecordRelationMethods + # Used for page_entry_info + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#7 + def entry_name(options = T.unsafe(nil)); end + + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#12 + def reset; end + + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#17 + def total_count(column_name = T.unsafe(nil), _options = T.unsafe(nil)); end + + # Turn this Relation to a "without count mode" Relation. + # Note that the "without count mode" is supposed to be performant but has a feature limitation. + # Pro: paginates without casting an extra SELECT COUNT query + # Con: unable to know the total number of records/pages + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#50 + def without_count; end +end + +# source://kaminari-activerecord//lib/kaminari/activerecord/version.rb#4 +module Kaminari::Activerecord; end + +# source://kaminari-activerecord//lib/kaminari/activerecord/version.rb#5 +Kaminari::Activerecord::VERSION = T.let(T.unsafe(nil), String) + +# A module that makes AR::Relation paginatable without having to cast another SELECT COUNT query +# +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#56 +module Kaminari::PaginatableWithoutCount + # The page wouldn't be the last page if there's "limit + 1" record + # + # @return [Boolean] + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#109 + def last_page?; end + + # Overwrite AR::Relation#load to actually load one more record to judge if the page has next page + # then store the result in @_has_next ivar + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#90 + def load; end + + # Empty relation needs no pagination + # + # @return [Boolean] + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#114 + def out_of_range?; end + + # Force to raise an exception if #total_count is called explicitly. + # + # source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#120 + def total_count; end +end + +# source://kaminari-activerecord//lib/kaminari/activerecord/active_record_relation_methods.rb#57 +module Kaminari::PaginatableWithoutCount::LimitValueSetter; end diff --git a/sorbet/rbi/gems/kaminari-core@1.2.2.rbi b/sorbet/rbi/gems/kaminari-core@1.2.2.rbi new file mode 100644 index 0000000..d0efcd7 --- /dev/null +++ b/sorbet/rbi/gems/kaminari-core@1.2.2.rbi @@ -0,0 +1,657 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for types exported from the `kaminari-core` gem. +# Please instead update this file by running `bin/tapioca gem kaminari-core`. + + +# source://kaminari-core//lib/kaminari/core.rb#3 +module Kaminari + class << self + # source://kaminari-core//lib/kaminari/config.rb#13 + def config; end + + # @yield [config] + # + # source://kaminari-core//lib/kaminari/config.rb#9 + def configure; end + + # Wrap an Array object to make it paginatable + # ==== Options + # * :limit - limit + # * :offset - offset + # * :total_count - total_count + # * :padding - padding + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#70 + def paginate_array(array, limit: T.unsafe(nil), offset: T.unsafe(nil), total_count: T.unsafe(nil), padding: T.unsafe(nil)); end + end +end + +# source://kaminari-core//lib/kaminari/config.rb#18 +class Kaminari::Config + # @return [Config] a new instance of Config + # + # source://kaminari-core//lib/kaminari/config.rb#22 + def initialize; end + + # Returns the value of attribute default_per_page. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def default_per_page; end + + # Sets the attribute default_per_page + # + # @param value the value to set the attribute default_per_page to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def default_per_page=(_arg0); end + + # Returns the value of attribute left. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def left; end + + # Sets the attribute left + # + # @param value the value to set the attribute left to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def left=(_arg0); end + + # Returns the value of attribute max_pages. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def max_pages; end + + # Sets the attribute max_pages + # + # @param value the value to set the attribute max_pages to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def max_pages=(_arg0); end + + # Returns the value of attribute max_per_page. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def max_per_page; end + + # Sets the attribute max_per_page + # + # @param value the value to set the attribute max_per_page to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def max_per_page=(_arg0); end + + # Returns the value of attribute outer_window. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def outer_window; end + + # Sets the attribute outer_window + # + # @param value the value to set the attribute outer_window to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def outer_window=(_arg0); end + + # Returns the value of attribute page_method_name. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def page_method_name; end + + # Sets the attribute page_method_name + # + # @param value the value to set the attribute page_method_name to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def page_method_name=(_arg0); end + + # If param_name was given as a callable object, call it when returning + # + # source://kaminari-core//lib/kaminari/config.rb#36 + def param_name; end + + # Sets the attribute param_name + # + # @param value the value to set the attribute param_name to. + # + # source://kaminari-core//lib/kaminari/config.rb#20 + def param_name=(_arg0); end + + # Returns the value of attribute params_on_first_page. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def params_on_first_page; end + + # Sets the attribute params_on_first_page + # + # @param value the value to set the attribute params_on_first_page to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def params_on_first_page=(_arg0); end + + # Returns the value of attribute right. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def right; end + + # Sets the attribute right + # + # @param value the value to set the attribute right to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def right=(_arg0); end + + # Returns the value of attribute window. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def window; end + + # Sets the attribute window + # + # @param value the value to set the attribute window to. + # + # source://kaminari-core//lib/kaminari/config.rb#19 + def window=(_arg0); end +end + +# source://kaminari-core//lib/kaminari/models/configuration_methods.rb#6 +module Kaminari::ConfigurationMethods + extend ::ActiveSupport::Concern + + mixes_in_class_methods ::Kaminari::ConfigurationMethods::ClassMethods +end + +# source://kaminari-core//lib/kaminari/models/configuration_methods.rb#8 +module Kaminari::ConfigurationMethods::ClassMethods + # This model's default +per_page+ value + # returns +default_per_page+ value unless explicitly overridden via paginates_per + # + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#19 + def default_per_page; end + + # Overrides the max_pages value per model when a value is given + # class Article < ActiveRecord::Base + # max_pages 100 + # end + # + # Also returns this model's max_pages value (globally configured + # +max_pages+ value unless explicitly overridden) when no value is given + # + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#44 + def max_pages(val = T.unsafe(nil)); end + + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#54 + def max_pages_per(val); end + + # Overrides the max +per_page+ value per model + # class Article < ActiveRecord::Base + # max_paginates_per 100 + # end + # + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#27 + def max_paginates_per(val); end + + # This model's max +per_page+ value + # returns +max_per_page+ value unless explicitly overridden via max_paginates_per + # + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#33 + def max_per_page; end + + # Overrides the default +per_page+ value per model + # class Article < ActiveRecord::Base + # paginates_per 10 + # end + # + # source://kaminari-core//lib/kaminari/models/configuration_methods.rb#13 + def paginates_per(val); end +end + +# source://kaminari-core//lib/kaminari/engine.rb#4 +class Kaminari::Engine < ::Rails::Engine; end + +# source://kaminari-core//lib/kaminari/helpers/tags.rb#4 +module Kaminari::Helpers; end + +# Link with page number that appears at the leftmost +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#103 +class Kaminari::Helpers::FirstPage < ::Kaminari::Helpers::Tag + include ::Kaminari::Helpers::Link + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#105 + def page; end +end + +# Non-link tag that stands for skipped pages... +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#161 +class Kaminari::Helpers::Gap < ::Kaminari::Helpers::Tag; end + +# Link with page number that appears at the rightmost +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#111 +class Kaminari::Helpers::LastPage < ::Kaminari::Helpers::Tag + include ::Kaminari::Helpers::Link + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#113 + def page; end +end + +# Tag that contains a link +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#74 +module Kaminari::Helpers::Link + # target page number + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#76 + def page; end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#83 + def to_s(locals = T.unsafe(nil)); end + + # the link's href + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#80 + def url; end +end + +# The "next" page of the current page +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#140 +class Kaminari::Helpers::NextPage < ::Kaminari::Helpers::Tag + include ::Kaminari::Helpers::Link + + # TODO: Remove this initializer before 1.3.0. + # + # @return [NextPage] a new instance of NextPage + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#144 + def initialize(template, params: T.unsafe(nil), param_name: T.unsafe(nil), theme: T.unsafe(nil), views_prefix: T.unsafe(nil), **options); end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#155 + def page; end +end + +# source://kaminari-core//lib/kaminari/helpers/tags.rb#5 +Kaminari::Helpers::PARAM_KEY_EXCEPT_LIST = T.let(T.unsafe(nil), Array) + +# A page +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#90 +class Kaminari::Helpers::Page < ::Kaminari::Helpers::Tag + include ::Kaminari::Helpers::Link + + # target page number + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#93 + def page; end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#96 + def to_s(locals = T.unsafe(nil)); end +end + +# The main container tag +# +# source://kaminari-core//lib/kaminari/helpers/paginator.rb#9 +class Kaminari::Helpers::Paginator < ::Kaminari::Helpers::Tag + # @return [Paginator] a new instance of Paginator + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#10 + def initialize(template, window: T.unsafe(nil), outer_window: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), inner_window: T.unsafe(nil), **options); end + + # enumerate each page providing PageProxy object as the block parameter + # Because of performance reason, this doesn't actually enumerate all pages but pages that are seemingly relevant to the paginator. + # "Relevant" pages are: + # * pages inside the left outer window plus one for showing the gap tag + # * pages inside the inner window plus one on the left plus one on the right for showing the gap tags + # * pages inside the right outer window plus one for showing the gap tag + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#43 + def each_page; end + + # enumerate each page providing PageProxy object as the block parameter + # Because of performance reason, this doesn't actually enumerate all pages but pages that are seemingly relevant to the paginator. + # "Relevant" pages are: + # * pages inside the left outer window plus one for showing the gap tag + # * pages inside the inner window plus one on the left plus one on the right for showing the gap tags + # * pages inside the right outer window plus one for showing the gap tag + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#43 + def each_relevant_page; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#67 + def first_page_tag; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#67 + def gap_tag; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#67 + def last_page_tag; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#67 + def next_page_tag; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#61 + def page_tag(page); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#67 + def prev_page_tag; end + + # render given block as a view template + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#28 + def render(&block); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#73 + def to_s; end + + private + + # delegates view helper methods to @template + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#81 + def method_missing(name, *args, &block); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#52 + def relevant_pages(options); end +end + +# Wraps a "page number" and provides some utility methods +# +# source://kaminari-core//lib/kaminari/helpers/paginator.rb#87 +class Kaminari::Helpers::Paginator::PageProxy + include ::Comparable + + # @return [PageProxy] a new instance of PageProxy + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#90 + def initialize(options, page, last); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#177 + def +(other); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#181 + def -(other); end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#185 + def <=>(other); end + + # current page or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#100 + def current?; end + + # Should we display the link tag? + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#165 + def display_tag?; end + + # the first page or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#105 + def first?; end + + # inside the inner window or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#144 + def inside_window?; end + + # the last page or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#110 + def last?; end + + # within the left outer window or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#134 + def left_outer?; end + + # the next page or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#120 + def next?; end + + # the page number + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#95 + def number; end + + # The page number exceeds the range of pages or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#155 + def out_of_range?; end + + # the previous page or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#115 + def prev?; end + + # relationship with the current page + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#125 + def rel; end + + # within the right outer window or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#139 + def right_outer?; end + + # Current page is an isolated gap or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#149 + def single_gap?; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#169 + def to_i; end + + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#173 + def to_s; end + + # The last rendered tag was "truncated" or not + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/helpers/paginator.rb#160 + def was_truncated?; end +end + +# The "previous" page of the current page +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#119 +class Kaminari::Helpers::PrevPage < ::Kaminari::Helpers::Tag + include ::Kaminari::Helpers::Link + + # TODO: Remove this initializer before 1.3.0. + # + # @return [PrevPage] a new instance of PrevPage + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#123 + def initialize(template, params: T.unsafe(nil), param_name: T.unsafe(nil), theme: T.unsafe(nil), views_prefix: T.unsafe(nil), **options); end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#134 + def page; end +end + +# A tag stands for an HTML tag inside the paginator. +# Basically, a tag has its own partial template file, so every tag can be +# rendered into String using its partial template. +# +# The template file should be placed in your app/views/kaminari/ directory +# with underscored class name (besides the "Tag" class. Tag is an abstract +# class, so _tag partial is not needed). +# e.g.) PrevLink -> app/views/kaminari/_prev_link.html.erb +# +# When no matching templates were found in your app, the engine's pre +# installed template will be used. +# e.g.) Paginator -> $GEM_HOME/kaminari-x.x.x/app/views/kaminari/_paginator.html.erb +# +# source://kaminari-core//lib/kaminari/helpers/tags.rb#19 +class Kaminari::Helpers::Tag + # @return [Tag] a new instance of Tag + # + # source://kaminari-core//lib/kaminari/helpers/tags.rb#20 + def initialize(template, params: T.unsafe(nil), param_name: T.unsafe(nil), theme: T.unsafe(nil), views_prefix: T.unsafe(nil), **options); end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#36 + def page_url_for(page); end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#31 + def to_s(locals = T.unsafe(nil)); end + + private + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#44 + def params_for(page); end + + # source://kaminari-core//lib/kaminari/helpers/tags.rb#63 + def partial_path; end +end + +# source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#4 +module Kaminari::PageScopeMethods + # Current page number + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#47 + def current_page; end + + # Current per-page number + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#58 + def current_per_page; end + + # First page of the collection? + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#77 + def first_page?; end + + # Last page of the collection? + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#82 + def last_page?; end + + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#21 + def max_paginates_per(new_max_per_page); end + + # Next page number in the collection + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#67 + def next_page; end + + # Out of range of the collection? + # + # @return [Boolean] + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#87 + def out_of_range?; end + + # @raise [ArgumentError] + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#27 + def padding(num); end + + # Specify the per_page value for the preceding page scope + # Model.page(3).per(10) + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#7 + def per(num, max_per_page: T.unsafe(nil)); end + + # Previous page number in the collection + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#72 + def prev_page; end + + # Total number of pages + # + # source://kaminari-core//lib/kaminari/models/page_scope_methods.rb#35 + def total_pages; end +end + +# Kind of Array that can paginate +# +# source://kaminari-core//lib/kaminari/models/array_extension.rb#6 +class Kaminari::PaginatableArray < ::Array + include ::Kaminari::ConfigurationMethods::ClassMethods + + # ==== Options + # * :limit - limit + # * :offset - offset + # * :total_count - total_count + # * :padding - padding + # + # @return [PaginatableArray] a new instance of PaginatableArray + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#18 + def initialize(original_array = T.unsafe(nil), limit: T.unsafe(nil), offset: T.unsafe(nil), total_count: T.unsafe(nil), padding: T.unsafe(nil)); end + + # Used for page_entry_info + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#37 + def entry_name(options = T.unsafe(nil)); end + + # returns another chunk of the original array + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#49 + def limit(num); end + + # source://activesupport/7.2.1/lib/active_support/core_ext/module/attr_internal.rb#44 + def limit_value; end + + # source://activesupport/7.2.1/lib/active_support/core_ext/module/attr_internal.rb#44 + def limit_value=(_arg0); end + + # returns another chunk of the original array + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#59 + def offset(num); end + + # source://activesupport/7.2.1/lib/active_support/core_ext/module/attr_internal.rb#44 + def offset_value; end + + # source://activesupport/7.2.1/lib/active_support/core_ext/module/attr_internal.rb#44 + def offset_value=(_arg0); end + + # source://kaminari-core//lib/kaminari/models/array_extension.rb#43 + def page(num = T.unsafe(nil)); end + + # total item numbers of the original array + # + # source://kaminari-core//lib/kaminari/models/array_extension.rb#54 + def total_count; end +end + +# source://kaminari-core//lib/kaminari/models/array_extension.rb#9 +Kaminari::PaginatableArray::ENTRY = T.let(T.unsafe(nil), String) + +# source://kaminari-core//lib/kaminari/railtie.rb#5 +class Kaminari::Railtie < ::Rails::Railtie; end + +# source://kaminari-core//lib/kaminari/exceptions.rb#4 +class Kaminari::ZeroPerPageOperation < ::ZeroDivisionError; end diff --git a/sorbet/rbi/gems/tapioca@0.16.5.rbi b/sorbet/rbi/gems/tapioca@0.16.5.rbi index 9d11fb2..72ad848 100644 --- a/sorbet/rbi/gems/tapioca@0.16.5.rbi +++ b/sorbet/rbi/gems/tapioca@0.16.5.rbi @@ -1316,6 +1316,311 @@ end # source://tapioca//lib/tapioca/dsl/compilers.rb#6 module Tapioca::Dsl::Compilers; end +# `Tapioca::Dsl::Compilers::ActiveRecordRelations` decorates RBI files for subclasses of +# `ActiveRecord::Base` and adds +# [relation](http://api.rubyonrails.org/classes/ActiveRecord/Relation.html), +# [collection proxy](https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html), +# [query](http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html), +# [spawn](http://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html), +# [finder](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html), and +# [calculation](http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html) methods. +# +# The compiler defines 3 (synthetic) modules and 3 (synthetic) classes to represent relations properly. +# +# For a given model `Model`, we generate the following classes: +# +# 1. A `Model::PrivateRelation` that subclasses `ActiveRecord::Relation`. This synthetic class represents +# a relation on `Model` whose methods which return a relation always return a `Model::PrivateRelation` instance. +# +# 2. `Model::PrivateAssociationRelation` that subclasses `ActiveRecord::AssociationRelation`. This synthetic +# class represents a relation on a singular association of type `Model` (e.g. `foo.model`) whose methods which +# return a relation will always return a `Model::PrivateAssociationRelation` instance. The difference between this +# class and the previous one is mainly that an association relation also keeps track of the resource association +# for this relation. +# +# 3. `Model::PrivateCollectionProxy` that subclasses from `ActiveRecord::Associations::CollectionProxy`. +# This synthetic class represents a relation on a plural association of type `Model` (e.g. `foo.models`) +# whose methods which return a relation will always return a `Model::PrivateAssociationRelation` instance. +# This class represents a collection of `Model` instances with some extra methods to `build`, `create`, +# etc new `Model` instances in the collection. +# +# and the following modules: +# +# 1. `Model::GeneratedRelationMethods` holds all the relation methods with the return type of +# `Model::PrivateRelation`. For example, calling `all` on the `Model` class or an instance of +# `Model::PrivateRelation` class will always return a `Model::PrivateRelation` instance, thus the +# signature of `all` is defined with that return type in this module. +# +# 2. `Model::GeneratedAssociationRelationMethods` holds all the relation methods with the return type +# of `Model::PrivateAssociationRelation`. For example, calling `all` on an instance of +# `Model::PrivateAssociationRelation` or an instance of `Model::PrivateCollectionProxy` class will +# always return a `Model::PrivateAssociationRelation` instance, thus the signature of `all` is defined +# with that return type in this module. +# +# 3. `Model::CommonRelationMethods` holds all the relation methods that do not depend on the type of +# relation in their return type. For example, `find_by!` will always return the same type (a `Model` +# instance), regardless of what kind of relation it is called on, and so belongs in this module. +# This module is used to reduce the replication of methods between the previous two modules. +# +# Additionally, the actual `Model` class extends both `Model::CommonRelationMethods` and +# `Model::PrivateRelation` modules, so that, for example, `find_by` and `all` can be chained off of the +# `Model` class. +# +# **CAUTION**: The generated relation classes are named `PrivateXXX` intentionally to reflect the fact +# that they represent private subconstants of the Active Record model. As such, these types do not +# exist at runtime, and their counterparts that do exist at runtime are marked `private_constant` anyway. +# For that reason, these types cannot be used in user code or in `sig`s inside Ruby files, since that will +# make the runtime checks fail. +# +# For example, with 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 +# +# class Post +# extend CommonRelationMethods +# extend GeneratedRelationMethods +# +# module CommonRelationMethods +# sig { params(block: T.nilable(T.proc.params(record: ::Post).returns(T.untyped))).returns(T::Boolean) } +# def any?(&block); end +# +# # ... +# end +# +# module GeneratedAssociationRelationMethods +# sig { returns(PrivateAssociationRelation) } +# def all; end +# +# # ... +# +# sig { params(args: T.untyped, blk: T.untyped).returns(PrivateAssociationRelation) } +# def where(*args, &blk); end +# end +# +# module GeneratedRelationMethods +# sig { returns(PrivateRelation) } +# def all; end +# +# # ... +# +# sig { params(args: T.untyped, blk: T.untyped).returns(PrivateRelation) } +# def where(*args, &blk); end +# end +# +# class PrivateAssociationRelation < ::ActiveRecord::AssociationRelation +# include CommonRelationMethods +# include GeneratedAssociationRelationMethods +# +# sig { returns(T::Array[::Post]) } +# def to_a; end +# +# sig { returns(T::Array[::Post]) } +# def to_ary; end +# +# Elem = type_member { { fixed: ::Post } } +# end +# +# class PrivateCollectionProxy < ::ActiveRecord::Associations::CollectionProxy +# include CommonRelationMethods +# include GeneratedAssociationRelationMethods +# +# sig do +# params(records: T.any(::Post, T::Array[::Post], T::Array[PrivateCollectionProxy])) +# .returns(PrivateCollectionProxy) +# end +# def <<(*records); end +# +# # ... +# end +# +# class PrivateRelation < ::ActiveRecord::Relation +# include CommonRelationMethods +# include GeneratedRelationMethods +# +# sig { returns(T::Array[::Post]) } +# def to_a; end +# +# sig { returns(T::Array[::Post]) } +# def to_ary; end +# +# Elem = type_member { { fixed: ::Post } } +# end +# end +# ~~~ +# +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#151 +class Tapioca::Dsl::Compilers::ActiveRecordRelations < ::Tapioca::Dsl::Compiler + extend T::Generic + include ::Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper + + ConstantType = type_member { { fixed: T.class_of(ActiveRecord::Base) } } + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#178 + sig { override.void } + def decorate; end + + private + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#260 + sig { returns(::RBI::Scope) } + def association_relation_methods_module; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#281 + sig { params(method_name: ::Symbol).returns(T::Boolean) } + def bang_method?(method_name); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#268 + sig { returns(::RBI::Scope) } + def common_relation_methods_module; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#276 + sig { returns(::String) } + def constant_name; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#320 + sig { void } + def create_association_relation_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#347 + sig { void } + def create_association_relation_group_chain_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#602 + sig { void } + def create_association_relation_methods; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#425 + sig { void } + def create_association_relation_where_chain_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#286 + sig { void } + def create_classes_and_includes; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#458 + sig { void } + def create_collection_proxy_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#475 + sig { params(klass: ::RBI::Scope).void } + def create_collection_proxy_methods(klass); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#1038 + sig do + params( + name: T.any(::String, ::Symbol), + parameters: T::Array[::RBI::TypedParam], + return_type: T.nilable(::String) + ).void + end + def create_common_method(name, parameters: T.unsafe(nil), return_type: T.unsafe(nil)); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#647 + sig { void } + def create_common_methods; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#358 + sig { params(klass: ::RBI::Scope).void } + def create_group_chain_methods(klass); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#301 + sig { void } + def create_relation_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#339 + sig { void } + def create_relation_group_chain_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#1083 + sig do + params( + name: T.any(::String, ::Symbol), + parameters: T::Array[::RBI::TypedParam], + relation_return_type: ::String, + association_return_type: ::String + ).void + end + def create_relation_method(name, parameters: T.unsafe(nil), relation_return_type: T.unsafe(nil), association_return_type: T.unsafe(nil)); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#554 + sig { void } + def create_relation_methods; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#417 + sig { void } + def create_relation_where_chain_class; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#433 + sig { params(klass: ::RBI::Scope, return_type: ::String).void } + def create_where_chain_methods(klass, return_type); end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#1047 + sig { void } + def create_where_relation_method; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#244 + sig { returns(::RBI::Scope) } + def model; end + + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#252 + sig { returns(::RBI::Scope) } + def relation_methods_module; end + + class << self + # source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#189 + sig { override.returns(T::Enumerable[::Module]) } + def gather_constants; end + end +end + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#194 +Tapioca::Dsl::Compilers::ActiveRecordRelations::ASSOCIATION_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#231 +Tapioca::Dsl::Compilers::ActiveRecordRelations::BATCHES_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#238 +Tapioca::Dsl::Compilers::ActiveRecordRelations::BUILDER_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#232 +Tapioca::Dsl::Compilers::ActiveRecordRelations::CALCULATION_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#199 +Tapioca::Dsl::Compilers::ActiveRecordRelations::COLLECTION_PROXY_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#233 +Tapioca::Dsl::Compilers::ActiveRecordRelations::ENUMERABLE_QUERY_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#226 +Tapioca::Dsl::Compilers::ActiveRecordRelations::FINDER_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#234 +Tapioca::Dsl::Compilers::ActiveRecordRelations::FIND_OR_CREATE_METHODS = T.let(T.unsafe(nil), Array) + +# From ActiveRecord::ConnectionAdapter::Quoting#quote, minus nil +# +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#159 +Tapioca::Dsl::Compilers::ActiveRecordRelations::ID_TYPES = T.let(T.unsafe(nil), Set) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#205 +Tapioca::Dsl::Compilers::ActiveRecordRelations::QUERY_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#227 +Tapioca::Dsl::Compilers::ActiveRecordRelations::SIGNED_FINDER_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#239 +Tapioca::Dsl::Compilers::ActiveRecordRelations::TO_ARRAY_METHODS = T.let(T.unsafe(nil), Array) + +# source://tapioca//lib/tapioca/dsl/compilers/active_record_relations.rb#222 +Tapioca::Dsl::Compilers::ActiveRecordRelations::WHERE_CHAIN_QUERY_METHODS = T.let(T.unsafe(nil), Array) + # DSL compilers are either built-in to Tapioca and live under the # `Tapioca::Dsl::Compilers` namespace (i.e. this namespace), and # can be referred to by just using the class name, or they live in @@ -1326,9 +1631,39 @@ module Tapioca::Dsl::Compilers; end # source://tapioca//lib/tapioca/dsl/compilers.rb#13 Tapioca::Dsl::Compilers::NAMESPACES = T.let(T.unsafe(nil), Array) -# source://tapioca//lib/tapioca/dsl/helpers/active_record_constants_helper.rb#6 +# source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#6 module Tapioca::Dsl::Helpers; end +# source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#7 +module Tapioca::Dsl::Helpers::ActiveModelTypeHelper + class << self + # Returns the type indicated by the custom ActiveModel::Type::Value. + # Accepts subclasses of ActiveModel::Type::Value as well as classes that implement similar methods. + # + # source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#14 + sig { params(type_value: T.untyped).returns(::String) } + def type_for(type_value); end + + private + + # source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#51 + sig { params(obj: T.untyped, method: ::Symbol).returns(T.nilable(::T::Types::Base)) } + def lookup_arg_type_of_method(obj, method); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#43 + sig { params(obj: T.untyped, method: ::Symbol).returns(T.nilable(::T::Types::Base)) } + def lookup_return_type_of_method(obj, method); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#61 + sig { params(obj: T.untyped, method: ::Symbol).returns(T.untyped) } + def lookup_signature_of_method(obj, method); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_model_type_helper.rb#38 + sig { params(type: T.untyped).returns(T::Boolean) } + def meaningful_type?(type); end + end +end + # source://tapioca//lib/tapioca/dsl/helpers/active_record_constants_helper.rb#7 module Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper; end diff --git a/sorbet/tapioca/require.rb b/sorbet/tapioca/require.rb index 83a6ad7..283ec51 100644 --- a/sorbet/tapioca/require.rb +++ b/sorbet/tapioca/require.rb @@ -9,6 +9,7 @@ require "rails/generators" require "rails/generators/app_base" require "tapioca/dsl/compilers" +require "tapioca/dsl/compilers/active_record_relations" require "tapioca/dsl/helpers/active_record_constants_helper" require "tapioca/helpers/test/dsl_compiler" require "zeitwerk" diff --git a/spec/tapioca/dsl/compilers/kaminari_spec.rb b/spec/tapioca/dsl/compilers/kaminari_spec.rb new file mode 100644 index 0000000..db43873 --- /dev/null +++ b/spec/tapioca/dsl/compilers/kaminari_spec.rb @@ -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 From 12ea6b5b664a23b6d94bbad44f0e96fc62e0b8b0 Mon Sep 17 00:00:00 2001 From: Ray Zane Date: Tue, 3 Dec 2024 09:20:27 -0500 Subject: [PATCH 3/4] Add missing require to fix shim --- sorbet/rbi/gems/tapioca@0.16.5.rbi | 81 ++++++++++++++++++++++++++++++ sorbet/tapioca/require.rb | 1 + 2 files changed, 82 insertions(+) diff --git a/sorbet/rbi/gems/tapioca@0.16.5.rbi b/sorbet/rbi/gems/tapioca@0.16.5.rbi index 72ad848..c710da4 100644 --- a/sorbet/rbi/gems/tapioca@0.16.5.rbi +++ b/sorbet/rbi/gems/tapioca@0.16.5.rbi @@ -1664,6 +1664,87 @@ module Tapioca::Dsl::Helpers::ActiveModelTypeHelper end end +# source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#9 +class Tapioca::Dsl::Helpers::ActiveRecordColumnTypeHelper + include ::Tapioca::SorbetHelper + include ::Tapioca::RBIHelper + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#69 + sig do + params( + constant: T.class_of(ActiveRecord::Base), + column_type_option: ::Tapioca::Dsl::Helpers::ActiveRecordColumnTypeHelper::ColumnTypeOption + ).void + end + def initialize(constant, column_type_option: T.unsafe(nil)); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#80 + sig { params(attribute_name: ::String, column_name: ::String).returns([::String, ::String]) } + def type_for(attribute_name, column_name = T.unsafe(nil)); end + + private + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#223 + sig { params(base_type: ::String, column_nullability: T::Boolean).returns(::String) } + def as_non_nilable_if_persisted_and_not_nullable(base_type, column_nullability:); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#109 + sig { params(column_name: T.nilable(::String)).returns([::String, ::String]) } + def column_type_for(column_name); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#233 + sig { params(column_type: ::ActiveRecord::Enum::EnumType).returns(::String) } + def enum_setter_type(column_type); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#89 + sig { returns([::String, ::String]) } + def id_type; end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#261 + sig { params(column_type: T.untyped).returns(T::Boolean) } + def not_nilable_serialized_column?(column_type); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#244 + sig { params(column_type: ::ActiveRecord::Type::Serialized).returns(::String) } + def serialized_column_type(column_type); end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#132 + sig { params(column_type: T.untyped, column_nullability: T::Boolean).returns(::String) } + def type_for_activerecord_value(column_type, column_nullability:); end +end + +# source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#13 +class Tapioca::Dsl::Helpers::ActiveRecordColumnTypeHelper::ColumnTypeOption < ::T::Enum + enums do + Nilable = new + Persisted = new + Untyped = new + end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#53 + sig { returns(T::Boolean) } + def nilable?; end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#48 + sig { returns(T::Boolean) } + def persisted?; end + + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#58 + sig { returns(T::Boolean) } + def untyped?; end + + class << self + # source://tapioca//lib/tapioca/dsl/helpers/active_record_column_type_helper.rb#31 + sig do + params( + options: T::Hash[::String, T.untyped], + block: T.proc.params(value: ::String, default_column_type_option: ::Tapioca::Dsl::Helpers::ActiveRecordColumnTypeHelper::ColumnTypeOption).void + ).returns(::Tapioca::Dsl::Helpers::ActiveRecordColumnTypeHelper::ColumnTypeOption) + end + def from_options(options, &block); end + end +end + # source://tapioca//lib/tapioca/dsl/helpers/active_record_constants_helper.rb#7 module Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper; end diff --git a/sorbet/tapioca/require.rb b/sorbet/tapioca/require.rb index 283ec51..ba51f2a 100644 --- a/sorbet/tapioca/require.rb +++ b/sorbet/tapioca/require.rb @@ -10,6 +10,7 @@ require "rails/generators/app_base" require "tapioca/dsl/compilers" require "tapioca/dsl/compilers/active_record_relations" +require "tapioca/dsl/helpers/active_record_column_type_helper" require "tapioca/dsl/helpers/active_record_constants_helper" require "tapioca/helpers/test/dsl_compiler" require "zeitwerk" From 59bb7e3221610e91e348fe454e93226cc01fd8a7 Mon Sep 17 00:00:00 2001 From: Ray Zane Date: Tue, 3 Dec 2024 09:36:57 -0500 Subject: [PATCH 4/4] These tests require isolation to avoid polluting the global namespace --- spec/boba/active_record/attribute_service_spec.rb | 1 + spec/boba/active_record/reflection_service_spec.rb | 1 + spec/spec_helper.rb | 2 ++ 3 files changed, 4 insertions(+) diff --git a/spec/boba/active_record/attribute_service_spec.rb b/spec/boba/active_record/attribute_service_spec.rb index 7ffcb6e..a69c00d 100644 --- a/spec/boba/active_record/attribute_service_spec.rb +++ b/spec/boba/active_record/attribute_service_spec.rb @@ -10,6 +10,7 @@ module ActiveRecord class AttributeServiceSpec < ::Minitest::Spec extend T::Sig include Tapioca::Helpers::Test::Content + include Tapioca::Helpers::Test::Isolation before do ::ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") diff --git a/spec/boba/active_record/reflection_service_spec.rb b/spec/boba/active_record/reflection_service_spec.rb index c017542..40879d9 100644 --- a/spec/boba/active_record/reflection_service_spec.rb +++ b/spec/boba/active_record/reflection_service_spec.rb @@ -10,6 +10,7 @@ module ActiveRecord class ReflectionServiceSpec < ::Minitest::Spec extend T::Sig include Tapioca::Helpers::Test::Content + include Tapioca::Helpers::Test::Isolation before do ::ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d10a093..4b85655 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,6 +7,8 @@ require "minitest/spec" require "rails/test_unit/line_filtering" require "tapioca/internal" +require "tapioca/helpers/test/content" +require "tapioca/helpers/test/isolation" require_relative "dsl_spec_helper"