【问题标题】:Ruby Class variable and on access assignmentsRuby 类变量和访问分配
【发布时间】:2011-11-23 05:54:03
【问题描述】:

我有一个包含多个类变量的模块。我正在寻找一个类级别的 getter 实现,它只会在类尝试访问它时实例化 @@ 变量,如下所示

    module MyProducts
      @@products = nil

      def self.get_product(id)
        # i'm looking for a way that the first call on @@products does a find via AR                 like the following
        # @@products = Product.all
        # this module is in the lib directory of a Rails 2.3.5 app
        @@products.find do |prod|
          prod.id.eql?(id)
        end
      end
    end

我希望它是透明的,这样我就不必修改整个模块。大约有 10 个具有相似功能的类级变量,都是 ActiveRecord .find 调用的结果

【问题讨论】:

    标签: ruby-on-rails ruby class-variables


    【解决方案1】:

    最好不要使用类变量——它们有非常奇怪的行为。看 http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html

    在任何情况下,您都可以使用 civars(类实例变量):

    module MyProducts
      class << self
        @products = nil
      end
    end
    

    【讨论】:

      【解决方案2】:

      只需使用||= 运算符。只有当正确的部分是 nilfalse 时,它才会评估正确的表达式

      def foo
        @@any ||= some_value
      end
      

      第一次调用该方法时,它将使用some_value 的结果初始化变量,随后的调用将返回@@any 值,无需重新计算some_value

      更新

      这是一个小脚本,向您展示了如何做到这一点。如果你执行它,你会看到方法 complex_function 被调用一次,因为两个打印语句都返回 1。但是从你的评论中我看到你的 Product 是一个活动记录,所以不要使用这种方法你的要求是什么,这将是非常低效的(阅读我回答的最后部分)

      #!/usr/bin/env ruby
      
      module Foo
        def self.products
          @@products ||=complex_function
        end
      
        @@a = 0
      
        def self.complex_function
          @@a += 1
        end
      end
      
      p Foo.products
      p Foo.products
      

      更新结束

      但是您保存Product.all 的方法效率很低:

      • 首先它将获取所有数据库中的产品,当您拥有大量产品时,这些产品可能会消耗大量内存
      • 当您有很多产品时,您的第二次迭代代码会比数据库慢得多

      Product.find(id) 调用替换整个方法。

      如果您的 Product 模型未存储在数据库中(可能是 ActiveResource),请忽略我之前的评论。

      你也可以看看mattr_accessor和这个SO问题Difference between mattr_accessor and cattr_accessor in ActiveSupport?

      最后还要看看this article,它解释了上述称为 memoization 的技术

      【讨论】:

      • 我正在寻找类似的东西,但是 rails (v2.3.5) 在启动时加载库时不知道如何解析 Product.all。我正在尝试解决此问题,因为它使开发变得痛苦,因为它填充了带有产品的@@products 数组,但我无法在开发模式下遍历关系。我不知道这是否可能,但我正在寻找类似函数的东西,类似于它是否只是一个实例变量,我可以在其中执行 def MyModule.@@products @@products ||= Products.all end抱歉,如果格式不起作用
      • @grizzgreen 我已经用一个完整的例子更新了我的答案。但是不要对 ActiveRecord 类执行此操作。使用 AR finder,或者如果您需要复杂的查询,请使用 named_scopes 和 lambdas
      • 是的,我知道不要为 AR 课程这样做,不幸的是我没有编写这段代码,而且我有很短的时间来完成这个演示(该应用程序被封存了 3 年)。如果我转换了在其他地方调用的函数来自己进行查找,AR 会兑现这些查询的结果还是只兑现 sql?所有查询都在加载函数中,因此转换起来很简单。
      猜你喜欢
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      • 2018-06-28
      • 2013-04-18
      • 1970-01-01
      • 2011-10-13
      • 2022-01-05
      • 1970-01-01
      相关资源
      最近更新 更多