Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object does not get loaded

This is the weirdest thing ever happened to me with ruby/rails.

I have a model, Store, which has_many Balances. And I have a method that gives me the default balance based on the store's currency.

Store model.

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

Balance model.

class Balance < ActiveRecord::Base

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

Ok, so then in the Balance controller I have the show action, that will give me a specific balance or the default one.

Balance controller.

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

Now here is where the weird part comes...

If I make a call to the show action; for example:

GET /api/stores/148/balances/default

It returns null (because the currency was set as null, and there is no Balance with null currency), and the SQL query generated is:

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

So I DON'T know why... it is setting the currency as NULL. BUT if in any where in that process I put

puts @store.inspect

or inside the default_balance method:

puts self.inspect

it magically works!!!.

So I don't know why is that happening?... It seems like the store object is not getting loaded until I "inspect" it or something like that.

Thanks

like image 401
esbanarango Avatar asked Nov 13 '22 17:11

esbanarango


1 Answers

Sam and Adrien are on the right path.

ActiveRecord overrides method_missing to add a whole bunch of dynamic methods including the accessors for the column-backed attributes like Store#currency. While I'm glossing over a lot, suffice it to say that when the logic is invoked then the dynamic class/instance methods are added to the Store class/instances so that subsequent calls no longer require the method_missing hook.

When YOU overrode method_missing without calling super, you effectively disabled this functionality. Fortunately, this functionality can be invoked by other means, one of which you tripped upon when you called store#inspect.

By adding the call to super, you simply assured that ActiveRecord's dynamic methods are always added to the class when they're needed.

like image 89
AndyV Avatar answered Dec 17 '22 15:12

AndyV