views:

144

answers:

3

Suppose in Ruby on Rails, there is a return of JSON data:

render :json => array_of_purchases

and this array_of_purchases contains many Purchase objects, each one with a product_id. Now if we want to add a property to the JSON returned, so that each Purchase object will also include its Product object data:

"product": { "id": 123, "name": "Chip Ahoy", image_file: "chip_ahoy.jpg", ... etc }

Then how can a new instance variable be added inside this controller action?

It might be

def get_data
  #  getting data ...
  class Purchase
    attr_accessor :product   # adding an instance variable
  end
  array_of_purchases.each {|p| p.product = Product.find(p.product_id)}
  render :json => array_of_purchases
end

but adding an instance variable to a class within this method (which is a controller action) won't work.

Update: this is assuming 1 Order has many Purchases, and 1 Purchase is a product and a quantity. (maybe some system call it an order line?)

A: 

I assume a Purchase class. Maybe an ActiveRecord model?

# in Controller

def show_full_display
  purchases = Purchase.find_all_by_customer_id(session.customer_id,
                 :include => :product)
  render :json => purchases.map{|p| p.full_display}.to_json  # convert to json
end

# in Purchase class
# fields include
#   id
#   customer_id
#   product_id
#        
belongs_to :product

def full_display
   # returns a hash for use by the full_display clients
   # Will be converted to JSON
   {"id" => id, "name" => product.name, "image_file" => product.image}
end

Notes: the full_display method for class Purchase returns a ruby hash. The controller then converts the array of hashes to a JSON array of objects. (Javascript uses objects as hashes.)

The full_display method could be in the controller instead of the model. I put it into the model for better testing, etc. It's the "Skinny controller, fat model" idea.

Larry K
will this return the Purchase object as well? (Purchase object with 1 field being the Product object data)
動靜能量
Add the purchase fields that you want into the full_display hash. Or add all of them as subsidiary element: "purchase" => attributes
Larry K
+1  A: 

Can I assume that a Purchase has_many Products?

Rails has some options on to_json that are useful here.

class Purchase
  def to_json
    super :include => :products
  end
end

Edit: Can you please post what your models look like? It's not clear exactly what you're trying to do.

Remember that in Ruby you can define methods on individual objects on the fly.

def purchase.to_json
  super :include => :products
end
jdl
what if I don't want to re-define to_json for all cases but just for this one case (for this action)?
動靜能量
+1 for style. OP says he has an array of purchases so this may not fit the bill exactly. But I do like how you've redefined the class' to_json method.
Larry K
@Jian, if you want a specific json response for the specific action, then you'll need a specific method to generate the hash that you want. That's what the full_display method does in my answer.
Larry K
+1  A: 

Assuming you have an association in your Purchase model to the Product model:

class Purchase
  belongs_to :product
end

class Product
  has_many :purchases
end

render :json => array_of_purchases.to_json(:include => :product)

You can further optimize this eager loading products while creating the purchases array.

KandadaBoggu