Rails 3: Wrapping as_json response with additional lines - ruby-on-rails

I was very happy to learn of as_json to make my code DRY. And I've added the following to a model:
class ProductType < ActiveRecord::Base
has_many :component_types
def as_json(parameter)
{:name => self.name,
:description => self.description,
:children => self.componentTypes}
end
end
This is great. The only thing is that for my client side application, I need to wrap the response I get into this format, (where "items" contains what is created by as_json):
{
"identifier": "name",
"label": "name",
"items":
[
{
"name": "myName1",
"description": "myDesc1",
"children":[]
},
{
"name": "myName2",
"description": "myDesc2",
"children":[]
}
]
}

There are a lot of limitations to overriding as_json, and your issue is one of them. I'd suggest having a look at the RABL gem as I think it will help you reach your goal.

Related

Rails 5 - include nested associations not working

I have two simple models:
note.rb
with
:title -> string, :content -> string
has_and_belongs_to_many :tags, join_table: :tags_notes
accepts_nested_attributes_for :tags
tag.rb
with
:name -> string
has_and_belongs_to_many :notes, join_table: :tags_notes
Both models are connected through has_and_belongs_to_many relationship.
The association table is called tags_notes as indicated above.
Well, the problem I have here is, in my RESTful controller, to get notes, I have this:
GET /api/notes
This only returns Note objects:
[
{
"id": 1,
"title": "12231",
"content": "121213"
},
{
"id": 2,
"title": "test",
"content": "testtest"
}
]
However, each note has tags, and I would like to dump those in the response as well, like this:
[
{
"id": 1,
"title": "12231",
"content": "121213",
tags: [
{
"name": "test",
"id": 1
},
{
...
}
]
},
...
]
In my controller, I've tried
Note.includes(:tags).
Current controller code:
def index
notes = Note.includes(:tags)
render json: notes, status: :ok
end
They only seem to return notes, without tags. Same is the case with Note.eager_load(:tags) What am I doing wrong? Cannot find enough documentation that will help me fix this issue.
If someone can help me with this I will be grateful.
Thanks a bunch.
Shortly after posting my question I found the answer myself. The include has to go in render.
So the controller code
def index
notes = Note.all
render json: notes, :include => :tags, status: :ok
end
Seems to do the trick!

Rails Active model serializer returns array instead of json

I am using Rails to create APIs containing basic todo information: name, list, and items. I want it to return json format, to look something like this:
{
"data": [
{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API is awesome!",
"body": "You should be using JSON API",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
}
}
],
"links": {
"href": "http://example.com/api/posts",
"meta": {
"count": 10
}
}
}
^code from Active Serializer's Github.
When I look on my localhost http://localhost:3000/api/users/, it shows
[{"id":1,"username":"Iggy1","items":[{"id":1,"list_id":1,"name":"Wash dishes","completed":true},{"id":7,"list_id":1,"name":"Finish this assignment","completed":false}],"lists":[{"id":1,"name":"Important!","user_id":1,"permission":"private"},...
It is returning an array of hashes. I am sure I missed an important step when I was setting up my serializer. How can I reformat my array of hashes into JSON API format?
I've read getting started guide, rendering, and JSON API section but I still couldn't figure it out. I might have overlooked it.
Some of my codes:
app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :username#, :email
has_many :items, through: :lists
has_many :lists
end
app/controllers/api/users_controller.rb
def index
#users = User.all
render json: #users, each_serializer: UserSerializer
end
routes
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
resources :users do
resources :lists
end
end
end
Let me know if I can clarify it better. Thanks!!
(Answer from comments)
To use the JSON API adapter, you need to declare you want to use it.
ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
As per the AMS readme.
ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
from 0-10-stable documentation
However, this did not solve my problem. It was due to the Hash being nested as discussed in this SO post

Rails API Design: best way to include other attributes with json_api relationships

I have a Rails 5 app in which I use the gem active_model_serializers(https://github.com/rails-api/active_model_serializers). In my app I have a simplified data model that looks something like this:
# LocalizedString.rb
has_many :translations
# Translation.rb
belongs_to :localized_string
I'm trying to follow the best practices from JSON API, I have configured active_model_serializers like this:
ActiveModelSerializers.config.adapter = :json_api
When a user of the API requests translations (http://[root]/api/apps/117/translations) I currently get the following result:
{
"data": [
{
"id": "152",
"type": "translations",
"attributes": {
"value": "Test",
},
"relationships": {
"language": {
"data": {
"id": "1",
"type": "languages"
}
},
"localized-string": {
"data": {
"id": "162",
"type": "localized-strings"
}
}
}
},
[...]
From my localised-string I also want to include another attribute that is critical for the consumer of the API, and I don't want to have to make another API call to get the value of the attribute. I wonder what is the best / recommended way to do this that also follows json_api if possible.
Something like this could work:
"localized-string": {
"data": {
"id": "162",
"key": "my key value", # the attribute I need.
"type": "localized-strings"
}
}
But I'm not sure how to achieve that using active_model_serializers or if it is another recommended way of doing what I want with [json_api][1].
For completion, my relevant serialiser files looks lik this:
class TranslationSerializer < ActiveModel::Serializer
attributes :id, :value, :created_at, :updated_at
has_one :language
has_one :localized_string, serializer: LocalizedStringParentSerializer
end
class LocalizedStringParentSerializer < ActiveModel::Serializer
# I want to include the key attribute in my relationship object, but this doesn't work.
attributes :id, :key
end
So, any ideas on what I need to do to achieve what I want?
Per spec, relationships are represented by resource object identifiers. To include more than just the id and type, you'll want to use the include param. In AMS, I think that would be 'include: [:localizations], fields: { localizations: [:key]}' (not at computer now, but is approx right)

Rails JBuilder recursive lookup

I am using Rails 4, and I have some simple models as follow:
class Question < ActiveRecord::Base
# columns (id, text)
has_many :answers
end
class Answer < ActiveRecord::Base
# columns (id, text, next_question_id)
belongs_to :question
end
You can see that an answer has a next_question_id column, which will be used to look up another question. I want to generate a tree-structure json like this:
{
"text": "This is question 1",
"answers": [
{
"text": "This is answer a",
"next_question": {
"text": "This is question 2",
"answers": [
{
"text": "This is answer c",
"next_question":
}
]
}
},
{
"text": "This is answer b",
"next_question": {
"text": "This is question 2",
"answers": [
{
"text": "This is answer d",
"next_question":
}
]
}
}
]
}
How can I achieve this with JBuilder? I tried the solution here, but I cannot pass the json argument to the helper function.
The standard aproach for rendering trees is using a recursive partial. To implement this you'll first need to add a method to your Answer model like this.
def next_question
Question.find(next_question_id) if next_question_id
end
(hint: alternetively you could just set a belongs_to :next_question, class_name: Question association on your Answer model)
Then you create a partial like _question.json.jbuilder that goes like this:
json.(question,:id, :text)
json.answers question.answers do |answer|
json.(answer, :id, :text)
json.partial!(:question, question: answer.next_question) if answer.next_question
end
Then in the controller you take the first question in your survey and put it in say the #first_question variable.
And the last thing: in your view you write
json.partial! :question, question: #first_question

Rabl conforming to jsonapi.org

I'm having a lot of trouble getting rabl to conform the the jsonapi.org spec. I have seen the wiki entry on github regarding this (https://github.com/nesquena/rabl/wiki/Conforming-to-jsonapi.org-format) but I dont understand how to get multiple root level nodes in rabl and still have collections work. The wiki entry on collections says this should work
collection [#post] => :posts
attributes :id, :title
child #post => :links do
node(:author) { #post.author_id }
node(:comments) { #post.comments_ids }
end
which for single root documents it does, but as soon as I try to add a meta or links to the root of the document as declared in the jsonapi.org spec, those nodes are appended to the existing collection node. Here is my rabl_init.rb
require 'rabl'
Rabl.configure do |config|
config.cache_sources = Rails.env != 'development'
config.include_child_root = false
config.include_json_root = false
end
I would like json that looks like this:
{
"links": {
"posts.comments": "http://example.com/posts/{posts.id}/comments"
},
"posts": [{
"id": "1",
"title": "Rails is Omakase"
}, {
"id": "2",
"title": "The Parley Letter"
}]
}
is Rabl capable of doing this?
Try something like this:
object false
node(:links) do
{"posts.comments" => "http://example.com/posts/{posts.id}/comments"}
end
child(#posts) do
attributes :id, :title
end

Resources