This document describes the design of rbs_rails
gem, and the future works.
It also describes Rails-related gems' RBS files in ruby/gem_rbs_collection repository.
Some classes, modules, and interfaces are depend on RBS Rails. Be careful to introduce breaking changes to them.
Currently this repository contains generated and hand-written RBS files. I want to distinguish them to run the generator continuously, but unfortunately they are mixed.
In the future, probably unifying these files is better. We do not need to distinguish generated and hand-written files.
But probably we need to distinguish patch.rbs
because the patch.rbs
file contains RBS that is not for the gem itself, but other dependencies.
As I mentioned the previous section, the RBS files are generated. They contain not only public APIs but also private APIs.
I think we can drop RBS definitions for private APIs. They are unnecessary information for Rails user. They also make the RBS file larger. We can keep the RBS files slim by removing unnecessary definitions.
I'm concerning that some private APIs are actually used by the user… Probably we need to leave some private API.
This section describes the design of RBS Rails gem.
Such as action_args.
The generated RBS files by RBS Rails should be valid only with the Rails related RBS files in ruby/gem_rbs_collection.
It means the user does not need to write RBS definition to make the generated RBS valid.
"valid" means rbs validate
command passes
RBS Rails construct RBS content as a String.
We can also construct with RBS AST, but I think it is over-engineering. String construction is enough in this case.
But probably it should escape correctly, such as method name. Probably we can solve this problem with using RBS::Writer
partially.
This section describes what I want to achieve by RBS Rails in the future.
Currently, the generated method is defined in a pseudo module. It introduces some problems, so we should define these method in the right places.
I didn't focus on it. Currently RBS Rails generates type definitions for path helpers, but actually it is not utilized. Probably we can improve it.
Currently, RBS Rails fetches metadata with two ways. They are static and dynamic analysis.
In short, we should remove static analysis because it is incomplete.
For example, currently RBS Rails uses static analysis to get enum metadata.
https://github.com/pocke/rbs_rails/blob/4ba3f0fa61f961590213cc04e83a5433166691ed/lib/rbs_rails/active_record.rb#L356-L359
It's because Rails does not store enough information to construct the RBS definitions.
But the static analysis has a problem. It does not work on enum
definitions in another files. For example:
# publishable.rb
module Publishable
extend AS::Concern
included do
enum state: { draft: 1, published: 2 }
end
end
# note.rb
class Note < ApplicationRecord
include Publishable
end
In the above case, RBS Rails does not detect state
enum because RBS Rails only parse note.rb
but the enum defined in publishable.rb
.
To solve this problem, I think we need to introduce dynamic analysis with different approach with other features. We can add a module to override tne enum
method. For example:
module EnumInspection
def enum(entry)
store_the_enum_entry entry
super
end
end
ActiveRecord::Base.singleton_class.prepend EnumInspection
It can use the data that enum
method received, so it has enough information to generate RBS definitions. It also depends only on public APIs, so it is stable.
However, I'm concerning that there is a right timing to prepend the module. This module should be prepended before evaluating all user defined code.
Perhaps we can replace other dynamic analysis with this solution. Such as has_many
. I think the prepending solution is better if the dynamic analysis depends on a Rails private API. We need to research that RBS Rails depends on private APIs or not.