4

I have a class that exposes two interface methods client_options and user_options, and at this ancestry level, they are equivalent to default_options. I don't want other developers to implement default_options directly, hence it's private.

class Foo
  def client_options
    default_options
  end
  def user_options
    default_options
  end

  private
    def default_options
      { foo: :bar }
    end
end

To save some lines of code, I wanted to alias the methods:

class Foo
  alias_method :client_options, :default_options
  alias_method :user_options, :default_options

  private
    def default_options
      { foo: :bar }
    end
end

but alias_method only aliases public methods.

I found how to alias private methods on this blog:

class Foo
  def default_options
    { foo: :bar}
  end

  private :default_options
  alias_method :client_options, :default_options
  public :client_options
end

but, it's little bit unreadable.

Is there a more straight solution to alias a private method?

sawa
  • 165,429
  • 45
  • 277
  • 381
equivalent8
  • 13,754
  • 8
  • 81
  • 109
  • 3
    If I was to implement a `Foo` subclass, I would appreciate explicit method definitions. Using `alias_method` might save a line or two but it makes it more difficult to understand your class. – Stefan Aug 13 '15 at 09:32
  • Seems to me that if I count the lines of code literally every answer is more lines (or at most 1 less) of simply writing the first public method as you have and aliasing the second. As that's also more readable than any of the more complex suggestions why not just do that? – Peter Gerdes Aug 03 '21 at 22:04

3 Answers3

13

Alias, then privatize:

alias_method :client_options, :default_options
alias_method :user_options, :default_options
private :default_options

Or, whether you are so aware of this “scissors rule”:

%i(client_options user_options).each do |m|
  define_method m { default_options }
end

Or create your own alias_method alike method

  module AliasPrivateMethod
    def alias_private_method_to_interface(name, original_name)
      define_method(name) do |*args, &block|
        send(original_name, *args, &block)
      end
    end
  end

  class Foo
    extend AliasPrivateMethod
    alias_private_method_to_interface(:client_options, :default_options)
    private
      def default_options
        { foo: :bar }
      end
  end

  foo = Foo.new
  foo.public_methods(false) # => [:client_options]
  foo.client_options        # => { foo: :bar }
equivalent8
  • 13,754
  • 8
  • 81
  • 109
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • hi thank you for answer, but that's the same thing I've posted in the question – equivalent8 Aug 13 '15 at 08:53
  • Really? Probably SO shows me the uglified version of the question, but I can not see that is posted. What´s wrong with this approach then? – Aleksei Matiushkin Aug 13 '15 at 08:55
  • it is a valid approach I'm not saying that It's just I was looking for a solution that would respect the "scissors rule" ....funny enough I cannot google any article on this rule but Robert C. Martin (creator Clean Coders videos) was talking about it. It's a rule from old days where you try to keep public methods / interface methods at the top of your code and private methods below. Ruby is well implementing this when you stick private methods below the keyword `private` . So imagine if my class have more private methods they are below private, yet `default_options` must be above private. – equivalent8 Aug 13 '15 at 09:03
  • Hope I make sence, I run of allowed comment size to explain it :) – equivalent8 Aug 13 '15 at 09:04
  • 2
    @equivalent8 your last example doesn't respect this rule either. – Stefan Aug 13 '15 at 09:06
  • you man the example in my Question or in the Answer ??? ... if you mean the example in Question ... yes exactly :) that's why I posted this question because I thought there is some built in method like `alias_private_method` or something , but it looks like it's not :( – equivalent8 Aug 13 '15 at 09:13
  • I fully agree this is a good solution but the thing is if a developer must define `private` method then make it public again I guess it's just better if he delegates the method to self (like in my answer http://stackoverflow.com/a/31983509/473040) just my opinion – equivalent8 Aug 13 '15 at 09:17
  • just wrote an article explaining the scissors rule further if anyoune want to learn more about it http://www.eq8.eu/blogs/16-scissors-rule-in-coding – equivalent8 Aug 13 '15 at 10:19
1

One approach I find good alternative is to delegate the private method to self

require 'forwardable'
class Foo
  extend Forwardable
  def_delegator :self, :default_options, :client_options
  def_delegator :self, :default_options, :user_options

  private
    def default_options
      { foo: :bar }
    end
end

f = Foo.new
f.client_options 
# => { foo: :bar }
equivalent8
  • 13,754
  • 8
  • 81
  • 109
1

What about implementing the method you want to hide in a prepended module?

module ProtoFoo
  protected
    def default_options
      {foo: :bar}
    end
end

class Foo
  prepend ProtoFoo
  def client_options; default_options end
  def user_options; default_options end
end

Even if a user overwrites default_options in Foo, it will not have effect.

If you insist on writing the hidden things after the exposed things, you can do:

class Foo
  def client_options; default_options end
  def user_options; default_options end
end

module ProtoFoo
  def default_options
    {foo: :bar}
  end
end
Foo.prepend ProtoFoo
equivalent8
  • 13,754
  • 8
  • 81
  • 109
sawa
  • 165,429
  • 45
  • 277
  • 381