5

这是 ruby​​/rails 发生在我身上最奇怪的事情。

我有一个模型,Store,它 has_many Balances。而且我有一种方法可以根据商店的货币为我提供默认余额。

店铺型号。

class Store < ActiveRecord::Base

  has_many :balances, as: :balanceable, dependent: :destroy

  def default_balance
    #puts self.inspect <- weird part.
    balances.where(currency: self.currency)[0]
  end
  ...
end

平衡模型。

class Balance < ActiveRecord::Base

  belongs_to :balanceable, :polymorphic => true
  ...
end

好的,那么在 Balance 控制器中我有show动作,它会给我一个特定的平衡或默认平衡。

平衡控制器。

class Api::Stores::BalancesController < Api::Stores::BaseController

  before_filter :load_store

  # Returns a specific alert
  # +URL+:: GET /api/stores/:store_id/balances/:id
  def show
    #puts @store.inspect <- weird part.
    @balance = (params[:id] == "default") ? @store.default_balance : Balance.find(params[:id])
    respond_with @balance, :api_template => :default
  end
  ...

  private
    # Provides a shortcut to access the current store
    def load_store
      @store = Store.find(params[:store_id])
      authorize! :manage, @store
    end
end

现在这就是奇怪的部分来的地方......

如果我打电话给表演动作;例如:

获取 /api/stores/148/balances/default

返回null(因为货币被设置为null,并且没有Balance with null currency),生成的SQL查询为:

SELECT `balances`.* FROM `balances` WHERE `balances`.`balanceable_id` = 148 AND `balances`.`balanceable_type` = 'Store' AND `balances`.`currency` IS NULL

所以我不知道为什么......它将货币设置为NULL。但是如果我在那个过程中的任何地方

放@store.inspect

或在default_balance方法内部:

自我检查

它神奇地起作用了!!!

所以我不知道为什么会这样?......似乎商店对象在我“检查”它或类似的东西之前没有被加载。

谢谢

4

2 回答 2

1

山姆和阿德里安走在正确的道路上。

ActiveRecord 覆盖 method_missing 以添加一大堆动态方法,包括用于列支持的属性(如 Store#currency)的访问器。虽然我在掩饰很多,但只要说当调用逻辑时动态类/实例方法被添加到 Store 类/实例中,以便后续调用不再需要 method_missing 钩子就足够了。

当您覆盖 method_missing 而不调用 super 时,您实际上禁用了此功能。幸运的是,可以通过其他方式调用此功能,其中一种方式是您在调用 store#inspect 时遇到的。

通过添加对 super 的调用,您只需确保 ActiveRecord 的动态方法总是在需要时添加到类中。

于 2013-11-07T21:22:22.273 回答
0

OK finally after a lot of debugging, I found the reason...

In the Store model I have a method_missing method and I had it like this:

def method_missing method_name, *args
  if method_name =~ /^(\w+)_togo$/
    send($1, *args).where(togo: true)
  elsif method_name =~ /^(\w+)_tostay$/
    send($1, *args).where(tostay: true)
  end
end

So when I was calling self.currency it went first to the method_missing and then returned null. What I was missing here was the super call.

def method_missing method_name, *args
  if method_name =~ /^(\w+)_togo$/
    send($1, *args).where(togo: true)
  elsif method_name =~ /^(\w+)_tostay$/
    send($1, *args).where(tostay: true)
  else
    super
  end
end

But I continue wondering why after I had called puts @store.inspect or puts self.inspect it worked well?. I mean, why in that case that super call wasn't needed?

于 2012-11-14T13:33:47.967 回答