Convert active record result into Json object - ruby-on-rails

I have below output from active record query
[{"image_id"=>1, "image_name"=> "image1", action_type"=>"Call", "count"=>2},
`{"image_id"=>1, "image_name"=> "image1","action_type"=>"sms", "count"=>1},
{"image_id"=>2, "image_name"=> "image2","action_type"=>"sms", "count"=>1} ]`
Now I want this to be converted into Json object like below
{ "1": { "counts": { "call": 2, "sms": 1 } , "title":'image1' },
"2": { "counts": {"sms": 1} , 'title':'image2'}}

Please check this code.
#xx = [{"image_id"=>1, "image_name"=>"image1", "action_type"=>"Call", "count"=>2}, {"image_id"=>1, "image_name"=>"image1", "action_type"=>"sms", "count"=>1}, {"image_id"=>1, "image_name"=>"image1", "action_type"=>"sms", "count"=>1}]
#arr = []
#xx.each_with_index do |x, i|
#arr << {(i+1).to_s.to_sym => {"counts" => {x["action_type"].to_sym => x["count"]}}}
end
respond_to do |f|
f.json {render :json => #arr}
end

There are two popular libraries that are both very helpful:
active_model_serializers
jbuilder
I prefer active_model_serializers, personally. Many disagree.

Related

I meet a trouble about hash in ruby on rails

i have a trouble about hash in rails
example:
grades = {"a" => 10, "b" => 6}
grades = {"c"=>15, "d"=>16}
respond_to do |format|
format.json { render json: grades }
end
but result is received : "c"=>15, d"=>16
why doesn't it return all : a,b,c,d.
You are overriding your variable. You need either do:
grades = { "a" => 10, "b" => 6 }
grades.merge!({"c"=>15, "d"=>16})
or
grades = { "a" => 10, "b" => 6 }
grades['c'] = 15
greade['d'] = 16
for your 2nd problem in the comment:
I guess you want something like this:
records['data'] = []
(0..2).each do |i|
records['data'] << [#items_record[i]['id'], #items_record[i]['id'], #items_record[i]['name_vi']]
end

Rails json response. Int converting to date

I have a problem, with responding data in json.
Here is a code:
#data [
actions_by_type.each do |action|
[ action[:date].to_i, action[:activity] ]
end
]
respond_to do |format|
format.json { render :json => #data }
end
But responde is:
...{"date":"2013-04-29T20:20:00Z","activity":"87"}...
Why rails convert my int time, to string datetime?
You should use .map instead of .each.
#data = actions_by_type.map do |action|
[ action[:date].to_i, action[:activity] ]
end
respond_to do |format|
format.json { render :json => #data }
end
With .each the result of #data will be the actions_by_type instead of the new array.
x.each returns x so this:
x = actions_by_type.each do |action|
[ action[:date].to_i, action[:activity] ]
end
is equivalent to:
x = actions_by_type
You want to use map instead of each:
#data = actions_by_type.map do |action|
[ action[:date].to_i, action[:activity] ]
end

JBuilder loop that produces hash

I need loop that produces hash, not an array of objects. I have this:
json.service_issues #service.issues do |issue|
json.set! issue.id, issue.name
end
that results:
service_issues: [
{
3: "Not delivered"
},
{
6: "Broken item"
},
{
1: "Bad color"
},
{
41: "Delivery problem"
}
]
I need this:
service_issues: {
3: "Not delivered",
6: "Broken item",
1: "Bad color",
41: "Delivery problem"
}
Is it possible to do this without converting AR result to hash manually?
Jbuilder dev here.
Short answer: Yes. It's possible without converting array of models into hash.
json.service_issues do
#service.issues.each{ |issue| json.set! issue.id, issue.name }
end
but it'd probably be easier to prepare hash before-hand.
json.service_issues Hash[#service.issues.map{ |issue| [ issue.id, issue.name ] }]
For anyone who is interested in having an hash of arrays (objects), you can use the following code:
#bacon_types.each do |bacon_type|
json.set! bacon_type.name, bacon_type.bacons do |bacon|
bacon.title bacon.title
...
end
You can do it like this way
Jbuilder.encode do |json|
json.service_issues #service.issues.inject({}) { |hash, issue| hash[issue.id] = issue.name; hash }
end
The code generating hash technique may be understood by following example.
[1] pry(main)> array = [{id: 1, content: 'a'}, {id: 2, content: 'b'}]
=> [{:id=>1, :content=>"a"}, {:id=>2, :content=>"b"}]
[2] pry(main)> array.inject({}) { |hash, element| hash[element[:id]] = element[:content]; hash }
=> {1=>"a", 2=>"b"}
The key point of inject to generate hash, return created hash every after inserting new element. Above example, it is realized by ; hash.

Rails 3 : Generate view using rabl

In my Rails Application i have Two instance variables #departments and #register
#departments =
{
"users": [
{
"departmentid": "DP11"
},
{
"departmentid": "DP12"
},
{
"departmentid": "DP13"
},
{
"departmentid": "DP10"
}
]
}
#register =
{
"attendance": [
0,
0,
2,
1
]
}
#register contains an array .
Is it possible to show like below format using rabl (attendancebydepartment.json.rabl) view
{
"users": [
{
"departmentid": "DP11",
"attendance"=0
},
{
"departmentid": "DP12",
"attendance"=0
},
{
"departmentid": "DP13",
"attendance"=2
},
{
"departmentid": "DP10",
"attendance"=1
}
]
}
My controller looks like
def attendancebydepartment
#register = Array.new
#departments = User.select('departmentid').uniq
startdate = params[:startdate]
enddate = params[:enddate]
#count = #departments.count
#departments.each do |d|
#register << (Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:one))+(Register.where(:date => startdate..enddate , :departmentid => d.departmentid).sum(:two))
end
end
Is it possible to add each department and its corresponding attendance to array,so that i can display like expected format.
Perhaps use the zip method to create a new variable and then present that.
irb(main):001:0> departments = {'users' => [{'id' => 1}, {'id' => 2}]}
=> {"users"=>[{"id"=>1}, {"id"=>2}]}
irb(main):002:0> register = {'attendance' => [0,1]}
=> {"attendance"=>[0, 1]}
irb(main):004:0> departments['users'].zip(register['attendance'])
=> [[{"id"=>1}, 0], [{"id"=>2}, 1]]
On the other hand, it looks like a simpler design would be to have a Department model that has a has_many association with Users. Then you could refer to the count of users directly from an instance of Department.
It may be easiest to create objects in your controller using OpenStruct, something like this, but I would recommend re-writting attendancebydepartment to not loop twice.
#users = []
#departments.each_with_index do |dep, index|
user = OpenStruct.new
user.departmentid = dep.departmentid
user.attendence = #register[index].attendence
#users << user
end
And in the view:
collection #users => :users
attribute :departmentid, :attendence

How to paginate Rabl's collections

I have this template:
# app/views/posts/index.rabl
collection #posts => :posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(#user) }
Witch returns:
{
"posts": [
{
"post": {
"id": 5,
"title": "...",
"subject": "...",
"user": {
"full_name": "..."
},
"read": true
}
}
]
}
And I would like to add to add some pagination params in order to rendering this:
{
"posts": [
{
"post": {
"id": 5,
"title": "...",
"subject": "...",
"user": {
"full_name": "..."
},
"read": true
}
}
],
"total": 42,
"total_pages": 12
}
Any ideas? Many thanks!
Sorry for my noob question, whitch was answered by the README. Here's an example of pagination:
object false
node(:total) {|m| #posts.total_count }
node(:total_pages) {|m| #posts.num_pages }
child(#posts) do
extends "api/v1/posts/show"
end
Note: I'm using Kaminari for pagination.
When searching for kaminari and rabl this is the first and pretty much only relevant result. As such, I would like to leave here a solution according to the HAL Specification that generates links like this.
So first, start with the view:
# api/v1/posts/index.rabl
object false
child(#posts) do
extends 'api/v1/posts/show'
end
node(:_links) do
paginate #posts
end
Then proceed to define the paginate method:
# app/helpers/api_helper
module ApiHelper
def paginate(collection)
current_page_num = collection.current_page
last_page_num = collection.total_pages
{
:first => first_page,
:previous => previous_page(current_page_num),
:self => current_page(current_page_num),
:next => next_page(current_page_num, last_page_num),
:last => last_page(last_page_num)
}
end
def first_page
{ :href => url_for(:page => 1) }
end
def previous_page(current_page_num)
return nil if current_page_num <= 1
{ :href => url_for(:page => current_page_num-1) }
end
def current_page(current_page_num)
{ :href => url_for(:page => current_page_num) }
end
def next_page(current_page_num, last_page_num)
return nil if current_page_num >= last_page_num
{ :href => url_for(:page => current_page_num+1) }
end
def last_page(last_page_num)
{ :href => url_for(:page => last_page_num) }
end
end
And finally, include the helper in the necessary controllers. The helper could be included in a Api::BaseController, from which all API controllers inherit:
helper :api
I could not have done this without Zag zag..'s solution, so.. Thank you so much!
note, for will_paginate 3.0.0 the following works:
node(:total) {|m| #posts.total_entries }
node(:total_pages) {|m| (#posts.total_entries.to_f / #posts.per_page).ceil }
node(:page_num){|m| #posts.current_page}
This might be what you are looking for ;)
object false
node :comments do
partial('posts/index', object: #posts)
end
node(:pagination) do
{
total:#posts.count,
total_pages: 20
}
end

Resources