-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compilers can define their execution order #2114
Conversation
This PR is an alternative to #2112 |
I don't think this is true. You could prepend a module in the Since the last few versions, Tapioca gives you the guarantee that it will load its internal compilers before loading custom compilers, so that this kind of modification is possible.
Same comment here. Injecting behaviour into built-in Tapioca compilers is possible. See this test for more details on how that is possible: tapioca/spec/tapioca/cli/dsl_spec.rb Lines 2250 to 2308 in cc54939
In general, I really don't want to add any kind of ordering guarantees to compilers, since that will be brittle and full of race conditions. |
Thanks @paracycle, I'm aware that I could monkey patch Tapioca to achieve the desired results, but is that really the best way to solve this problem? Is there another solution to this problem that you might be more open to? Can you explain why sorting the compilers would be brittle and full of race conditions? |
Because Tapioca does not control a central list of compilers, they could be coming from the app, any of the gems it includes, or any other source. If we were to add the ability to declare ordering, that implies:
All of this is highly problematic, and means additional code and workflows to support. Instead, it is already possible to modify the behaviour of built-in compilers, and until we meet a case where that is not enough, we shouldn't have to take on the burden of adding a new feature. |
I probably should have included in my example that the compiler needs to require the other compiler, but I thought that was implied. So, for example, you'd say: require "tapioca/dsl/compilers/active_record_columns"
class Tapioca::Dsl::Compilers::SteadyState < Tapoica::Dsl::Compiler
def self.run_after_compilers
[Tapioca::Dsl::Compilers::ActiveRecordColumns]
end
# ... etc ...
end
I agree that this could be a little awkward, but this strategy does provide a way to deal with that issue. Isn't this already a problem anyway? If B and C both define a method, your RBI files are invalid.
Yes, through monkey-patching, which is a much bigger code smell. The If we want the Ruby community to adopt Tapioca, this feels like a requisite feature, in my opinion. Do we really want gems that ship Tapioca compilers to be required to monkey-patch Tapioca? |
Motivation
Tapioca generates types for ActiveModel and ActiveRecord attributes, and usually does the right thing. However, there are some cases where more flexibility is necessary. It would be useful to be able to override the definitions provided by Tapioca's default compilers in very special cases.
Example 1: ActiveModel attributes without a type
Not all ActiveModel attributes can be represented with ActiveModel's built-in types. It also isn't practical to define an
ActiveModel::Type
for every type of value that might be passed to an ActiveModel.For example, many ActiveModel instances take a
User
as a parameter (e.g.attribute :user
). I'd like to be able to tell Sorbet that this is aUser
. Unfortunately, the Tapioca compiler defines this field asT.untyped
. Without forking the ActiveModel compiler, I have no way of overriding the type defined by Tapioca.Example 2: Column types don't necessarily represent method return types
We use a gem called
steady_state
. It defines a state machine and stores the state in a string column on the record.This gem overrides the attribute reader to return an instance of
SteadyState::Attribute::State
, which is basically anActiveSupport::StringInquirer
.Without forking the ActiveRecordColumns compiler, I have no way of overriding the type defined by Tapioca.
Implementation
Step 1: Allow DSL compilers to control execution order
This PR implements an optional compiler method
.run_after_compilers
. Compilers can tell Tapioca which other compilers they expect to precede them.So, for example, my SteadyState compiler could say:
Tapioca can use
TSort
to ensure that the steady state compiler runs afterActiveRecordColumns
.Step 2: Allow DSL compilers to redefine methods
This PR implements
RBI::Tree#remove_method
, which removes a method from the tree by name.Tests
This PR includes tests.