代码之家  ›  专栏  ›  技术社区  ›  lrleon

如何将块转发到生成方法的方法

  •  2
  • lrleon  · 技术社区  · 7 年前

    class MyClass
    
      def method1(pars1, ...)
        preamble
    
        # implementation method1
    
        rescue StandardError => e
          recovery
      end
    
      def method2(pars2, ...)
        preamble
    
        # implementation method2
    
        rescue StandardError => e
          recovery
      end
    
      # more methods with the same pattern
    end
    

    class MyClass
    
      define_operation :method1, pars1, ... do
        # implementation method1
      end
    
      define_operation :method2, pars2, ... do
        # implementation method2
      end
    
      # more methods with the same pattern but generated with define_wrapper_method
      end
    

    我试图实现一种元生成器,但在转发接收该生成器的块时遇到了问题。这或多或少是我尝试过的:

    def define_operation(op_name, *pars, &block)
      define_method(op_name.to_s) do |*pars|
        preamble
        yield # how can I do here for getting the block? <-----
        rescue StandardError => e
          recovery
      end
    end
    

    不幸的是,我找不到转发的方法 block define_method 方法。而且,参数(其编号是可变的)很可能正在传递给

    如果有任何线索、帮助、建议,我将不胜感激。

    2 回复  |  直到 7 年前
        1
  •  7
  •   Raj    7 年前

    您不需要元编程来实现这一点。只需添加一个新的方法来包装通用逻辑,如下所示:

    class MyClass
    
      def method1(param1)
        run_with_recovery(param1) do |param1|
           # implementation method1
        end
      end
    
      def method2(param1, param2)
        run_with_recovery(param1, param2) do |param1, param2|
           # implementation method2
        end
      end
    
      private
    
      def run_with_recovery(*params)
        preamble
        yield(*params)
        rescue StandardError => e
          recovery
      end
    end
    

    在这里测试它: http://rubyfiddle.com/riddles/4b6e2


    如果您真的想进行元编程,这将起作用:

    class MyClass
    
      def self.define_operation(op_name)
        define_method(op_name.to_s) do |*args|
          begin
            puts "preamble"
            yield(args)
          rescue StandardError => e
            puts "recovery"
          end
        end
      end
    
      define_operation :method1 do |param1|
        puts param1
      end
    
      define_operation :method2 do |param1, param2|
        puts param1
        puts param2
      end
    
    end
    
    MyClass.new.method1("hi")
    MyClass.new.method2("hi", "there")
    

    在此处进行测试: http://rubyfiddle.com/riddles/81b9d/2

        2
  •  2
  •   engineersmnky    7 年前

    class Operation
      def self.op(name,&block)
        define_method(name) do |*args|
          op_wrap(block.arity,*args,&block)
        end
      end
    
      def op_wrap(arity=0,*args)
        if arity == args.size || (arrity < 0  && args.size >= arity.abs - 1) 
          begin
            preamble 
            yield *args
          rescue StandardError => e
            recovery 
          end  
        else 
          raise ArgumentError, "wrong number of arguments (given #{args.size}, expected #{arity < 0 ? (arity.abs - 1).to_s.+('+') : arity })"
        end
      end
    
      def preamble
        puts __method__
      end
    
      def recovery
        puts __method__
      end
    end
    

    所以你的用法是

    class MyClass < Operation
    
      op :thing1 do |a,b,c| 
        puts "I got #{a},#{b},#{c}"
      end
    
      op :thing2 do |a,b|
        raise StandardError
      end
    
      def thing3
        thing1(1,2,3) 
      end
    end
    

    此外,这为您提供了两个选项,您仍然可以这样做

    def thing4(m1,m2,m3) 
       @m1 = m1
       op_wrap(1,'inside_wrapper') do |str| 
         # no need to yield because the m1,m2,m3 are in scope 
         # but you could yield other arguments
         puts "#{str} in #{__method__}" 
       end
    end
    

    允许您预处理参数并决定向块屈服的内容

    例子

    m = MyClass.new
    
    m.thing1(4,5,6)
    # preamble
    # I got 4,5,6
    #=> nil
    m.thing2('A','B')
    # preamble
    # recovery
    #=> nil
    m.thing3  
    # preamble
    # I got 1,2,3
    #=> nil
    m.thing1(12)
    #=> #<ArgumentError: wrong number of arguments (given 1, expected 3)>