What purpose can anonymous modules serve?

心不动则不痛 提交于 2019-12-03 06:37:37

There is a more general principle at work here.

Phil Karlton famously said: "There are only two hard problems computer science: cache invalidation and naming things." So, naming things is hard. Which means that if we can get away with not naming a thing, we should do it!

Or, if you look at it from a different perspective: if naming things is hard, then giving something a name means that thing is important. But sometimes, there are things in our programs which aren't important and thus aren't worthy of a name.

This is not unique to Ruby modules. You could ask the question about any anonymous concept, and in fact, the question does get asked all the time. When C# 2.0 introduced anonymous methods, people asked why one would ever want to use a method without a name, when C# 3.0 introduced anonymous lambdas (and anonymous types), people asked why one would ever want to use them. Python's anonymous functions are severely restricted compared to Python's named functions, and the Python community asks why one would ever need full-blown anonymous functions. Of course, we, as Ruby programmers are so used to lightweight (blocks) and fully reified (Procs) anonymous functions that we can't understand why one would ever not want to use one.

Java has anonymous classes since 1.1 and anonymous lambdas since 8. Basically, anonymous "things" are everywhere and they are useful, especially for quick one-off usage.

For example, if you just want to wrap some existing method, without going through the hassle of alias_method (which you really shouldn't use any more for that problem, Module#prepend now exists and is a much better solution), you could do:

class Array
  prepend(Module.new do
    def [](*)
      puts 'Before-hook'
      super.tap { puts 'After-hook' }
    end
  end)
end

p [42][0]
# Before-hook
# After-hook
# => 42

This is a Rails specific answer, it's not about anonymous modules in general.

Short answer

Being able to call super when overriding generated methods.

Long answer

Given a module that creates methods:

module Generator
  def generate_method(name)
    define_method(name) do
      "I am #{name}"
    end
  end
end

Calling generate_method from within a class creates a new instance method:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass#foo>

Invoking the method works as expected:

MyClass.new.foo #=> "I am foo"

But you can't easily alter foo:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> no superclass method `foo'

If our generator uses an anonymous module to define the methods within:

module Generator
  def generate_method(name)
    generated_methods.module_eval do
      define_method(name) do
        "I am #{name}"
      end
    end
  end

  def generated_methods
    @generated_methods ||= begin
      mod = Module.new
      include(mod)
      mod
    end
  end
end

We get:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass(#<Module:0x007fbd29833658>)#foo>

And altering foo now works as expected:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> "I AM FOO"
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!