Custom json key in ruby on rails - ruby-on-rails

I have an as_json method I'm overriding to include an association.
def as_json(options)
super((options || { }).merge({
:include => {:otherobject => {:include => :category}}
}))
end
So this give me json like this
{
"prop1": "val1",
"prop2": "val2",
"otherobject": {"category": {} }
}
I need to adjust the json to look like this because of my processing code.
{
"prop1": "val1",
"prop2": "val2",
"otherobject": { "otherobject": { "category": {} } }
}
I basically need to wrap the data with another key. How can I do this in rails?

def as_json(options = {})
old = super(options.merge({ :include => {:otherobject => {:include => :category}}))
new = old.slice!(:otherobject)
new[:otherobject] = old
new
end

Related

Merge two hashes in ruby

I have two collections of hashes
and_filters = [{:filter=>:brand, :value=>"Fila"}, {:filter=>:brand, :value=>"Adidas"}]
or_filters = [{:filter=>:gender, :value=>"Hombre"}]
and i need make like the following struct
:_or => [
{ :_and => [
{:gender => "Hombre"},
{:brand => "Adidas"}]
},
{ :_and => [
{:gender=>"Hombre"},
{:brand=>"Fila"}]
}
]
For this i did
query[:_or] = []
or_filters.each do |or_f|
query[:_or] << {
:_and => [
and_filters.map do |and_f|
{and_f[:filter] => and_f[:value]}
end
{ or_f[:filter] => or_f[:value] }
]
}
end
but an error Expected: { shows in code. Apparently the second loop is badly syntactically
It's not pretty, but I believe this gives the desired results:
{_or: or_filters.each_with_object([]) do |or_filter, or_filter_ary|
or_filter_hsh = {or_filter[:filter] => or_filter[:value]}
and_filters.each do |and_filter|
and_filter_hsh = {and_filter[:filter] => and_filter[:value]}
or_filter_ary << {_and: [or_filter_hsh, and_filter_hsh]}
end
end
}
Which gives:
{:_or => [
{ :_and => [
{:gender=>"Hombre"},
{:brand=>"Fila"}
]},
{ :_and => [
{:gender=>"Hombre"},
{:brand=>"Adidas"}
]}
]}
It looks like you want every combination of the given and_filters with the given or_filters. In that case, and assuming you don't care about order (:gender before :brand vs. the other way around) Array#product is your friend:
result = {
_or: and_filters.product(or_filters).map do |a|
{ _and: a.map {|filter:, value:| { filter => value }} }
end
}
# => {
# :_or => [
# {:_and => [{:brand=>"Fila"}, {:gender=>"Hombre"}]},
# {:_and => [{:brand=>"Adidas"}, {:gender => "Hombre"}]}
# ]
# }
See it in action on repl.it: https://repl.it/#jrunning/HorizontalDirectCharmap
Thats what i was looking for
query = {}
query[:_or] = or_filters.map do |or_f|
and_filters_aux = and_filters.dup
and_filters_aux << or_f
{ :_and => and_filters_aux.map{|hsh| {hsh[:filter] => hsh[:value]} } }
end
https://repl.it/repls/ShyLateClients

Custom JSON response with acts_as_taggable_on

I just implemented acts_as_taggable_on in my app and now I'm trying to trim my JSON response so it doesn't return everything for the Model in question. Here's what my as_json method looks like:
def as_json(options={})
super(:only => [:serial_number],
:include => {
:device_functions => { :only => [:can_scan, :can_brute] },
:scan_options => { :methods => :scan_ip_list}
}
)
end
Which currently returns:
{
"serial_number": "abcdefg12345",
"device_functions": [
{
"can_scan": true
}
],
"scan_options": [
{
"id": 1,
"device_id": 11,
"created_at": "2016-02-05T02:26:26.090Z",
"updated_at": "2016-02-05T02:26:26.090Z",
"scan_ip_list": [
"10.10.10.100-110",
"10.10.10.1"
]
}
]
}
I want to get rid of extra data that I don't need, such as id, device_id, created_at and updated_at now.
Also, using :only => worked find for the :device_functions response, but I had to use :methods => for :scan_options since I'm using acts_as_taggable_on... at least that's what I read and was the only option that returned something (I tried :only => and :include => as well but they returned an empty hash:
{
"serial_number": "abcdefg12345",
"device_functions": [
{
"can_scan": true
}
],
"scan_options": [
{}
]
}
You just need to add the :only option to your :scan_options hash too:
# ...
:scan_options => { :methods => :scan_ip_list, :only => :scan_ip_list }
Also, FWIW, you should probably merge into option in case you ever want to supply some of your own options, so:
# ...
super options.merge( :only => ...etc.

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

How to "trasform" an array of hash so that you can customize logic to access its data?

I am using Ruby on Rails 3 and I would like to "trasform" the following array so that I can use my custom logic to access its data.
This is the original array from which I have to build a new one
[
{
"account" => {
"id" => 45,
"name" => "Test_name",
"..." => ..."
}
},
{
"other" => {
"sub_other" => {...}
}
}
]
I would like to trasform the above array so that I can do in my controller something like
array_name[45]
# => {
"name" => "Test_name",
"..." => ..."
}
but only for the account hashs. The other hash should remain untouched.
How can I proceed to build the new array?
If I understand your requirements correctly, I think you are better off constructing a hash from account id to account data. Perhaps something like this will work:
arr = [
{
"account" => {
"id" => 45,
"name" => "Test_name",
"..." => "..."
}
},
{
"other" => {
"sub_other" => "..."
}
}
]
account_hashes = arr.select {|item| item.keys.first == "account"}
answer = account_hashes.inject({}) do |acc, item|
acc[item["account"].delete("id")] = item["account"]
acc
end

Resources