ActiveModel Serializer - Passing params to serializers - ruby-on-rails

AMS version: 0.9.7
I am trying to pass a parameter to an ActiveModel serializer without any luck.
My (condensed) controller:
class V1::WatchlistsController < ApplicationController
def index
currency = params[:currency]
#watchlists = Watchlist.belongs_to_user(current_user)
render json: #watchlists, each_serializer: WatchlistOnlySerializer
end
My serializer:
class V1::WatchlistOnlySerializer < ActiveModel::Serializer
attributes :id, :name, :created_at, :market_value
attributes :id
def filter(keys)
keys = {} if object.active == false
keys
end
private
def market_value
# this is where I'm trying to pass the parameter
currency = "usd"
Balance.watchlist_market_value(self.id, currency)
end
I am trying to pass a parameter currency from the controller to the serializer to be used in the market_value method (which in the example is hard-coded as "usd").
I've tried #options and #instance_options but I cant seem to get it work. Not sure if its just a syntax issue.

AMS version: 0.10.6
Any options passed to render that are not reserved for the adapter are available in the serializer as instance_options.
In your controller:
def index
#watchlists = Watchlist.belongs_to_user(current_user)
render json: #watchlists, each_serializer: WatchlistOnlySerializer, currency: params[:currency]
end
Then you can access it in the serializer like so:
def market_value
# this is where I'm trying to pass the parameter
Balance.watchlist_market_value(self.id, instance_options[:currency])
end
Doc: Passing Arbitrary Options To A Serializer
AMS version: 0.9.7
Unfortunately for this version of AMS, there is no clear way of sending parameters to the serializer. But you can hack this using any of the keywords like :scope (as Jagdeep said) or :context out of the following accessors:
attr_accessor :object, :scope, :root, :meta_key, :meta, :key_format, :context, :polymorphic
Though I would prefer :context over :scope for the purpose of this question like so:
In your controller:
def index
#watchlists = Watchlist.belongs_to_user(current_user)
render json: #watchlists,
each_serializer: WatchlistOnlySerializer,
context: { currency: params[:currency] }
end
Then you can access it in the serializer like so:
def market_value
# this is where I'm trying to pass the parameter
Balance.watchlist_market_value(self.id, context[:currency])
end

Try using scope in controller:
def index
#watchlists = Watchlist.belongs_to_user(current_user)
render json: #watchlists, each_serializer: WatchlistOnlySerializer, scope: { currency: params[:currency] }
end
And in your serializer:
def market_value
Balance.watchlist_market_value(self.id, scope[:currency])
end

You can send your params to your serializer like this
render json: #watchlists, each_serializer: WatchlistOnlySerializer, current_params: currency
and in your serializer you can use this to get the value
serialization_options[:current_params]

Related

Using Rails Path and URL Helpers with fast_jsonapi

I would like to use the rails URL helper instead of hard coding the path to access the article.
I checked into the documentation but nothing is specified.
The article_path helper method exists (I checked by running rake routes)
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles).serialized_json
end
end
class V3::ArticleItemSerializer
include FastJsonapi::ObjectSerializer
attributes :title
link :working_url do |object|
"http://article.com/#{object.title}"
end
# link :what_i_want_url do |object|
# article_path(object)
# end
end
What you want to do is pass in the context to your serializer from your controller:
module ContextAware
def initialize(resource, options = {})
super
#context = options[:context]
end
end
class V3::ArticleItemSerializer
include FastJsonapi::ObjectSerializer
include ContextAware
attributes :title
link :working_url do |object|
#context.article_path(object)
end
end
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles, context: self).serialized_json
end
end
You should also switch to the jsonapi-serializer gem which is currently maintained as fast_jsonapi was abandoned by Netflix.
I found a solution thanks to max's example.
I also changed the gem to jsonapi-serializer
class V3::ArticlesController < Api::V3::BaseController
def index
articles = Article.all
render json: ::V3::ArticleItemSerializer.new(articles, params: { context: self }).serialized_json
end
end
class V3::ArticleItemSerializer
include JSONAPI::Serializer
attributes :title
link :working_url do |object|
"http://article.com/#{object.title}"
end
link :also_working_url do |object, params|
params[:context].article_path(object)
end
end

Serializing Nested Attributes Active Model Serializer

I have the following code and can't seem to get my JSON to output as per my serializer.
I receive the following log [active_model_serializers] Rendered SimpleJobSerializer with Hash
My controller is as below:
# Jobs Controller
def home
return_limit = 2
#dev_jobs = Job.where(category: 'developer').limit(return_limit)
#marketing_jobs = Job.where(category: 'marketing').limit(return_limit)
#sales_jobs = Job.where(category: 'sales').limit(return_limit)
#jobs = {
developer: #dev_jobs,
marketing: #marketing_jobs,
sales: #sales_jobs
}
render json: #jobs, each_serializer: SimpleJobSerializer
end
And my serializer:
class SimpleJobSerializer < ActiveModel::Serializer
attributes :title, :company, :job_type, :date
def date
d = object.created_at
d.strftime("%d %b")
end
end
I am receiving the full API response but expect to only receive title, company, job_type and date.
It's worth mentioning the jobs model is completely flat and there are currently no associations to take into account. It seems to be just the nesting of the jobs into the #jobs object that's stopping serialization.
Any help would be much appreciated.
each_serializer expects you to pass an array but here you're passing a hash:
#jobs = {
developer: #dev_jobs,
marketing: #marketing_jobs,
sales: #sales_jobs
}
Since you want that structure, I'd recommend two approachs depending on which you prefer. One is to change the serializer which should control the format:
class JobsSerializer < ActiveModel::Serializer
attributes :developer, :marketing, :sales
def developer
json_array(object.where(category: "developer"))
end
def marketing
json_array(object.where(category: "marketing"))
end
def sales
json_array(object.where(category: "sales"))
end
def json_array(jobs)
ActiveModel::ArraySerializer.new(jobs, each_serializer: SimpleJobSerializer)
end
end
You can still leave your current serializer as is:
class SimpleJobSerializer < ActiveModel::Serializer
attributes :title, :company, :job_type, :date
def date
d = object.created_at
d.strftime("%d %b")
end
end
Or option 2 would be to do this in the controller:
#jobs = {
developer: ActiveModel::ArraySerializer.new(#dev_Jobs, each_serializer: SimpleJobSerializer),
marketing: ActiveModel::ArraySerializer.new(#marketing_jobs, each_serializer: SimpleJobSerializer),
sales: ActiveModel::ArraySerializer.new(#sales_jobs, each_serializer: SimpleJobSerializer)
}
render json: #jobs

How to render json with extra data with active_model_serializer on rails?

Using Rails 4.1.6 and active_model_serializers 0.10.3
app/serializers/product_serializer.rb
class ProductSerializer < ActiveModel::Serializer
attributes :id, :title, :price, :published
has_one :user
end
app/controllers/api/v1/products_controller.rb
class Api::V1::ProductsController < ApplicationController
respond_to :json
def index
products = Product.search(params).page(params[:page]).per(params[:per_page])
render json: products, meta: pagination(products, params[:per_page])
end
end
When I check the response body, it shows the products data only:
[{:id=>1, :title=>"Side Auto Viewer", :price=>"1.6999510872877", :published=>false, :user=>{:id=>2, :email=>"indira.sawayn#watsica.us", :created_at=>"2016-12-29T03:44:40.450Z", :updated_at=>"2016-12-29T03
:44:40.450Z", :auth_token=>"ht7CsFWM1hvSGKM_zPmU"}}, {:id=>2, :title=>"Direct Gel Mount", :price=>"56.7935950121941", :published=>false, :user=>{:id=>3, :email=>"jaye.rolfson#leuschke.info", :created_at=>
"2016-12-29T03:44:40.467Z", :updated_at=>"2016-12-29T03:44:40.467Z", :auth_token=>"MTK_5rkFv8E6Fy7gyAtM"}}, {:id=>3, :title=>"Electric Tag Kit", :price=>"46.4689779902597", :published=>false, :user=>{:id=
>4, :email=>"tatiana#moen.co.uk", :created_at=>"2016-12-29T03:44:40.479Z", :updated_at=>"2016-12-29T03:44:40.479Z", :auth_token=>"fTd8z7PCLHxZ7aewLPDY"}}, {:id=>4, :title=>"Remote Tuner", :price=>"48.2478
906626996", :published=>false, :user=>{:id=>5, :email=>"pauline.gaylord#hettinger.info", :created_at=>"2016-12-29T03:44:40.486Z", :updated_at=>"2016-12-29T03:44:40.486Z", :auth_token=>"XC7ZhcyfPrpEyDw-M15
1"}}]
The extra data meta was not been picked up. Is this active_model_serializers version not support that? Or is there a way can get extra data?
Edit
The pagination method:
def pagination(paginated_array, per_page)
{ pagination: { per_page: per_page.to_i,
total_pages: paginated_array.total_pages,
total_objects: paginated_array.total_count } }
end
I had the same issue, and it's been fixed by specifying the adapter to use in the call to render method:
app/controllers/api/v1/products_controller.rb
class Api::V1::ProductsController < ApplicationController
respond_to :json
def index
products = Product.search(params).page(params[:page]).per(params[:per_page])
render json: products, meta: pagination(products, params[:per_page]), adapter: :json
end
end

Rails API active model serializer root node not working

I have a rails 4.2.5 API app. For some reason, the JSON root node is not included in the response and I don't understand why.
curl http://localhost:3000/api/v1/category/science
returns
{"title":"science","sub_categories":34}%
instead of
{"category": {"title":"science","sub_categories":34}%}
The code:
controller
class Api::V1::CategoryController < ApplicationController
def show
category = params[:category] || "sports"
#category = Category.where(cat_title: category.capitalize).first
respond_to do |format|
format.json { render json: #category, serializer: CategorySerializer, root: "category" }
end
end
end
serializer
class CategorySerializer < ActiveModel::Serializer
attributes :title, :sub_categories
def title
URI::encode(object.cat_title.force_encoding("ISO-8859-1").encode("utf-8", replace: nil).downcase.tr(" ", "_"))
end
def sub_categories
object.cat_subcats
end
end
have a look into your initializers, this should be commented out in wrap_paramters.rb:
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end
Rails 5.0.0.1
Using ActiveMovelSerializers (0.10.2) you just need to add an initializer:
app/config/initializers/json_api.rb
require 'active_model_serializers/register_jsonapi_renderer'
ActiveModelSerializers.config.adapter = :json_api

Mongoid virtual attributes in to_json

I'm trying to get some virtual (non-persisted) attributes to show up in the JSON representation of some Mongoid models, but can't seem to get it to work:
class MyModel
include Mongoid::Document
def virtual_attribute
#my_attribute || false
end
def virtual_attribute=(value)
#my_attribute=value
end
end
class MyController
def myaction
false_values=MyModel.where( whatever )
true_values=MyModel.where( something_else ).map{ |model| model.virtual_attribute=true }
#val['my_models']=false_values+true_values
render json: #val.to_json( :include => {:my_models => {:methods => %w(virtual_attribute)}} )
end
end
virtual_attribute doesn't appear in the json. What am I doing wrong?
Edit - ok, so I guess my actual problem is that I can't figure out how to invoke the virtual_attribute method on each of an array of objects that is nested in the root object.
to_json passes the options directly to the array and the objects. :include is only a Mongoid thing:
render json: #val.to_json(methods: :virtual_attribute)

Resources