I'm updating my Rails app to active_model_serializers 0.10.4 but I'm having trouble updating a necessary feature: the ability to add request information to every json response from the controller.
In AMS 0.9.x I used to do it by using default_serializer_options but that funcionality is gone, and apparently the only way to achieve this is manually adding the meta key to EVERY request.
Has anyone found a workaround to make this work?
in config/initializers/active_model_serializer.rb file add this line:
ActiveModel::Serializer.config.adapter = :json
The example below will add a data attribute at the top level, and render the #users objects under data key in your response.
In your controller:
def index
#users = User.all
render json: #users, root: "data"
end
If you're going for a JSON API schema with a meta tag and a data tag with objects for every response, simply change initializer to
ActiveModel::Serializer.config.adapter = :json_api
and controller to
def index
#users = User.all
#extra_meta = {"totalCount": #users.size}
render json: #users, root: "data", meta: default_meta_attributes(#users, #extra_meta)
end
In my case default_meta_attributes is in the base controller and merges in some details about the request like current_user_id, etc with the option to add additional details in each method
Could you give a more specific example of what you're trying to do? You could make an ApplicationSerializer and set the serialization_scope to :view_context then do whatever you want in the serializer. You could also customize the JSON adapter. Or, if you want, you can prepend a module to SerializableResource to add options.
Related
I'm writing a Rails API using ActiveModel::Serializers. I am following the JSON API spec and would like to include some data about current_user for authorization and authentication in the response's top-level meta key. With ActiveModel::Serializers, top-level meta information is specified like this:
render json: #posts, meta: { 'current-user': #current_user }
But I would like to have this information available on all JSON responses. It's a big hassle to define this information every single time I call render in each of my controllers.
Is there any way I can pass the meta: option to all my controller's render calls by default, say somewhere in my ApplicationController or something?
Create a def and append to before_action in application controller
This might work for you
Create this def in application controller
def append_user_to_json(data)
user = current_user.to_json
data = data.to_json
return data = data+user
end
Is there any way to remove sensitive fields from the result set produced by the default ActiveRecord 'all', 'where', 'find', etc?
In a small project that I'm using to learn ruby I've a reference to User in every object, but for security reasons I don't want to expose the user's id. When I'm using a simple HTML response it is easy to remove the user_id simply by not using it. But for some task I'd like to return a json using something like:
def index
#my_objects = MyObject.all
respond_to do |format|
...
format.json { render json: #my_objects, ...}
...
end
end
How do I prevent user_id to be listed? Is there any way to create a helper that removes sensitive fields?
You can use the as_json to restrict the attributes serialized in the JSON response.
format.json { render json: #my_objects.as_json(only: [:id, :name]), ...}
If you want to make it the default, then simply override the method in the model itself
class MyObject
def serializable_hash(options = nil)
super((options || {}).merge(only: [:id, :name]))
end
end
Despite this approach is quick and effective, it rapidly becomes unmaintainable as soon as your app will become large enough to have several models and possibly different serialization for the same type of object.
That's why the best approach is to delegate the serialization to a serializer object. It's quite easy, but it will require some extra work to create the class.
The serializer is simply an object that returns an instance of a model, and returns a JSON-ready hash. There are several available libraries, or you can build your own.
This is the code in my People Controller for the Create action :
def create
#person = Person.new(person_params)
#person.branch_id = session[:branch_id]
#person.added_by = current_user.id
if #person.save
respond_to do |format|
format.json { render json: #person}
end
end
end
And I have a method in my Person Model to get the person's full name :
def full_name
[self.first_name, self.middle_name, self.last_name].reject(&:blank?).join(" ")
end
What is the best way to add the Person's full name to #person when it is returned back as a JSON object
As you want it to be in the JSON you could just override the as_json method of the Person class to include the options to add the full_name method:
def as_json(options = {})
super options.merge(methods: [:full_name])
end
This method is used by Rails whenever an object should be serialized into a JSON string, for example by render json: #person. If you only want to add the extra method on this single occasion you can also call it directly inline using render json: #person.as_json(methods: [:full_name]) or if you want to make more complex customizations to the JSON output you could think about using dedicated serializer classes, for example by using the ActiveModel::Serializers gem. However, as you only want to add one method and that probably everywhere you are serializing #person, the best way for your case would be to just override as_json as described in the beginning.
Try this
#person.as_json(methods: :full_name)
Check out this article:
http://www.tigraine.at/2011/11/17/rails-to_json-nested-includes-and-methods
It's got instructions for doing exactly what you're trying to do, the short story being that you need to pass in :my_method => full_name to the json rendering.
I'm getting a ActionView::MissingTemplate when trying to get a custom output to behave with respond_to/with like it behaves xml/json output.
I have a custom output format available on my object.
#item.to_custom
I've registered the format with a custom Mime::Type registered in the mime_types.rb.
Mime::Type.register "application/custom", :custom
I have it listed in my respond_to options in my controller
class ItemsController < ApplicationController
respond_to :html, :xml, :custom
And then I have a respond_with method before the end of my show action.
def show
#item = Item.find(params[:id])
respond_with(#item)
end
When I access items/1234.xml I get the xml output. When I try to access items/1234.custom I get an error ActionView::MissingTemplate. I can fix it by adding the file app/views/show.custom.ruby with the contents:
#item.to_custom
Is there a way to get to_custom to work like to_xml or to_json in a respond_to/with setup? To just use the to_custom method without needing a template? Or will I have to use a view template that calls the method explicitly?
You'll need to manually add a Renderer for your custom format if you want it to render without explicitly adding a view template.
The Rails built-in formats xml and json have renderers automatically added from within the Rails framework, which is why they work right out of the box and your custom format does not (source code).
Try adding this in an initializer or right below where you register your MIME type
# config/initializers/renderers.rb
ActionController::Renderers.add :foo do |object, options|
self.content_type ||= Mime::FOO
object.respond_to?(:to_foo) ? object.to_foo : object
end
NOTE: Don't use custom as your format name because it will conflict with a method in the respond_with internals.
There is an excellent blog post that explains building custom Renderers in depth here: http://beerlington.com/blog/2011/07/25/building-a-csv-renderer-in-rails-3/
Is there an easy way to return data to web service clients in JSON using Rails?
Rails resource gives a RESTful interface for your model. Let's see.
Model
class Contact < ActiveRecord::Base
...
end
Routes
map.resources :contacts
Controller
class ContactsController < ApplicationController
...
def show
#contact = Contact.find(params[:id]
respond_to do |format|
format.html
format.xml {render :xml => #contact}
format.js {render :json => #contact.json}
end
end
...
end
So this gives you an API interfaces without the need to define special methods to get the type of respond required
Eg.
/contacts/1 # Responds with regular html page
/contacts/1.xml # Responds with xml output of Contact.find(1) and its attributes
/contacts/1.js # Responds with json output of Contact.find(1) and its attributes
http://wiki.rubyonrails.org/rails/pages/HowtoGenerateJSON
Rails monkeypatches most things you'd care about to have a #to_json method.
Off the top of my head, you can do it for hashes, arrays, and ActiveRecord objects, which should cover about 95% of the use cases you might want. If you have your own custom objects, it's trivial to write your own to_json method for them, which can just jam data into a hash and then return the jsonized hash.
There is a plugin that does just this,
http://blog.labnotes.org/2007/12/11/json_request-handling-json-request-in-rails-20/
And from what I understand this functionality is already in Rails. But go see that blog post, there are code examples and explanations.
ActiveRecord also provides methods to interact with JSON. To create JSON out of an AR object, just call object.to_json. TO create an AR object out of JSON you should be able to create a new AR object and then call object.from_json.. as far as I understood, but this did not work for me.