ruby filename path to string error - ruby

This is my code:
file = File.open('result.txt', 'w+').read
path = Dir[ENV['HOME'] + '/Desktop/Test/*.txt']
file.puts "this is a #{path} test: "
It comes up with an error:
C:/Users/User/RubymineProjects/Comparison/test.rb:5:in `<top (required)>': private method `puts' called for "":String (NoMethodError)
from -e:1:in `load'
from -e:1:in `<main>'
my intended result is:
this is a C:/Users/User/Desktop/Test/new_1.txt test:
i've tried this:
puts "this is a #{path[0]} test: "
which achieves what i want but as soon as i do file.puts it comes up with the same error again.

When you do file.puts here, you're sending a method #puts to a string object that's now stored in the variable file. This is because File#read method returns a string. So, on the first line, file takes the contents of the result.txt and then stores it in the variable file. Then you're callingputson that string. AndString#puts` is a private method, so you can't use it the way you used it in the code above.
If your intention is to write the result this is a C:/Users/User/Desktop/Test/new_1.txt test:, then you need to use the File.open method this way:
File.open('result.txt', 'w+') do |file|
path = Dir[ENV['HOME'] + '/Desktop/Test/*.txt']
file.puts "this is a #{path} test: "
# whatever else needs to be written goes here
end
Or, if you prefer the imperative style instead of the block style:
file = File.new('result.txt', 'w+')
# If you prefer `open`, that works too!
# file = File.open('result.txt', 'w+')
path = Dir[ENV['HOME'] + '/Desktop/Test/*.txt']
file.puts "this is a #{path} test: "
# ensure this is closed, or you'll have memory issues if you do this often
file.close

Related

undefined method `+' for nil:NilClass (NoMethodError) ruby

trying to build a pptx to scorm converter. I get an error NoMethodError Undefined method `+' for NilClass. I guess it may be due to a defined method. any idea on how i can remove this error ?
dir = ARGV.shift
dest = ARGV.shift
pptx = dir + "/presentation.pptx"
lis = []`enter code here`
STDERR.puts "Copy template => #{dest}"
FileUtils.cp_r "template", dest
Dir["#{dir}/*.PNG"].each do |file|
STDERR.puts "Copy #{file} => #{dest}/img"
FileUtils.cp file, "#{dest}/img/"
STDERR.puts "Creating thumb #{file} => #{dest}/img/thumb"
name = file.split(/\//).last
system "/usr/bin/convert", "-scale", "200x", file, "#{dest}/img/thumb/#{name}"
lis.push name
end
ordered = lis.sort_by { |x| x[/\d+/].to_i }
DIR is nil
If you debug your code as follows:
puts dir.nil? # true
So, in order to run this code you must provide the ruby shell with 2 arguments, as follows:
ruby test.rb DIRECTORY_NAME DESTINATION_NAME

Create directory with ARGV (ruby)

I created this program to create a folder in the program directory if there is an ARGV.
def check_if_user_gave_input
abort("mkdir: missing input") if ARGV.empty?
end
def get_folder_name
return folder_name = ARGV.first
end
def create_folder(name)
Dir.mkdir(name)
end
def perform
folder_name = get_folder_name
create_folder(folder_name)
end
perform
So, if I run this program in my terminal everything is OK. But, if I try it in my terminal and don't write anything after $ ruby app.rb I get a nice error message like this and I don't see the string "mkdir: missing input"
Traceback (most recent call last):
3: from app.rb:18:in `<main>'
2: from app.rb:15:in `perform'
1: from app.rb:10:in `create_folder'
app.rb:10:in `mkdir': no implicit conversion of nil into String (TypeError)
How to fix this? Thank you.
Just add check_if_user_gave_input method in perform
def check_if_user_gave_input
abort("mkdir: missing input") if ARGV.empty?
end
def get_folder_name
ARGV.first
end
def create_folder(name)
Dir.mkdir(name)
end
def perform
check_if_user_gave_input
folder_name = get_folder_name
create_folder(folder_name)
end
perform

Facing "no implicit conversion of nil into String" error inside eval statement

Exercise :Creating Classes from CSV Started
Read a csv format file and construct a new class with the name of the file dynamically. So if the csv is persons.csv, the ruby class should be person, if it's places.csv, the ruby class should be places
Also create methods for reading and displaying each value in "csv" file and values in first row of csv file will act as name of the function.
Construct an array of objects and associate each object with the row of a csv file.
For example the content of the csv file could be
name,age,city
gaurav,23,karnal
vilok,23,hissar
My expected output is correct. I am able to print the method name as first row of CSV and remaining lines of code has been evaluated and printed as strings on console.
But along with expected output I'm facing below error.
classes_from_CSV.rb:21:in `eval': no implicit conversion of nil into String (TypeError)
from classes_from_CSV.rb:21:in `block (2 levels) in new_method'
from classes_from_CSV.rb:25:in `call'
from classes_from_CSV.rb:34:in `<main>'
I just want to know how should I remove this error and what is the correct way of evaluating string type user input inside dynamic methods.
require 'csv'
class CsvManipulator
def first_line_csv
CSV.open("Input.csv", 'r') { |csv| csv.first }
end
def remaining_line_csv
text = File.readlines("Input.csv")[1..-1].join()
csv = CSV.parse(text, headers: true)
end
end
class MethodCreator < CsvManipulator
def initialize(class_name)
#klass = Class.new
Object.const_set(class_name, #klass)
end
def new_method(method_name, code_str)
#klass.class_eval do
puts define_method(method_name) { eval(puts"#{code_str}") }
end
end
def call(method_name)
#klass.new.send(method_name)
end
end
class_name = "Input"
obj = MethodCreator.new(class_name)
method_name = (obj.first_line_csv).join(', ')
code_str = obj.remaining_line_csv
obj.new_method(method_name, code_str)
puts obj.call(method_name)
Expected output :
name, age, city
gaurav,23,karnal
vilok,23,hissar
Actual output :
name, age, city
gaurav,23,karnal
vilok,23,hissar
classes_from_CSV.rb:21:in `eval': no implicit conversion of nil into String (TypeError)
from classes_from_CSV.rb:21:in `block (2 levels) in new_method
from classes_from_CSV.rb:25:in `call'
from classes_from_CSV.rb:34:in `<main>'
You are only calling eval in one place, so the error is not hard to find:
eval(puts"#{code_str}")
puts returns nil, so you are effectively calling
eval(nil)
However, eval must be called with a string.

How to automatically call classes in Ruby script from a specific directory

I have a directory /lib where I store *.rb files. Each one of them contains a class with a single class method remove_user().
How can I make the main script automatically go over those files and call the same method on all of them? I want to just drop-in files in that directory in the future without modifying the main script in any way.
I do know how to require all the files from a directory based on "Best way to require all files from a directory in ruby?", but I'm not very sure how to invoke the classes "in a loop" from here.
Update
I've tried a the code suggested in "How do I create automatically a instance of every class in a directory?"
files = Dir.glob("lib/*.rb")
def load_modules(class_files)
puts class_files
before = ObjectSpace.each_object(Class).to_a
class_files.each {|file| require_relative file }
after = ObjectSpace.each_object(Class).to_a
(after - before).each {|klass| klass.new.delete_user('myemail#mail.com', 'Audit', 'Test')}
load_modules(files)
end
It produces an error:
/Users/apinchuk/RubymineProjects/autoaudit/init.rb:16:in `new': can't create instance of singleton class (TypeError)
from /Users/RubymineProjects/autoaudit/init.rb:16:in `block in load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `each'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:20:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'
And there is nothing I could find about this error.
The create_uat_robot.rb has a structure like this:
class CreateUatRobot
def self.delete_user(email, first_name, last_name)
...
end
end
The name of the file is create_uat_robot.rb
Trying #moveson suggestion as follows:
files = Dir.glob("lib/*.rb")
files.each {|file| require_relative file }
klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize}
klasses.each { |klass| klass.delete_user(arguments) }
worked for me.
First you need to create an array of class names:
>> klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize }
Then you can call your method on each of them in turn:
>> klasses.each { |klass| klass.remove_user }
If you are not using Rails, you can require ActiveSupport's String extension methods (require 'active_support/core_ext/string'), which will give you the camelize and constantize methods.

Ruby Class, I suspect I am doing instance markers wrong

I want this class to initialize to receive a message to be saved and enter a filename for it. Am I drawing up an error because Ruby only wants values to be instantiated in the init method? Be gentle, I'm new to this. Traceback pasted below.
class DobbsyKretts
idea = 'arbitaryvalue'
def initialize
#Receive idea
puts "Enter an idea, a secret or anything else you want to encrypt. Hit enter to stop typing and save the file"
#idea.gets.reverse.upcase
#Filename and saving - to encrypt the file.
puts "Enter the file name you'd like to have this saved as. Type PLAN at the beginning for plans and REM for reminders"
#file_name.gets.strip
File::open("DobbsyKrett-"+ file_name + ".txt", "w") do |f|
f>>#idea
end
end
end
something = DobbsyKretts.new
Traceback:
testy.rb:11:in `initialize': private method `gets' called for nil:NilClass (NoMethodError)
from testy.rb:21:in `new'
from testy.rb:21:in `<main>'
Enter an idea, a secret or anything else you want to encrypt. Hit enter to stop typing and save the file
You are calling getson #idea before having assigned a value - that's one of the reasons why you get the error. Also, gets should not be called on the instance variable here. Try it like this:
class DobbsyKretts
def initialize
#Receive idea
puts "Enter an idea, a secret or anything else you want to encrypt. Hit enter to stop typing and save the file"
(#idea = gets).reverse.upcase
#Filename and saving - to encrypt the file.
puts "Enter the file name you'd like to have this saved as. Type PLAN at the beginning for plans and REM for reminders"
#file_name = gets.strip
File::open("DobbsyKrett-"+ #file_name + ".txt", "w") do |f|
f << #idea
end
end
end
something = DobbsyKretts.new
This works as you expected, but I just would like to remind you that it is a very bad idea to do something like this in the constructor. You should rather use a dedicated method for generating files and/or asking the user for input.
gets is either a Kernel#gets or IO#gets (I will omit ARGF#gets for brevity), #idea in your case is not an IO object (by default any instance variable is set to nil), and calling Kernel#gets with explicit receiver is prohibited. So correct code is #idea = gets.

Resources