Fixing width of text from file.write - ruby

I am trying to fix the width of the written text going to my .txt file.
Currently the values are collected through a gets command from user input. Then they are passed through a class object and assigned to various #___ variables. Then called under a Grade_Log.new(add_class, add_assignment, add_grade). And eventually written individually to the .txt file.
class Grade_Log
attr_accessor :which_class, :assignment_type, :grade
def initialize(which_class, assignment_type, grade)
#which_class = which_class
#assignment_type = assignment_type
#grade = grade
end
def attribute_class
#which_class
end
def attribute_assignment
#assignment_type
end
def attribute_grade
#grade
end
end
input = Grade_Log.new(add_class, add_assignment, add_grade)
File.open("grade_log.txt", "a") do |file|
file.write(input.attribute_class)
file.write(" ")
file.write(input.attribute_assignment)
file.write(" ")
file.write(input.attribute_grade)
file.write("\n")
end
I am getting the output that I am intending to get... "PHYSICS HOMEWORK 97" however, I would like for the spacing to be fixed at 20 width for each variable so I don't have to manually insert the " ".

Related

Ruby - reading from .csv and creating objects out of it

I have .csv file with rows of which every row represents one call with certain duration, number etc. I need to create array of Call objects - every Call.new expects Hash of parameters, so it's easy - it just takes rows from CSV. But for some reason it doesn't work - when I invoke Call.new(raw_call) it's nil.
It's also impossible for me to see any output - I placed puts in various places in code (inside blocks etc) and it simply doesn't show anything. I obviously have another class - Call, which holds initialize for Call etc.
require 'csv'
class CSVCallParser
attr_accessor :io
def initialize(io)
self.io = io
end
NAMES = {
a: :date,
b: :service,
c: :phone_number,
d: :duration,
e: :unit,
f: :cost
}
def run
parse do |raw_call|
parse_call(raw_call)
end
end
private
def parse_call(raw_call)
NAMES.each_with_object({}) do |name, title, memo|
memo[name] = raw_call[title.to_s]
end
end
def parse(&block)
CSV.parse(io, headers: true, header_converters: :symbol, &block)
end
end
CSVCallParser.new(ARGV[0]).run
Small sample of my .csv file: headers and one row:
"a","b","c","d","e","f"
"01.09.2016 08:49","International","48627843111","0:29","","0,00"
I noticed a few things that isn't going as expected. In the parse_call method,
def parse_call(raw_call)
NAMES.each_with_object({}) do |name, title, memo|
memo[name] = raw_call[title.to_s]
end
end
I tried to print name, title, and memo. I expected to get :a, :date, and {}, but what I actually got was [:a,:date],{}, and nil.
Also, raw_call headers are :a,:b,:c..., not :date, :service..., so you should be using raw_call[name], and converting that to string will not help, since the key is a symbol in the raw_call.
So I modified the function to
def parse_call(raw_call)
NAMES.each_with_object({}) do |name_title, memo|
memo[name_title[1]] = raw_call[name_title[0]]
end
end
name_title[1] returns the title (:date, :service, etc)
name_title[0] returns the name (:a, :b, etc)
Also, in this method
def run
parse do |raw_call|
parse_call(raw_call)
end
end
You are not returning any results you get, so you are getting nil,
So, I changed it to
def run
res = []
parse do |raw_call|
res << parse_call(raw_call)
end
res
end
Now, if I output the line
p CSVCallParser.new(File.read("file1.csv")).run
I get (I added two more lines to the csv sample)
[{:date=>"01.09.2016 08:49", :service=>"International", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"},
{:date=>"02.09.2016 08:49", :service=>"International", :phone_number=>"48622454111", :duration=>"1:29", :unit=>"", :cost=>"0,00"},
{:date=>"03.09.2016 08:49", :service=>"Domestic", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"}]
If you want to run this program from the terminal like so
ruby csv_call_parser.rb calls.csv
(In this case, calls.csv is passed in as an argument to ARGV)
You can do so by modifying the last line of the ruby file.
p CSVCallParser.new(File.read(ARGV[0])).run
This will also return the array with hashes like before.
csv = CSV.parse(csv_text, :headers => true)
puts csv.map(&:to_h)
outputs:
[{a:1, b:1}, {a:2, b:2}]

Ruby: file output blank

So I'm trying to build a program which tells me how many days are left till someone's birthday. I have a text file that I'm drawing data from. The problem is the save method is not working for some reason, and nothing is being printed to the output file. Thank you so much in advance!
require 'date'
Person = Struct.new(:name, :bday)
module Family
Member = Hash.new
File.readlines("bday_info.txt").each do |line|
name, bday = line.split(',')
person = Person.new(name, bday)
Member[name] = bday
end
def self.next_bday(name)
birthday = Date.parse(Family::Member[name])
this_year = Date.new(Date.today.year, birthday.month, birthday.day)
next_year = Date.new(Date.today.year + 1, birthday.month, birthday.day)
if this_year > Date.today
puts "\n#{(this_year - Date.today).to_i} days to #{name}'s birthday"
else
puts "\n#{(next_year - Date.today).to_i} days to #{name}'s birthday"
end
end
def self.save(name)
File.open("days_left.txt", "w") do |file|
file.puts "#{next_bday(name)}"
end
end
end
loop do
puts "\nName:"
response = gets.chomp.capitalize
if response.downcase == "quit"
break
elsif Family::Member.has_key?("#{response}") == false
puts "\nYou ain't on the list"
else
Family.next_bday(response)
Family.save(response) #WHY IS THIS LINE NOT WORKING???
end
end
Your Family.next_bday does not return anything (to be more precise - it's returning nil that last puts returns), thus nothing is written to the file.
Other notes(not directly related):
No reason in calling Family.next_bday(response) twice, just save return value from the first call
Family::Member constant looks more like a variable, more clean design will be to make the whole thing a class that takes input file name, reads data in initializer and then stores into a instance variable

Ruby create a class from a specification file

I have a specification file Spec.txt like this
title :Test
attribute :fieldOne, String
attribute :fieldTwo, Fixnum
constraint :fieldOne, 'fieldOne != nil'
constraint :fieldTwo, 'fieldTwo >= 0'
from which I need to dynamically create a class with classname Test and the attributes fieldOne and fieldTwo and the constraints of the attributes.
I got so far to read in the file split up the lines and store them into arrays and then dynamically create the class with
dynamic_name = ##TITLE
Object.const_set(dynamic_name, Class.new {
def init *args
...
end
})
But I am not sure if this is the right way to go or even how to create the attributes and the constraints now?
One approach might be:
file=File.open('Spec.txt')
attrs=[]
constraints=[]
all_attrs=""
new_class=""
file.each do |line|
if line =~ /title/
value= line.split[1].tr(':,','')
new_class=value
elsif line =~ /attribute/
value= line.split[1]
attrs << value
elsif line =~ /constraint/
field= line.split[2].tr('\'','')
constraint= line.split[3]
constraints << "\n def #{field}=\n validation here (#{constraint}) \n end\n"
end
end
attrs.map!{|attr| attr+" "}
all_attrs.chomp!(", ")
all_constraints=constraints.join
result=
"Class "+new_class+"\n"+
"attr_reader "+
"#{all_attrs}\n"+
"#{all_constraints}\n"+
"end\n"
printf "#{result}"
run:
$ ruby create_class.rb
Class Test
attr_reader :fieldOne, :fieldTwo
def fieldOne=
validation here (!=)
end
def fieldTwo=
validation here (>=)
end
end
$
Needs some more work on the validations but you get the idea.
To use immediately you could send the output to a ruby file and then include it as code, e.g.
# You would add this after the first section of code, after the 'printf "#{result}"'
File.open("#{new_class}.rb", "w") do |file|
file.write(result)
end
require_relative "#{new_class}.rb"
test_it= Object.const_get(new_class).new
puts "#{test_it}"
Otherwise if creating the ruby file is enough:
ruby create_class.rb > class.rb
As Vaughan suggested.

Problems saving a class method into txt

I am new to ruby and make a lot of mistakes, so I hope people who are experienced in ruby can share a bit of knowledge.
I can't figure out how I can make ruby save the text into a txt file that a method all writes.
class generator
def all
puts "i want to save this text into a txt file"
end
end
new_gen = generator.new
new_gen.all
my_file = File.new("Story.txt", "a+")
my_file.puts("all")
my_file.puts("\n")
my_file.close
I tried everything, but the txt file either has the "all" in it or it's completely blank. Any ideas? I also tried my_file.puts(all) and my_file.puts(new_gen.all).
If you want Generator to do the writing you could pass it an IO object.
class Generator
def initialize(io)
#io = io
end
def all
#io.puts "i want to save this text into a txt file"
end
end
# write to STDOUT
gen = Generator.new(STDOUT)
gen.all
# write to file
File.open("Story.txt", "a+") do |file|
gen = Generator.new(file)
gen.all
end
You method should simply return a string. Puts displays the string, does not return it. So change the class to:
class generator
def all
"i want to save this text into a txt file" # optionally add a return
end
end
new_gen = generator.new
new_gen.all
Then use the last version you tried: my_file.puts(new_gen.all)
Try this:
class Generator
def all
"i want to save this text into a txt file"
end
end
gen = Generator.new
f = File.new("Story.txt", "a+")
f.puts gen.all
f.close

How to get around "Can't convert nil into String (TypeError)" when reading a csv file in Ruby?

I am working on a program that will eventually compare two .csv files and print out any variances between the two. However, at the moment I can't get past a "can't convert nil into String (TypeError)" when reading one of the files.
Here is a sample line from the problematic csv file:
11/13/15,11:31:00,ABCD,4000150097,1321126281700ABCDEF,WR00002440,,,4001,1392,AI,INTERNAL RETURN,INBOUND,,ABCDEF
And here is my code so far:
require 'csv'
class CSVReportCompare
def initialize(filename_data, filename_compare)
puts "setting filename_data=", filename_data
puts "setting compare=", filename_compare
#filename_data = filename_data
#filenam_compare = filename_compare
end
def printData
#data = CSV.read(#filename_data)
puts #data.inspect
end
def printCompareData
#compareData = CSV.read(#filename_compare)
puts #compareData.inspect
end
def compareData
end
end
c1 = CSVReportCompare.new("data.csv", "compare_data.csv")
c1.printData
c1.printCompareData
Anyways, is there a way to get around the error?
You have a typo in your initialize method:
#filenam_compare = filename_compare
#-------^ missing "e"
So you're setting the wrong instance variable. Instance variables are created when they're first used and initialized to nil so later, when you try to access #filename_compare, the instance variable with the correct name is created and has a value of nil.

Resources