rails formatting a custom json output - ruby-on-rails

How do would I format my model so that it will output a json document with an id and name field?
Because my model has custom field names and I am using tokeninput and it requires me to output it to id and name.
any ideas?

You have so many options here, you can use jbuilder, rabl. But I think the easiest one is to use Active Model Serializers.
Let's say you have a model name User.
First install the bundle, then:
rails g serializer user
Then at app/serializers/user_serializer.rb:
class ArticleSerializer < ActiveModel::Serializer
attributes :id, :name
end

You might want to pass a only option to to_json
:only => [:id, :name]
For example, if you want to get id and name of User
User.all.to_json :only => [:id, :name]
If the model does not contain Id and Name as described by OP. Then you use custom select at the time of querying the db.
User.select('filed1 as id, field2 as name').all.to_json

Maybe in your controller
require 'json'
# ...
def show_json
user_obj_to_json = User.first.to_json
render user_obj_to_json
end

Related

Add fields to 'new' view in Rails Admin

I would like to add additional fields to the Rails Admin 'new' view for a specific model object, 'User'. These fields would not be attributes on the model itself but instead just fields that I would like users to be able to submit information with in order to calculate another field.
Is this possible?
Add virtual field to your model in rails admin using,
config.model Address do
list do
# virtual field
configure :full_address do
# any configuration
end
fields :full_address, :street, :number #, ...
end
end
Reference - https://github.com/sferik/rails_admin/wiki/Fields#virtual-fields
I'm not entirely familiar with Rails Admin, but you should be able to get what you want with Rails' virtual attributes mechanism.
In your user.rb model file, you need to add an attr_accessor line, listing the symbols you want to assign to your non-model fields, like this:
class User < ActiveRecord::Base
attr_accessor :virtual_field_one, :virtual_field_two
# Remainder of your code
end
You can add fields to the corresponding view that populate those values:
<%= f.text_field :virtual_field_one %>
Then you can add those attributes to the strong parameters method of your users_controller.rb, like this:
class ActivitiesController < ApplicationController
# other code
def user_params
params.require(:activity).permit(:mode_field_one, :mode_field_two, :virtual_field_one, :virtual_field_two)
end
# other code
end
Now you should be able to access virtual_field_one and virtual_field_two from the params hash like any other field in your User model:
virtual_field_one = params[:virtual_field_one]

Present subset of an object with ActiveModel Serializer

I am using ActiveModel Serializers in a Rails project.
The default serializer for the object is fairly large, and nesting an object in API responses result in rather large JSON objects.
Sometimes, I want to embed an object, but only need a small subset of the object's attributes to be present in the JSON.
Obviously, I could do something like this:
render json: #user, serializer: SmallerUserSerializer
but that would lead to a lot of duplication.
Is there an option that I can pass to the serializer so that it will only include a subset of the serializers attributes? Eg:
class BlogSerializer
# This is pseudocode. Does not actually work.
has_one :user, only_show: [:user_id, :profile_url]
end
Create a method and call to_json on the user object. Then add that method name to your list of attributes. The method can be called user also.
class BlogSerializer
attributes :id, :user
def user
object.user.to_json( only: [ :id, :profile_url ] )
end
end
Use the active model serialzers gem.
Your pseudo code will become the following simple modularized code:
class BlogSerializer < ActiveModel::Serializer
attributes :user_id, :profile_url
end
Guide: http://railscasts.com/episodes/409-active-model-serializers
Create a method and call to_json on the user object. Then add that method name to your list of attributes. The method can be called user also.
class BlogSerializer
require 'json'
attributes :id, :user
def user
JSON.parse "#{object.user.to_json( only: [ :id, :profile_url ] )}"
end
end

Ember-data and MongoDB, how to handle _id

I'm using ember-data with rails and MongoDB and am having problem with the way IDs are stored in MongoDB - in a _id field.
Ember-data will use id as the default field for ID so I tried to override it like this:
App.User = DS.Model.extend
primaryKey: "_id"
name: DS.attr "string"
image: DS.attr "string"
This seems to work most of the time but in some instances I get exceptions from ember saying:
Uncaught Error: assertion failed: Your server returned a hash with the
key _id but you have no mappings
I suspect this might be a bug in ember-data because it's still heavily under development, but I was trying to find a way to get to map _id to id on the server side in rails? I'm using mongoid to do the mongo mapping.
If you are using Mongoid here is a solution that makes it so you don't have to add a method def id; object._id.to_s; end to every serializer
Add the following Rails initializer
Mongoid 3.x
module Moped
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end
end
Mongoid 4
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end
Active Model Serializer for Building
class BuildingSerializer < ActiveModel::Serializer
attributes :id, :name
end
Resulting JSON
{
"buildings": [
{"id":"5338f70741727450f8000000","name":"City Hall"},
{"id":"5338f70741727450f8010000","name":"Firestation"}
]
}
This is a monkey patch suggested by brentkirby and updated for Mongoid 4 by arthurnn
An other way could be to use (if possible for you) the ActiveModel::Serializer. (I think it should be close to rabl (?))
From the ember-data gihtub: https://github.com/emberjs/data:
Out of the box support for Rails apps that follow the active_model_serializers gem's conventions
When we began with ember-data we were crafting as_json(), but using the gem is definitely better :)
Ahh, instead of including _id in your JSON, you could craft the JSON to instead use the id method rather than the _id attribute. Ways:
You could use rabl, and the JSON could be like:
object #user
attributes :id, :email
node(:full_name) {|user| "#{user.first_name} #{user.last_name}"}
You could also craft the as_json method
class User
def as_json(args={})
super args.merge(:only => [:email], :methods => [:id, :full_name])
end
end
I had a similar problem using ember.js with ember-resource and couchdb, which also stores it's IDs as _id.
As solution to this problem I defined a superclass for all my model classes containing a computed property to duplicate _id into id like this:
// get over the fact that couchdb uses _id, ember-resource uses id
id: function(key, value) {
// map _id (couchdb) to id (ember)
if (arguments.length === 1) {
return this.get('_id');
}
else {
this.set('_id', value);
return value;
}
}.property('_id').cacheable()
Maybe this could solve your problem too?
The best way is to use ActiveModel::Serializers. Since we are using Mongoid, you will need to add an include statement like that (see this gist from benedikt):
# config/initializers/active_model_serializers.rb
Mongoid::Document.send(:include, ActiveModel::SerializerSupport)
Mongoid::Criteria.delegate(:active_model_serializer, :to => :to_a)
And then include your serializer. Something like that:
# app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :email
def id
object._id
end
end
This fixes the _id problem
The second part of joscas's answer fixed the id issue for me with Rails4/Ruby2 except I had to .to_s the _id.
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :email
def id
object._id.to_s
end
end
If you use Mongoid3, here is the monkey patch may work for you.
https://gist.github.com/4700909
I don't know exactly when this was added, but you can just tell Ember-Data that the primaryKey is _id:
DS.RESTAdapter.extend({
serializer: DS.RESTSerializer.extend({
primaryKey: '_id'
})
});
Although the question is quite old but i still think my answer could help others:
If you are using ActiveModelSerializer then you just need to do this :
class UserSerializer < ActiveModel::Serializer
attributes :id , :name
end
It works all fine. I am using emberjs on the front end btw.

Rails 3 and ActiveAdmin. Filter is displaying Objects not the company name

I have a list of customers but on the filter section on the right column, I get a list like this #<Customer0X0B500> in the select menu. How can I display the company_name attribute of Customer instead?
Figured it out, thanks!
filter :customer, :collection => proc {(Customer.all).map{|c| [c.company_name, c.id]}}
i'm not sure I understand you but probably you should define to_s method inside your Customer class e.g.
class Customer
def to_s
self.company_name
end
end
it would be easier if you shared some code
class Customer
def display_name
self.company_name
end
end
Defining display_name instead of to_s works better...
to_s may be called automatically by other objects.
display_name only affects ActiveAdmin
You can also define:
show :title => :display_name
This will make your company_name appear as the title on the view pages instead of Company #x.
To make ActiveAdmin display select menu properly try in Model.rb:-
alias_attribute :name, :category_name

Custom serialization for fields in Rails

Is there a way to have a custom serialization for fields in rails, a method that runs when a field is saved and loaded to convert from/to a string which is what ultimately is saved on the database.
Specifically what I want to do is have a field of type symbol like gender, with possible values :male and :female storing "male" and "female" on the database. There are some workarounds, like:
def gender
read_attribute(:gender).try(:to_sym)
end
but that leaves obj.attributes unchanged, so it's a leaky abstraction.
You can do it in Rails 3.1. The object you want to serialize has to reply to load and dump methods.
Here is an example of serializing a string in Base64.
class User < ActiveRecord::Base
class Base64
def load(text)
return unless text
text.unpack('m').first
end
def dump(text)
[text].pack 'm'
end
end
serialize :bank_account_number, Base64.new
end
For more details see: http://archives.edgerails.info/articles/what-s-new-in-edge-rails/2011/03/09/custom-activerecord-attribute-serialization/index.html
def whachamacallit
read_attribute("whachamacallit").to_sym
end
def whachamacallit=(name)
write_attribute("whachamacallit", name.to_s)
end
store them as stings in the database, but extract them as symbols when you pull them out then convert back before you save.
would work with any number or combination of strings / symbols.
to limit it to only a select few
validates_inclusion_of :whachamacallit, :in => [ :male, :female, :unknown, :hidden ]
From http://blog.quov.is/2012/05/01/custom-activerecord-attribute-serializers/
class Recipe < ActiveRecord::Base
serialize :ingredients, IngredientsList
end
class IngredientsList < Array
def self.dump(ingredients)
ingredients ? ingredients.join("\n") : nil
end
def self.load(ingredients)
ingredients ? new(ingredients.split("\n")) : nil
end
end
you can define the models to_xml for a model and it will do that
http://api.rubyonrails.org/classes/ActiveRecord/Serialization.html
its possible to define Marshall.dump and put in that way i think, but its something to look into
You could use serialize method inside the model. Please reference to this link:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html
(ps. search keyword "serialize" in that page ;D)
In short, you could do this:
class YourModel < ActiveRecord::Base
serialize :db_field
end
Rails would automatically serialize the field before saving to database, and deserialize it after fetched from the database.
well for just male/female you could just do a Boolean column like male and if it was false assume that meant female, add wrapper methods for it
def female?
return !self.male?
end
We just released a gem (AttributeHelpers) that does exactly this. Disclaimer: I am a maintainer for the gem.
It allows you to call attr_symbol :gender in your class definition and the serialization happens automagically.

Resources