Extending a Ruby module in another module, including the module methods

后端 未结 5 750
北海茫月
北海茫月 2020-12-02 23:12

Whenever I try to extend a ruby module, I lose the module methods. Neither include nor extend will do this. Consider the snippet:

module A 
  def self.say_         


        
相关标签:
5条回答
  • 2020-12-02 23:19

    I don't think there's any simple way to do it.

    So here is a complex way:

    module B
      class << self
        A.singleton_methods.each do |m|
          define_method m, A.method(m).to_proc
        end
      end
    end
    

    You can put it in a helper method like this:

    class Module
      def include_module_methods(mod)
        mod.singleton_methods.each do |m|
          (class << self; self; end).send :define_method, m, mod.method(m).to_proc
        end
      end
    end
    
    module B
      include_module_methods A
    end
    
    0 讨论(0)
  • 2020-12-02 23:20

    I don't like everyone using self.included. I have simpler solution:

    module A
      module ClassMethods
        def a
          'a1'
        end
      end
      def a
        'a2'
      end
    end
    
    module B
      include A
    
      module ClassMethods
        include A::ClassMethods
        def b
          'b1'
        end
      end
      def b
        'b2'
      end
    end
    
    class C
      include B
      extend B::ClassMethods
    end
    
    class D < C; end
    
    puts D.a
    puts D.b
    puts D.new.a
    puts D.new.b
    
    0 讨论(0)
  • 2020-12-02 23:22

    Johnathan, I am not sure if you're still wondering about this, but there are two different ways to use modules in ruby. A.) you use modules in their self contained form Base::Tree.entity(params) directly in your code, or B.) you use modules as mixins or helper methods.

    A. Will allow you to use modules as a Namespace pattern. This is good for larger projects where there is the chance for method name conflicts

    module Base
      module Tree
        def self.entity(params={},&block)
          # some great code goes here
        end
      end
    end
    

    Now you can use this to create some sort of Tree structure in your code, without having to instantiate a new class for every call to Base::Tree.entity.

    Another way to do Namespace-ing is on a class by class basis.

    module Session
      module Live
        class Actor
          attr_accessor :type, :uuid, :name, :status
          def initialize(params={},&block)
            # check params, insert init values for vars..etc
            # save your callback as a class variable, and use it sometime later
            @block = block
          end
    
          def hit_rock_bottom
          end
    
          def has_hit_rock_bottom?
          end
    
          ...
        end
     end
     class Actor
       attr_accessor :id,:scope,:callback
       def initialize(params={},&block)
         self.callback = block if block_given?
       end
    
       def respond
         if self.callback.is_a? Proc
           # do some real crazy things...
         end
       end
     end
    end
    

    Now we have the potential for overlap in our classes. We want to know that when we make a create an Actor class that it is the correct class, so this is where namespaces come in handy.

    Session::Live::Actor.new(params) do |res|...
    Session::Actor.new(params) 
    

    B. Mix-Ins These are your friends. Use them whenever you think you will have to do something more than once in your code.

    module Friendly
      module Formatter
        def to_hash(xmlstring)
          #parsing methods
          return hash
        end
    
        def remove_trailing_whitespace(string,&block)
          # remove trailing white space from that idiot who pasted from textmate
        end
      end
    end
    

    Now whenever you need to format an xmlstring as a hash, or remove trailing whitespace in any of your future code, just mix it in.

    module Fun
      class Ruby
        include Friendly::Formatter
    
        attr_accessor :string
    
        def initialize(params={})
        end
    
      end
    end
    

    Now you can format the string in your class.

    fun_ruby = Fun::Ruby.new(params)
    fun_ruby.string = "<xml><why><do>most</do><people></people><use>this</use><it>sucks</it></why></xml>"
    fun_ruby_hash = fun_ruby.to_hash(fun_ruby.string)
    

    Hope this is a good enough explanation. The points raised above are good examples of ways to extend classes, but with modules, the hard part is when to use the self keyword. It refers to the scope of the object within ruby's object hierarchy. So if you want to use a module as a mix-in, and don't want to declare anything singleton, don't use the self keyword, however if you want to keep state within the object, just use a class and mix-in the modules you want.

    0 讨论(0)
  • 2020-12-02 23:37

    If you are the author of module A and will frequently need this, you can re-author A like so:

    module A 
      module ClassMethods
        def say_hi
          puts "hi"
        end
      end
      extend ClassMethods
      def self.included( other )
        other.extend( ClassMethods )
      end
    end
    
    module B 
      include A
    end
    
    A.say_hi #=> "hi"
    B.say_hi #=> "hi" 
    
    0 讨论(0)
  • 2020-12-02 23:37

    Use include_complete

    gem install include_complete

    module A 
      def self.say_hi
        puts "hi"
      end
    end
    
    module B 
      include_complete A
    end
    
    B.say_hi #=> "hi"
    
    0 讨论(0)
提交回复
热议问题