JSON::ParserError in Ruby - ruby

Am trying to read data I just stored in the .json file, when running the script, am getting this error:
/Users/topazjos/.rvm/gems/ruby-3.0.0/gems/json-2.6.1/lib/json/common.rb:216:in `parse': 859: unexpected token at '[] (JSON::ParserError)
from /Users/topazjos/.rvm/gems/ruby-3.0.0/gems/json-2.6.1/lib/json/common.rb:216:in `parse'
from /Users/topazjos/Documents/SQL & Ruby Projects/school-library/savedata.rb:48:in `fetch_people_datas'
it is showing that the error is coming from thefecth_people_datas method
class IOmanager
def save_people(persons)
file = File.open('./people.json', 'a')
person_data = persons.map do |person|
if person.instance_of?(Teacher)
{ occupation: 'Teacher', name: person.name, age: person.age, specialization: person.specialization }
else
{ occupation: 'Student', name: person.name, age: person.age, parent_permission: person.parent_permission }
end
end
file.puts(JSON.generate(person_data))
end
def fetch_people_datas
return [] unless File.exist?('./people.json')
file = File.read('./people.json')
array = []
if file.empty?
array
else
person_data = JSON.parse(file)
person_data.map do |data|
if data['occupation'] == 'Teacher'
teacher = Teacher.new(data['age'], data['specialization'], data['name'])
array.push(teacher)
else
student = Student.new(data['age'], data['classroom'], data['name'], data['parent_permission'])
array.push(student)
end
end
end
array
end
end
then am calling that method like this
class CreatePeople
def initialize
#iomanager = IOmanager.new
#books = #iomanager.fetch_book_data
#persons = #iomanager.fetch_people_datas
#rentals = []
end
def create_student
puts 'Create a new student'
print 'Enter student age: '
age = gets.chomp.to_i
print 'Enter name: '
name = gets.chomp
print 'Has parent permission? [Y/N]: '
parent_permission = gets.chomp.downcase
case parent_permission
when 'n'
Student.new(age, 'classroom', name, parent_permission: false)
#persons << student
puts 'Student doesnt have parent permission, cant rent books'
when 'y'
student = Student.new(age, 'classroom', name, parent_permission: true)
#persons << student
puts 'Student created successfully'
end
end
def create_teacher
puts 'Create a new teacher'
print 'Enter teacher age: '
age = gets.chomp.to_i
print 'Enter teacher name: '
name = gets.chomp
print 'Enter teacher specialization: '
specialization = gets.chomp
teacher = Teacher.new(age, specialization, name)
#persons << teacher
puts 'Teacher created successfully'
end
def json_runner
#iomanager.save_book(#books)
#iomanager.save_people(#persons)
#iomanager.save_rental(#rentals)
end
end
I have used the same way to read from the previous file fecth_book_data inside the same class and it has worked but I don't understand what went wrong here

Related

Rubocop Lint: Useless assignment to variable

This code runs perfectly in the console but the linters keep failing with the error below..
This is the error:
refactored.rb:17:5: W: Lint/UselessAssignment: Useless assignment to variable - user_name. Did you mean user_age?
user_name = gets.chomp
refactored.rb:14:5: W: Lint/UselessAssignment: Useless assignment to variable - user_age. Did you mean user_name?
user_age = Integer(gets.chomp)
^^^^^^^^
This is the code:
require './school-library/student'
require './school-library/rental'
require './school-library/persons'
require './school-library/teacher'
require './school-library/book'
class Methods
def initialize
#person_array = []
end
def user_input
print 'Age: '
user_age = Integer(gets.chomp)
print 'Name: '
user_name = gets.chomp
end
def create_person
puts 'Do you want to create a student (1) or a teacher (2)? [Input the number]: '
person_option = Integer(gets.chomp)
case person_option
when 1
(user_age, user_name) = user_input
print 'Has parent permission? [Y/N]: '
user_permission = gets.chomp.to_s.upcase
case user_permission
when 'Y'
user_permission = true
when 'N'
user_permission = false
end
student = Student.new(user_age, user_name, parent_permission: user_permission)
#person_array.push({
output: "[Student] Name: #{student.name}, ID: #{student.id}, Age: #{student.age}",
object: student
})
puts 'Person created successfully!'
puts "\n"
when 2
(user_age, user_name) = user_input
print 'Specialization: '
user_specialization = gets.chomp
teacher = Teachers.new(user_age, user_name, user_specialization)
#person_array.push({
output: "[Teacher] Name: #{teacher.name}, ID: #{teacher.id}, Age: #{teacher.age}",
object: teacher
})
puts 'Person created successfully!'
puts "\n"
else
puts 'Person not created.'
end
end
def people_list
#person_array.each do |person|
puts person[:output]
end
end
def person_rent
#person_array.each_with_index do |person, index|
puts "#{index}) #{person[:output]}"
end
end
end
class BooksList
def initialize
#books = []
end
def create_book
print 'Title: '
book_title = gets.chomp
print 'Author: '
book_author = gets.chomp
puts 'Book created successfully!'
book = Book.new(book_title, book_author)
#books.push({
output: "Title: #{book.title}, Author: #{book.author}",
object: book
})
end
def book_list
#books.each do |book|
puts book[:output]
end
end
def rent_book
#books.each_with_index do |book, index|
puts "#{index}) #{book[:output]}"
end
end
end
RuboCop is trying to tell you that a useless variable exists in your code, but before fix that we need to talk about a bug happening right here:
(user_age, user_name) = user_input
In Ruby, a method returns the last line of code. Looking at the method user_input we can see that it returns only the user_name variable. The problem is that the (user_age, user_name) expects two values. The first is being assigned, but the second is given nil.
To fix that, update the method user_input to:
def user_input
print 'Age: '
user_age = Integer(gets.chomp)
print 'Name: '
user_name = gets.chomp
[user_age, user_name]
end
This will fix both the RuboCop offense and the bug.

I can't interpolate my hash because apparently it is nil class? Could anyone shed some light on this?

Is there an alternative to iteration or a condition I can make that will allow me to interpolate the values of just one hash? I've inspected the results of my input and apparently the array that i'm saving my hashes into disappears when there is only one hash. I've also tested the results and they're of Nil class.
def print_with_index(students)
students.each_with_index do |student, index|
index_plus_one = index + 1
puts "#{index_plus_one}. #{students[:name]} (#{students[:cohort]} cohort)"
end
end
How do i solve my problem and also why do hashes behave this way?
Full code:
def print_header
puts "The students of Villains Academy"
puts "--------------"
end
def print_footer(names)
puts "Overall, we have #{names.count} great students"
end
def input_students
puts "Please enter the names and then the cohort of the students"
puts "To finish, just hit return twice"
#created an empty array
students = []
#getting the first name
name = gets.chomp
cohort = gets.chomp.to_sym
if cohort.empty?
cohort = :november
end
if cohort !~ /january|february|march|april|may|june|july|august|september|october|november|december/
puts "Please enter a valid month"
puts "Warning months are case sensitive. Please enter in lowercase characters."
cohort = gets.chomp.to_sym
end
while !name.empty? do
# add the student hash to the array called students
students << {name: name, cohort: cohort}
if students.count > 1
puts "Now we have #{students.count} students"
else students.count == 1
puts "Now we have #{students.count} student"
end
#getting another name from the user
name = gets.chomp
cohort = gets.chomp.to_sym
if cohort.empty?
cohort = :november
end
if cohort !~ /january|february|march|april|may|june|july|august|september|october|november|december/
puts "Please enter a valid month"
puts "Warning months are case sensitive. Please enter in lowercase characters."
cohort = gets.chomp.to_sym
end
end
bycohort = students.sort_by { |v| v[:cohort] }
filter = students.select! { |student| student[:cohort] == :november }
puts bycohort #This allows me to see all of my hashes before they are filtered
puts ""
bycohort
filter
end
def print_with_index(students)
students.each_with_index do |students, index|
index_plus_one = index + 1
puts "#{index_plus_one}. #{students[:name]} (#{students[:cohort]} cohort)"
end
end
### body ###
students = input_students
print_header
print_with_index(students)
print_footer(students)
this works for me though, i think with each_with_index enum, you have to pass in an array of hashes i.e. [{...}, {...}, {...}], not a single hash with multiple keys-values
def print_with_index(students)
students.each_with_index do |students, index|
index_plus_one = index + 1
puts "#{index_plus_one}. #{students[:name]} (#{students[:cohort]} cohort)"
end
end
print_with_index([{name:"b", cohort: "c"}])
# 1. b (c cohort)
You should use student instead of students as the block parameter. You can check if the student object is nil.
def print_with_index(students)
students.each_with_index do |student, index|
if !student.nil?
index_plus_one = index + 1
puts "#{index_plus_one}. #{student[:name]} (#{student[:cohort]} cohort)"
end
end
end

How to use Hash values in ruby

I wrote a small Ruby program, but can't access the hash value stored in the parent class.
Here is the code:
class College
##dep = ["cs" => 60000, "mat" => 20000, "che" => 30000]
end
class Student < College
def get_det
puts "Enter name... \n"
#name = gets
puts "Enter department...\n"
#dpt = gets
end
def set_fee
case #dpt
when "cs"
#fee = (##dep["cs"]).to_i
when "mat"
#fee = ##dep["mat"].to_i
when "che"
#fee = ##dep["che"].to_i
else
puts "Eror!!!"
end
end
def print_det
puts "Name : #{#name}"
puts "Department : #{#dpt}"
puts "Course fee : #{#fee}"
end
end
det = Student.new
det.get_det
det.set_fee
det.print_det
I got the output as:
Output:
You've defined your ##dep variable as an array, not as a hash. You need to replace [ ] with { }, like so:
##dep = {"cs" => 60000, "mat" => 20000, "che" => 30000}
Then you'll be able to access your hash values via the string keys:
##dep['cs'] # Will return 6000
And just an FYI, your set_fee method could be refactored to just be this:
def set_fee
#fee = ##dep[#dpt] || 'Error!'
puts #fee
end
Since you're simply passing in the value you're checking against for each of your when statements, you can just pass the value directly to your ##dep object. And you don't need to_i, because the values in your hash are already integers.

Ruby output formats differently on dynamic v. static input

FYI: Fist time I've written Ruby.
We have to write an Address Book program. I have many parts working at this point but have run into something I don't get.
I add contacts to the address book statically before the program runs. Then I try to have a user add a contact dynamically. When I print this address book the contacts added statically are printed according to formatting I expect, but the ones added dynamically are all over the place.
I've added all my code here. It's sort of a big chunk but not too bad...
# Address class
class Address
attr_accessor :street, :city, :state, :zip
# default constructor
def initialize(*args)
if (args.size == 0 )
#street = #city = #state = #zip = ""
elsif (args.size == 4)
#street = args[0]
#city = args[1]
#state = args[2]
#zip = args[3]
else
puts('Constructor takes 0 or 4 arguments. No address information has been set.')
end
end
# string representation of address
def to_s
" " + #street + "\n" + \
" " + #city + " " + #state + ", " + #zip
end
end
# Person class which holds full name, phone, and
# address as an object
class Person
attr_accessor :fname, :lname, :phone, :address
def initialize(*args)
if (args.size == 0 )
#fname = #lname = #phone = ""
#address = Address.new
elsif (args.size == 4)
#fname = args[0]
#lname = args[1]
#phone = args[2]
#address = args[3]
else
puts('Incorrect number of arguments.')
end
end
# returns full name
def full_name
#lname + ", " + #fname
end
def to_s
" " + full_name + "\n" + \
#address.to_s + "\n" + \
" " + #phone
end
end
# AddressBook class to hold addresses
class AddressBook
def initialize
# empty array
#persons = []
end
# adds a person to the address book
def add(person)
#persons += [person]
#persons = #persons.sort{|a,b| by_name(a,b)}
end
def by_name(a,b)
if a.lname == b.lname
a.fname <=> b.fname
else
a.lname <=> b.lname
end
end
# removes a person from the address book
def remove(person)
# Use Array#delete
#persons.delete(person)
end
def to_s
add_book = ""
for p in #persons do
add_book += p.to_s + "\n\n"
end
return add_book
end
end
#
# Here I add three contacts in two different ways
#
#
# FIRST ONE
#
sandy_addr = Address.new
sandy_addr.street = "324 Campus Dr."
sandy_addr.city = "College Park"
sandy_addr.state = "OH"
sandy_addr.zip = "55555"
sandy = Person.new
sandy.fname = "Sandy"
sandy.lname = "Koh"
sandy.phone = "651-442-5710"
sandy.address = sandy_addr
#
# SECOND ONE
#
bill_addr = Address.new('536 Green Rd.', "Saint Paul", "MN", "56545")
bill = Person.new('William', 'Perry', '675-778-6754', bill_addr)
#
# THIRD ONE
#
angela_addr = Address.new('3390 Crookston Rd.', "Miami", "FL", "78654")
angela = Person.new('Angela', 'Anderson', '345-748-1754', angela_addr)
# Contacts added to the Address Book
#addressBook = AddressBook.new
#addressBook.add(sandy)
#addressBook.add(angela)
#addressBook.add(bill)
# Main method loop that runs the program
# Allows you to enter a contact, print a list of
# contacts and exit the program
def loop(addBook)
selection = 0
until(selection == 5)
puts("Wlecome to the Address Book\n")
puts("1. Add a Contact\n")
puts("2. Delete a Contact\n")
puts("3. Retrieve a Contact\n")
puts("4. Print all Contacts\n")
puts("5. Exit Address Book\n")
selection = gets().to_i
if(selection == 1)
addContact(addBook)
elsif(selection == 2)
#delete_contact
elsif(selection == 3)
#retrieve_contact
elsif(selection == 4)
puts(addBook)
end
end
end
def addContact(addBook)
print("Enter First Name: ")
fname = gets()
print("Enter Last Name: ")
lname = gets()
print("Enter Phone Number: ")
phone = gets()
print("Enter Street Address: ")
street = gets()
print("Enter City: ")
city = gets()
print("Enter State: ")
state = gets()
print("Enter Zip Code ")
zip = gets()1
newAddress = Address.new(street, city, state, zip)
newPerson = Person.new(fname, lname, phone, newAddress)
addBook.add(newPerson)
out = %q/#{fname} #{lname} has been added to the Address Book./
puts(out)
end
loop(#addressBook)
The gets method includes the newline so your fname in addContact will be, for example, "Bob\n" rather than the "Bob" that you're expecting. Have a look at chomp.
You problem is probably caused because gets() returns the entire line including the newline character at the end. Try replacing every gets() with gets.chomp.

Help! check_in': undefined method `push' for nil:NilClass (NoMethodError)

Hi Im getting an core error which is really standard I suppose in Ruby but dont know what to make of it. I have a program that I have written. Its purpose is to register guests at a camping. You have a menu with 5 different options. 1. Checkin. When i do this I get a undefined method generateParkingLot' for #<Camping:0x10030a768> (NoMethodError)
When I choose Checkout I get a undefined local variable or methoddeparture' for Menu:Class (NameError).
So please i someone has a clue to my problem it would be great. I will here paste all my code. The code is separated in different files and I have used require for the different files. Here though I will paste all the code in one trace. Thankful for all help.
-Sebastien
require 'guest'
require 'parking_lot'
class Camping
attr_accessor :current_guests, :parking_lots, :all_guests, :staticGuests
def initialize(current_guests, parking_lots, all_guests, staticGuests)
#current_guests = Array.new()
# initiera husvagnsplatserna
#parking_lots = Array.new(32)
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
#staticGuests = Array[
Guest.new("Logan Howlett", "Tokyo", "07484822",1, #parking_lots[0]),
Guest.new("Scott Summers", "Chicago", "8908332", 2, #parking_lots[1]),
Guest.new("Hank Moody", "Boston", "908490590", 3, #parking_lots[2]),
Guest.new("Jean Grey", "Detroit", "48058221", 4, #parking_lots[3]),
Guest.new("Charles Xavier","Washington DC", "019204822",5, #parking_lots[4])
]
end
#all_guests = []
#staticGuests.each do |guest|
#current_guests[guest.plot.nr] = guest
#all_guests.push(guest)
end
end
def to_s
# creates an empty string
list = " "
# loop from 1 to 32
(1..32).each do |n|
if (!#current_guests[n-1].nil?)
list += #current_guests[n-1].to_s
else
# else adds the text "Vacant"
list += n.to_s + ": Vacant!\n"
end
return list
end
def generateParkingLot
randomNr = 1+rand(32)
# exists a guest at the (0-based) position?
if (!#current_guests[randomNr-1].nil?)
# if so generate a new figure
generateEmpty(array)
else
# returns the generated number
return randomNr
end
end
end
end
class Guest
attr_accessor :firstname, :lastname, :address, :phone, :departure
attr_reader :arrived, :plot
def initialize (firstName, lastName, address, phone, plot)
#firstName = firstName
#lastName = lastName
#address = address
#phone = phone
#arrived = arrived
#plot = plot
end
def to_s
"Personal information:
(#{#firstName}, #{#lastName}, #{#address}, #{#phone}, #{#arrived}, #{#departure}, #{#plot})"
end
end
require 'ruby_camping'
require 'camping_guests'
class Main
if __FILE__ == $0
$camping = Camping.new(#current_guests, #all_guests, #parking_lots,#staticGuests)
puts "\n"
puts "Welcome to Ruby Camping!"
while (true)
Menu.menu
end
end
end
require 'date'
require 'camping_guests'
require 'guest'
class Menu
def initialize(guests = [])
#camping = Camping.new(guests)
end
def self.menu
puts "---------------------------"
puts " Menu"
puts " 1. Checkin"
puts " 2. Checkout"
puts " 3. List current guests"
puts " 4. List all guests"
puts " 5. Exit\n"
puts ""
puts " What do you want to do?"
puts "---------------------------"
print ": "
action = get_input
do_action(action)
end
# fetches menu choice and returns chosen alternativ
def self.get_input
input = gets.chomp.to_i
while (input > 5 || input < 1) do
puts "Ooups, please try again."
input = gets.chomp.to_i
end
return input
end
def self.do_action(action)
case action
when 1:
check_in
when 2:
check_out
when 3:
puts $camping.current_guests
when 4:
puts $camping.all_guests
when 5:
puts "You are now leaving the camping, welcome back!"
exit
end
end
def self.check_in
puts "Welcome to the checkin"
puts "Please state your first name: "
firstName = gets.chomp
puts "Please state your last name:"
lastName = gets.chomp
puts "Write your address: "
address = gets.chomp
puts "and your phone number: "
phone = gets.chomp
puts "finally, your arrival date!"
arrived = gets.chomp
newPLot = $camping.generateParkingLot
newGuest = Guest.new(firstName, lastName, address, phone,arrived,$camping.parking_lots[newPLot-1])
$camping.current_guests[newPLot-1] = newGuest
#all_guests.push(newGuest)
puts "The registration was a success!! You have received the " + newPLot.to_s + "."
end
def self.check_out
puts "Welcome to checkout!"
puts $camping.all_guests
puts "State plot of the person to checkout!"
plot = gets.chomp.to_i
puts "Ange utcheckningsdatum: "
departureDate = gets.chomp.to_i
guest = $camping.current_guests[plot-1]
#departure = departure
guest.departure = departureDate
guestStayedDays = departureDate - guest.arrived
guest.plot.increase(guestStayedDays)
puts guest
$camping.current_guests[plot-1] = nil
end
end
class Parking_Lot
attr_accessor :nr
attr_reader :electricity_meter
def initialize (nr)
#nr = nr
#electricity_meter = 4000-rand(2000)
end
def increase_meter(days)
generatedUse = (10+rand(70))*days
puts "Increases the meter with " + generatedUse.to_s + " kWh."
#electricity_meter += generatedUse
end
def to_s
"Plot #{#nr+1} Electricity meter: #{#electricity_meter} kWh"
end
end
It looks (although I haven't tried this out) like some of your your 'end's are wrong.
The one which is causing your first error (generateParkingLot undefined) is that generateParkingLot is actually defined inside to_s, so you need an extra 'end' at the end of your to_s method.
As for the second error (departure not recognised), the folowing line in self.check_out is at fault:
#departure = departure
because there is no 'departure' variable. (Perhaps you meant DepartureDate?). I suspect there may be a few other issues with this code, but I'm afraid I don't really have time to check now.
One I noticed was that when you have
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
I think you might want to end that, either with an 'end' or curly brackets, e.g.
32.times do |nr|
#parking_lots[nr] = Parking_Lot.new(nr)
end
Although that would make your other blocks not match.. In general, just try and make sure your blocks are all defined properly (e.g. everything has a matching end).

Resources