Convert two models attribute in a json response - ruby-on-rails

I am on ruby on rails and i have two models, the goal is to do a search of the website on two models, i am using twitter typeahead but the issue i have is the json has to be one object.
I am not sure what is the best way to convert my two objects into one. Here the code
#users= Search.user(params[:query])
#articles= Search.article(params[:query])
respond_to do |format|
format.html # index.html.erb
format.json {
render :json => {
:art=> #articles.map(&:title),
:user=> #users.map(&:first_name)
}}
end
end
I am not sure what the best way or i can't seem to find the best documentation to merge these two models into one. I dont know if to_json, as_json, or concat would be the best.
The idea is to have a result of the following json from
{"art":["John","Serge","Dean","feng","Heather"],"user":["Ontario high school teachers drop next week's walkout plan","Air Canada to appeal Quebec court ruling on Aveos"]}
To the following
{"result":["John","Serge","Dean","feng","Heather", "Ontario high school teachers drop next week's walkout plan","Air Canada to appeal Quebec court ruling on Aveos"]}

So if you want to get an array of both users and articles:
respond_to do |format|
format.json { render :json => {:art => #articles,
:user => #users }}
end
end
Based on your edit:
format.json {
render :json => {
result => #articles.map(&:title) | #users.map(&:first_name)
}}
Based on last comment, just trap the nil issue:
format.json {
render :json => {
result => (#articles.map(&:title) || []) | (#users.map(&:first_name) || [])
}}

Can I suggest that you use a JSON view template. There are many options for you but the two most popular are RABL and JBuilder. I can highly recommend the RABL gem.
There is a reason for their popularity, they make rendering json a breeze
You can find the RABL gem here https://github.com/nesquena/rabl
You can find the JBuilder gem here https://github.com/rails/jbuilder
There are excellent railsasts on both of them showing how to use them.
RABL
http://railscasts.com/episodes/322-rabl
JBuilder
http://railscasts.com/episodes/320-jbuilder
I favour RABL purely out of personal preference you should look at both options to see which best suits you.
Adding a gem is not normally something I would recommend but I think you will find that either of these solutions will match your needs

Related

Rails: JSON.pretty_generate(obj) in the controller does not produce pretty output

I need a pretty output of the JSON for an activerecord object in the rails controller. Based on the answer to this question by jpatokal, I tried the following:
respond_to do |format|
format.json { render :json => JSON.pretty_generate(record) }
end
where
record
is an activerecord object. It does not produce a prettified output. However, when I try outputting a hash using the same code, viz,
respond_to do |format|
format.json { render :json => JSON.pretty_generate({"abc" => "1", "def" => "2"}) }
end
it does produce a prettified output (so pretty_generate is working, and so is my browser).
How do I use pretty_generate to produce a pretty output of an activerecord object?
To get pretty output of JSON from an active record object you have to first request the object as JSON.
record.as_json
The above code will do this for you, the short way to render out pretty JSON from the controller is:
render json: JSON.pretty_generate(record.as_json)
This also solves the "only generation of JSON objects or arrays allowed" error you can get trying to convert AR objects to pretty_generate JSON.
EDIT: I forgot to mention this can all be done in rails 4.1.8 and above (possibly even earlier) using the standard json and multi-json gems packaged with rails project.

How to create a Rails model without an HTML view

I have a model that I only want to ever return JSON, regardless of any conneg or file-like extensions on the URI (e.g. /app/model.json). Google-fu is coming up short and this can't be that difficult.
In your controllers you simple have to create a respond_to block that only responds to JSON:
respond_to do |format|
format.json { render :json => #model }
end
This is actually a decision made by the controller, not because there is/is not a model or view present. In your controller you can:
render json: #your_model
However you will quickly find that the default implementation of to_json (which is what is used internally, above) can be annoying hard to do exactly what you want. When you reach that point you can use RABL to create views that massage the JSON from your model(s) exactly as you want.

What is the best way to return multiple variables and arrays formatted in xml, json etc with respond_with

I'm looking for a good way to return xml or json from a controller including multiple variables. For example:
def index
#ad = Ad.some_annoying_ad
#map = Map.some_map_for_something
#articles = Articles.trending
respond_with #articles
end
How would I best add the #ad and #map var to the #articles array? I have seen people using the merge function, but I am not sure if that's what I'm looking for. Just want to know which way is most standard, flexible, DRY. Thanks!
Note: I am aware that respond with will automatically format the results in xml or json depending on the file extension added to the url. Thanks!
Instead of merging #ad, #map, just create create a new hash then put add the arrays to it, like
respond_with({:ad => #ad, :map => #map, :article => #article})
It just render all the datas with groups.
I would recommend you to take a look at RABL (stands for Ruby API Builder Language) gem (cast, github). It offers you a DSL for defining the structure of your JSON/XML response in templates (like Haml or CoffeeScript does).
In your case it can be like this:
# in *.json.rabl or *.xml.rabl
object false
child(#ad) { attributes :field1, :field2 }
child(#map) { attributes :field3, :field4 }
child(#article) { attributes :body => :content } # remap is easy!
There is even partials support for DRYing your code. Worth trying.

How do you output JSON from Ruby on Rails?

I'm looking to have a model that gets created / updated via AJAX. How do you do this in Ruby on Rails?
Also, more specifically: how do you output JSON in RoR?
def create
response = {:success => false}
#source = Source.new(params[:source])
if #source.save
response.success = true
end
render :json => response.to_json
end
All you need to do is call render :json with an object, like so:
render :json => my_object
For most objects, this will just work. If it's an ActiveRecord object, make sure to look at as_json to see how that works. For your case illustrated above, your hash will be transformed to json and returned.
However, you do have an error: you cant access the success key via response.success -- you should instead do response[:success]
jimothy's solution is really good butI believe it isn't scalable in the long term. Rails is meant to be a model, view, and controller framework. Right now JSON is cheated out of a view in default rails. However there's a great project called RABL which allows JSON view. I've written up some arguments for why I think it's a good option and how to get up and running with it quickly. See if this is useful for you: http://blog.dcxn.com/2011/06/22/rails-json-templates-through-rabl/
#source = Source.new(params[:source])
respond_to do | f |
f.html {
# do stuff to populate your html view
# maybe nothing at all because #source is set
}
f.any(:xml, :json) {
render request.format.to_sym => #source
}
end

Filter a model's attributes before outputting as json

I need to output my model as json and everything is going fine. However, some of the attributes need to be 'beautified' by filtering them through some helper methods, such as number_to_human_size. How would I go about doing this?
In other words, say that I have an attribute named bytes and I want to pass it through number_to_human_size and have that result be output to json.
I would also like to 'trim' what gets output as json if that's possible, since I only need some of the attributes. Is this possible? Can someone please give me an example? I would really appreciate it.
Preliminary search results hint at something regarding as_json, but I can't find a tangible example pertaining to my situation. If this is really the solution, I would really appreciate an example.
Research: It seems I can use to_json's options to explicitly state which attributes I want, but I'm still in need of figuring out how to 'beautify' or 'filter' certain attributes by passing them through a helper before they're output as json.
Would I create a partial for a single json model, so _model.json.erb, and then create another one for the action I'm using, and within that simply render the partial with the collection of objects? Seems like a bunch of hoops to jump through. I'm wondering if there's a more direct/raw way of altering the json representation of a model.
Your model can override the as_json method, which Rails uses when rendering json:
# class.rb
include ActionView::Helpers::NumberHelper
class Item < ActiveRecord::Base
def as_json(options={})
{ :state => state, # just use the attribute when no helper is needed
:downloaded => number_to_human_size(downloaded)
}
end
end
Now you can call render :json in the controller:
#items = Item.all
# ... etc ...
format.json { render :json => #items }
Rails will call Item.as_json for each member of #items and return a JSON-encoded array.
I figured out a solution to this problem, but I don't know if it's the best. I would appreciate insight.
#items = Item.all
#response = []
#items.each do |item|
#response << {
:state => item.state,
:lock_status => item.lock_status,
:downloaded => ActionController::Base.helpers.number_to_human_size(item.downloaded),
:uploaded => ActionController::Base.helpers.number_to_human_size(item.uploaded),
:percent_complete => item.percent_complete,
:down_rate => ActionController::Base.helpers.number_to_human_size(item.down_rate),
:up_rate => ActionController::Base.helpers.number_to_human_size(item.up_rate),
:eta => item.eta
}
end
respond_to do |format|
format.json { render :json => #response }
end
Basically I construct a hash on the fly with the values I want and then render that instead. It's working, but like I said, I'm not sure if it's the best way.

Resources