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

Make Module#prepend take a block #1639

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

adelcambre
Copy link

In our codebase we have a few instances where we need, for a variety of reasons, to monkey patch a class. With the advent of Module#prepend, this is now much cleaner and safer than the days of alias_method_chain from Rails or carefully aliasing and calling the original in plain ruby.

We have a pattern when we do these kinds of things that look like this:

Module.new do
  def some_original_method
    do_something_else
    super
  end
end.tap { |mod| OriginalClass.prepend(mod) }

This works great, but it's not the nicest syntax. It would be really nice if you could just pass a block directly to Module#prepend that's what this PR does. This would allow one to rewrite the snippet above more like this:

OriginalClass.prepend do
  def some_original_method
    do_something_else
    super
  end
end

@nobu
Copy link
Member

nobu commented Jun 3, 2017

You can write the former without tap.

OriginalClass.prepend Module.new {
  def some_original_method
    do_something_else
    super
  end
}

eval.c Outdated

CONST_ID(id_prepend_features, "prepend_features");
CONST_ID(id_prepended, "prepended");

rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
if(rb_block_given_p()) {
Copy link
Member

Choose a reason for hiding this comment

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

should check arity when a block is given.

Copy link
Author

Choose a reason for hiding this comment

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

@nobu My assumption in this case is that if a block is given then no arguments are required, which would then result in an arity check of 0, UNLIMITED_ARGUMENTS which as I understand it would be a noop. Is that not the case?

Copy link
Member

Choose a reason for hiding this comment

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

To allow any number of arguments and to ignore given arguments silently are different.

Copy link
Author

@adelcambre adelcambre Jun 8, 2017

Choose a reason for hiding this comment

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

The PR as it currently stands still uses the other modules passed on the command line too. It doesn't ignore any arguments.

Copy link
Author

Choose a reason for hiding this comment

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

I added another test showing this behavior in 3331f39

eval.c Outdated

CONST_ID(id_prepend_features, "prepend_features");
CONST_ID(id_prepended, "prepended");

rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
if(rb_block_given_p()) {
new_mod = rb_module_new();
Copy link
Member

Choose a reason for hiding this comment

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

adjust the style and indent.
in general, not only ruby, you should respect the style in the original files, not your favorite style.

Copy link
Author

Choose a reason for hiding this comment

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

I attempted to more closely match the style in 0439e5c. There were already two indentation styles so I tried to follow the one that seemed more common.

@@ -1572,6 1572,16 @@ def test_prepend
assert_equal(expected, obj.m1)
end

def test_prepend_block
C0.prepend do
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this modifying C0 for all following tests? If you'll run test_prepend after test_prepend_block, will it fail?

Copy link
Author

Choose a reason for hiding this comment

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

@simi I couldn't get it to fail in any order, but that's a great point. I modified the test to just make a dynamic class in the test rather than modifying C0.

@k0kubun k0kubun changed the base branch from trunk to master August 15, 2019 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants