Add identifier for JSON array in Ruby on Rails - ruby-on-rails

I am looking for a way to add an identifier to my JSON output so it can be more easily parsed. Currently, the output is :
[
{
"id":9,
"name":"Test Location",
"description":"Test Description",
"address":"123 Fake Street",
"latitude":-85.0,
"longitude":-101.10101,
"created_at":"2015-11-15T21:25:08.643Z",
"updated_at":"2015-11-15T21:27:23.419Z"
},
{
"id":10,
"name":"Test Location",
"description":"testest",
"address":"estesets",
"latitude":1.0,
"longitude":1.0,
"created_at":"2015-11-15T22:05:39.224Z",
"updated_at":"2015-11-15T22:05:39.224Z"
}
]
The ideal output would be:
{ locations:
[
{
"id":9,
"name":"Test Location",
"description":"Test Description",
"address":"123 Fake Street",
"latitude":-85.0,
"longitude":-101.10101,
"created_at":"2015-11-15T21:25:08.643Z",
"updated_at":"2015-11-15T21:27:23.419Z"
},
{
"id":10,
"name":"Test Location",
"description":"testest",
"address":"estesets",
"latitude":1.0,
"longitude":1.0,
"created_at":"2015-11-15T22:05:39.224Z",
"updated_at":"2015-11-15T22:05:39.224Z"
}
]
}
My current controller is:
module Api
module V1
class LocationsController < ApplicationController
unless Rails.env.test?
before_filter :restrict_access
end
respond_to :json
def index
#locations = Location.all
respond_with #locations
end
private
def restrict_access
api_key = ApiKey.find_by_access_token(params[:access_token])
head :unauthorized unless api_key
end
end
end
end
I would like for it to have a name of Locations so I can more easily parse it. Thanks for the help!

def index
#locations = Location.all
respond_with locations: #locations
end
Results in proper output

Just work with your #locations.
You can do something like:
#new_locations = {}
#new_locations = {'locations' => #locations}

Related

How do I check if params is in a json file and if not throw error response?

I have a json file as below names.json. When you append the URL /list?name=Canada or /list?name=CANADA be it Uppercase or Lowercase, I want to check if the param[:name] is inside names.json file and throw error if not there.
[
{
"id": 1,
"name": "Canada"
},
{
"id": 17,
"name": "Denmark"
},
{
"id": 23,
"name": "Austria"
}
]
Here is what I have done but did not work…..
controller/concerns
require 'json'
JSON_NAMES = 'names.json'.freeze
module NameFileLoader
class JsonLoader
def self.json_data_hash
file = File.read(JSON_NAMES)
JSON.parse(file)
end
end
end
name_controller.rb
def check_name_validity_in_file
data = NameFileLoader::JsonLoader.json_data_hash
name = data.each { |item| item['name'] } # The problem is here.
if name.include?(params[:name])
{ errorCode: 400, message: 'Name provided is not valid' }
end
end
You’d better cache the JSON once loaded from the file in the first place. Also you probably want to maintain a cached list of allowed countries in the lowercase to compare.
module NameFileLoader
class JsonLoader
class << self
def json_data_hash
#json ||= JSON.parse(File.read(JSON_NAMES))
end
def countries
#countries ||= json_data_hash.map { |h| h['name'].downcase }
end
end
end
end
Now upon receival a parameter you might check it as:
if NameFileLoader::JsonLoader.countries.include?(params[:name].downcase)
...
end

How to display environment variables as a group in Rails

I am using Rails 5.2 application. I want to display the Environment variables as a group in the endpoint.
env_controller.rb
class EnvController < ApplicationController
def index
render json: ENV.to_h
end
end
When I load http://localhost:3000/env, I see the following result
{
"XDG_VTNR": "7",
"MANPATH": "/home/ubuntu/.nvm/versions/node/v4.6.0/share/man:/home/ubuntu/.rvm/rubies/ruby-2.6.2/share/man:/home/ubuntu/.rvm/man:/usr/lib/jvm/java-8-oracle/man:/usr/local/man:/usr/local/share/man:/usr/share/man",
"S3_SOURCE_PATH": "dev/source",
"DB_ENV_USER": "postgres",
"XDG_SESSION_ID": "c2",
"rvm_bin_path": "/home/ubuntu/.rvm/bin",
"S3_DESTINATION_PATH": "dev/destination",
"SESSION": "ubuntu",
"DB_PORT_5432_TCP_ADDR": "localhost",
"S3_REGION": "us-east-1"
}
I want to group the environment variables as follows. My expected result is as follows
{
"S3": {
"S3_SOURCE_PATH": "reports/source",
"S3_DESTINATION_PATH": "reports/destination",
"S3_REGION": "us-east-1"
},
"DB": {
"DB_ENV_USER": "postgres",
"DB_PORT_5432_TCP_ADDR": "localhost"
},
"Others": {
"XDG_VTNR": "7",
"MANPATH": "/home/ubuntu/.nvm/versions/node/v4.6.0/share/man:/home/ubuntu/.rvm/rubies/ruby-2.6.2/share/man:/home/ubuntu/.rvm/man:/usr/lib/jvm/java-8-oracle/man:/usr/local/man:/usr/local/share/man:/usr/share/man",
"XDG_SESSION_ID": "c2",
"rvm_bin_path": "/home/ubuntu/.rvm/bin",
"SESSION": "ubuntu"
}
}
I definitely need "S3" and "DB" to be grouped. There are more variables in my application should be grouped like this. Above are the samples.
How can I display it as the above?
I think what you want is a custom serializer. Something similar to this:
class EnvSerializer
def as_json
{
S3: env_select('S3'),
DB: env_select('DB'),
Other: env_reject('S3', 'DB')
}
end
private
def env_select(prefix)
ENV.select { |k, v| k.start_with?(prefix) }
end
def env_reject(*prefixes)
ENV.reject { |k, v| k.start_with?(*prefixes) }
end
end
Which would look like this in the controller:
class EnvController < ApplicationController
def index
render json: EnvSerializer.new.as_json
end
end

Cache api (json) request to avoid repeating the request

So I created this request which gives me a response as json.
require 'dotenv/load'
require 'faraday'
class OverviewController < ApplicationController
def api_key
ENV["API_KEY"]
end
def url
"https://example.com"+api_key
end
def index
conn = Faraday.new(url, request: {open_timeout: 1, timeout: 1}) do |c|
c.response :json, :content_type => /\bjson$/
c.adapter Faraday.default_adapter
end
response = conn.get url
#hash = response.body['data']
end
end
Response:
{
"type": "products",
"version": "x.x.x",
"data": {
"Product 1": {
"title": "xxx",
"attributes": {
"x"=1
},
"id": 22,
"name": "Product 1"
},
"Product 2": {
"title": "xXx",
"attributes": {
"x"=2
},
"id": 25,
"name": "Product 2"
},
...
This works great so far. But since the data in this json changes really rarely and there is a policy to not request the api as much I would like to cache my result.
I tried different solutions with "faraday-http-cache" but I can't get it to work. But I like to not use another lib.
I read the "rails - Guides - caching" segment and I think I need low level caching Rails.cache.fetch
I would be really glad if someone could help me :-)
EDIT(after Panic's comment):
I tried this, but I need some more help
require 'dotenv/load'
require 'faraday'
class StatsClient
def api_key
ENV["API_KEY"]
end
def url
"https://example.com"+api_key
end
def index
conn = Faraday.new(url, request: {open_timeout: 1, timeout: 1}) do |c|
c.response :json, :content_type => /\bjson$/
c.adapter Faraday.default_adapter
end
response = conn.get url
#hash = response.body['data']
end
end
class OverviewController < ApplicationController
def index
#hash = Rails.cache.fetch('something', expires_in: 15.minutes) do
StatsClient.products
end
end
end
Like this? What has to go actually as 'something'. Also I get the error that "StatsClient.products is not recognised.
Move the code from your controller into a separate class (or module) StatsClient. Then in your controller:
def index
#hash = Rails.cache.fetch('something', expires_in: 15.minutes) do
StatsClient.products
end
end

Simplify .to_json array result in Rails 4

I have this code:
#post.to_json(include: {tags: { only: :name} } )
which produces this output:
{ ... "tags": [{"name": "Lorem"}, {"name": "ipsum"}, {"name": "cupcake"}] ... }
When what I want is:
{ ... "tags": ["Lorem", "ipsum", "cupcake"] ... }
Any ideas?
It's simple, write your own serializer rather than trying to hack the to_json.
class PostWithTagsSerializer
attr_reader :object
def initialize(object)
#object = object
end
def as_json(*)
hash = object.as_json
hash[:tags] = object.tags.pluck(:name)
hash
end
def to_json(*)
as_json.to_json
edn
end
Then simply use
PostWithTagsSerializer.new(#post).to_json

Show won't render correct json

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

Resources