Liyali
Liyali

Reputation: 5693

How to add virtual attributes to API json response

Context: ruby "2.0.0" and rails "4.0.0"

I am wondering how to return more data than a simple JSON representation of my model. I have this method in my API controller:

def show
  @entry = params[:id].to_i != 0 ? Entry.find(params[:id]) : Entry.where(slug: params[:id]).first
  respond_with @entry
end

which returns a well formatted JSON article when called via http://pow.url.dev/en/tenant/api/v1/entries/23.

I also have two other method to get the next and previous article, for instance, next looks like this:

def next
  @current_entry = params[:entry_id].to_i != 0 ? Entry.find(params[:entry_id]) : Entry.where(slug: params[:entry_id]).first
  @entry = Entry.where( status_id: 0 ).where( "published_at > ?", @current_entry.published_at ).first
  respond_with @entry
end

The problem is that the client application needs to make 3 API calls to 1) fetch an article, 2) fetch the next article and 3) fetch the previous article.

It would be much better if the client had to do only one call to get all the information at once.

I would like to add into the show method the Title and ID of the next and previous article along with the JSON result, what would be the best way to do that?

Upvotes: 0

Views: 1173

Answers (3)

Liyali
Liyali

Reputation: 5693

Here is the solution I came up with:

Implementing two methods in the Entry model object for next and previous as follows:

def next
  Entry.where( status_id: 0 ).where( "published_at > ?", self.published_at ).select('id, title, slug').first
end

def previous
  Entry.where( status_id: 0 ).where( "published_at < ?", self.published_at ).select('id, title, slug').last
end

and replace respond_with by render :json in the show action of my controller:

def show
  @entry = params[:id].to_i != 0 ? Entry.find(params[:id]) : Entry.where(slug: params[:id]).first
  render :json => @entry.to_json(:include => :entry_fields, 
                                 :methods => [:next, :previous])
end

Output:

{
    "id": 20,
    "title": "Video Test",
    "slug": "video-test",
    "status_id": 0,
    "promoted": true,
    "published_at": "2014-01-20T11:51:00.000Z",
    "created_at": "2014-01-20T11:51:37.406Z",
    "updated_at": "2014-03-05T13:42:49.981Z",
    "excerpt": "Video",
    "user_id": "933dc175-0d73-45fb-9437-61ecc4f55705",
    "next": {
        "id": 21,
        "title": "Test Multiple",
        "slug": "test-multiple"
    },
    "previous": {
        "id": 18,
        "title": "Example Entry",
        "slug": "example-entry"
    },
    ...
    ... (associations)
    ...
}

Upvotes: 1

Slicedpan
Slicedpan

Reputation: 5015

Quick and dirty:

def show
  @entry = ...
  render :json => @entry.attributes.merge({
    :next_id => id_of_next_article, 
    :next_title => title_of_next_article...
  })
end

More flexible solution: use some sort of JSON serialization customizers (or roll your own), one I've used before is ActiveModel Serializers (https://github.com/rails-api/active_model_serializers). I'm sure there are others.

Upvotes: 3

0xd
0xd

Reputation: 1901

Will the user always use the Next and the Prev articles? If so, put them together on the JSON, otherwise, use link location tags with REL to prev and next on the returning JSON, then the clint will be able to know where to get the next and prev articles. You can get more info: RESTful Web Service usage of custom link relations - 'rel'

Upvotes: 1

Related Questions