I have a Rails Controller who responds with JSON objects. Let's take this theoretical example :
respond_to :json
def index
respond_with Comment.all
end
This would respond with something like
[{"id":1,"comment_text":"Random text ", "user_id":1 ,"created_at":"2013-07-26T15:08:01.271Z","updated_at":"2013-07-26T15:08:01.271Z"}]
What i'm looking for is a "best practice" method to interfere with the formating of the json object and return something like this :
[{"id":1,"comment_text":"Random text ", "username": "John Doe", "user_id":1 ,"created_at":"3 hours ago"}]
As you can see, i'm adding a column that doesn't exist in the database model "username" , i'm taking out "updated_at" , and i'm formatting "created_at" to contain human readable text rather than a date.
Any thoughts anyone ?
Overwriting as_json or working with JSON ERB views can be cumbersome, that's why I prefer using ActiveModel Serializers (or RABL):
class CommentSerializer < ActiveModel::Serializer
include ActionView::Helpers::DateHelper
attributes :id, :created_at
def created_at
time_ago_in_words(object.created_at)
end
end
Look here for more information:
https://github.com/rails-api/active_model_serializers
https://github.com/nesquena/rabl
2 ways:
first: define a view, where you build and return an hash that you'll convert to json.
controller:
YourController < ApplicationController
respond_to :json
def index
#comments = Comment.all
end
end
view: index.json.erb
res = {
:comments => #comments.map do |x|
item_attrs = x.attributes
item_attrs["username"] = calculate_username
end
}
res.to_json.html_safe
second: use gem active_model_serializers
I'd redefine the as_json method of your model.
In your Comment model,
def username
"John Doe"
end
def time_ago
"3 hours ago"
end
def as_json(options={})
super(:methods => [:username, :time_ago], except: [:created_at, :updated_at])
end
You don't have to change your controller
Take a look at the documentation for as_json
Related
I want to show only the emails of my users, this is my controller
def all
#users = User.all
end
I am trying to do that using jbuilder, but when I do the request, it does not give nothing
just i see nothing, the problem what i have is finding the correct view for jbuilder
this is my route:
namespace :api do
namespace :v1, defaults: { format: :json} do
get '/all', to: 'users#all'
end
end
end
Firstly you need to assign instance varible in the controller action to pass it to jbuider view
def all
#users = User.all
end
Than build JSON in app/views/api/v1/user/all.json.jbuilder. BTW controller names are usually plural, users is better than user
json.array! #users, :email, :name
It will generate JSON like this
[{ "email": "email1", "name": "Name1" }, { "email": "email2", "name": "Name2" }]
The code you are showing looks like its missing an end for your do block - thats not the error though.
Without further digging I think you might not have a corresponding view file, something like views/users/index.json.jbuilder.
inside there you might want to stick your jbuilder stuff (going with the rails convention):
# users_controller.rb
def index
#users = User.all
end
# views/users/index.json.jbuilder
json.array! #users do |user|
json.extract! user, :email, :name
end
you are using an all action in your controller however and if your route for that is fine you can swap index for all
# users_controller.rb
def all
#users = User.all
end
# views/users/all.json.jbuilder
json.array! #users do |user|
json.extract! user, :email, :name
end
its always good to take a look at the docs when you bump up against this kinda stuff:
jbuilder gem
This question already has answers here:
Rails 4 - Remove "created_at" and "updated_at" from render
(2 answers)
Closed 5 years ago.
I am new to Ruby, so this question might look stupid for you, yeah. But still...
I got a model Group, and when I run this
class GroupsController < ApplicationController
prepend SimpleCommand
...
def index
#group = Group.all
render json: #group
end
...
# some other stuff
...and then I get a response with array like this:
[
{
"id": 1020,
"name": "НІ-16-1",
"has_subgroups": true,
"created_at": "2017-09-29T10:14:08.000Z",
"updated_at": "2017-09-29T10:14:08.000Z"
}
...
]
but I don't want to have those two last fields, so my answer is
How to get rid of "created_at" and "updated_at" fields in a response?
UPD:
Found a great solution in original question I've accidentally made a duplicate for. Go ahead and read it
You could simply do:
class GroupsController < ApplicationController
prepend SimpleCommand
...
def index
render json: Group.pluck(:id, :name, :has_subgroups)
end
end
It may not matter to you, but I think this approach is faster because it doesn't instantiate all the Group objects.
Every time you serve an object to the view as JSON Rails internally calls the function as_json. One way to get rid of those two fields is to override that function in your Group model:
class Group < ActiveRecord::Base
def as_json(options={})
{ id: id,
name: name,
has_subgroups: has_subgroups
}
end
end
Or as #MrYoshiji suggested you could filter them out like so:
render json: #groups.to_json(except: [:created_at, :updated_at])
Rails come with jbuilder by default to generate the json response. You just have to find group/index.json.jbuilder and remove created_at and updated_at inside. And change your controller action to:
def index
#group = Group.all
end
or if you need to support other formats:
def index
#group = Group.all
respond_to do |format|
format.json
end
end
You can see you don't have to specify anything as Rails will use your jbuilder file by default.
I have a post model that has a virtual attribute that I would like to set and then include in a response to a JSON call to my post#index action. I can't seem to get the virtual attribute to be included in the response.
class Post < ActiveRecord::Base
attr_accessible :height
attr_accessor :m_height
end
class PostsController < ApplicationController
respond_to :html, :json, :js
def index
story = Story.find(params[:story_id])
#posts = story.posts.where("posts.id >= ?", 100)
#posts.each do |post|
post.m_width = post.height * 200
end
results = { :total_views => story.total_views,
:new_posts => #posts }
respond_with(results)
end
end
I think that I must need something similar to #post.to_json(:methods => %w(m_width)), but I don't see how to use :methods in a respond_with
This seems to provide the answer. Implement a to_json and to_xml in your models, as appropriate, with definitions like:
There's a better answer implied here.
Following code stolen from the post:
def as_json(options={})
super(options.merge(:methods => [...], :only => [...], :include => [...])
end
to_json won't be called on your model in this case, from what I can tell in the source, but as_json will be, in the process of serialization.
So, here's what happens, in overview form:
You call respond_with with the results hash you've constructed.
Rails (ActionController) calls to_json on that.
to_json sends you over to JSON::Encoding which keeps calling as_json all the way down until everything is JSONified.
That's why there was the confusion about to_json and as_json in an earlier version of this answer.
I am trying to include a virtual attribute/method within a respond_to JSON hash.
The Model (employee.rb)
attr_reader :my_method
def my_method
return "foobar"
end
The Controller (employees_controller.rb)
respond_to :json
def index
#employees = Employee.all
respond_with(:data => #employees, :total => Employee.all.count)
end
It is important that I have "data" as the json root for the collection of "employees" and also to include the "total" within the hash. This works well and returns a nice JSON result of all the employees and the total value.
My qustion is: How do I include the virtual attribute "my_method" for each employee within the employees hash in the JSON response?
Thanks for your time!
This is what worked for me.
Employee.rb
def as_json(options={})
super.as_json(options).merge({:my_method => my_method})
end
Thanks for cmason for pointing me in the right direction. Any other solutions are welcome.
In Rails 3 one can use following
#yourmodel.to_json(methods: ['virtual_attr1', 'virtual_attr2']
Overwriting as_json in your model should do the trick:
def as_json(options={})
{ :methods=>[:my_method] }.merge(options)
end
Suppose I have a controller method like so...
def index
#burried_treasures = BurriedTreasure.all
render :xml => #burried_treasure
end
Right now it places all values in tags such as:
<burried_treasure>
<name>Red Beard</name>
</burried_treasure>
I would like it to use attributes like this:
<burried_treasure name="Red Beard">
Does anyone know how to accomplish this?
You will have to override your models to_xml method
class BurriedTreasure < ActiveRecord::Base
def to_xml(options = {})
options[:indent] ||= 2
xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
xml.instruct! unless options[:skip_instruct]
xml.buried_treasure('name' => self.name)
xml.some_nodes do |some_node|
some_node.some_level2_node "some_level_2_node_content"
end
end
end
See more info on Builder::XmlMarkup usage at http://ap.rubyonrails.org/classes/Builder/XmlMarkup.html