How to deal a module included by a specified class like ActiveRecord::Base?
I write a concern module that supposes to be included by ActiveRecord::Base's subclass.
like following,
module A
extend ActiveSupport::Concern
included do
has_many :hoge
end
def bar
save
end
end
In such a case, module A supposes to have ActiveRecord::Base methods.
But, in the current RBS, developers must write all ActiveRecord::Base method signatures by themselves.
Because module cannot inherit class and class cannot be included (extended).
I want a helper syntax to include method signatures of other classes whether are not modules or interfaces.
For example:
module A
included_by ActiveRecord::Base # define method signatures with ActiveRecord::Base type
end
How about to use module self types?
# a.rbs
module A : ActiveRecord::Base
extend ActiveSupport::Concern
end
It will supports A#bar signature.
As for #included, I'm still looking for a solution, but self type binding may help.
It works with a few patches at hand. (need rbs 2.7.0.pre.1 and steep master)
module ActiveSupport
module Concern
def included: () { () [self: singleton(ActiveRecord::Base)] -> void } -> void
| ...
end
end
module A : ActiveRecord::Base
extend ActiveSupport::Concern
end
However, this approach causes all ActiveSupport::Concerns#included to expect ActiveRecord::Base.
Thanks!!
It is very similar to what I want.
I solved many check errors with self-type-binding
And, I have one more point.
ActiveSupport::Concern provides ClassMethods module technique.
module A
module ClassMethods
def find_by_hoge
end
end
end
I don't know how to bind ClassMethods with singleton(ActiveRecord::Base) or singleton(A)
If you have a solution, could you please let me know?
@joker1007
I'm not sure if I correctly catch your question: One of the possible workarounds to support ClassMethods pattern is explicitly extending it.
class ActiveRecord::Base
include A
extend A::ClassMethods # Explicitly extend it because RBS doesn't support `included` hook
end
@soutaro Perhaps @joker1007 is asking how to specify the inner context of the ClassMethods module using self module type?(Sorry I don't know the answer...)
module A : ActiveRecord::Base
module ClassMethods : ???
def find_by_hoge(hoge)
find_by(hoge: hoge) # want to check type of `find_by`
end
end
end
@ksss Thanks! That's correct!
I want to check ClassMethods module itself and ClassMethods supposes to have class methods of ActiveRecord::Base like find_by, find_or_initialize, and ActiveRecord::Relation methods.
However, the current type checking by RBS cannot find those method signatures, so I think I need to copy the type definitions or write them myself. It is a certain amount of work.
@soutaro Is there any solution to define method signature easily?
@joker1007 We don't have a straightforward way to do it. You can define an interface, that would help defining the requirements on the module.
module A : ActiveRecord::Base
interface _WithFindBy[Relation < ActiveRecord::Relation]
def find_by: (**untyped) -> Relation
end
module ClassMethods[Relation < ActiveRecord::Relation] : _WithFindBy[Relation]
def find_by_hoge(hoge): ...
end
end
But, it doesn't look great. Looks too complicated...
We may need to let the module self types to be singleton type.
module A : ActiveRecord::Base
module ClassMethods : singleton(ActiveRecord::Base)
def find_by_hoge(hoge): ...
end
end