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

后端 未结 4 674
遇见更好的自我
遇见更好的自我 2021-01-30 04:01

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 wh

相关标签:
4条回答
  • 2021-01-30 04:40

    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.

    0 讨论(0)
  • 2021-01-30 04:42

    I guess it’s really just about using a class as a namespace, which is sometimes just more convenient that putting everything in a module. I’ve never seen that in practice, but it’s perfectly valid Ruby code either way.

    The only real-life scenario I can think of is using EventMachine in a class:

    class Api
      def initialize
        EM.start_server "0.0.0.0", 8080, Server
      end
    
      module Server
        def receive_data (data)
          # do stuff
        end
      end
    end
    
    0 讨论(0)
  • 2021-01-30 04:50

    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.

    0 讨论(0)
  • 2021-01-30 04:52
    class Image
        module Colors
            Red = ...
            Blue = ...
        end
        include Colors
    end
    
    include Image::Colors
    
    Image.new.set_pixel x, y, Red 
    
    0 讨论(0)
提交回复
热议问题