Skip to content
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

Stop requiring stuff from DSL compilers #1765

Merged
merged 6 commits into from
Jan 23, 2024
Merged

Conversation

paracycle
Copy link
Member

@paracycle paracycle commented Jan 15, 2024

Motivation

Fix #1759
Fix #1425

DSL compilers should not change the object space of the application they run in, and thus should not really be doing any requires. This commit removes all requires from DSL compilers, and instead relies on the application (or, in our case, the tests) to require the necessary files.

A DSL compiler should be responsible for checking if the constants it is expecting to have been loaded are actually loaded, and if not, it should not be defining the DSL compiler class at all.

Implementation

  • Remove all the require lines from DSL compilers, replace them with early returns guarding for defined? for relevant constants.
  • Add necessary require lines inside before_setup methods in tests, so that they get required after we fork (for isolation), but before we load the actual DSL compiler class.
  • DSL extension are loaded before the application is loaded, so there is no good way to test what framework components an app is using for those. However, we can rely on Active Support lazy load hooks to only trigger the mixing-in of the extension module into the target module, thus allowing us to only load the extensions when they are needed. This works perfectly for Active Record (since it runs a lazy load hook when the top level constant is defined), but doesn't work that well for other extensions. Currently, we use the Rails :before_configuration hook to check to see if FrozenRecord or Kredis have been loaded and do the mix-in. This, however, only will work properly for Rails applications.

Tests

Updates tests

@paracycle paracycle requested a review from a team as a code owner January 15, 2024 22:10
DSL compilers should not change the object space of the application they run in, and thus should not really be doing any requires. This commit removes all requires from DSL compilers, and instead relies on the application to require the necessary files.

A DSL compiler should be responsible for checking if the constants it is expecting to have been loaded are actually loaded, and if not, it should not be defining the DSL compiler class at all.
@paracycle paracycle force-pushed the uk-change-dsl-activation-logic branch 2 times, most recently from edda967 to c481d0f Compare January 15, 2024 23:12
@sambostock
Copy link
Contributor

sambostock commented Jan 16, 2024

When do the compiler extensions get loaded? I see they still use the "attempt to require, or return" approach, but maybe that's fine?

Nevermind, I just missed them somehow... 🤦‍♂️

rescue LoadError
return
end
return unless defined?(AASM)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was requiring Active Record as well. Does the compiler need ActiveRecord to be defined too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't. I think we were requiring AR to activate the AR support in AASM, but I don't think that's something that is required.

I'll check this again, though.

lib/tapioca/dsl/compilers/action_text.rb Show resolved Hide resolved
lib/tapioca/dsl/compilers/url_helpers.rb Outdated Show resolved Hide resolved
@andyw8
Copy link
Contributor

andyw8 commented Jan 16, 2024

Would it be worth adding a cop to prevent any accidental future use of require within DSL compilers?

@paracycle
Copy link
Member Author

When do the compiler extensions get loaded? I see they still use the "attempt to require, or return" approach, but maybe that's fine?

Nevermind, I just missed them somehow... 🤦‍♂️

You actually make a great point. I need to change how extensions work, since they are loaded before the application is loaded. As things stand, these extensions just won't work.

Extensions cannot guard against the constants being available at load time, since we load extensions before we load the application. Instead, we need to register load hooks that will be called when the relevant constants are available.

For now, this only works for Active Record in every case, since Active Record has a lazy load hook that we can use. For Frozen Record and Kredis we need to rely on the Rails load hooks to delayed trigger of the extension loading. But, that won't be available outside of Rails.
@paracycle paracycle force-pushed the uk-change-dsl-activation-logic branch from 6753bfe to fe1dae4 Compare January 22, 2024 20:57
@paracycle
Copy link
Member Author

Ok, I've reworked the extensions to do lazy loading, and made the UrlHelpers compiler to work in a more fine-grained way. This PR should be ready for another round of reviews.

@Morriar
Copy link
Collaborator

Morriar commented Jan 22, 2024

Test failures seem legit

Instead of refusing to load the compiler if certain constants are not defined, we can make our dependency on those constants to be dynamic instead.
@paracycle paracycle force-pushed the uk-change-dsl-activation-logic branch from bc161a7 to c2983e7 Compare January 22, 2024 23:55
@paracycle
Copy link
Member Author

Ok, tests are fixed!

rescue LoadError
return
end
return unless defined?(Rails) && defined?(ActiveSupport::TestCase) && defined?(ActiveRecord::TestFixtures)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could define a defined?(*args) in self?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's possible since defined? is a special language construct and not a method. If we define a defined? method, then arguments to it will be evaluated at the call site, but that's not what we want.

@paracycle paracycle merged commit 6908fbc into main Jan 23, 2024
33 checks passed
@paracycle paracycle deleted the uk-change-dsl-activation-logic branch January 23, 2024 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for granular rails framework usage Tapioca loads ActiveRecord every time
5 participants