Dynamically generate prefixes for method names

1.3k Views Asked by At

Let's say we have a bunch of methods with typical prefixes.

def pref_foo
  # code
end

def pref_bar
  # code
end

I want to learn how to automatically prepend those prefixes to my method names (like how it's done in Rails: Model.find_by_smth).

In other words I want to create some scope pref_, that takes methods and prepends pref_ to their names, so my method foo is becoming available as pref_foo.


module Bar
  # definition of some wrapper `pref_`
end

class Foo
  include Bar

  <some wrapper from Bar> do
    def foo
      puts 'What does a computer scientist wear on Halloween?'
    end

    def bar
      puts 'A bit-mask.'
    end
  end
end

foo = Foo.new

foo.pref_foo # => "What does a computer scientist wear on Halloween?"
foo.pref_bar # => "A bit-mask."
2

There are 2 best solutions below

3
On BEST ANSWER

Try this,

class Module
  def with_prefix(prefix, &block)
    m = Module.new
    m.instance_eval(&block)
    m.methods(false).each do |name|
      define_method "#{prefix}_#{name}", &m.method(name)
      module_function "#{prefix}_#{name}" unless self.is_a?(Class)
    end
  end
end

class A
  with_prefix :pref do
    with_prefix :and do
      def foo
        puts "foo called"
      end

      def bar
        puts "bar called"
      end
    end
  end
end

A.new.pref_and_foo
A.new.pref_and_bar

How does this work?

  • We define a new function with_prefix on the superclass of all classes
  • This function takes a name and a block
  • Evaluate the block in the context of an anonymous module.
  • This executes the def statements on the anonymous module rather than the class
  • Enumerate over all functions of that module
  • Create prefixed methods for each of those functions
0
On

You could use the callback method Module#included and the class methods Method#instance_methods, Method#alias_method, Module#remove_method and Object#send as follows.

module Bar
  def self.included(klass)
    klass.instance_methods(false).each do |m|
      klass.send :alias_method, "pref_#{m.to_s}".to_sym, m
      klass.send :remove_method, m
    end
  end
end

class Foo
  def foo
    puts 'hi'
  end

  def bar
    puts 'ho'
  end

  include Bar
end

Foo.instance_methods.select { |m| [:foo, :bar, :pref_foo, :pref_bar].include?(m) } 
  #=> [:pref_foo, :pref_bar] 

Foo.new.pref_foo
  #=> "hi"

Foo.new.foo
  #=> NoMethodError: undefined method `foo' for #<Foo:0x007fdecb0633d8>

send must be used because alias_method and remove_method are private class methods. The statement include Bar obviously must follow the definitions of the instance methods in Foo.

As the question is (sic), "I want to automatically prepend a prefix to my instance method names...", the prefix must be hard-wired.