【问题标题】:How to wrap a Ruby/Rails method in a sub-class by altering the base class?如何通过更改基类将 Ruby/Rails 方法包装在子类中?
【发布时间】:2014-11-10 15:09:41
【问题描述】:

我有一个 Ruby/Rails 类 MyBase 定义如下:

module MyModule1
  class MyBase < Sinatra::Base
      register Sinatra::RespondTo
      register Sinatra::CrossOrigin

  ...

我定义了MyBase的一个子类,并且有一个可以调用的APIsomeAction

module MyModule1
  module MyModule2

    class MySubClass < MyModule1::MyBase
      post 'someAction' do
        ...
      end

      ...

为简洁起见,MySubClass 中还有许多其他 API 未在上面显示。

我想计时,然后记录每次命中任何 API 的持续时间。我知道如何使用STATSD rails 模块计时,我也知道如何记录。但是我应该在哪里/如何放置这个计时代码来保持我的代码干燥?我只想写一次,最好在基类中。但我不知道如何定义一个包装子类方法并在子类方法触发时调用的基类方法。

【问题讨论】:

    标签: ruby-on-rails ruby oop polymorphism object-oriented-analysis


    【解决方案1】:

    幸运的是,这是一个简单的例子,在这里编写了一个不同的post 方法,它包装了你传递给它的块并调用了原始的post。如果您尝试包装常规 def methods,代码会稍微多一些。

    创建一个新的post 类方法并在其中调用sinatra 的post(这适用于getput 等):

    module MyModule1
      class MyBase < Sinatra::Base
        register Sinatra::RespondTo
        register Sinatra::CrossOrigin
    
        def self.post_with_logging(action_name, &block)
          post action_name do
            start_your_benchmarks! # start your benchmarking
    
            block.call # this is your subclass' action block
    
            finalize_benchmarks! # finish up
            log_whatever!
          end 
        end
      end
    end
    

    在您的子类中使用这个新的post_with_logging 方法,而不是普通的post 方法:

    module MyModule1
      module MyModule2
    
        class MySubClass < MyModule1::MyBase
          post_with_logging 'someAction' do
            ...
          end
    

    这本质上是包装块并在其中使用普通的post 方法。希望这很清楚。如果需要更多解释,请告诉我。

    更新:为了不必更改子类,请使用super

    module MyModule1
      class MyBase < Sinatra::Base
        register Sinatra::RespondTo
        register Sinatra::CrossOrigin
    
        def self.post(action_name, &block)
          super action_name do
            start_your_benchmarks! # start your benchmarking
    
            block.call # this is your subclass' action block
    
            finalize_benchmarks! # finish up
            log_whatever!
          end 
        end
      end
    end
    

    并正常使用您的子类。

    【讨论】:

    • 有没有办法在不改变子类的情况下做到这一点?
    • 覆盖帖子本身并在内部调用super
    • 迭戈,你能告诉我怎么做吗?我不知道如何从父类的post 方法中调用子类的post 方法。
    【解决方案2】:

    你要找的是 Rack 中间件。

    你可以像这样注入你自己的。由于这一点,响应日志记录时间的逻辑完全从您的 Sinatra 应用程序中取出,因为它在请求的底层工作(Sinatra 构建在原始 Rack 应用程序之上)。

    class MeasureResponse
      def initialize(app)
        @app = app
      end
    
      def call(env)
        start = Time.now
        status, headers, response = @app.call(env)
        stop = Time.now
    
        log_time(start, stop)
    
        [status, headers, response]
      end
    
      private
    
      def log_time(start, stop)
        total_time = start - stop
        puts "Time of request: #{total_time}" #replace with your logger
      end
    end
    
    
    module MyModule1
        class MyBase < Sinatra::MyBase
            configure do
                use MeasureResponse
            end
    
           post 'someAction' do
             ...
           end
        end
    end
    

    【讨论】:

    • 有没有办法在不改变子类的情况下做到这一点?
    • 这种方式对子类没有任何改变
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 1970-01-01
    • 2017-09-24
    • 2019-11-03
    相关资源
    最近更新 更多