Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions lib/tapioca/dsl/compilers/identity_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def create_fetch_by_methods(field, klass)
is_cache_index = field.instance_variable_defined?(:@attribute_proc)

# Both `cache_index` and `cache_attribute` generate aliased methods
create_aliased_fetch_by_methods(field, klass)
create_aliased_fetch_by_methods(field, klass, is_cache_index: is_cache_index)

# If the method used was `cache_index` a few extra methods are created
create_index_fetch_by_methods(field, klass) if is_cache_index
Expand Down Expand Up @@ -182,8 +182,8 @@ def create_index_fetch_by_methods(field, klass)
)
end

#: (untyped field, RBI::Scope klass) -> void
def create_aliased_fetch_by_methods(field, klass)
#: (untyped field, RBI::Scope klass, is_cache_index: bool) -> void
def create_aliased_fetch_by_methods(field, klass, is_cache_index:)
type, _ = Helpers::ActiveRecordColumnTypeHelper.new(
constant,
column_type_option: Helpers::ActiveRecordColumnTypeHelper::ColumnTypeOption::Nilable,
Expand All @@ -195,6 +195,15 @@ def create_aliased_fetch_by_methods(field, klass)
create_param(arg.to_s, type: "T.untyped")
end

multi_value_type =
if field.unique
type
elsif is_cache_index
COLLECTION_TYPE.call(multi_type)
else
COLLECTION_TYPE.call(type)
end

klass.create_method(
"fetch_#{suffix}",
class_method: true,
Expand All @@ -206,7 +215,7 @@ def create_aliased_fetch_by_methods(field, klass)
"fetch_multi_#{suffix}",
class_method: true,
parameters: [create_param("keys", type: "T::Enumerable[T.untyped]")],
return_type: COLLECTION_TYPE.call(multi_type),
return_type: "T::Hash[T.untyped, #{multi_value_type}]",
)
end
end
Expand Down
62 changes: 53 additions & 9 deletions spec/tapioca/dsl/compilers/identity_cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ def fetch_multi_by_blog_id(index_values, includes: nil); end
sig { params(index_values: T::Enumerable[T.untyped], includes: T.untyped).returns(T::Array[::Post]) }
def fetch_multi_by_title(index_values, includes: nil); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::Integer]]) }
def fetch_multi_id_by_blog_id(keys); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::Integer]]) }
def fetch_multi_id_by_title(keys); end
end
end
Expand Down Expand Up @@ -160,10 +160,10 @@ def fetch_multi_by_blog_id(index_values, includes: nil); end
sig { params(index_values: T::Enumerable[T.untyped], includes: T.untyped).returns(T::Array[::Post]) }
def fetch_multi_by_title(index_values, includes: nil); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::Integer]]) }
def fetch_multi_id_by_blog_id(keys); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T.nilable(::Integer)]) }
def fetch_multi_id_by_title(keys); end
end
end
Expand Down Expand Up @@ -205,7 +205,7 @@ def fetch_id_by_blog_id_and_title(blog_id, title); end
sig { params(index_values: T::Enumerable[T.untyped], includes: T.untyped).returns(T::Array[::Post]) }
def fetch_multi_by_blog_id_and_title(index_values, includes: nil); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::Integer]]) }
def fetch_multi_id_by_blog_id_and_title(keys); end
end
end
Expand Down Expand Up @@ -260,10 +260,10 @@ def fetch_multi_by_title(index_values, includes: nil); end
sig { params(index_values: T::Enumerable[T.untyped], includes: T.untyped).returns(T::Array[::Post]) }
def fetch_multi_by_title_and_review_date(index_values, includes: nil); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::Integer]]) }
def fetch_multi_id_by_title(keys); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::Integer]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T.nilable(::Integer)]) }
def fetch_multi_id_by_title_and_review_date(keys); end
end
end
Expand Down Expand Up @@ -401,6 +401,7 @@ def fetch_user; end
ActiveRecord::Schema.define do
create_table :posts do |t|
t.string :author
t.string :name
end
end
end
Expand All @@ -412,6 +413,7 @@ class Post < ActiveRecord::Base

cache_attribute :author, by: :id
cache_attribute :author, by: [:id, :author]
cache_attribute :name, by: :author, unique: false
end
RUBY

Expand All @@ -426,11 +428,53 @@ def fetch_author_by_id(id); end
sig { params(id: T.untyped, author: T.untyped).returns(T.nilable(::String)) }
def fetch_author_by_id_and_author(id, author); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::String]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T.nilable(::String)]) }
def fetch_multi_author_by_id(keys); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Array[::String]) }
sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T.nilable(::String)]) }
def fetch_multi_author_by_id_and_author(keys); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T::Array[::T.nilable(::String)]]) }
def fetch_multi_name_by_author(keys); end

sig { params(author: T.untyped).returns(T::Array[::T.nilable(::String)]) }
def fetch_name_by_author(author); end
end
end
RBI

assert_equal(expected, rbi_for(:Post))
end

it "allows missing unique cache attributes for non-nullable columns" do
add_ruby_file("schema.rb", <<~RUBY)
ActiveRecord::Migration.suppress_messages do
ActiveRecord::Schema.define do
create_table :posts do |t|
t.string :author, null: false
end
end
end
RUBY

add_ruby_file("post.rb", <<~RUBY)
class Post < ActiveRecord::Base
include IdentityCache

cache_attribute :author, by: :id
end
RUBY

expected = <<~RBI
# typed: strong

class Post
class << self
sig { params(id: T.untyped).returns(T.nilable(::String)) }
def fetch_author_by_id(id); end

sig { params(keys: T::Enumerable[T.untyped]).returns(T::Hash[T.untyped, T.nilable(::String)]) }
def fetch_multi_author_by_id(keys); end
end
end
RBI
Expand Down
Loading