# Using Ruby to record tasks

I started writing this program to get a better understanding of different aspects of Ruby. I tried to follow the ruby style guide here.

What it's supposed to do is take the exercises/tasks you give it, and record those tasks. It uses a record of those tasks to automatically create/update the header row of a .csv file for the month, and to automatically create a row for every day that information (repetitions of a given exercise) is inputted. I know this program may seem a little useless, after all, many of us own excel, and currently it just gets input from gets, but I was thinking I might do more with it in the future.

I wrote two versions:

Version one:

# Requires
require "date"
require "csv"

# Functions
def standardize(string)
# 'Standardizes' strings so that "word word" becomes "Word-Word"
new_string = ""
string_parts = string.strip.split
string_parts.each do |part|
part.capitalize!
new_string << part + "-"
end
return new_string[0..-2]
end

def ensure_file(file_path)
# Checks if a file exists and creates it if it doesn't exist
if !File.file?(file_path)
file = File.new(file_path, "w")
file.close
end
end

def overwrite_csv(path, csv_table)
# Overwrites a CSV file
# Made this a function so I can change how I do it throughout the file
# from one place
CSV.open(path, "w") do |csv_file|
csv_table.each { |row| csv_file << row }
end
end

# Constants
KNOWN_EXERCISES = "known_exercises.txt"
MONTH_FILE = Date::MONTHNAMES[Date.today.month] + ".csv"

# Gets an array of known exercises
ensure_file(KNOWN_EXERCISES)
exercises = []
File.foreach(KNOWN_EXERCISES) do |exercise|
exercise.strip!
exercise != "" ? exercises << exercise.strip : nil
end

# Gets information from the user
print "Exercise: "
exercise = standardize(gets.strip)
print "Repetitions: "
reps = gets.strip

# Checks if the exercise is in the known exercises file, writes it in if not
if !exercises.include?(exercise)
File.open(KNOWN_EXERCISES, "a") do |known_exercises|
known_exercises.write("\n" + exercise)
end
exercises << exercise
end

# Creates/modifies the .CSV file if necessary
current_day = Date.today.month.to_s + "/" + Date.today.day.to_s
if !File.file?(MONTH_FILE)
CSV.open(MONTH_FILE, "w") do |csv_file|
end
end

# Writes any new exercises to the file on disk
if !records[0].include?(exercise)
records[0] << exercise
overwrite_csv(MONTH_FILE, records)
end

# Enters user inputted data into the records in memory to edit
if !records["Day"].include?(current_day)
data = [current_day]
data[exercise_col] = reps
records << data
elsif records["Day"].include?(current_day)
cur_day_row_i = records["Day"].index(current_day) # Index of the current day's row
records[cur_day_row_i][exercise] = reps
end

# Overwrites file on disk with edits in memory
overwrite_csv(MONTH_FILE, records)


Version two:

# Requires
require "date"
require "csv"

# Classes
class CSVFile
attr_accessor :path, :new_file
alias new_file? new_file

def initialize(path)
@path = path
@new_file = false

# Creates file if necessary
if !File.file?(@path)
@new_file = true
create_file(path)
end

yield(self) if block_given?
end

def overwrite_table(csv_table)
# Overwrites a CSV file with data from a Table object
CSV.open(@path, "w") do |csv_file|
csv_table.each { |row| csv_file << row }
end
end

def overwrite(csv)
# Overwrites a CSV file with anything that can be appended to CSV.open
CSV.open(@path, "w") do |csv_file|
csv_file << csv
end
end

def append(csv)
# Overwrites the whole csv file to append a line
records << csv
overwrite_table(records)
end

# CSV.read but without having to specify path
end

alias << append

def to_s
end
end

# Functions
def create_file(file_path)
# Creates a blank file at file_path
file = File.new(file_path, "w")
file.close
end

def standardize(string)
# 'Standardizes' strings so that "word word" becomes "Word-Word"
new_string = ""
string_parts = string.strip.split
string_parts.each do |part|
part.capitalize!
new_string << part + "-"
end
new_string[0..-2]
end

# Constants
KNOWN_EXERCISES = "known_exercises.txt"
RECORDS = Date::MONTHNAMES[Date.today.month] + ".csv"

# Creates exercises file if necessary
create_file(KNOWN_EXERCISES) if !File.file?(KNOWN_EXERCISES)

# Gets an array of known exercises
exercises = []
File.foreach(KNOWN_EXERCISES) do |exercise|
exercise.strip!
exercises << exercise if exercise != ""
end

# Gets information from the user
print "Exercise: "
exercise = standardize(gets.strip)
print "Repetitions: "
reps = gets.strip

# Checks if the exercise is in the known exercises file, writes it in if not
if !exercises.include?(exercise)
File.open(KNOWN_EXERCISES, "a") do |known_exercises|
known_exercises.write("\n" + exercise)
end
exercises << exercise
end

# Creates/modifies the .CSV file
current_day = Date.today.month.to_s + "/" + Date.today.day.to_s
CSVFile.new(RECORDS) do |csv_file|
# Creates the header row if necessary
if csv_file.new_file?
end

# Enters user inputted data into the records in memory to edit

# Edits the header row when necessary
if !records[0].include?(exercise)
records[0] << exercise
end

# Edits the other rows when necessary
if !records["Day"].include?(current_day)
data = [current_day]
data[exercise_col] = reps
records << data
elsif records["Day"].include?(current_day)
cur_day_row_i = records["Day"].index(current_day) # Index of the current day's row
records[cur_day_row_i][exercise] = reps
end

# Overwrites file on disk with edits in memory
csv_file.overwrite_table(records)
end


So if you entered room cleaning for the exercise, and 1 for the reps on June 11, you would get a .csv file that looked like:

Day,Room-Cleaning
6/11,1


And the next month, automatically the header row would look like:

Day,Room-Cleaning


Without you having to tell the program that that is one of the tasks you want to add.

I have a slight background in Python, if that helps in explaining any mistakes I may have made.

• A little style nitpicking, personally I think it looks better to do this when using the ! operator if !(some command). – 13aal Jun 12 '16 at 18:22
• Where does this store the files btw..? – 13aal Jun 12 '16 at 18:30
• @13aal This program stores the files in a "known_exercises.txt" and "[Month].csv" format in the directory of the script. Those values are specified in the constants near the middle of the script. Also, I kind of like the not operator on conditionals without the parentheses, which I like to save for more complex conditionals to use when necessary. Even then, if I have to use too many parentheses in one if statement, it may or may not be an indication that I am able to refactor that code to look much better. – Thonanth Siddef Jun 12 '16 at 23:25
• Yeah I ran the script and saw, all around I'd say it looks pretty good, nicely done man – 13aal Jun 13 '16 at 1:48
• @13aal Thanks! Character limit is preventing me from just thanking you, so take these extra words too!!! – Thonanth Siddef Jun 14 '16 at 4:41

Sorry I don't have time to review the full program, but here's a much simpler replacement for the standardize method. Note strip is a redundant if you're then going to be splitting on white space:
def standardize(string)

• @ThonanthSiddef joins are extremely helpful. I've started using them more often. – Carcigenicate Jun 13 '16 at 12:39
• @Carcigenicate I've already started my next mini project, so I'll look into joins soon and see if they might have any efficacy in executing the task at hand. They probably will from looking at this answer – Thonanth Siddef Jun 14 '16 at 4:43