CLI Data Gem using Ruby - ruby

Newbie question here. I'm working on my first CLI Data Gem project that scrapes live data from 2 websites.
The user can press '1' to see article #1 or '2' to see article #2.
The user can type 'list' to see both article titles stacked one on top of the other.
Typing 'exit' should exit the app with "See you tomorrow for more articles."
When the user types literally anything else, the program should say "No Bueno. Type 1, 2, list, or exit"
So far, pressing '1' or '2' works. Typing 'list' works
The error happens when I press 'exit'..
It will say
"No Bueno. Type 1, 2, list, or exit" (my custom error message)
and
"See you tomorrow for more articles."
when it should just say - "See you tomorrow for more articles."
def call
list_items
menu
goodbye
end
def list_items
# here doc - http://blog.jayfields.com/2006/12/ruby-multiline-strings-here-doc-or.html
puts "Today's Designer News:"
#articles = DesignerNews::Article.today
#articles.each.with_index(1) do |article, i|
puts "#{i}. #{article.title} - #{article.name} - #{article.date} - #{article.url}"
end
end
def menu
input = nil
while input !="exit"
puts "Enter the number of the item you'd like to read, type list to see the list, or type exit:"
input = gets.strip.downcase
if input.to_i > 0
the_article = #articles[input.to_i-1]
puts "#{the_article.title} - #{the_article.name} - #{the_article.date} - #{the_article.url}"
elsif input == "list"
list_items
else
puts "No Bueno. Type 1, 2, list, or exit"
end
end
end
def goodbye
puts "See you tomorrow for more articles."
end
end
any and all help is appreciated.

You're not checking for exit input inside the loop. Changing the last else to this should work:
elsif input != "exit"
puts "No Bueno. Type 1, 2, list, or exit"
end

Related

How to simulate multiple `gets` (user input) with RSpec

This project is a simple Ruby project, Github Repo, which I am trying to re-build using TDD, and I'm using Ruby (ruby 2.7.3) and rspec (RSpec 3.10).
My issue is, no matter how I try to simulate user input (see the commented out lines of my 'Test Code below to see what I've tried.)
Am I missing something? I feel like I don't understand this well enough, I've tried most of the suggestions I can find on Google.
Code file: /lib/address_checker.rb
Test file: /spec/address_checker_spec.rb
I am testing the AddressChecker class, specifically the following functions:
def handle_manual_input
puts "Add at least 2 Addresses, hit enter after each address, type 'quit' when done"
while true do
address = gets.chomp
break if address == "quit"
check_ethereum_address_validity(address) ? #origin_addresses << address : (puts "Error: Invalid Ethereum Address")
end
end
def input_origin_addresses_manually?(y_n)
if y_n == "y"
handle_manual_input
elsif y_n == "n"
#origin_addresses = ["0x72140C1886f8F2Dd932DCe06795901F8FB6378a7","0x0613Cd2076bd432C7A60a1b926b11B17BaAaFE11"]
else
print "Please only enter 'y' or 'n' into 'input_origin_addresses_manually'"
end
end
I'm trying to test whether the output of input_origin_addresses_manually?("y") will populate #origin_addresses with more than 1 address.
When I run rspec though, I am prompted for user input (because of my address = gets.chomp within handle_manual_input.
Terminal Output from rspec:
➜ cryptoAddressWeb git:(main) ✗ rspec
...Add at least 2 Addresses, hit enter after each address, type 'quit' when done
and my desired input would loop like this:
...Add at least 2 Addresses, hit enter after each address, type 'quit' when done
0xa95aea385130718be87b380b419eeac8da40de55
0xa95aea385130718be87b380b419eeac8da40de55
quit
....
Test Code
it 'If user wants to add their own addresses, check that they add more than 1 address' do
subject.input_origin_addresses_manually?("y")
# let(:user_input) { ["rock\n", "rock\n"] }
# allow_any_instance_of(Object).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
# expect(subject).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
# allow(subject).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
# subject.stub(gets: 'user input')
# expect(STDOUT).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
# allow(STDIN).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
# expect($stdout).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
# allow($stdin).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
expect(subject.origin_addresses.length).to be > 1
end
Would really appreciate assistance!
Thank you,
Ed
[How to] Simulate Multiple Gets (User Input) on rspec
class Inquisitor
def gets_twice
[gets, gets]
end
end
RSpec.describe Inquisitor do
describe '#gets_twice' do
it 'calls gets twice' do
inq = Inquisitor.new
allow(inq).to receive(:gets).and_return('Apricot', 'Banana')
expect(inq.gets_twice).to eq(%w[Apricot Banana])
end
end
end

Not getting desired return from method in Ruby

In the #play_song method, I need to show the list of songs generated by the #list_songs method. There are many other classes in other files I have not included here, but all tests up until now are passing. The list_songs method is not returning the list how it is supposed to look: 1. Artist name - Song name - Genre, 2...
It is instead returning an array of objects. I don't get it because my tests are passing for #list_songs, which requires that the songs are outputed in the format I just described. So why is calling #list_songs within the play_song method returning an array of objects instead of displaying the list as 1. 2 3 ...?
class MusicLibraryController
extend Concerns::Findable
attr_accessor :path
def initialize(path = './db/mp3s')
music_importer = MusicImporter.new(path)
music_importer.import
end
def call
response = nil
until response == "exit" do
puts "Welcome to your music library!"
puts "To list all of your songs, enter 'list songs'."
puts "To list all of the artists in your library, enter 'list artists'."
puts "To list all of the genres in your library, enter 'list genres'."
puts "To list all of the songs by a particular artist, enter 'list artist'."
puts "To list all of the songs of a particular genre, enter 'list genre'."
puts "To play a song, enter 'play song'."
puts "To quit, type 'exit'."
puts "What would you like to do?"
response = gets
end
end
def list_songs
#"1. Thundercat - For Love I Come - dance" order by song name
ordered_songs = Song.all.uniq.sort_by {|song| song.name} #need to use "uniq" because there are duplicates of each song for some reason
ordered_songs.each do |song|
number = ordered_songs.index(song) + 1
puts "#{number}. #{song.artist.name} - #{song.name} - #{song.genre.name}"
end
end
def list_artists
ordered_artists = Artist.all.sort_by {|artist| artist.name}
ordered_artists.each do |artist|
number = ordered_artists.index(artist) + 1
puts "#{number}. #{artist.name}"
end
end
def list_genres
ordered_genres = Genre.all.sort_by {|genre| genre.name}
ordered_genres.each do |genre|
number = ordered_genres.index(genre) + 1
puts "#{number}. #{genre.name}"
end
end
def list_songs_by_artist
puts "Please enter the name of an artist:"
artist_name = gets
artist = Artist.all.select { |artist| artist.name == artist_name }.first #need to use .first b/c there are duplicates of each object and we want to select only one object
if artist != nil
ordered_songs = artist.songs.sort_by {|song| song.name}
i = 1
ordered_songs.each do |song|
puts "#{i}. #{song.name} - #{song.genre.name}"
i += 1
end
end
end
def list_songs_by_genre
puts "Please enter the name of a genre:"
genre_name = gets
genre = Genre.all.select { |genre| genre.name == genre_name }.first
if genre != nil
ordered_songs = genre.songs.sort_by {|song| song.name}
i = 1
ordered_songs.each do |song|
puts "#{i}. #{song.artist.name} - #{song.name}"
i += 1
end
end
end
binding.pry
def play_song
list_songs
puts "Which song number would you like to play?"
end
end

Ruby - Create a class with a file as a variable - possible?

I need to create a program in which the user can take different tests. As i dont want to copy paste my code all over for every test, i have tried to setup a class for that purpose - but i have problems with this class.
Error message = undefined variables or method in 'display_test'
I have predefined som test as a .txt file
I want to choose the file in the class depending on what the user answer - is that possible?
Class code:
class Test
#correct_answers = 0
def display_question( question, options, answer )
puts question
options.each_with_index { |option, idx| puts "#{ idx + 1 }: #{ option
}" }
print 'Answer: '
reply = gets.to_i
if answer == reply
puts 'Correct!'
#correct_answers += 1
puts "#{#correct_answers}"
else
puts 'Wrong. The correct answer was: ' + answer.to_s
end
end
def display_test()
f = File.new(userinput, 'r')
while ! (f.eof?) #logikken til at splitte
line = f.gets()
question = line.split("|")
question[1] = question[1].split(";")
display_question question[0], question[1], question[2].to_i
end
end
display_test
puts "________________________________________________________"
puts "Total score:"
puts "You've got" + " #{#correct_answers}" + " correct answers!"
Before hand i have used ("geografitest.txt") instead of username in the File.new so it looked like this:
f = File.new('geografitest.txt','r')
But now i am trying to let the user decide what test to take.
I am very new to ruby, so please bear with me.
I have tried to do it this way, which obviously is not working.
puts "Which test do you want to take?"
select = 0
while (select != 3)
puts "Press 1 to take Geografi test."
puts "Press 2 to take Math test."
puts "Press 3 to take Religion test."
puts "Press 3 to exit"
select = gets.chomp.to_i
if (select == 1)
gets.chomp = userinput
userinput =`geografitest.txt`
echo $userinput
end
if (select == 2)
gets.chomp = userinput
userinput =`matematiktest.txt`
echo $userinput
end
if (select == 3)
gets.chomp = userinput
userinput =`religionstest.txt`
echo $userinput
end
if (select > 4)
puts "Not a correct selection"
elsif (select == 4)
puts "Goodbye"
end
end
abort
So my questions is now;
How can i make the user choose what test to take? Can i make a variable instead of the textfile as i have tried, but in a different way? Or is there a smarter way?
And in what way is my class wrong and how do i fix it? I know its not the way to make it, but i simple cant get my head around how to make it right.
Please help a rookie out.
Cheers!
You can pass file as dependency to you Test class based on user input with object constructor. Something like this
class Test
attr_reader :correct_answers_count
def initialize(file)
#file = file
#correct_answers_count = 0
end
#other code goes here
end
loop do
case user_input = gets.chomp
when '1'
file_name = 'some_file1'
when '2'
file_name = 'some_file1'
when '3'
break
else
puts 'wrong variant'
end
test = Test.new(File.new(file_name, 'r'))
test.display
end

Deleting a row from a CSV file with Ruby

I've seen similar answers to this question but I think I need something more specific to my code. Basically I've called the row from the CSV file but now I need to delete the called row. Sounds simple as I write this yet here I am asking you all for help. I know there is a lot of code here but I figured the more there is the more easier you will be able to understand the context. Apologies if there is too much noise in the code.
def delete_user_menu
puts "============================================"
delete_users_active_list
puts " "
puts "Please type in the name of the user you wish to eradicate: "
print "> "
eradicate(gets.chomp)
end
def eradicate(delete_input)
delete_row = delete_authentication(delete_input)
if delete_row
puts "Are you sure you want to delete #{delete_input} from the database?"
puts "[y]es or [n]o"
print "> "
delete_answer = gets.chomp
if delete_answer == "y"
delete_user
after_deletion_menu
elsif delete_answer == "n"
puts "Close call! Taking you back to main menu."
sleep 2
admin_main_menu
else
puts "Input not recognised. Please try again."
eradicate(delete_input)
end
else
puts "User not recognized. Please try again."
sleep 1
delete_user_menu
end
end
def delete_user
# this is where the delete user function needs to go
after_deletion_menu
end
def after_deletion_menu
puts " "
puts "User deleted! What would you like to do now?"
puts "1. Delete another user"
puts "2. Back to main menu"
print "> "
after_deletion_choice = gets.chomp
if after_deletion_choice == "1"
delete_user_menu
elsif after_deletion_choice == "2"
admin_main_menu
else
puts "Input not recognized. Please try again."
after_deletion_menu
end
end
def delete_users_active_list
CSV.foreach("./users.csv", headers: true) do |row|
username = row['username']
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
puts "Username: #{username}"
end
end
def delete_authentication(username)
CSV.open('users.csv', headers: true).find { |row| row['username'] == username }
end
I've had a look at this question How to remove a row from a CSV with Ruby
but I don't fully understand the answers, hence why I'm here. Any help is much appreciated.
I looked at the link. First, they are reading the entire csv file into table:
table = CSV.table(#csvfile)
then deleting the row from table:
table.delete_if do |row|
row[:foo] == 'true'
end
Finally, they are completely replacing the original file with the new table minus the row:
File.open(#csvfile, 'w') do |f|
f.write(table.to_csv)
end
This is generally how you have to do this kind of operation when you are dealing with a file. It's not like a database.
EDIT - in your case:
delete_user(delete_input)
...
def delete_user(user)
...
table.delete_if { |row| row[:username] == user }
...

Trying to pull from API with Ruby -- how to pass variable into string?

Im trying to practice pulling APIs with Ruby. Im trying to pull videogame news from Steam.
Below is my code.
The idea is, when the program is ran, the the user is prompted to enter a game ID between 200 and 440.
Anything not in between dont exist or the numbers arent continuous.
Anyway, Im trying to pass the gameID variable into the string:
"http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002/?appid=#{gameID}&count=5&maxlength=300&format=json"
The string is wrapped in a function. When I try to run the program, the error says wrong number of arguments ( 0 for 1 ).
What am i doing wrong, and what am I missing? Many thanks in advance as usual :)
*been doing nothing but asking questions so far, hope to contribute someday once I get better :)
require 'json'
require 'HTTParty'
puts "----------------------------------------------------------"
puts "Welcome to my practice"
puts "The purpose of this exercise is to use the SteamAPI"
puts "to pull videogame news from Steam"
puts "----------------------------------------------------------"
reset = true
while reset
puts "Please enter a game ID between 200 - 440"
gameID = gets.to_i
if gameID < 200
puts "--Invalid input--"
reset = true
elsif gameID > 400
puts "--Invalid input--"
reset= true
else
reset = false
end
end
puts "--------------------Loading API----------------------------"
def get_news( gameID )
string = "http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002/?appid=#{gameID}&count=5&maxlength=300&format=json"
page = HTTParty.get( string )
browse = page["appnews"]["newsitems"]
browse.map do |content|
{title: content["title"], contents: content["contents"]}
end
end
def display_story( content )
puts "Title: #{content[:title]}"
puts "--------------------"
puts " #{content[:contents]}"
puts "--------------------"
end
get_news.each do |content|
display_story( content )
end
You've defined get_news to take a gameID argument, but you haven't passed it anything. At the end of your file, you need get_news(gameID).each.

Resources