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

RSpec makes define_method public? #873

Merged
merged 3 commits into from
Apr 18, 2013

Conversation

JonRowe
Copy link
Member

@JonRowe JonRowe commented Apr 18, 2013

Beats me, but I just stumbled upon a passing test that failed IRL because it used the private Module.define_method. Seems that after requiring RSpec, it's not private any more.

On Ruby v1.9.3 with RSpec v2.13:

>> Object.define_method
NoMethodError: private method `define_method' called for Object:Class
    from (irb):1
    from /usr/local/bin/irb:12:in `<main>'
>> require "rspec"
=> true
>> Object.define_method
ArgumentError: wrong number of arguments (0 for 1)
    from (irb):3:in `define_method'
    from (irb):3
    from /usr/local/bin/irb:12:in `<main>'

@thomas-holmes
Copy link
Member

Looks like it is intentionally being made to be this way in memoization_helpers.rb:475

@myronmarston
Copy link
Member

Looks like it is intentionally being made to be this way in memoization_helpers.rb:475

That's only making it public in an anonymous module, though. I don't think that affect Object, although I could be wrong...

Regardless, we should fix this. Thanks for reporting it!

@thomas-holmes
Copy link
Member

I will try to look into it further, I'd like to help 👍

@thomas-holmes
Copy link
Member

I tried defining another method in the same place that was private (method_undefined) and it became public on Object. I think that definitively makes this the culprit. Any suggestions on how to proceed containing this?

@moll
Copy link
Author

moll commented Apr 17, 2013

Other than ditching Ruby? :) I'd say removing line 475 as the following holds true:

>> Object.define_method
NoMethodError: private method `define_method' called for Object:Class
        from (irb):1
        from /usr/local/bin/irb:12:in `<main>'
>> module Foo
>>   public_class_method :define_method
>> end
=> Foo
>> Object.define_method
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):3:in `define_method'
        from (irb):3
        from /usr/local/bin/irb:12:in `<main>'

@myronmarston
Copy link
Member

To me, it feels like a bug in ruby. Regardless, we should fix the code so it doesn't do this. The reason I had put public_class_method :define_method was to make it easy to define new methods on those modules, as that's basically the role they serve. We can remove that line and use send instead, though. Alternately, if you can find another way to make define_method public w/o it leaking....then have at it :).

thomas-holmes pushed a commit to thomas-holmes/rspec-core that referenced this pull request Apr 18, 2013
MemoizedHelpers::ClassMethods module was making define_method
public for ease of use within the module. No longer do this and
use send instead. This fixes rspec#873.
@ghost ghost assigned JonRowe Apr 18, 2013
@JonRowe
Copy link
Member

JonRowe commented Apr 18, 2013

I'm guessing public_class_method works on the original class it's defined on rather than the one we're creating, changing how we define the method as public (by redefining the method) works on the current class.

I've also added a test to ensure we don't leak it.

Review? /cc @myronmarston @samphippen @soulcutter

@moll
Copy link
Author

moll commented Apr 18, 2013

Hehe, after reporting this I investigated how visibility works in Ruby and found out the public method applies only to instance methods of the current class and thereby does not apply to methods set on the singleton class. It's a nuts system because the public you, @JonRowe, added on line 475 is useless.

Which, on a side note, is probably a SNAFU everywhere where people expect methods defined via def self.foo following private to be private.

@myronmarston
Copy link
Member

Thanks to both of you for working on this!

The spec added by @JonRowe is nice (we want specs for this kind of stuff to prevent regressions), but I think I prefer the simplicity of the @thomas-holmes's fix. I had originally made the method public as a convenience...but the solution of redefining define_method to make it public seems more complicated then just using send.

JonRowe and others added 2 commits April 18, 2013 16:02
MemoizedHelpers::ClassMethods module was making define_method
public for ease of use within the module. No longer do this and
use send instead. This fixes #873.
@JonRowe
Copy link
Member

JonRowe commented Apr 18, 2013

Ok I've picked across @thomas-holmes' fix, just rerunning the specs then I'll merge it.

@myronmarston
Copy link
Member

@JonRowe -- thanks! Please add a changelog entry, too.

@thomas-holmes
Copy link
Member

Thanks guys!

JonRowe added a commit that referenced this pull request Apr 18, 2013
@JonRowe JonRowe merged commit 5fd60b5 into master Apr 18, 2013
@myronmarston
Copy link
Member

So this is interesting:

https://bugs.ruby-lang.org/issues/8284

I started a discussion on the ruby rogues mailing list about this, someone reporte the bug to the ruby issue tracker and it got fixed. Turns out it's been a bug in MRI since ruby 1.0.

@JonRowe
Copy link
Member

JonRowe commented Apr 18, 2013

I liked this bit

I don't know if it will be, but in any case there is always
`singleton_class.send :public, :define_method`
instead or equivalent.

It's a bit more clear, IMO, as using "public_class_method" on a Module is a bit strange...
Modules don't really have "class methods".

FWIW, there are no specs in RubySpec about "public_class_method" on a Module.

@JonRowe JonRowe deleted the prevent_define_method_leaking_as_public branch March 12, 2023 19:23
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.

None yet

4 participants