I need to output custom JSON in order to be backwards compatible with existing software, which is written in Javascript, so I need to wrap my JSON with "parseDate(" at the beginning of it, and ");" at the end of it.
I tried doing it in controller like this
def index
#data = Data.all
#products = Product.all
respond_to do |format|
format.html
format.json {render :json => { :products => {:product => #data.name}}}
end
end
And then specify it in the view:
app/views/products.json.erb
<%= p "parseData(" %>
<%= render :json %>
<%= p "};" %>
But it outputs pure JSON completely skipping both "parseData(" and ");", why? How do I make JSON to be printed in the middle of the view and then append strings to the top and bottom?
JSON renderer supports a callback option.
format.json { render :json => { :products => {:product => #data.name }}, :callback => 'parseDate' }
You can read the implementation in the renderer.rb source code.
Related
I am curious if this is even possible. I am looking to render data based on the results of an action in ruby on rails. Here is the code:
def convert
render :text => Item.convert(params[:file], params[:output])
end
As you can see currently it just renders text. The item model takes in two parameters: a file and output. The output could be xml, json, plain text, etc. Some type of data. What I was hoping I could do is that I could render the output based on the output parameter. But make it dynamic. I was hoping I could do:
def convert
#items = Item.convert(params[:file], params[:output])
respond_to do |format|
format.json { render :json => #items }
format.xml { render :xml => #items }
end
end
Something that would call the method and based on the data returned it would render the appropriate data. Testing the above code returns an empty page. The body tag is completely empty.
Any ideas?
thanks
Mike Riley
If you are relying on the name of params[:output] to determine what to render, I would write is as below:
def convert
#items = Item.convert(params[:file], params[:output])
if File.extname(params[:output].match("text"))
render :text => Item.convert(params[:file], params[:output])
elsif File.extname(params[:output].match("json"))
render :json => Item.convert(params[:file], params[:output])
elsif File.extname(params[:output].match("xml"))
render :xml => Item.convert(params[:file], params[:output])
else
render :html => "default_html"
end
end
and you should have a default_html.html.erb that you would render if the extension name of the file does not match what is expected
I have the following #create method:
def create
begin
#order = #api_user.orders.create!(order_params)
render :json => #order, :only => [:id], :status => :created, :location => #order
rescue
render :json => {}, :status => :unprocessable_entity
end
end
However, I am using a generalistic approach for the rescue. If the order could not be created because one of the passed fields failed the validation, I would like to let the user know about that. So, if the creation of the order raised this:
ActiveRecord::RecordInvalid: Validation failed: Description1 is too long (maximum is 35 characters)
What is the proper way of catching and letting the API user know about it?
One thing you can do is make use of a light API library like rocketpants (https://github.com/Sutto/rocket_pants)
in which case, the method you want could be written like this:
def create
if #order = #api_user.orders.create!(order_params)
expose #order
else
error! :bad_request, :metadata => {:error_description => "#{#order.errors.full_messages}"}
end
end
This is assuming you have set the #api_user instance variable earlier somewhere. Also, the gem uses Active Model Serializers (https://github.com/rails-api/active_model_serializers) to serialize the #order into JSON, so you can always customize the output to your liking by creating a basic serializer, look at the github page for more info :)
Here is another way:
def create
#order = #api_user.orders.build(order_params)
if #order.save
render :json => #order,
:only => [:id], :status => :created, :location => #order
else
render :status => :unprocessable_entity,
:json => {:errors => #order.errors.full_messages}
end
end
You'll get back an array of errors in the JSON
I'm porting an application from Merb 1.1 / 1.8.7 to Rails 3 (beta) / 1.9.1 that uses JSON responses containing HTML fragments, e.g., a JSON container specifying an update, on a user record, and the updated user row looks like . In Merb, since whatever a controller method returns is given to the client, one can put together a Hash, assign a rendered partial to one of the keys and return hash.to_json (though that certainly may not be the best way.) In Rails, it seems that to get data back to the client one must use render and render can only be called once, so rendering the hash to json won't work because of the partial render.
From reading around, it seems one could put that data into a JSON .erb view file, with <%= render partial %> in and render that. Is there a Rails-way of solving this problem (return JSON containing one or more HTML fragments) other than that?
In Merb:
controller:
only_provides :json
...
self.status = 204 # or appropriate if not async
return {
'action' => 'update',
'type' => 'user',
'id' => #user.id,
'html' => partial('user_row', format: :html, user: #user)
}.to_json
In Rails:
controller:
respond_to do |format|
format.json do
render template: '/json/message-1',
locals: {
action: 'update',
type: 'user',
id: #user.id,
partial: 'user_row.html.erb',
locals: { user: #user }
}
end
end
view: json/message-1.json.erb
{
"action": <%= raw action.to_json %>,
"type": <%= raw type.to_json %>,
"id": <%= raw id.to_json %>,
"html": <%= raw render(partial: partial, locals: locals).to_json %>
}
The closest to the original from Merb approach I could find in Rails is to use #render_to_string
render json: {
'action' => 'update',
'type' => 'user',
'id' => #user.id,
'html' => render_to_string(partial: 'user_row.html.erb', locals: { user: #user })
}
This gets around a fair bit of complexity that comes in from adding a layer of json.erb templates into the mix, whether it's Rails Purist approach I couldn't say; possibly something with RJS would typically be used.
There's another question that has more solutions for json.erb files. See json erb template cannot find other html partial
class UsersController < ApplicationController
respond_to :json
def show
#user = User.find(params[:id])
respond_with(#user) do |format|
if #user.save
format.json { render :json => #user }
else
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
end
[
{
"id":"123",
"name":"House"
},
{
"id":"1456",
"name":"Desperate Housewives"
},
{
"id":"789",
"name":"Dollhouse"
},
{
"id":"10",
"name":"Full House"
}
]
How can I render to produce this JSON format from within Ruby? I have all the data from the DB (#result) and don't know what data structure to use in Ruby that will render to this when I do this:
respond_to do |format|
format.json { render :json => #result}
end
What data structure should #result be and how can I iterate to produce it? Thank you!
If #result is an array of ActiveRecord model instances then render :json => #result will produce something like what you are after, but will include all the attributes of the model (render calls to_json on the object you pass it unless it is a string).
To only include the id and name attributes, you can use the :only parameter of to_json:
respond_to do |format|
format.json { render :json => #result.to_json(:only => [:id, :name] }
end
Alternatively, you can create a array of Hash objects that only contain the required attributes:
respond_to do |format|
format.json { render :json =>
#result.collect {|o| {:id => o.id, :name => o.name} } }
end
Edit: See #dt's comment below. There is an attribute in the model named text that needs to be output as name. This can be done by creating an alias for text in the model:
class Model < ActiveRecord::Base
alias_method :name, :text
and including the name using :methods:
respond_to do |format|
format.json { render :json => #result.to_json(:only => :id, :methods => :name }
end
Alternatively, the array of hashes approach can be used to rename the attribute:
respond_to do |format|
format.json { render :json =>
#result.collect {|o| {:id => o.id, :name => o.text} } }
end
Try using the json gem. It will allow to you do things like
#result.to_json
to convert your structures (say, a Hash) to json format. If you're using Ruby on Rails, this functionality should already be accessible to you, so just create a Hash and call to_json.
To create a JSON object of that particular format, you need a ruby array containing hashes for its elements.
#result = [{:id => "10", :name => "Full House"}, {:id => "789", :name => "blahblah"},...]
Rails will convert the ruby array to json correctly in your render response block:
respond_to do |format|
format.json { render :json => #result}
end
I was using a jQuery plugin (FCBKComplete) that needed the json results with specific key names, specifically 'caption' and 'value', which did not exist in the array I was calling to_json on.
So I did this (hacky, but it works):
render :json => taggings.map { |t| {:caption => t.tag.name, :value => t.tag.name} }.to_json
Where taggings is an array returned by an ActiveRecord find, and that returns json like so:
[{"value":"tag.a","caption":"tag.a"},{"value":"tag.b","caption":"tag.b"}]
I'd love to use render :json but it seems its not as flexible. Whats the right way to do this?
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #things }
#This is great
format.json { render :text => #things.to_json(:include => :photos) }
#This doesn't include photos
format.json { render :json => #things, :include => :photos }
end
I've done something similar with render :json. This is what worked for me:
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #things.to_json(:include => { :photos => { :only => [:id, :url] } }) }
end
I guess this article can be useful for you - Rails to_json or as_json? by Jonathan Julian.
The main thought is that you should avoid using to_json in controllers. It is much more flexible to define as_json method in your model.
For instance:
In your Thing model
def as_json(options={})
super(:include => :photos)
end
And then you can write in your controller just
render :json => #things
Managing complex hashes in your controllers gets ugly fast.
With Rails 3, you can use ActiveModel::Serializer. See http://api.rubyonrails.org/classes/ActiveModel/Serialization.html
If you're doing anything non-trivial, see
https://github.com/rails-api/active_model_serializers. I recommend creating separate serializer classes to avoid cluttering your models and make tests easier.
class ThingSerializer < ActiveModel::Serializer
has_many :photos
attributes :name, :whatever
end
# ThingsController
def index
render :json => #things
end
# test it out
thing = Thing.new :name => "bob"
ThingSerializer.new(thing, nil).to_json
format.json { render #things.to_json(:include => :photos) }
in case of array what I done is
respond_to do |format|
format.html
format.json {render :json => {:medias => #medias.to_json, :total => 13000, :time => 0.0001 }}
end