RABL: JSON objects without root key - ruby-on-rails

I have this rabl template:
object #photo
attributes :id
child :comments do
attributes :id, :body
end
Which gives me this JSON response:
{
photo: {
id: 1,
comments: [
{
comment: {
id: 1,
body: 'some comment'
}
},
{
comment: {
id: 2,
body: 'another comment'
}
}
]
}
}
But I want it to look like this:
{
id: 1,
comments: [
{
id: 1,
body: 'some comment'
},
{
id: 2,
body: 'another comment'
}
]
}
Why does rabl wrap each element in the array with an extra object called comment. In this way when I access the collection in javascript I have to write:
var comment = image.comments[0].comment
instead of:
var comment = image.comments[0]
I know that if I include :comments in the attributes list for the #photo object it works the way I want, but when I want another level of nested associations for each comment object, there isn't a way to handle that besides using child, but that gives me the JSON response that I don't want.
Maybe I'm just misunderstanding the whole thing -- can someone explain or help? Thanks!

Got it!
Create a new file in config/initializers/rabl_config.rb:
Rabl.configure do |config|
config.include_json_root = false
config.include_child_root = false
end

Related

How to chaange my as_json method?

Now i have used the as_json method like this in my model
def as_json(options = {})
{
id: id,
diary_id: diary_id,
title: title,
post_date_gmt: date,
post_content: strip_tags(content),
smiley_id: smiley_id,
author_id: user_id,
author_name: user.display_name,
attachments: filter_attachments(options[:version]),
root_comments: format_comments(nested_comments.arrange(:order => :created_at)),
post_readings: post_readings.size,
is_read: read_by(options[:current_user])
}
end
I need to change this structure a bit as follows, Actually i want group this array by the date.
{
date_01: {
[post1], [post2], [post3]
},
date_02: {
[post1], [post2], [post3]
}
}
What should I do ?
I fixed the issue as follows
post_dates = (no_of_days.days.ago.to_date..(date_as_string.to_date + no_of_days.days)).map{ |date| date.strftime("%Y-%m-%d") }
# Arrange posts details under each date
i = 0
post_dates.each do |post_date|
posts_grouped_by_date[i] = {:post_date => post_date, :posts_for_date => diary.posts_for_date(Date.parse(post_date) )}
i = i + 1
end
render json: posts_grouped_by_date.sort_by {|hash| hash['post_date']}.as_json(current_user: current_user)
replace the values of data keys to an array of arrays. like below.
{
date_01: [
[post1], [post2], [post3]
],
date_02: [
[post1], [post2], [post3]
]
}

Rails Ember strong parameters clarification

What should the strong parameters for my chapters_controller be if I have a Book entity and a Chapter entity?
Note: I am using JSON API.
In my chapters_controller, should my strong parameters be:
:title, :order, :content, :published, :book, :picture
Or should it be:
:title, :order, :content, :published, :book_id, :picture
If I use :book instead of :book_id, then in my Ember application, when I go to create a new chapter, I am able to create it and associate this chapter to the parent book, however, my test fails:
def setup
#book = books(:one)
#new_chapter = {
title: "Cooked Wolf Dinner",
order: 4,
published: false,
content: "The bad wolf was very mad. He was determined to eat the little pig so he climbed down the chimney.",
book: #book
}
end
def format_jsonapi(params)
params = {
data: {
type: "books",
attributes: params
}
}
return params
end
...
test "chapter create - should create new chapter assigned to an existing book" do
assert_difference "Chapter.count", +1 do
post chapters_path, params: format_jsonapi(#new_chapter), headers: user_authenticated_header(#jim)
assert_response :created
json = JSON.parse(response.body)
attributes = json['data']['attributes']
assert_equal "Cooked Wolf Dinner", attributes['title']
assert_equal 4, attributes['order']
assert_equal false, attributes['published']
assert_equal #book.title, attributes['book']['title']
end
end
I get error in my console saying Association type mismatch.
Perhaps my line:
book: #book
is causing it?
Either way, gut feeling is telling me I should be using :book in my chapters_controller strong parameters.
It's just my test isn't passing, and I am not sure how to write the parameter hash for my test to pass.
After a few more hours of struggle and looking at the JSON API docs:
http://jsonapi.org/format/#crud-creating
It has come to my attention, in order to set a belongsTo relationship to an entity with JSON API, we need do this:
POST /photos HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"data": {
"type": "photos",
"attributes": {
"title": "Ember Hamster",
"src": "http://example.com/images/productivity.png"
},
"relationships": {
"photographer": {
"data": { "type": "people", "id": "9" }
}
}
}
}
This also led me to fixing another problem I had in the past which I couldn't fix. Books can be created with multiple genres.
The JSON API structure for assigning an array of Genre to a Book entity, we replace the data hash with a data array in the relationship part like this:
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
Additonally, in my controllers, anything strong parameters like so:
:title, :content, genre_ids: []
Becomes
:title, :content, :genres
To comply with JSON API.
So for my new test sample datas I now have:
def setup
...
#new_chapter = {
title: "Cooked Wolf Dinner",
order: 4,
published: false,
content: "The bad wolf was very mad. He was determined to eat the little pig so he climbed down the chimney.",
}
...
end
def format_jsonapi(params, book_id = nil)
params = {
data: {
type: "chapters",
attributes: params
}
}
if book_id != nil
params[:data][:relationships] = {
book: {
data: {
type: "books",
id: book_id
}
}
}
end
return params
end
Special note on the relationship settings - only add relationships to params if there is a relationship, otherwise, setting it to nil is telling JSON API to remove that relationship, instead of ignoring it.
Then I can call my test like so:
test "chapter create - should create new chapter assigned to an existing book" do
assert_difference "Chapter.count", +1 do
post chapters_path, params: format_jsonapi(#new_chapter, #book.id), headers: user_authenticated_header(#jim)
assert_response :created
json = JSON.parse(response.body)
attributes = json['data']['attributes']
assert_equal "Cooked Wolf Dinner", attributes['title']
assert_equal 4, attributes['order']
assert_equal false, attributes['published']
assert_equal #book.id, json['data']['relationships']['book']['data']['id'].to_i
end

How can i map one query result to another query result in ruby on rails

Hi I am new to Ruby on Rails development. I have two queries with different model. My first_query is get from question model and second query is get from favourite model. I want to map with a column user_favourite from second query result to first query result.
this is my controller queries
def index
#first_query = Question.order('created_at DESC').page(params[:page]).per( (ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i )
#second_query=Favourite.with_user_favourite(#user)
#combined_queries = #first_query + #second_query
end
favourite.rb
scope :with_user_favourite, -> (user) {
joins(:user).
where(favourites: {user_id: user})
}
index.json.builder
json.questions #combined_events
json for the result is
{
questions: [ #this is first query result
{
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{ #this is second query result
id: 1,
user_id: 2,
question_id: 84,
created_at: "2016-05-12T06:51:54.555-04:00",
updated_at: "2016-05-12T06:51:54.555-04:00"
},
{
id: 2,
user_id: 2,
question_id: 81,
created_at: "2016-05-12T07:23:47.770-04:00",
updated_at: "2016-05-12T07:23:47.770-04:00"
}
]
}
i want response like
{
questions: [
{ #first query result
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: { #corresponding result from second query result
id: 1,
user_id: 2,
question_id: 88
}
},
{ #first query result
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: {} #corresponding result from second query result if there is no result for particular question in favourite table
},
]
}
The model relationships are:
class Question
belongs_to :user
has_many :favourite
end
class Favourite
belongs_to :user
belongs_to :question
end
class User
has_many :questions
has_many :favourite
end
You should modify your jBuilder template to support nesting.Since your model association is like one question has_many favorite so it will be an array and you can easily nest one object inside another.
json.array! #questions do |question|
json.user_id question.user_id
json.content question.content
json.user_favorites question.favorites do |json,favorite|
json.id question.favorite.id
json.user_id question.favorite.user.id
json.question_id question.id
end
end
Here is a link that you can refer to for more clarity.
Generate a nested JSON array in JBuilder
Using JBuilder to create nested JSON output in rails
Hope it helps!.
You can add an association between user_favourite and question so that you can select all user favourites on one question.
Question.rb:
has_many :user_favourites
UserFavourite.rb:
belongs_to :question
Then, as your web action:
def index
#questions = Question.all.order('created_at DESC').page(params[:page]).per((ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i)
end
And finally, in index.json.builder:
json.questions #questions do |question|
json.user_favourites question.user_favourites
end
including whatever other fields you want.

Structure json inside controller

I need to do a json structure like this, but with more than 1 items:
charge = {
items: [{
name: "Product A",
value: 1000,
amount: 2
}]
}
I have an #items that is #items.pluck(:name, :price)
And I'm trying to create my json like this:
charge = {
items: [{
#items.each do |item|
'name:' = item.name,
'value:' = item.price,
'amount:' = 2
end
}]
}
And return this error:
SyntaxError in CoursesController#qualquer
syntax error, unexpected '=', expecting keyword_end
'name:' = item.name,
How i do this structure?
There are two things I see wrong. First, you're using "=" operator to set a Ruby Hash value. That's not correct, as Ruby hashes use symbols or strings. So your hash values will need to look like this:
{ "may_hash_key" => my.has_value }
or
{ my_hash_key: my.hash_value }
or
{ :may_hash_key => my.has_value }
Take your pick.
Additionally, if you are rendering JSON from your controller action, you can do something like this:
def index
# presumably some setup code
charge = {
items: #items.map do |item| {
name: item.name,
value: item.price,
amount: 2
} end
}
render json: charge
end
If you are not rendering JSON from your controller action, what you can do is set #charge and interact with it as a Ruby hash in your view instead.

how to create a custom json from raw data (database data)

I have create a show method in tour controller and want to render the data from the database into json format.
This the definition of the tour controller
class ToursController < ApplicationController
respond_to :json
def show
#tourcategory = Tourcategory.find(params[:id])
#tours= #tourcategory.tours
#tourcategories = Tourcategory.all
render :layout => false
end
end
This is the definition of the show.html.haml view
%h3 Tour for #{#tourcategory.title}
= #tours.to_json
The output of this code is following:
[{"content":"dscfds","created_at":"2015-12-12T09:48:32Z","elementid":"test1","id":8,"jobid":2,"next_button_title":"next","priority":23,"title":"test1","updated_at":"2015-12-12T09:48:32Z"}]
But i just want to render the data in this kind of json format, following:
var tour = {
id: "tour",
steps: [
{
title: "abc",
content: "Click this Button.",
target: "#abc",
placement: "bottom",
showNextButton: false,
skipIfNoElement : true
},
It's not clear what you're trying to achieve but it seems to me that you want to extract certain attributes from your #tours and group them as an array, if that's the case you can do something like this:
(list all the attributes you want inside t.attributes.slice())
tour = { id: "tour", steps: #tours.map { |t| t.attributes.slice("title", "content", "target") } }
and if you want to convert your keys from snake (underscore) to camel format:
tour = {
id: "tour",
steps: #tours.map {|t| t.attributes.slice("title", "content").map {|k,v| [k.camelize(:lower), v]}.to_h}
}

Resources