Right now I have my index action set up like this, and I when the games component is rendered it basically receives games.to_json, I would like to reuse the template I have for GET /games.json which is specified in views/games/index.json.builder
I tried passing render(template: 'games/index.json') but I get this error:
Render and/or redirect were called multiple times in this action
def index
#games = Game.all
respond_to do |format|
format.html { render component: 'games', props: { games: #games } }
format.json { render :index }
end
end
On views/games/index.json.jbuilder
json.array! #games do |game|
json.extract! game, :id, :title, :cover_thumbnail
json.url game_url(game, format: :json)
end
If I call GET /games.json I get something like this:
[
{
"id": 133,
"title": "Final Fantasy VII: Remake",
"coverThumbnail": "url",
"url": "http://localhost:3000/games/133.json"
},
...
]
But when I call GET /games, the games component receives every single attribute, I want to send only the attributes specified on the jbuilder template
Related
I'm running a rails application that calls Simplecasts API to display my podcast episodes. I followed a tutorial to setup the API services using Faraday. My question is how to only display published episodes on my index page? Normally, I would add a .where(:status => "live") in my controller, IE #podcasts = Episodes.where(:status => "published") but this doesn't seem to work.
Simplecast's API for the podcast returns a collection that contains all the available episodes, each has a status node.
Any help would be appreciated as I'm new to working with external APIs in Rails
Sample API response
"collection": [
{
"updated_at": "2020-03-25T17:57:00.000000-04:00",
"type": "full",
"token": "lgjOmFwr",
"title": "Test",
"status": "draft",
Episode.rb
module Simplecast
class Episodes < Base
attr_accessor :count,
:slug,
:title,
:status
MAX_LIMIT = 10
def self.episodes(query = {})
response = Request.where('/podcasts/3fec0e0e-faaa-461f-850d-14d0b3787980/episodes', query.merge({ number: MAX_LIMIT }))
episodes = response.fetch('collection', []).map { |episode| Episode.new(episode) }
[ episodes, response[:errors] ]
end
def self.find(id)
response = Request.get("episodes/#{id}")
Episode.new(response)
end
def initialize(args = {})
super(args)
self.collection = parse_collection(args)
end
def parse_collection(args = {})
args.fetch("collection", []).map { |episode| Episode.new(episode) }
end
end
end
Controller
class PodcastsController < ApplicationController
layout "default"
def index
#podcasts, #errors = Simplecast::Episodes.episodes(query)
#podcast, #errors = Simplecast::Podcast.podcast(query)
render 'index'
end
# GET /posts/1
# GET /posts/1.json
def show
#podcast = Simplecast::Episodes.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
private
def query
params.permit(:query, {}).to_h
end
end
Looks like collection is just an array of hashes so rails ActivrRelations methods aka .where are not supported. However It is an array so you can just filter this array:
published_episodes = collection.filter { |episode| episode[:status] == “ published” }
Also look through their API - may be the do support optional filtering params so you would get only published episodes in the first place.
BTW: second thought is to save external API request data in your own DB and then fetch require episodes with standard .where flow.
I'm currently using a controller to receive POST with one json object at a time. And I want it change to receiving the whole array. How can I modify my controller?
Current Controller
def create
respond_to do |format|
#targetrecord = TargetRecord.new(targetrecord_params)
#targetrecord.save
if #targetrecord.save
format.json{ render :json => #targetrecord.to_json ,status: 200 }
else
format.json { render json: #targetrecord.errors, status: 404 }
end
end
end
end
def targetrecord_params
params.require(:targetrecord).permit(:id, :uuid, :manor, :mac, :beacon_type, :longitude, :latitude, :address, :findTime, :rssi, :finderID, :created_at, :updated_at )
end
I'm sending the POST as below right now
"targetrecord":
{"id":"","name":"",.....}
And I want to send multiple sets as an array like
"targetrecord":[
{"id":"1","name":"",.....},
{"id":"2","name":"",.....},
....]
How can I let my controller know that she needs to extract and create one by one? Thanks a lot!
If you are POSTing an array, then the array will just be part of your params object when processed by the controller action. So you should be able to loop through the array and create an array of TargetRecord objects. You'll need to modify your targetrecord_params method to allow it to accept an argument since you can't just look at 'params' in that context once you make the change. You'll also need to find a way to track whether or not all the records have saved successfully.
I haven't tested this code, but something like this should get you going in the right direction, I think:
def create
respond_to do |format|
#targetrecords = []
save_succeeded = true
params[:targetrecord].each do |record|
tr = TargetRecord.new(targetrecord_params(record))
save_succeeded = false unless tr.save
targetrecords << tr
end
if save_succeeded
format.json{ render :json => #targetrecord.to_json ,status: 200 }
else
format.json { render json: #targetrecord.errors, status: 404 }
end
end
end
end
def targetrecord_params(record)
record.require(:targetrecord).permit(:id, :uuid, :manor, :mac, :beacon_type, :longitude, :latitude, :address, :findTime, :rssi, :finderID, :created_at, :updated_at )
end
I want to move the below logic to somewhere else so I can use it both in my controller and in a rake task.
My controller action looks something like this:
def show
#user = User.find(params[:id])
#account = # load account
#sales = # load sales
..
render :json => {
"user": user,
"account": #account.map do |a|
JSON.parse(a.to_json(include: :addresses))
end,
"sales": #sales.map do |s|
JSON.parse(s.to_json(include: :products))
end
}
end
Basically the point is that I have to traverse the associations so the JSON has all of the data in it.
How can I move this logic somewhere else so I can then call it in my controller action and also in a rake task.
Extract the code to a presenter or use ActiveModel::Serializers, so that the controller and the Rake task call this new class.
class UserPresenter
def initialize(user, account, sales)
#user = user
#account = account
#sales = sales
end
def as_json(*)
{
"user": #user,
"account": #account.map do |a|
JSON.parse(a.to_json(include: :addresses))
end, # or #account.as_json(include: :addresses))
"sales": #sales.map do |s|
JSON.parse(s.to_json(include: :products))
end # or #sales.as_json(include: :products))
}
end
end
# In the controller
render json: UserPresenter.new(#user, #account, #sales)
I am attempting to include some extra bits in my JSON using the below in my vehicles_controller:
# GET /vehicles/1
# GET /vehicles/1.json
def show
#vehicle = Vehicle.find(params[:id])
respond_to do |format|
format.json { #vehicle.to_json(:methods => [:product_applications_with_notes], :include => [:product_applications]) }
end
end
The vehicle model has both the method :product_applications_with_notes and the relationship has_many: :product_applications. However, when I run a request to http://localhost:3000/vehicles/1 the JSON output is as below:
{
"id": 1,
"make": "Acura",
"model": "ALL",
"year": 2001,
"body_style": "Car",
"created_at": "2014-10-22T20:06:00.157Z",
"updated_at": "2014-10-22T20:07:09.827Z"
}
It does not show the included extra bits. Why?
try to override the as_json method in Vehicle model.
something like:
def as_json(options=nil)
json_hash = super(options)
json_hash[:product_applications] = product_applications
json_hash
end
I am trying Rabl, however, I seem to receive a practically empty json block.
require_dependency "api/application_controller"
module Api
class RentablePropertiesController < ApplicationController
respond_to :json
def index
#r = Core::RentableProperty.all
# render :text => #r.to_json --> note: this renders the json correctly
render "api/rentable_properties/index" #note: rabl here does not
end
end
end
index.json.rabl
collection #r
Output
[{"rentable_property":{}}]
Note: with a simply #r.to_json, it renders correctly:
[{"id":1,"description":"description","property_type_id":1,"created_at":"2013-08-22T19:04:35.000Z","updated_at":"2013-08-22T19:04:35.000Z","title":"Some Title","rooms":null,"amount":2000.0,"tenure":null}]
Any idea why rabl doesn't work?
The documentation of RABL (https://github.com/nesquena/rabl#overview) says that you need to precise what attributes you want to show in your JSON.
Their example:
# app/views/posts/index.rabl
collection #posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(#user) }
Which would output the following JSON or XML when visiting /posts.json:
[{ "post" :
{
"id" : 5, title: "...", subject: "...",
"user" : { full_name : "..." },
"read" : true
}
}]