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
Related
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
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}
This is my ruby code / JSON File. Three functions required, I have implemented the first two but am having trouble with the third one. I have only recently started learning ruby - any simplified explanations/answers are much appreciated
class Company
attr_accessor :jobs
jobs = Array.new
## TODO: Implement this method to load the given JSON file into Ruby built-in data
## structures (hashes and arrays).
def self.load_json(filepath)
require 'json'
file = File.read(filepath)
data_hash = JSON.parse(file)
end
## TODO: This method should update the `jobs` property to an array of instances of
## class `Job`
def initialize(filepath)
# Load the json file and loop over the jobs to create an array of instance of `Job`
# Assign the `jobs` instance variable.
load_json(filepath)
data_hash.each { |jobs|
array_of_jobs.insert(jobs['name'])
}
end
## TODO: Impelement this method to return applicants from all jobs with a
## tag matching this keyword
def find_applicants(keyword)
# Use the `jobs` instance variable.
end
end
Below is the JSON file code I am supposed to retrieve the information from.
{
"jobs": [
{
"id": 1,
"title": "Software Developer",
"applicants": [
{
"id": 1,
"name": "Rich Hickey",
"tags": ["clojure", "java", "immutability", "datomic", "transducers"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
}
]
},
{
"id": 2,
"title": "Software Architect",
"applicants": [
{
"id": 42,
"name": "Rob Pike",
"tags": ["plan-9", "TUPE", "go", "google", "sawzall"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
},
{
"id": 1337,
"name": "Jeffrey Dean",
"tags": ["spanner", "BigTable", "MapReduce", "deep learning", "massive clusters"]
}
]
}
]
}
Code provided by you will not compile and approach used is not very convenient.
Steps you may follow to implement it:
First implement your models. May look like:
class Applicant
attr_accessor :id, :name, :tags
def initialize(id, name=nil, tags=nil)
#id = id
#name = name
#tags = tags
end
end
class Job
attr_accessor :id, :title, :applicants
def initialize(id, title=nil, applicants=nil)
#id = id
#title = title
#applicants = applicants
end
end
Then define your Company class that works with jobs
class Company
attr_accessor :jobs
def initialize(jobs)
#jobs = jobs
end
def find_applicants(keyword)
# Now you can iterate through jobs,
# job's applicants and finally applicant's tags
# like this
applicants = []
#jobs.each do |job|
job.applicants.each do |applicant|
applicant.tags.each do |tag|
if keyword.eql? tag
# ...
end
end
end
end
applicants
end
end
And then you can load data from Json file and construct proper objects:
require 'json'
class DataLoader
def load(filepath)
hash = JSON.parse(filepath)
construct(hash)
end
private
def validate(hash)
# validate your data here
end
def construct(hash)
validate(hash)
jobs = []
hash['jobs'].each do |job|
applicants = []
job['applicants'].each do |applicant|
applicants << Applicant.new(applicant['id'], applicant['name'], applicant['tags'])
end
jobs << Job.new(job['id'], job['title'], applicants)
end
jobs
end
end
And all together will look like:
tag = 'google'
data = DataLoader.new.load(File.read('data.json'))
company = Company.new(data)
applicants = company.find_applicants(tag)
puts "Applicants that have '#{tag}' in taglist"
applicants.each do |applicant|
puts " #{applicant.id}: #{applicant.name}"
end
#Applicants that have google in taglist
# 2: Guido van Rossum
# 42: Rob Pike
Here is a simple implementation of find_applicants. JSON objects can be iterated through like any other data structure.
Ideone example here.
def find_applicants(myJson, keyword)
names = []
myJson["jobs"].each do |job|
job["applicants"].each do |applicant|
tags = applicant["tags"]
if tags.include? keyword then
names << applicant["name"]
end
end
end
names
end
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 building a REST API using rails and I have a few controllers that accept nested and recursive JSON, for example, when doing a PUT on /taxonomies/:id.json you can pass something like:
{
"names":[
"brands",
"secondary_brands"
],
"taxonomy_data":{
"some":"data"
},
"terms":[
{
"slug":"apple",
"data":{
"value":"Apple California"
},
"overridable_data":{
"weight":0.5
},
"term_data":{
"description":{
"en":"Apple makes the iPhone"
}
}
},
{
"slug":"microsoft",
"data":{
"value":"Microsoft Inc"
},
"overridable_data":{
"weight":0.5
},
"term_data":{
"description":{
"en":"Microsoft makes windows"
}
},
"children":[
{
"data":{
"value":"Xbox"
},
"overridable_data":{
"weight":0.5
},
"term_data":{
"description":{
"en":"Xbox one is bad"
}
}
}
]
},
{
"slug":"hp",
"data":{
"value":"HP Inc"
},
"overridable_data":{
"weight":0.5
},
"term_data":{
"description":{
"en":"HP makes atomic clocks"
}
}
}
]
}
Right now, I put the following code in my model:
class Taxonomy < ActiveRecord::Base
has_many :terms,
-> {order(:id)}
def update_terms(params)
existing_term_ids = terms.map &:id
create_term = lambda do |term_params, parent=nil|
t = terms.find_by(:id => term_params[:id]) if term_params[:id]
t ||= terms.build
t.attributes = term_params.slice(:slug, :data, :overridable_data, :term_data)
t.parent = parent
t.save
existing_term_ids.delete(t.id)
if term_params.has_key?(:children)
term_params[:children].each do |child_params|
create_term.call(child_params, t)
end
end
end
params.each do |term_params|
create_term.call(term_params)
end
terms.where(:id => existing_term_ids).destroy_all
save
end
end
This version (rapidly written to test rails 4) uses slice to filter parameters because attr_accessible is gone.
This makes me wonder if this kind of code should be in the model or the controller.
Read this acticle: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
My opinion, you should do the service in this case, something like this:
# app/services/recursive_update.rb
class RecursiveUpdate
def initalize(source)
#source = source
end
def update(params)
# your code here
end
def create_term(term_params, parent=nil)
#....
end
def permitted_params
#....
end
def save
#source.save
end
end
in the controller:
updater = RecurciveUpdate.new #model
updater.update params
update.save