roganartu
roganartu

Reputation: 623

Rails as_json with two unrelated models

Given two models and a controller:

Apples

class Apples < ActiveRecord::Base
  belongs_to :not_oranges
  ...
  def as_json(options={})
    opts = {:include => [:not_oranges]}
    super(options.reverse_merge! opts)
  end
end

Oranges

class Oranges < ActiveRecord::Base
  belongs_to :not_apples
  ...
  def as_json(options={})
    opts = {:include => [:not_apples]}
    super(options.reverse_merge! opts)
  end
end

Search Controller

class SearchController < ApplicationController
  a = Apples.search params[:q]
  o - Oranges.search params[:q]

  @results = {
    :apples => a,
    :oranges => o
  }

  respond_to do |format|
    format.json { render :json => @results }
end

As you can see, the two models are completely unrelated and both have different :include options in their as_json definitions.

All works as expected if the search query only hits apples or only hits oranges, but once both objects aren't empty I get:

undefined method `not_apples' for #<Oranges:0x00000004af8cd8>

Seems either the two as_json definitions are being merged, or Oranges.as_json is being overriden by Apples.as_json.

Is this expected behaviour? Is there any clean way around it without using something like RABL? I feel it would be overkill for my needs.

Upvotes: 0

Views: 250

Answers (1)

Frederick Cheung
Frederick Cheung

Reputation: 84134

In pseudo code the code for hash as_json method looks like

def as_json(options={})
  Hash[collect {|key,element| [key.to_s,element.as_json(options)]}]
end

But your element is modifying the options argument you pass to it. Hash is unaware of this and so passes the modified options hash to as json.

It's usually a good idea not to modify in place the arguments passed to you, except when it is very clear this is ok. I'd rewrite your method as

def as_json(options={})
  defaults = {:include => :not_apples}
  super(defaults.merge(options))
end

Upvotes: 1

Related Questions