removing escape characters when parsing a string from file - ruby

I'm trying to read from a file a string that interpolates variables which are defined in the code, and substitutes the string value of those variables.
The text file:
my name is #{name}
the code:
file = File.open("tesst.txt", "r")
arr = []
name = "CDJ"
file.each_line.with_index { |line, index|
puts line
}
file.close
Desired output:
My name is CDJ
Actual output:
My name is #{name}
When output is line.inspect:
"My name is \#{name}"
Can anyone help me format this string correctly so it reads #{name} as a variable instead of a string with the inserted escape character?

file= 'tesst.txt'
name = 'CDJ'
File.readlines(file).each do |line|
eval("puts \"#{line}\"")
end

Consider this:
class Template
def initialize(source: DATA)
#text = source.read.chomp.split("\n")
end
def render(locals: {})
locals.each do |key, value|
instance_variable_set("##{key}", value)
self.class.send(:attr_reader, key)
end
#text
.map { |line| eval("\"#{line}\"") }
.join("\n")
end
end
puts Template
.new(source: File.open('./tesst.txt', 'r'))
.render(locals: {name: 'George', age: 29})
__END__
my name is #{name}
I am #{age} years old
Notes:
Variables can be provided through the hash.
File can be switched with DATA to read contents from bottom of file after __END__

Related

Fixing width of text from file.write

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 " ".

Refactoring my code so that file closes automatically once loaded, how does the syntax work?

My program loads a list from a file, and I'm trying to change the method so that it closes automatically.
I've looked at the Ruby documentation, the broad stackoverflow answer, and this guy's website, but the syntax is always different and doesn't mean much to me yet.
My original load:
def load_students(filename = "students.csv")
if filename == nil
filename = "students.csv"
elsif filename == ''
filename = "students.csv"
end
file = File.open(filename, "r")
file.readlines.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
file.close
puts "List loaded from #{filename}."
end
My attempt to close automatically:
def load_students(filename = "students.csv")
if filename == nil
filename = "students.csv"
elsif filename == ''
filename = "students.csv"
end
open(filename, "r", &block)
line.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
puts "List loaded from #{filename}."
end
I'm looking for the same result, but without having to manually close the file.
I don't think it'll be much different, so how does the syntax work for automatically closing with blocks?
File.open(filename, 'r') do |file|
file.readlines.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
end
I’d refactor the whole code:
def load_students(filename = "students.csv")
filename = "students.csv" if filename.to_s.empty?
File.open(filename, "r") do |file|
file.readlines.each do |line|
add_students(line.chomp.split(",").first)
end
end
puts "List loaded from #{filename}."
end
Or, even better, as suggested by Kimmo Lehto in comments:
def load_students(filename = "students.csv")
filename = "students.csv" if filename.to_s.empty?
File.foreach(filename) do |line|
add_students(line.chomp.split(",").first)
end
puts "List loaded from #{filename}."
end

Ruby script to search a string and delete entire raw?

I have a text file and I need to search for a string, if that string is found I want to delete entire raw containing that string and add a new raw. As for now I just try to find a string and delete entire raw. And the string I search should be given as an argument.
Code I have as far goes here:
arg1, arg2, arg3 = ARGV
read_file = File.new('conf.txt', "r").read
write_file = File.new('conf.txt', "w")
read_file.each_line do |line|
write_file.write(line) unless line.include? arg1 arg2 arg3
end
But the output I get is as follows:
C:/Ruby/tests/replace3.rb:9:in `block in <main>': undefined method `arg2' for ma
in:Object (NoMethodError)
from C:/Ruby/tests/replace3.rb:8:in `each_line'
from C:/Ruby/tests/replace3.rb:8:in `<main>'
Another approach I try is the following:
file_names = ['config.txt']
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/regex_string/, "new_string")
File.open(file_name, "w") {|file| file.puts new_contents }
end
But the problem here is that the regex string finds only certan string and doesn't take all that raw. Maybe you have any idea how I can optimize this code for my problem.
So include? expects to receive a single argument:
line.include? single_variable
Take a look here:
http://apidock.com/ruby/Array/include%3F
EDIT:
You could try something like this:
unwanted_array = ['unwanted', 'error', 'string']
read_file.each_line do |line|
write_file.write(line) unless unwanted_array.map { |val| line.include? val }.reduce(:|)
end
Unwanted array could also be built up by doing:
unwanted_array = [arg1, arg2, arg3]

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.

Parse and substitute markup in Ruby

I want to parse a simple string like:
"My name is **NAME**" to "My name is <strong>NAME</strong\>"
Note:
Cannot use any external gems, even though markdown gem might have done the job.
If I understood you correctly it should be quite simple:
text = "My name is **NAME**"
=> "My name is **NAME**"
text = text.gsub(([a-zA-Z\s]*)(\*\*)([a-zA-Z\s]*)(\*\*)/,"\\1<strong>\\3</strong>")
=> "My name is <strong>NAME</strong>"
I've tested it in irb with this command text.gsub(([a-zA-Z\s]*)(\*\*)([a-zA-Z\s]*)(\*\*)/,"\\1<strong>\\3</strong>")
UPDATED
Consider this, if you wish to handle some more cases:
class SurroundMarkup
def initialize(markup_map: {})
#markup_map = markup_map
end
def format(text)
text.tap do |formatted_text|
#markup_map.each do |markup, tag|
formatted_text.gsub!(/#{markup}(?<text>.*?)#{markup}/) do |m|
start_tag(tag) + $~[:text] + stop_tag(tag)
end
end
end
end
private
def start_tag(tag)
"<#{tag}>"
end
def stop_tag(tag)
"</#{tag}>"
end
end
And you can use as follows:
markup_map = {
/\*\*/ => "strong",
/\*/ => "i",
}
s = SurroundMarkup.new(markup_map: markup_map)
s.format("My name is **NAME**") #=> "My name is <strong>NAME</strong>"
s.format("*Ruby* is cool") #=> "<i>Ruby</i> is cool"

Resources