I want add ruby hash to a yaml file(need to add value to the same keys repeatedly) - ruby

I tried to write a yaml file from a hash using ruby. And am able to create the yaml file.
The program is just a report that i tried to create. But the real problem occurred is that it works perfectly for one date. When I tried to run the on day1, its work perfectly(the yaml file is created correctly), but when i tried to run on another date, the values in the yaml files are overwrite, but actually I want to add the new value with the previous one in the yaml.
I know my english is bad and sometimes little lack to create logics. Please help me to correct the issue. The code is below
def write_monthly_yaml_file(daily_data)
#daily = File.exist?(#monthly_yaml_file) ? YAML::load_file(#monthly_yaml_file) : Hash.new
#daily[Time.parse(#date).strftime("%m")] = daily_data
File.open(#monthly_yaml_file, "w") {|f| f.puts #daily.to_yaml}
end
here daily_data is the hash, will anyone please help me to add new daily_data with old one(I need to add each value with previous value like summing)
Thank you

I assume you want to have many entries for one month:
# the line below overwrites the value for the key given
# #daily[Time.parse(#date).strftime("%m")] = daily_data
# will collect all data in an array instead:
(#daily[Time.parse(#date).strftime("%m")] ||= []) << daily_data
Here we create an array as a value unless it existed and append the daily_data value to it. For this code to be run successfully, please remove your existing yaml file from the disk.
The reason is that currently there is a single instance of daily_data associated with the key and the interface changed.

If your data in #daily[day] is a number you want to add to, try this:
#daily = File.exist?(#monthly_yaml_file) ?
YAML::load_file(#monthly_yaml_file) : Hash.new
day = Time.parse(#date).strftime("%m")
#daily[day] = (#daily[day] + daily_data)
File.open(#monthly_yaml_file, "w") {|f| f.puts #daily.to_yaml}

Related

Asking user for information, and never having to ask again

I want to ask for user input, but I only want to do it once (possibly save the information within the program), meaning, something like this:
print "Enter your name (you will only need to do this once): "
name = gets.chomp
str = "Hello there #{name}" #<= As long as the user has put their name in the very first
# time the program was run, I want them to never have to put thier name in again
How can I got about doing this within a Ruby program?
This program will be run by multiple users throughout the day on multiple systems. I've attempted to store it into memory, but obviously that failed because from my understand that memory is wiped everytime a Ruby program stops executing.
My attempts:
def capture_user
print 'Enter your name: '
name = gets.chomp
end
#<= works but user has to put in name multiple times
def capture_name
if File.read('name.txt') == ''
print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
#esd_user = gets.chomp
File.open('name.txt', 'w') { |s| s.puts(#esd_user) }
else
#esd_user = File.read('name.txt')
end
end
#<= works but there has to be a better way to do this?
require 'tempfile'
def capture_name
file = Tempfile.new('user')
if File.read(file) == ''
print "\e[36mEnter name to appear on email (you will only have to do this once):\e[0m "
#esd_user = gets.chomp
File.open(file, 'w') { |s| s.puts(#esd_user) }
else
#esd_user = File.read(file)
end
end
#<= Also used a tempfile, this is a little bit over kill I think,
# and doesn't really help because the users can't access their Appdata
You will want to store the username in a file on the local file system. Ruby provides many ways to do this, and we'll explore one in this answer: YAML files.
YAML files are a structured storage file that can store all kinds of different data, and is a good place to store config data. In fact, YAML configuration files are key parts of the largest Ruby projects in existence. YAML gives you a good starting point for supporting future configuration needs, beyond the current one, which is a great way to plan feature development.
So, how does it work? Let's take a look at your requirement using a YAML config:
require 'yaml'
config_filename = "config.yml"
config = {}
name = nil
if file_exists(config_filename)
begin
config = YAML.load_file(config_filename)
name = config["name"]
rescue ArgumentError => e
puts "Unable to parse the YAML config file."
puts "Would you like to proceed?"
proceed = gets.chomp
# Allow the user to type things like "N", "n", "No", "nay", "nyet", etc to abort
if proceed.length > 0 && proceed[0].upcase == "N"
abort "User chose not to proceed. Aborting!"
end
end
end
if name.nil? || (name.strip.length == 0)
print "Enter your name (you will only need to do this once): "
name = gets.chomp
# Store the name in the config (in memory)
config["name"] = name
# Convert config hash to a YAML config string
yaml_string = config.to_yaml
# Save the YAML config string to the config file
File.open(config_filename, "w") do |out|
YAML.dump(config, out)
end
end
Rather than show you the bare minimum to meet your needs, this code includes a little error handling and some simple safety checks on the config file. It may well be robust enough for you to use immediately.
The very first bit simply requires the YAML standard library. This makes the YAML functions work in your program. If you have a loader file or some other common mechanism like that, simply place the require 'yaml' there.
After that, we initialize some variables that get used in this process. You should note that the config_filename has no path information in it, so it will be read from the current directory. You will likely want to store the config file in a common place, such as in ~/.my-program-name/config.yml or C:\Documents and Settings\MyUserName\Application Data\MyProgramName\. This can be done pretty easily, and there's plenty to help, such as this Location to Put User Config Files in Windows and Location of ini/config files in linux/unix.
Next, we check to see if the file actually exists, and if so, we attempt to read the YAML contents from it. The YAML.load_file() method handles all the heavy lifting here, so you just have to ask the config hash that's returned for the key that you're interested in, in this case, the "name" key.
If an error occurs while reading the YAML file, it indicates that the file might possibly be corrupted, so we try to deal with that. YAML files are easy to edit by hand, but when you do that, you can also easily introduce an error that will make loading the YAML file fail. The error handling code here will allow the user to abort the program and go back to fix the YAML file, so that it doesn't simply get overwritten.
After that, we try to see if we've been had a valid name from the YAML config, and if not, we go ahead and accept it from the user. Once they've entered a name, we add it to the config hash, convert the hash to a YAML-formatted string, and then write that string to the config file.
And that's all it takes. Just about anything that you can store in a Ruby hash, you can store in a YAML file. That's a lot of power for storing config information, and if you later need to add more config options, you have a versatile container that you can use exactly for that purpose.
If you want to do any further reading on YAML, you can find some good information here:
YAML in Ruby Tutorial on Robot Has No Heart
Jamming with Ruby YAML on Juixe Techknow
YAML on Struggling with Ruby
While some of these articles are a bit older, they're still very relevant and will give you a jumping off point for further reading. Enjoy!
If you need the name to persist across the user running the script several times, you're going to need to use some sort of data store. As much as I hate flat files, if all you're storing is the user's name, I think this is a valid option.
if File.exist?('username.txt')
name = File.open( 'username.txt', 'r' ) do |file|
name = file.gets
end
else
print "Enter your name (you will only need to do this once): "
name = gets.chomp
File.open( 'username.txt', 'w' ) do |file|
file.puts name
end
end
str = "Hello there #{name}"

How to read value in cell from database

I am still fairly new to Ruby and to databases in general, and am trying to better learn how to use the two together. I have browsed through several online tutorials but haven't been able to figure a few things out. I am working with PostgreSQL and am simply trying to read the data in my database and manipulate in some way the data contained in the actual cell. From a tutorial I have the following functions:
def queryUserTable
#conn.exec( "SELECT * FROM users" ) do |result|
result.each do |row|
yield row if block_given?
end
end
end
and a simple way to print out the information in the rows would be something like
p.queryUserTable {|row| printf("%s %s\n", row['first_name'], row['last_name'])}
(with p being the connection). However all this is doing it printing out each value in the row and column specified as a whole, then continuing to the next row. What I would like to know is how I can grab for instance the value in row 1 under column first name and use it for something else? From what I understand, it looks like the rows are hashes and so I should be able to do something similar to {|row, value| #my_var = value } but I get no results by doing so, so I am not understanding how this all works properly. I am hoping someone can better explain how this works. Hope that makes sense. Thanks!
EDIT:
Does it have anything to do with this line in my function?:
result.each do |row| #do I need to add |row,value| here as well?
Is there a reason you're not using an ORM like ActiveRecord? Although it certainly has some downsides, it may well be helpful for someone who is new to databases and ruby. If you want a tutorial on active record and rails, I highly recommend Michael Hartl's awesome free tutorial[1].
I'm not exactly sure what you're trying to do, but I can correct a couple of misconceptions. First of all, result is not a hash - it is an array of hashes. That is why doing result.each { |row, value| ... doesn't initialize value. Once you have an individual row, you can do row.each { |col_name, val| ...
Second, if you want to grab a value from a specific row, you should specify the row in the query. You must know something about the row you want information about. For getting the user with id = 1, for instance:
user = #conn.exec("SELECT first_name FROM users WHERE id = 1").first
unless user.nil?
# do something with user["first_name"]
If you were to use activerecord, you could just do
user = User.findById(1)
I would not want to set the value in the queryUserTable loop, because it will get set on each loop, and just retain the value of the last time it executes.
[1] https://www.railstutorial.org/book

How to import a column of a CSV file into a Ruby array?

My goal is to import a one column of a CSV file into a Ruby array. This is for a self-contained Ruby script, not an application. I'll just be running the script in Terminal and getting an output.
I'm having trouble finding the best way to import the file and finding the best way to dynamically insert the name of the file into that line of code. The filename will be different each time, and will be passed in by the user. I'm using $stdin.gets.chomp to ask the user for the filename, and setting it equal to file_name.
Can someone help me with this? Here's what I have for this part of the script:
require 'csv'
zip_array = CSV.read("path/to/file_name.csv")
and I need to be able to insert the proper file path above. Is this correct? And how do I get that path name in there? Maybe I'll need to totally re-structure my script, but any suggestions on how to do this?
There are two questions here, I think. The first is about getting user input from the command line. The usual way to do this is with ARGV. In your program you could do file_name = ARGV[0] so a user could type ruby your_program.rb path/to/file_name.csv on the command line.
The next is about reading CSVs. Using CSV.read will take the whole CSV, not just a single column. If you want to choose one column of many, you are likely better off doing:
zip_array = []
CSV.foreach(file_name) { |row| zip_array << row[whichever_column] }
Okay, first problem:
a) The file name will be different on each run (I'm supposing it will always be a CSV file, right?)
You can solve this problem with creating a folder, say input_data inside your Ruby script. Then do:
Dir.glob('input_data/*.csv')
This will produce an array of ALL files inside that folder that end with CSV. If we assume there will be only 1 file at a time in that folder (with a different name), we can do:
file_name = Dir.glob('input_data/*.csv')[0]
This way you'll dynamically get the file path, no matter what the file is named. If the csv file is inside the same directory as your Ruby script, you can just do:
Dir.glob('*.csv')[0]
Now, for importing only 1 column into a Ruby array (let's suppose it's the first column):
require 'csv'
array = []
CSV.foreach(file_name) do |csv_row|
array << csv_row[0] # [0] for the first column, [1] for the second etc.
end
What if your CSV file has headers? Suppose your column name is 'Total'. You can do:
require 'csv'
array = []
CSV.foreach(file_name, headers: true) do |csv_row|
array << csv_row['Total']
end
Now it doesn't matter if your column is the 1st column, the 3rd etc, as long as it has a header named 'Total', Ruby will find it.
CSV.foreach reads your file line-by-line and is good for big files. CSV.read will read it at once but using it you can make your code more concise:
array = CSV.read(, headers: true).map do |csv_row|
csv_row['Total']
end
Hope this helped.
First, you need to assign the returned value from $stdin.gets.chomp to a variable:
foo = $stdin.gets.chomp
Which will assign the entered input to foo.
You don't need to use $stdin though, as gets will use the standard input channel by default:
foo = gets.chomp
At that point use the variable as your read parameter:
zip_array = CSV.read(foo)
That's all basic coding and covered in any intro book for a language.

Ruby Appending an hash and saving it in a CSV

I am learning ruby from a book I got called The Pragmatic Programmers Guide and have got to a point where I'm reading and processing things from a .csv file. I taken upon myself the challenge of making a small program that opens a csv file and then reads all the data, saving it into a hash. This is my code so far:
require 'csv'
class NoxReader
phash = {}
def open
CSV.foreach('data.csv', headers: true) do |row|
row.each do |x|
name, password = line.chomp.split(",")
phash[name] = password
end
end
end
end
Now I want to be able to append to that hash and write it back to the csv file. I have an idea on how to write it back to csv, but it's just appending to the hash via a gets.chomp that blows me away. Any help on where to start would be much appreciated.
In fact you are already appending key-value pairs to your hash. This is what phash[name] = password does in your code.
If you want to add the resulting hash of your function login_prompt in your other question to phash, you need to
phash.merge login_prompt

Ruby parse comma separated text file

I need some help with a Ruby script I can call from the console. The script needs to parse a simple .txt file with comma separated values.
value 1, value2, value3, etc...
The values needs to be added to the database.
Any suggestions?
array = File.read("csv_file.txt").split(",").map(&:strip)
You will get the values in the array and use it to store to database. If you want more functions, you can make use of FasterCSV gem.
Ruby 1.9.2 has a very good CSV library which is useful for this stuff: http://www.ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html
On earlier versions of Ruby you could use http://fastercsv.rubyforge.org/ (which essentially became CSV in 1.9.2)
You could do it manually by reading the file into a string and using .split(',') but I'd go with one of the libraries above.
Quick and dirty solution:
result = []
File.open("<path-to-file>","r") do |handle|
handle.each_line do |line|
result << line.split(",").strip
end
end # closes automatically when EOF reached
result.flatten!
result # => big array of values
Now you can iterate the result array and save the values to the database.
This simple file iteration doesn't take care for order or special fields, because it wasn't mentioned in the question.
Something easy to get you started:
IO.readlines("csv_file.txt", '').each do |line|
values = line.split(",").collect(&:strip)
# do something with the values?
end
Hope this helps.

Resources