Why would we put a module inside a class in Ruby?

In Ruby, I see that it can be useful to put classes inside modules for the sake of namespacing. I also see that it's possible to put modules inside classes. But I don't see why you'd do that.

Modules are generally mixed into classes, right? So, what would be the purpose of defining a module inside a class?


We could use it when writing ape-like code like this:

class DrugDealer
  module Drug
    def happy?; true; end
  end

  def approach(victim)
    victim.extend Drug
  end
end

o = Object.new
DrugDealer.new.approach(o)
o.happy? # => true

Another example that would be more practical in the real world is to have mixins that are only applied by subclasses.

This is useful when some facets of a thing apply to some subclasses and other facets apply to other subclasses, without there being enough order in the way these aspects apply to make way for a clear class hierarchy (tree). Think multiple inheritance! A simplified example:

class Person
  def handshake
    :sloppy
  end

  def mind_contents
    :spam
  end

  module Proper
    def handshake
      :firm
    end
  end

  module Clever
    def mind_contents
      :theories
    end
  end
end

class Professor < Person
  include Proper
  include Clever

  # ...
end

And so on. Kind of nice, when used sensibly. Even super calls and constructors (I didn't define any here though) flow through all the mixins and classes the way I want them to.


class Image
    module Colors
        Red = ...
        Blue = ...
    end
    include Colors
end

include Image::Colors

Image.new.set_pixel x, y, Red 

I've since run into a use case in a large Rails app with complex namespacing. A simplified example:

# app/models/invoice/dependents/item.rb
class Invoice
  module Dependents
    class Item
      # Define invoice item
    end
  end
end

Here Invoice is a class of its own, but is also a good namespace for its dependent items. We can't say module Invoice because that constant is already defined as a class, but we can still use it as a namespace.

Giant Caveat

If you use a class as a namespace, and you're using Rails, ensure you do not accidentally declare that class elsewhere . Autoloading will ruin your day. For instance:

# app/helpers/invoice/dependents/items_helper.rb
class Invoice       # This line will cause you grief
  module Dependents
    module ItemsHelper
      # view helper methods
    end
  end
end

The fact that class Invoice is stated in this file creates a load order dependency; if this file's class Invoice line is executed before your intended class definition, your intended class definition may not work properly . In this example, I can't declare that Invoice sublcasses ActiveRecord::Base if Invoice has already been declared with no parent class.

You could require your "true" class definition file at the top of another file, but at least in a Rails autoloading scenario, you'll have less wrangling to do if you do this instead:

# app/helpers/invoice/dependents/items_helper.rb
module Invoice:Dependents::ItemsHelper  
  # view helper methods
end

With this syntax, Rails will see the Invoice constant and use autoload to look it up, finding it in your model file and defining it the way you intended.

链接地址: http://www.djcxy.com/p/44842.html

上一篇: 在Ruby中继承modules / mixins中的类方法

下一篇: 为什么我们要在Ruby的类中放一个模块?