1

So, I need to make a method within a class protected from re-definition. I am not really sure how else to explain it so here's the code:

module Foo
    def bar
        p "lol"
    end
end

Now, that's the original Foo#bar method and I need it to be like a constant. So, I did come up with a solution. That was to save the method in a Constant and detect when someone tried changing it it would simply re-load it from that constant (it was a Proc object):

module Foo
        Original_bar = Proc.new { p "lol" }

        def bar
            Original_bar.call
        end

        def self.method_added(method_name)
            if method_name == :bar
                def Foo::bar
                    Original_bar.call
                end
            end
        end
end

But this isn't completely safe since once could use the same "trick" I did to bypass method_added and I am not really fond of this, since it's not very ruby-like.


Tests

A normal test:

module Foo
    def bar
        p "lmao"
    end
end

Foo::bar # => "lol"

And another test using the trick:

def Foo::bar
    p "rofl"
end

Foo::bar # => "rofl"

tl;dr

How to make a method in Ruby "unredefinable" (if that's even a word)?

Community
  • 1
  • 1
omninonsense
  • 6,644
  • 9
  • 45
  • 66
  • I think Ruby is an exceptionally bad language to choose for this requirement. Using the `*_eval` and `*_exec` method, it would probably be possible to defeat every kind of "protection" you could apply. What are you trying to achieve, after all? – Niklas B. Jan 31 '12 at 18:08
  • You're looking for a ruby equivilent of `final` in Java, from what I can tell. I don't believe such a thing exists. – Matt Grande Jan 31 '12 at 18:09
  • @NiklasBaumstark Well, there's basically a way for people to ass their own "extensions" to an application I'm making. I just wouldn't like them shoving their fingers into the `Core` module. I wouldn't mind protecting the module as a whole either. :D – omninonsense Jan 31 '12 at 18:11
  • 1
    @Matt: `final` prevents overriding of methods in subclasses. In Ruby, you don't even need to create a child class, you can directly modify the original method. – Niklas B. Jan 31 '12 at 18:11
  • @with a dot: As I said, you can't do that in Ruby. It'd be much better if you use OS-provided protection mechanisms like process separation for this OR if you use [sandboxing](http://pivotallabs.com/users/chad/blog/articles/781-playing-with-fire-running-uploaded-ruby-code-in-a-sandbox-david-stevenson) (but I wouldn't trust that, either). **EDIT** If your application is written in C or C++ and just uses Ruby as an extension language, you could write the API in C and just make it available in Ruby. – Niklas B. Jan 31 '12 at 18:12
  • @NiklasBaumstark Well, the person who installs the "extension" should trust the source it comes from, they can also easily un-install it, but still I don't want them fracking things up. I mean, this isn't really a top priority: I could live without it. – omninonsense Jan 31 '12 at 18:15
  • @withadot. In that case I don't think it's worth the effort. Users could easily break your mechanisms if they wanted to, so why even bother? – Niklas B. Jan 31 '12 at 18:17
  • @NiklasBaumstark [regarding the edit] Nope, pure Ruby. They can optionally create extensions using Javascript through rubyracer – omninonsense Jan 31 '12 at 18:18
  • Is it an option to drop Ruby scripting support completely in favor of Javascript? You could make this pretty secure (users wouldn't even need to trust the extension authors). – Niklas B. Jan 31 '12 at 18:20
  • @NiklasBaumstark, well... Yeah, it could be an option... Definitely, actually. I don't mind it. I'm not sure anyone else might. – omninonsense Jan 31 '12 at 18:22
  • Right, so this should be closed or deleted, then? Since There's no answer. Or the answer is simply no. Someone answer it so I can ✓ it. – omninonsense Jan 31 '12 at 18:32
  • @FrederickCheung It has some `@variables` that change. So, nope. But it is a brilliant idea, though. – omninonsense Jan 31 '12 at 20:37
  • Oh well. I moved the @vars to singleton class. SO, that actually got it working for me. You should post this in the answer! :D – omninonsense Jan 31 '12 at 20:40
  • In the second last snippet, `Foo::bar` won't work, because `bar` is an instance method. – Andrew Grimm Jan 31 '12 at 21:54

2 Answers2

1

If you freeze the module that should prevent method being added to it.

Note that a c extension can unfreeze variables (see evil.rb for example.

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
0

You can make your code warn you that a method has been over-ridden by turning on warnings:

$VERBOSE = true

module Foo
  def self.bar
    p "lmao"
  end
end

def Foo::bar
    p "rofl"
end

(irb):9: warning: method redefined; discarding old bar

It may be possible, but not practical, to raise an exception when warnings are issued.

This won't warn you if you first undefine Foo.bar, however.

Community
  • 1
  • 1
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338