ActiveModel serializer ignores root key when posts or post is empty or nil - ruby

I am using active model serializer V0.10.0 with Rails 5 api only application. During implementation I noticed the AMS is completely ignoring the root key when the posts/post is empty or nil respectively. This behavior actually breaks my mobile app as it always expects root key data in all response.
So what I want to achieve here is no matter what I always want data as root element of my Rails app response for all requests.
Response for SHOW API when the post is empty
SHOW render json: #post, root: 'data'
Expected
{
"data": {}
}
Actual
null
Response for INDEX API when the posts are empty
INDEX render json: #posts, root: 'data'
Expected
{
"data": []
}
Actual
{
"posts": []
}
class ApplicationSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
ActiveModelSerializers.config.adapter = :json
def host
Rails.application.secrets.dig(:host_url)
end
end
class PostSerializer < ApplicationSerializer
attributes :id
has_many :comments
end

Related

Ruby Active model serializer with jsonapi , how to characterise links

I am using active model serializer and jsonapi format
I need to get :
{
"data": {
"id": "1234",
"type": "search",
"relationships": {
"foo": {
"data": [
{
"id": "12",
"type": "foo"
}
],
"links": "/foo/12"
},
}
},
I have tried several configuration for links but it does not display as above
require 'active_model_serializers'
module test
class SearchSerializer < ActiveModel::Serializer
has_one :foo, data: true, links: {self: true, related: true}
type 'search'
end
end
I want to respect the jsonapi format
Is anybody with a good example of active model serializer and json_api showing "links" as shwon on above json?
At the moment only the following is displayed
{"data": {
"id": "1234",
"type": "search",
"relationships": {
"foo": {
"data": [
{
"id": "12",
"type": "foo"
}
]
}
},
Note also that I am trying to do that outside the rails framework.
Thanks
Sorry to answer now, but if it is still of anyone interests...
It is quite simple really. First of all it's important to notice that JSON:API specification tell us that the related link should be their URL extension and it's better to show that path through the Search(for that case specific) path, for example: http://localhost:3000/searches/:search_id/foo.
So our SearchSerializer should be something like:
class SearchSerializer < ActiveModel::Serializer
# The attributes
attributes :id, :whatever, :something, :another_one
has_one :foo do
link(:related) { contact_foo_url(object.id) }
end
end
Note also that at this point you should include the routes and the controller show method, as similiar to the bellow:
For the routes.rb:
Rails.application.routes.draw do
resources :searches do
resource :foo, only: [:show]
# Also a best practice to dispose a 'relationships' path for this kinda example
resource :foo, only: [:show], path: 'relationships/foo'
end
end
And for the FoosController.rb:
class FoosController < ApplicationController
before_action :set_search
# GET /searches/1/foo
def show
render json: #search.foo
end
private
# Use callbacks to share common setup or constraints between actions.
def set_search
#search = Search.find(params[:search_id])
end
end
Going off of FredyK's answer, JSONAPI-SERIALIZER, is a lightweight no-rails serializer for your ruby objects (even though it does have rails integration if desired) which could be a much simpler solution than using Active Record.
Also if you are not using rails, JSONAPI-SERIALIZER pairs really well with the new gem EASY-JSONAPI, which is a middleware, parser, and response validator for JSON:API requests and responses.
After looking what is available in ruby for JSONAPI without rails, I ended using the gem JSONAPI-serializers, It is much easier to set and lighter to load (less dependencies). This fit better with PORO
My serializer becomes
require_relative ./common_serializer
module JsonSerializer
class SearchSerializer < CommonSerializer
attributes :foo, include_links: true
def relationship_related_link(attribute_name)
nil
end
end
end
This gem is much easier to use as the methods which create the json can be changed in the serializer (or in a commonClass)

How to get json response in rails api

I want to display all programmes which I got from a query as json response. I'm getting the programmes, but don't know how to render them through json. I'm using the jbuilder gem and created a create.json.buider.rb file. In my query I'm getting everything correctly, but I'm not receiving a JSON response with whatever details in I have in the query.
This is my controller. I have tried it like this but I'm not getting a json response. Only a status as 200.
class Api::V1::Categories::ProgrammesController < ApiController
respond_to :json
def category
#category=Category.all
#programmes=Programme.joins(:category).find_by(category_id: params[:category_id])
if #programmes.present?
render :json=> {:message=>"Programme not exists "}, :status=>422
else
render :json => #programmes
end
end
end
My create.json.jbuilder file:
json.programmes #programmes
I think you should change #programmes to { :programmers => #programmes.as_json }
class Api::V1::Categories::ProgrammesController < ApiController
def category
#category = Category.all
#programmes = Programme.joins(:category).find_by(category_id: params[:category_id])
if #programmes.present?
render :json=> {:message=>"Programme not exists "}, :status=>422
else
render :json => { :programmers => #programmes.as_json }
end
end
end

How to fetch a document by its ID in elasticsearch rails

I see in the elasticsearch docs you can fetch a document by its ID. Is there any equivalent in elasticsearch rails? I'm feeding by API with "as_indexed_json" and it's a somewhat expensive query, I'd like to return ths JSON straight out of elasticsearch in my API.
You can fetch a particular document from a given index by id with the get method on Elasticsearch::Transport::Client. The get method expects a single hash argument with keys for the index you want to fetch from and the id of the document you want to fetch.
So, all together you need 3 things:
A client object
The name of the index
The id of the document (i.e. your model id)
client = YourModel.__elasticsearch__.client
document = client.get({ index: YourModel.index_name, id: id_to_find })
Here how you can accomplish it.
This is from controller action and works well for me.
def show
client = Elasticsearch::Client.new host:'127.0.0.1:9200', log: true
response = client.search index: 'example', body: {query: { match: {_id: params[:id]} } }
#example = response['hits']['hits'][0]['_source']
respond_to do |format|
format.html # show.html.erb
format.js # show.js.erb
format.json { render json: #example }
end
#records = Example.search(#example['name']).per(12).results
end

Mongoid _destroy is not deleted embedded document using nested_form gem

I am on Rails 3.1, Mongoid 2.3.3, and using the nested_form gem. In my form, I have the nested_form link_to_add and link_to_remove set up to add and remove an embedded document in my model. The link_to_add helper method works great, but the link_to_remove help method changes are not persisted in MongoDB. In the rails output, I can see the JSON parameter passed to Mongoid has the _destroy: 1 value set but the change is not saved to MongoDB.
Here is the Model:
class MenuItem
include Mongoid::Document
include Mongoid::Timestamps
field :name
attr_accessible :name
embeds_many :ingredient_infos
accepts_nested_attributes_for :ingredient_infos, :allow_destory => true
attr_accessible :ingredient_infos_attributes
end
Here is the Controller's update method:
def update
#menu_item = MenuItem.find(params[:id])
respond_to do |format|
if #menu_item.update_attributes(params[:menu_item])
format.html { redirect_to #menu_item, notice: 'Menu item was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #menu_item.errors, status: :unprocessable_entity }
end
end
end
Here is the parameters sent to the controller:
{
"utf8"=>"✓",
"authenticity_token"=>"5abAWfFCr7hkzYXBEss75qlq8DMQ0pW5ltGmrgHwPjQ=",
"menu_item"=>
{
"name"=>"Bowl",
"ingredient_infos_attributes"=>
{
"0"=>
{
"ingredient"=>"Rice",
"_destroy"=>"false",
"id"=>"4eb1b0b118d72f1a26000022"
},
"1"=>
{
"ingredient"=>"Chicken",
"_destroy"=>"1",
"id"=>"4eb1b0b118d72f1a26000025"
}
}
},
"commit"=>"Update Menu item",
"id"=>"4eb1b0b118d72f1a2600001f"
}
In MongoDB, the Chicken document still exists; that document also shows up in the view online (the page pulls all the items in the embedded document).
I'm sure I missed something, but I haven't been able to figure out why the embedded document isn't removed.
yes, your :allow_destory should be :allow_destroy
I am experiencing the same issue -
RESOLVED with
accepts_nested_attributes_for :phones, :allow_destroy => true

How to respond_with multiple objects in Rails 3.1

I have a route for example
POST /interaction.json
where the client posts a new interaction. Normally my controller would look like
class InteractionController < ApplicationController
def create
respond_with #log
end
end
and I will get back a json response
{ "log" : { "id" : 20, .... } }
and the location header set to
http://foo.com/log/20
However if I wish to return more objects in my :json response than just the #log. For example to notify the client that some thing has changed with respects to this interaction the normal. Perhaps the user has won a prize for making this interaction. It would be nice to be able to do
response_with #log, #prize
and get the response
{ "log": { "id": 20, ... },
"prize": { "id": 50, ...}
}
but that is not the way respond_with works. It treats #prize as a nested resource of #log. Can anyone suggest an idea for this?
Merging two independent objects is dangerous and will override any existing attributes in the caller.
Instead you could always wrap the objects and respond with the wrapper instead:
#response = {:log => #log, :price => #price}
respond_with #response
Assuming that #log and #prize are both hashes, you could merge both hashes and return the merge.
respond_with #log.merge(#prize)
I'm thinking it might overwrite the #log.id with #prize.id though. Can try something else if it does.

Resources