【问题标题】:Using a mixin method inside an instance method在实例方法中使用 mixin 方法
【发布时间】:2012-07-19 21:23:15
【问题描述】:

为什么这不起作用:

class Myclass
include HTTParty

   def dosomething
     base_uri("some_url")
   end


end

base_uri 方法是HTTParty 的一个类方法。如果我从我的类、任何实例方法之外或从类方法调用它,它工作正常,但是当尝试从实例方法调用它时,我得到“NoMethodError: undefined method `base_uri' for #”

为什么?难道不应该有某种方法可以从我的实例方法中引用 HTTParty 类,以便我可以调用该 HTTParty 类方法吗?

我可以将其更改为类方法,但是我的类的每个实例都将具有相同的 base_uri 值。

【问题讨论】:

    标签: ruby gem


    【解决方案1】:

    为什么它不起作用?因为这不是 Ruby 的工作方式。同样,这不起作用:

    class Foo
      def self.utility_method; ...; end
      def inst_method
        utility_method  # Error! This instance has no method named "utility_method"
      end
    end
    

    您可以通过以下方式解决此问题:

    class MyClass
      include HTTParty
      def dosomething
        HTTParty.base_uri("some_url")
      end
    end
    

    让我们更深入地了解方法查找如何与模块一起使用。首先,一些代码:

    module M
      def self.m1; end
      def m2; end
    end
    
    class Foo
      include M
    end
    p Foo.methods     - Object.methods #=> []
    p Foo.new.methods - Object.methods #=> [:m2]
    
    class Bar
      extend M
    end
    p Bar.methods     - Object.methods #=> [:m2]
    p Bar.new.methods - Object.methods #=> []
    
    class Jim; end
    j = Jim.new
    j.extend M
    p j.methods       - Object.methods #=> [:m2]
    

    正如我们所见,您可以使用extend 使对象(类或实例)对对象本身(而不是实例)使用模块的“实例”方法,但不能使“类方法” ' 被任何东西继承的模块。你能得到的最接近的是这个成语:

    module M2
      module ClassMethods
        def m1; end             # Define as an instance method of this sub-module!
      end
      extend ClassMethods       # Make all methods on the submodule also my own
      def self.included(k)
        k.extend(ClassMethods)  # When included in a class, extend that class with
      end                       # my special class methods
    
      def m2; end
    end
    
    class Foo
      include M2
    end
    p Foo.methods     - Object.methods #=> [:m1]
    p Foo.new.methods - Object.methods #=> [:m2]
    

    如果HTTParty 模块使用了上述模式,因此使base_uri 方法在您的MyClass 上可用,那么您可以 这样做:

    class MyClass
      include HTTParty
      def dosomething
        self.class.base_uri("some_url")
      end
    end
    

    ...但这不仅仅是直接引用拥有该方法的模块。

    最后,因为这可能对您有所帮助,所以这是我几年前制作的图表。 (它缺少 Ruby 1.9 中的一些核心对象,例如 BasicObject,但在其他方面仍然适用。点击查看 PDF 版本。图中的注释 #3 特别适用。)


    (来源:phrogz.net

    【讨论】:

    • 谢谢。 HTTParty.get 在实例方法中工作,但 HTTParty.base_uri 不能。看起来那是因为 HTTParty 定义了 self.get 而不是 self.base_uri。所以,我认为这意味着没有办法为不同的实例使用具有不同 base_uri 的 HTTParty?
    • @wadesworld base_uri 在类的单例类上设置该属性。这意味着您不应该动态更改它,因为您的代码将不再是线程安全的。不过,没有什么需要您设置 base_uri,您只需在每个请求中指定 URI。
    • @wadesworld 如documentation for HTTParty::ClassMethods#base_uri 所示,该模块使用我上面描述的确切模式。因此,值存储在类上,而不是实例上,您需要修改库或根据需要重复更改每个实例的值。请参阅我上面的最后一个示例以及此评论中的链接,了解它的设计用途。
    • 感谢您的回复,并对我的新手表示歉意。困扰我的是,鉴于这种设计,base_uri 的便利性似乎只有在所有请求都到同一个地方时才有用。如果想从多个网站的多个对象中使用 HTTParty,并且可能以任何顺序查询,那么似乎必须始终在每个请求中传递主机和凭据,并且忽略 base_uri 和 basic_auth,否则会出现并发问题。虽然这是可行的,但它似乎不是很优雅,除非我遗漏了什么。
    • @wadesworld 我和你在一起,这个设计并不酷,我已经在github上提出了一个问题,看看这个设计是什么。
    【解决方案2】:
    class Myclass
      include HTTParty
    
      def dosomething
        Myclass.base_uri("some_url")
      end
    
    end
    

    【讨论】:

      猜你喜欢
      • 2019-07-09
      • 1970-01-01
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 2021-06-16
      • 2013-10-26
      相关资源
      最近更新 更多