Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What purpose can anonymous modules serve?

What purpose could anonymous modules in a Ruby app serve? The concept itself is easy to grasp, but I can't imagine any reason that you'd ever use such a thing. What problem do they solve?

like image 855
iconoclast Avatar asked Aug 18 '15 15:08

iconoclast


2 Answers

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
like image 151
Jörg W Mittag Avatar answered Oct 17 '22 16:10

Jörg W Mittag


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"
like image 23
Stefan Avatar answered Oct 17 '22 17:10

Stefan