postgres_ext-serializer each_serializer Rails 5 adding custom JSON meta data - ruby-on-rails

I'm using Rails 5.2.3, postgres_ext (3.0.0), active_model_serializers (0.8.4), I am trying to render JSON data in the format of
{
total: 500,
totalNotFiltered: 500,
rows: [
...
]
}
I am able to get close but I am not able to get the additional custom fields added.
So far I have
respond_to do |format|
format.json { render json: #people, each_serializer: PersonSerializer, root: :rows, meta{total: 500, totalNotFiltered: 500}}
end
This gives the correct root :rows, but the meta data isnt being added.
The postgres_ext gem does great in getting the JSON rows correctly, down from 190ms av to 25ms av, so I want to use this. I have tried FastJSON (Netflix) but that is far slower and again not easy to format the output generated.
It might be something simple here but I can fathom how to add the 2 elements missing. Ideas or suggestions please

in array_serializer.rb of the postgres_ext-serializers-0.0.3 gem, I can hack this JSON string by adding the options I need, then added it as a patch for a temp solution. If someone has a better idea or way, please advise.
in the controller (this is for bootstraptable btw)
render json: #people, each_serializer: PersonSerializer, root: :rows, total: #people_count, totalNotFiltered: Person.all.count}
then serializer.rb
module IncludeMethods
def to_json(*)
root = #options.fetch(:root, self.class.root)
total = #options[:total]
totalNotFiltered = #options[:totalNotFiltered]
if ActiveRecord::Relation === object && root != false
total = "{\"total\":\"#{total}\",\"totalNotFiltered\":\"#{totalNotFiltered}\","
_postgres_serializable_array.sub('{', total)
else
super
end
end
end
Its not the best but it works for now

Related

rails 5 ForbiddenAttributesError on bulk operations

i try to bulk operation in my rails controller this is my script
def update_by_user
user_skill_selected = UserSkillSelected.create(params[:user_skill_selected][:users])
# check through array if all is valid
if user_skill_selected.all? {|item| item.valid?}
render json: {json_status: save_success}
else
render json: {json_status: save_failed}
end
end
and this is my user_skill_selected_params
def user_skill_selected_params
params.require(:user_skill_selected).permit(:user_id, :subskill_id, :skill_id, :users => [])
end
unfortunately i get an error in my log, the log said
"exception": "#<ActiveModel::ForbiddenAttributesError:ActiveModel::ForbiddenAttributesError>",
after that i try to bulk operations from rails console with using create method with the array value and its work
can anyone solve this... :(
sorry for the bad english
This can be confusing. Your code is passing in params[:user_skill_selected][:users] to the model create method, instead of your user_skill_selected_params strong parameters, which is why you're seeing that error.
Change this line:
user_skill_selected = UserSkillSelected.create(params[:user_skill_selected][:users])
To this:
user_skill_selected = UserSkillSelected.create(user_skill_selected_params)
And it should eliminate this error.

Rails 5 API using Kaminari - how to provide the page count data to client side?

I have installed Kaminari gem and have the following code in my controller:
# GET /customers
def index
if params[:page]
#customers = Customer.page(params[:page]).per(params[:per_page])
pageCount = (Customer.count / params[:per_page].to_f).ceil
else
#customers = Customer.order('updated_at DESC')
pageCount = 1
end
render json: #customers, meta: { total: pageCount, records: Customer.count }
end
This attempts to add a 'meta' section to my response with the data I need in the client to build the paging controls.
The problem is that if I make a request (eg using Postman):
localhost:3000/customers?page=1
The meta data is not added to the response.
FYI I based this off code that I used in EmberJS using JSONAPI, but now I am using Angular2, so not sure how to get this 'meta' data added to the response.
You should write like
render json: { customers: #customers, meta: { total: pageCount, records: Customer.count } }
In your example you adding just #customers to json response, while meta part goes somewhere else(not to response). So to have your meta section in your json response, make sure it is in correct place

Serialize an array of models using active_model_serializers

I am trying to send the serialized version of a model to a view as a param, using the gem active_model_serializers
#app/serializers/admin_serializer.rb
class AdminSerializer < ActiveModel::Serializer
attributes :id, :email, :access_locked?
end
#app/controllers/dashboard/admins_controller.rb
def index
#search = Admin.search(params[:q])
#admins = #search.result(:distinct => true).page(params[:page]).per(10)
#page_entries_info = view_context.page_entries_info #admins
# render json: #admins
respond_to do |format|
format.html
format.js
format.json {render json: #admins}
end
end
#app/views/dashboard/admins/index.html.erb
<%= debug (ActiveModel::Serializer::Adapter.adapter_class(:json_api).new(ActiveModel::Serializer.serializer_for(#admins.first).new(#admins.first),{}).to_json) %>
<%= debug (#admins.all.map{|admin| AdminSerializer.new(admin).to_json}) %>
Above debugs are yielding the below response:
--- '{"data":{"id":"1","type":"admins","attributes":{"email":"tech#bluesapling.com","access_locked?":false}}}' //returned by the first debug
---
- '{"object":{"id":36,"email":"aubrey_schmitt#feeneykoch.io","created_at":"2016-03-28T05:15:17.546Z","updated_at":"2016-03-28T05:15:17.546Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":20,"email":"alysa_johnston#thompson.io","created_at":"2016-03-28T05:15:16.304Z","updated_at":"2016-03-28T05:15:16.304Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":22,"email":"kristofer.langosh#kunzeluettgen.com","created_at":"2016-03-28T05:15:16.459Z","updated_at":"2016-03-28T05:15:16.459Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":37,"email":"beryl_keler#wiza.biz","created_at":"2016-03-28T05:15:17.624Z","updated_at":"2016-03-28T05:15:17.624Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":5,"email":"wilhelmine_buckridge#crona.io","created_at":"2016-03-28T05:15:15.139Z","updated_at":"2016-03-28T05:15:15.139Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":14,"email":"edward_wisoky#corkery.net","created_at":"2016-03-28T05:15:15.838Z","updated_at":"2016-03-28T05:15:15.838Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":27,"email":"leonor#jerde.biz","created_at":"2016-03-28T05:15:16.848Z","updated_at":"2016-03-28T05:15:16.848Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":2,"email":"carley#wyman.net","created_at":"2016-03-28T05:15:14.873Z","updated_at":"2016-03-28T05:15:14.873Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":10,"email":"ervin.gleichner#cremin.org","created_at":"2016-03-28T05:15:15.527Z","updated_at":"2016-03-28T05:15:15.527Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":15,"email":"lonzo.dickens#johnscole.name","created_at":"2016-03-28T05:15:15.916Z","updated_at":"2016-03-28T05:15:15.916Z"},"instance_options":{},"root":null,"scope":null}'
In the first debug I am serializing only one object, while in the second one I am trying to do it for an array of objects.
The first debug is correctly returning the serialized version of the object(in json_api format) while second debug is not.
Tried ArraySerializer as well, with no success: ActiveModel::Serializer::ArraySerializer.new(#admins, each_serializer: AdminSerializer).as_json
how do I achieve the desired serialization. Moreover, if achieved, can I used some other simplified version of this? As this debug statement is way too verbose.
Tried all the solutions mentioned here - How do you initialize an ActiveModel::Serializer class with an ActiveRecord::Relation array?
The basic problem which I am trying to solve is, in the index method of the Admin controller, the Admin object is passed as a PORO to the index.html file. But I want the serialized json version of this object so that I can pass it to my react components as a prop
index method is rendering proper json on firing http://dashboard.localhost.com:3000/admins.json
UPDATE#1 for the index method
def index
#search = Admin.search(params[:q])
#admins_array = #search.result(:distinct => true).to_a
if params[:page]
#admins = #search.result(:distinct => true).page(params[:page][:number]).per(10)
#admins_json_array = Kaminari.paginate_array(#admins_array).page(params[:page][:number]).per(10)
else
#admins = #search.result(:distinct => true).page(1).per(10)
#admins_json_array = Kaminari.paginate_array(#admins_array).page(1).per(10)
end
#admins_json = ActiveModel::SerializableResource.new(#admins_json_array.to_a)
...
...
...
end
I have a controller that I need to specify the serializer in, due to wanting different attributes from the default serializer.
In Controller:
def index
search = User.ransack(search_params)
render json: search.result, each_serializer: MembershipRenewalSerializer::MemberSerializer
end
So, just to get things working, what happens if you specify the each_serializer option?
Edits:
Outside Controller:
ActiveModel::SerializableResource.new(
User.first(2),
each_serializer: MembershipRenewalSerializer::MemberSerializer
).to_json
Note, that without specifying each_serializer, SerializableResource would use the UserSerializer.
Edit #2,
It looks like there is something weird happening with the #admins data.
Try converting to an array:
ActiveModel::SerializableResource.new(#admins.to_a).to_json
Edit #3
To paginate your array, try the following:
#search = Admin.search(params[:q])
#results = #search.result(:distinct => true).to_a
#admins = Kaminari.paginate_array(#results).page(params[:page]).per(10)
Follow the guide: Serializing before controller render
You could use ActiveModel::SerializableResource.new(#admins, adapter: :json_api).to_json
in index.html.erb
<%= debug (ActiveModel::SerializableResource.new(#posts, adapter: :json_api).to_json) %>
below is the output(using posts)
'{"data":[{"id":"1","type":"posts","attributes":{"title":"first post","body":null}},{"id":"2","type":"posts","attributes":{"title":"second post","body":null}}],"links":{}}
I create a concern with some API helper methods and there you can check if its a collection pass find the appropiate serializer and pass it to the collection serializer.
def api_response(data)
render json: wrap_answer(data)
end
def wrap_answer(data)
if data.respond_to?(:each)
ActiveModel::Serializer::CollectionSerializer.new(data, each_serializer: ActiveModel::Serializer.serializer_for(data.first))
else
data
end
end
should have made that into string and use json.stringify to make that as a string and make your life easy

Association data into json output in Rails

I have a model where I can get my index action to return a list of objects back as json but I need each one to return a list of sub elements over an association.
I have tried the following but the coasters are not being output. Is there a way this can return each parks coasters?
format.json do
render json: Park.scoped(include: :coasters)
end
Try something like :
render json: Park.joins(:coasters).select("parks.*,coasters.*") # parks.*, coasters.* refers to actual table names
This could be achieved with active_model_serializers gem.
the code should looks like this
format.json do
render json: Park.scoped.to_json(include: :coasters)
end
more info can be find here https://github.com/rails/rails/pull/2200

Rendering large database record set as JSON in rails

Is there a way to present a large record set in JSON format without consuming large amount of memory? For example I have the following query:
...
records = Records.where(query)
respond_to do |format|
format.html
format.json { render :json => records.to_json }
end
There are times that records will contain thousands of entries and JSON is strictly used for getting the data without using pagination and such data must fit inside the memory for it to be returned. A Record entry will also contain a lot of fields (I am using MongoDB/Mongoid) and it is necessary to include such fields.
It's almost always a bad idea to return every resource in a database.
You can respond with limited set of results and provide total number of records.
For example:
{
total: 503
records: [
{ id: 1 },
{ id: 2 }
]
}
And add possibility to use limit and offset parameters to iterate through all pages.
There is chapter named Pagination and partial response in free e-book Web API Design describes it.
I would recommend you to read that book. It contains only 30 pages.
upd:
In your case you are able to paginate results using limit and skip mongoid's methods.
records = Records.where(query).limit(params[:limit]).skip(params[:offset])
respond_to do |format|
format.html
format.json { render :json => { total: records.count, records: records }.to_json }
end

Resources