KEMBAR78
Add Refinement#import_methods by palkan · Pull Request #85 · ruby-next/ruby-next · GitHub
Skip to content

Conversation

@palkan
Copy link
Collaborator

@palkan palkan commented Dec 2, 2021

What is the purpose of this pull request?

Provide Refinement#import_methods functionally to older versions to allow library authors to migrate to this safer way of re-using code in refinements.

See https://bugs.ruby-lang.org/issues/17429

What changes did you make? (overview)

  • Added RubyNext::Core#import_methods API
  • Added a rewriter to automatically pass binding to #import_methods and use RubyNext::Core.import_methods (see below).

Is there anything you'd like reviewers to focus on?

It's hardly possible to backport the #import_methods method using pure Ruby for several reasons:

  • We want to allow imported methods to access their original dynamic scope (e.g., use constants defined in the imported module)
  • We want to activate the current refinement for imported methods, i.e., the lexical scope should be kinda the same as for the current refinement.
  • Ruby 2.6- doesn't support using Module refinements within refine do ... end—that makes it impossible to rely on adding a Module#import_methods refinement, we have to use our own helper method (RubyNext::Core.import_methods).

There is no way to manipulate the lexical scope in Ruby. Okay, there is—we can use bindings. And the only way to create something with bindings is to evaluate source code.

So, here is how our back port works:

  • We extract the source of all imported module's own methods (and raise if there are some C methods or dynamically defined ones—this is the first limitation)
  • We also "extract" constants (so they're accessible in the refinement)
  • And we require a Binding object to be passed as the second argument; for that, we provide a rewriter (so, the Ruby Next transpiler does this for you).

Sounds too complex, right? That's okay for a transpiler 🙂 But I'm open for suggestions and any kind of critics.

Checklist

  • I've added tests for this change
  • I've added a Changelog entry
  • I've updated a documentation/SUPPORTED_FEATURES.md

@palkan palkan force-pushed the feat/refinement-import-methods branch from e9fb160 to 77912b4 Compare December 2, 2021 15:30
@palkan palkan force-pushed the feat/refinement-import-methods branch 3 times, most recently from cab32be to 64c0d63 Compare December 22, 2021 11:11
@palkan palkan marked this pull request as ready for review December 22, 2021 11:11
@palkan palkan force-pushed the feat/refinement-import-methods branch 2 times, most recently from 7b81e44 to 98a754a Compare December 22, 2021 11:14
Ruby 2.6- doesn't support refined Module methods for refinement modules. Since we already require transpiling, we can use a custom method instead of extending Module
@palkan palkan force-pushed the feat/refinement-import-methods branch from 98a754a to adfe9b4 Compare December 22, 2021 11:14
@palkan palkan merged commit f391874 into master Dec 24, 2021
@palkan palkan deleted the feat/refinement-import-methods branch December 24, 2021 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant