5

これは、Ruby/Railsでこれまでに起こった中で最も奇妙なことです。

私はモデルStoreを持っています。これはhas_manyBalancesです。そして、ストアの通貨に基づいてデフォルトの残高を取得する方法があります。

店舗モデル。

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

では、バランスコントローラーに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

ここで奇妙な部分が発生します...

ショーアクションを呼び出すと、例えば:

GET / api /stores/148/balances/default

nullを返し(通貨がnullに設定されていて、null通貨との残高がないため)、生成される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メソッド内:

self.inspectを置きます

それは魔法のように機能します!!!。

それで、なぜそれが起こっているのかわかりませんか?...ストアオブジェクトを「検査」するまで、ストアオブジェクトがロードされていないようです。

ありがとう

4

2 に答える 2

1

サムとアドリアンは正しい道を進んでいます。

ActiveRecordはmethod_missingをオーバーライドして、Store#currencyなどの列に基づく属性のアクセサーを含む一連の動的メソッドを追加します。私は多くのことをざっと見ていますが、ロジックが呼び出されると、動的クラス/インスタンスメソッドがStoreクラス/インスタンスに追加され、後続の呼び出しでmethod_missingフックが不要になると言えば十分です。

superを呼び出さずにmethod_missingを無効にすると、この機能が事実上無効になります。幸い、この機能は他の方法で呼び出すことができます。store#inspectを呼び出したときにその1つにつまずきました。

superへの呼び出しを追加することで、ActiveRecordの動的メソッドが必要なときに常にクラスに追加されることを保証しました。

于 2013-11-07T21:22:22.273 に答える
0

たくさんのデバッグを経て、ようやくOK、理由がわかりました...

Storeモデルには、method_missingメソッドがあり、次のようになっています。

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

したがって、私が呼び出していたときself.currency、それは最初にmethod_missingに行き、次にnullを返しました。ここで欠けていたのはsuper電話でした。

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

puts @store.inspectしかし、私が電話をした後、またはputs self.inspectそれがうまくいったのはなぜですか?つまり、なぜその場合、そのsuper呼び出しは必要なかったのですか?

于 2012-11-14T13:33:47.967 に答える