Jorge Israel Peña
Jorge Israel Peña

Reputation: 38616

Filter a model's attributes before outputting as json

I need to output my model as json and everything is going fine. However, some of the attributes need to be 'beautified' by filtering them through some helper methods, such as number_to_human_size. How would I go about doing this?

In other words, say that I have an attribute named bytes and I want to pass it through number_to_human_size and have that result be output to json.

I would also like to 'trim' what gets output as json if that's possible, since I only need some of the attributes. Is this possible? Can someone please give me an example? I would really appreciate it.

Preliminary search results hint at something regarding as_json, but I can't find a tangible example pertaining to my situation. If this is really the solution, I would really appreciate an example.

Research: It seems I can use to_json's options to explicitly state which attributes I want, but I'm still in need of figuring out how to 'beautify' or 'filter' certain attributes by passing them through a helper before they're output as json.

Would I create a partial for a single json model, so _model.json.erb, and then create another one for the action I'm using, and within that simply render the partial with the collection of objects? Seems like a bunch of hoops to jump through. I'm wondering if there's a more direct/raw way of altering the json representation of a model.

Upvotes: 3

Views: 3896

Answers (2)

Jorge Israel Peña
Jorge Israel Peña

Reputation: 38616

I figured out a solution to this problem, but I don't know if it's the best. I would appreciate insight.

@items = Item.all

@response = []

@items.each do |item|
  @response << {
      :state => item.state,
      :lock_status => item.lock_status,
      :downloaded => ActionController::Base.helpers.number_to_human_size(item.downloaded),
      :uploaded => ActionController::Base.helpers.number_to_human_size(item.uploaded),
      :percent_complete => item.percent_complete,
      :down_rate => ActionController::Base.helpers.number_to_human_size(item.down_rate),
      :up_rate => ActionController::Base.helpers.number_to_human_size(item.up_rate),
      :eta => item.eta
  }
end

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

Basically I construct a hash on the fly with the values I want and then render that instead. It's working, but like I said, I'm not sure if it's the best way.

Upvotes: 1

zetetic
zetetic

Reputation: 47548

Your model can override the as_json method, which Rails uses when rendering json:

# class.rb
include ActionView::Helpers::NumberHelper
class Item < ActiveRecord::Base
  def as_json(options={})
    { :state => state, # just use the attribute when no helper is needed
      :downloaded => number_to_human_size(downloaded)
    }
  end
end

Now you can call render :json in the controller:

@items = Item.all
# ... etc ...
format.json { render :json => @items }

Rails will call Item.as_json for each member of @items and return a JSON-encoded array.

Upvotes: 7

Related Questions