Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何更新Rails模型中的实例变量?_Ruby On Rails_Ruby_Ruby On Rails 3_Instance Variables - Fatal编程技术网

Ruby on rails 如何更新Rails模型中的实例变量?

Ruby on rails 如何更新Rails模型中的实例变量?,ruby-on-rails,ruby,ruby-on-rails-3,instance-variables,Ruby On Rails,Ruby,Ruby On Rails 3,Instance Variables,在我的Rails应用程序中,我有用户,他们可以进行许多支付 class User < ActiveRecord::Base has_many :invoices has_many :payments def year_ranges ... end def quarter_ranges ... end def month_ranges ... end def revenue_between(range, kind)

在我的Rails应用程序中,我有
用户
,他们可以进行许多
支付

class User < ActiveRecord::Base

  has_many :invoices
  has_many :payments

  def year_ranges
    ...
  end

  def quarter_ranges
    ...
  end

  def month_ranges
    ...
  end

  def revenue_between(range, kind)
    payments.sum_within_range(range, kind)
  end

end
实例变量(
@sum
)的使用大大减少了这里的SQL查询数量,因为相同的查询不会一次又一次地命中数据库

但是,问题是,当用户创建、删除或更改其
付款之一时,
@sum
实例变量中没有反映这一点。那么我如何重置它呢?还是有更好的解决办法


谢谢您的帮助。

也许您可以通过观察员来完成:

# payment.rb

def self.cached_sum(force=false)
  if @sum.blank? || force
    @sum = includes(:invoice => :items)
  end
  @sum
end

def self.sum_within_range(range)
  @sum = cached_sum
  @sum.select { |x| range.cover? x.date }.sum(&total)
end

#payment_observer.rb

class PaymentObserver < ActiveRecord::Observer
  # force @sum updating

  def after_save(comment)
    Payment.cached_sum(true)
  end

  def after_destroy(comment)
    Payment.cached_sum(true)
  end

end
#payment.rb
def self.cached_sum(force=false)
如果@sum.blank?|力
@金额=包括(:发票=>:项目)
结束
@总数
结束
定义范围内的自和(范围)
@sum=缓存的\u sum
@sum.select{| x | range.cover?x.date}.sum(&total)
结束
#支付(u observer.rb)
类PaymentObserver

你可以在

上找到更多关于观察者的信息。你的
@sum
基本上就是你所需要的值的缓存。与任何缓存一样,如果所涉及的值出现问题,则需要使其无效

您可以在保存后使用
或创建后使用
过滤器调用设置
@sum=nil
的函数。还可以保存缓存覆盖的范围,并在新的或更改的付款日期前决定失效

class Payment < ActiveRecord::Base

  belongs_to :user

  after_save :invalidate_cache

  def self.sum_within_range(range)
    @cached_range = range
    @sum ||= includes(:invoice => :items)
    @sum.select { |x| range.cover? x.date }.sum(&total)
  end

  def self.invalidate_cache
    @sum = nil if @cached_range.includes?(payment_date)
end
classpayment:项目)
@sum.select{| x | range.cover?x.date}.sum(&total)
结束
def self.invalidate_缓存
@如果@cached\u range.includes?(付款日期),则总和=零
结束

与其将关联存储为类
支付的实例变量,不如将其存储为
用户的实例变量(我知道这听起来很困惑,我已尝试在下面解释)

class用户:项目)。全部
#@payments\u with\u invoices现在是一个数组,因此不能对其使用Payment的class方法
@带发票的付款。选择{| x | range.cover?x.date}。总和(&:总计)
结束
结束
当您在类方法中定义
@sum
(类方法由
self.
表示)时,它成为类
支付的实例变量。这意味着您可以通过
Payment.sum
访问它。因此,这与特定用户及其付款无关
@sum
现在是类
Payment
的一个属性,Rails缓存它的方式与缓存类的方法定义的方式相同

一旦
@sum
被初始化,它将保持不变,正如您所注意到的,即使在用户创建了新的支付之后,或者如果其他用户为此登录,它也将保持不变!当应用程序重新启动时,它将更改

但是,如果您使用发票定义
@payments\u,如我上面所示,它将成为
用户
的特定实例的属性,或者换句话说,是实例级实例变量。这意味着您可以作为
某些用户访问它。使用发票进行支付。由于一个应用程序可以有许多用户,因此这些用户不会跨请求持久保存在Rails内存中。因此,每当用户实例更改时,都会再次加载其属性


因此,如果用户创建了更多的付款,
@payments\u with\u invoices
变量将在用户实例重新初始化后刷新。

这是您的问题附带的,但不要使用带有块的
\select

您要做的是选择所有付款,然后将关系过滤为数组。使用Arel解决这一问题:

scope :within_range, ->(range){ where date: range }

这将在语句之间构建SQL。对结果关系使用
#sum
将构建一个SQL sum()语句,这可能比加载所有记录更有效。

选择
是一种ActiveRecord查询方法,而不是数组。它只是scope.nope上的另一个链,而不是与块一起使用时。请自己尝试:它将加载所有记录。证据:。“使用块,就像数组一样工作#选择”参见源代码…如果在单个请求中为几个范围调用该方法,这是一个更好的解决方案。+1,很好的解决方法。虽然我会让
sum_在_范围内
使用SQL(见我的答案)进行所有计算@m_x ya通常会更好,但由于前面的问题,我有一些bkgrnd。这个函数在相同的重叠范围请求中被多次调用,所以我认为最好在Rails中进行过滤。但如果情况不再是这样,你的解决方案就更有意义了:)谢谢。当我复制您的确切代码时,仍然会得到大约80个SQL查询。因此,我将
includes(:invoice=>:items)
移动到
Payment
类中select方法的开头,现在我每页只得到大约10个SQL查询(与以前相同,因此很好),但是总呈现时间增加了很多,从大约300毫秒到3000毫秒。这肯定太多了,我想知道为什么。试试@m_x的建议,它可能会更快。我也更新了我的解决方案以保持一致性,但不认为它会加快速度
# payment.rb

def self.cached_sum(force=false)
  if @sum.blank? || force
    @sum = includes(:invoice => :items)
  end
  @sum
end

def self.sum_within_range(range)
  @sum = cached_sum
  @sum.select { |x| range.cover? x.date }.sum(&total)
end

#payment_observer.rb

class PaymentObserver < ActiveRecord::Observer
  # force @sum updating

  def after_save(comment)
    Payment.cached_sum(true)
  end

  def after_destroy(comment)
    Payment.cached_sum(true)
  end

end
class Payment < ActiveRecord::Base

  belongs_to :user

  after_save :invalidate_cache

  def self.sum_within_range(range)
    @cached_range = range
    @sum ||= includes(:invoice => :items)
    @sum.select { |x| range.cover? x.date }.sum(&total)
  end

  def self.invalidate_cache
    @sum = nil if @cached_range.includes?(payment_date)
end
class User < ActiveRecord::Base

  has_many :payments

  def revenue_between(range)
    @payments_with_invoices ||= payments.includes(:invoice => :items).all
    # @payments_with_invoices is an array now so cannot use Payment's class method on it
    @payments_with_invoices.select { |x| range.cover? x.date }.sum(&:total)
  end

end
scope :within_range, ->(range){ where date: range }