JSON data to instance variable in Ruby - ruby-on-rails

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

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

Rails - activerecord model serializer group results by specific column in the foreign table

I want to use the ActiveRecord model serializer to show results from the primary key table and foreign key table. However, I want the results to be presented grouped by a column in the foreign key table.
cats = Cats.paginate(page: params[:page], per_page: 20)
render json: cats, meta: pagination(cats), adapter: :json
In the ActiveRecord model serializer:
class CatSerializer < ActiveModel::Serializer
attributes :cat, :persons
def persons
object.person
end
def cat
{ id: object.id, cat_name: object.name}
end
Now cat_name is not unique and Persons can share many cat_names. please note that Person => has_many Cats, but cat_name can be similar to multiple Persons. How can I show the data in this format:
"results": [
{
"cat": {
"id": 11,
"cat_name": "Luzi",
...
},
"persons": [
{
"id": 1,
"name": "andy"
},
{
"id": 2,
"name": "david"
}
Please also note that groyp_by(&:cat_name) does not work with pagination.
You can use custom serializer that accepts an already groupby ActiveRecord result
def index
#cats = Cat.joins(:persons).group("persons.name")
render json: #cats, serializer: GroupedCatSerializer
end
And you can define custom serializer like
class GroupedCatSerializer < ActiveModel::Serializer
# method override
def serializable_object(options={})
#object.map do |group_key, models|
[ group_key , serialized_models(models) ]
end.to_h
end
private
def serialized_models models
models.map{ |model| CatSerializer.new(model, root:
false) }
end
end

How to add item to json object ruby on rails?

I want to join items from 2 tables. There output:
"costs":[
{
"id":2,
"cost_name":"rent office",
"user_id":2,
"fix":true,
"amount":300300,
"created_at":"2018-11-05T18:36:19.108+06:00",
"updated_at":"2018-11-05T18:36:19.108+06:00"
},
{
"id":3,
"cost_name":"new computer",
"user_id":2,
"fix":false,
"amount":350000,
"created_at":"2018-11-06T14:44:49.805+06:00",
"updated_at":"2018-11-06T14:44:49.805+06:00"
}
],
"users":[
[
"Vi_Ok",
2
]
]
}
I want to add parameter of users (user name which is "Vi_Ok") add to every cost. How you noticed there in both table exist userId. Now code looks:
def index
#costs = Cost.all
#user_name = #costs.pluck(:user_id)
#user_name = User.find(#user_name).pluck(:name, :id)
# #costs.push("name" => #user_name.pluck(:name) this one just try to add
render json: {costs: #costs, name: #user_name}
end
You can write a custom method in your model and call it in index action, which returns all the costs with the username like below:
def self.list_costs
cost_list = []
costs = Cost.all
costs.each do |cost|
cost_info = cost.attributes
cost_info[:user_name] = cost.user.name
cost_list << cost_info
end
cost_list
end
class CostsController < ApplicationController
def index
render json: {costs: Cost.cost_list }
end
end
Supposing User has_many Costs,
What you provided,
hash = {"costs"=>[{"id"=>2, "cost_name"=>"rent office", "user_id"=>2, "fix"=>true, "amount"=>300300, "created_at"=>"2018-11-05T18:36:19.108+06:00", "updated_at"=>"2018-11-05T18:36:19.108+06:00"}, {"id"=>3, "cost_name"=>"new computer", "user_id"=>2, "fix"=>false, "amount"=>350000, "created_at"=>"2018-11-06T14:44:49.805+06:00", "updated_at"=>"2018-11-06T14:44:49.805+06:00"}], "users"=>[["Vi_Ok", 2]]}
Proceed,
costs, users = hash['costs'], hash['users']
costs.each { |c| c['user_name'] = users.detect { |u| u[1] == c['user_id'] }[0] }
Above will add user_name in each cost hash.

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

Reduce complexity of RABL template

My RABL template seems to be very un-DRY and over complex. Because of this I think I may be using it wrong, or that there are better ways at generating my desired output.
As you can see from the show.rabl code, I have to turn the plugins_vulnerability.vulnerability association into a JSON hash, explicitly selecting which keys I need, then merge the plugins_vulnerability.fixed_in value into the hash, and finally adding the new hash, which now contains the fixed_in value, to the vulnerabilities_array array.
I'm doing this because I want the fixed_in value to be within the vulnerability node.
plugins_controller.rb
class Api::V1::PluginsController < Api::V1::BaseController
def show
#plugin = Plugin.friendly.includes(:plugins_vulnerability, :vulnerabilities).find(params[:id])
end
end
show.rabl:
object #plugin
cache #plugin if Rails.env == 'production'
attributes :name
# Add the 'vulnerabilities' node.
node :vulnerabilities do |vulnerabilities|
vulnerabilities_array = []
# turn the plugins_vulnerability association into an array
vulnerabilities.plugins_vulnerability.to_a.each do |plugins_vulnerability|
vulnerability = plugins_vulnerability.vulnerability.as_json # turn the plugins_vulnerability.vulnerability association into json
vulnerability = vulnerability.select {|k,v| %w(id title references osvdb cve secunia exploitdb created_at updated_at metasploit fixed_in).include?(k) } # only select needed keys
vulnerabilities_array << {
:vulnerability => vulnerability.merge(:fixed_in => plugins_vulnerability.fixed_in)
} # merge the fixed_in attribute into the vulnerability hash and add them to an array (fixed_in is from plugins_vulnerabilities)
end
vulnerabilities_array
end
output.json
{
"plugin": {
"name": "simple-share-buttons-adder",
"vulnerabilities": [
{
"vulnerability": {
"id": 88157,
"title": "Simple Share Buttons Adder 4.4 - options-general.php Multiple Admin Actions CSRF",
"references": "https:\/\/security.dxw.com\/advisories\/csrf-and-stored-xss-in-simple-share-buttons-adder\/,http:\/\/packetstormsecurity.com\/files\/127238\/",
"osvdb": "108444",
"cve": "2014-4717",
"secunia": "",
"exploitdb": "33896",
"created_at": "2014-07-15T17:16:51.227Z",
"updated_at": "2014-07-15T17:16:51.227Z",
"metasploit": "",
"fixed_in": "4.5"
}
},
{
"vulnerability": {
"id": 88158,
"title": "Simple Share Buttons Adder 4.4 - options-general.php ssba_share_text Parameter Stored XSS Weakness",
"references": "https:\/\/security.dxw.com\/advisories\/csrf-and-stored-xss-in-simple-share-buttons-adder\/,http:\/\/packetstormsecurity.com\/files\/127238\/",
"osvdb": "108445",
"cve": "",
"secunia": "",
"exploitdb": "33896",
"created_at": "2014-07-15T17:16:51.341Z",
"updated_at": "2014-07-15T17:16:51.341Z",
"metasploit": "",
"fixed_in": "4.5"
}
}
]
}
}
I guess you can do something like this:
object #plugin
cache #plugin if Rails.env == 'production'
attributes :name
child(#plugin.vulnerabilities => :vulnerabilities) {
attributes :id, :title, :references, :osvdb, :cve, :secunia, :exploitdb, :created_at, :updated_at, :metasploit
# Add the 'fixed_in' node.
node :fixed_in do |vulnerability|
#plugin.plugins_vulnerability.fixed_in
end
}
This should create the same output that you need. And it doesn't look awefully complex to me.

Resources