I deleted my original post, because I can now simplify my question with a very short program:
## a.rb
require 'tmpdir'
Executing a.rb is fine, no error.
But if I overwrite require as:
## b.rb
module Kernel
alias_method :original_require, :require
def require name
i = 1
begin
original_require name
rescue LoadError => e
puts "Failure #{i}: #{e}"
i = i + 1
retry if i < 3
end
end
end
require 'tmpdir'
Executing b.rb will get the following error:
Failure 1: no such file to load -- Win32API
Failure 2: no such file to load -- Win32API
/Users/chaol/.rvm/rubies/ruby-1.9.1-p376/lib/ruby/1.9.1/tmpdir.rb:19:in `<class:Dir>': uninitialized constant Dir::Win32API (NameError)
from /Users/chaol/.rvm/rubies/ruby-1.9.1-p376/lib/ruby/1.9.1/tmpdir.rb:9:in `<top (required)>'
from /Users/chaol/Documents/ruby-workspace/Test/root/aaa.rb:9:in `require'
from /Users/chaol/Documents/ruby-workspace/Test/root/aaa.rb:9:in `require'
from /Users/chaol/Documents/ruby-workspace/Test/root/aaa.rb:20:in `<main>'
Related
Given the following code:
test.rb
require 'csv'
def meth1
meth2
end
def meth2
begin
iter = CSV.foreach('').each # empty file path, will raise exception.
iter.next
rescue Exception => e
puts e
puts e.backtrace
end
end
meth1
Two questons.
First, why is the backtrace truncated and not showing meth1 or meth2 calls:
No such file or directory # rb_sysopen -
ruby test.rb
/Users/x/.rbenv/versions/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `initialize'
/Users/x/.rbenv/versions/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `open'
/Users/x/.rbenv/versions/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `open'
/Users/x/.rbenv/versions/2.7.2/lib/ruby/2.7.0/csv.rb:510:in `foreach'
test.rb:in `each'
Second, the (truncated) backtrace points to line 641 of CSV (using Ruby v 2.7.2). However line 641 has no initialize() method. Where is this coming from?
.rbenv/versions/2.7.2/lib/ruby/2.7.0/csv.rb
...
begin
f = File.open(filename, mode, **file_opts) ## line 641
rescue ArgumentError => e
raise unless /needs binmode/.match?(e.message) and mode == "r"
mode = "rb"
file_opts = {encoding: Encoding.default_external}.merge(file_opts)
retry
end
If I try another test calling File.open('') directly (instead of through CSV), the resulting backtrace shows everything as expected (including the calls to meth1 and meth2).
Any Ruby gurus out there know what is going on?
I'm not sure I understand; #foreach:510 calls open, #open:641 calls File.open.
In any case, Ruby backtraces have always been a bit wonky, particularly with local top-level files.
The reason you see the initialize is because foreach is a class method of CSV, so there's some behind-the-scenes Ruby shenanigans.
You can use the private method caller_locations, however:
puts e.send(:caller_locations)
which outputs:
test.rb:7:in `meth2'
test.rb:4:in `meth1'
test.rb:20:in `<main>'
This is just the file's top level methods.
You can play games like ruby -d that'll at least get you the line of the script:
Exception `LoadError' at /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/rubygems.rb:1424 - cannot load such file -- rubygems/defaults/operating_system
Exception `LoadError' at /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/rubygems.rb:1432 - cannot load such file -- rubygems/defaults/ruby
Exception `SyntaxError' at /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/forwardable/impl.rb:5 - /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/forwardable/impl.rb:5: syntax error, unexpected end-of-input
Exception `SyntaxError' at /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/forwardable/impl.rb:5 - /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/forwardable/impl.rb:5: syntax error, unexpected end-of-input
Exception `Errno::ENOENT' at /Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/csv.rb:641 - No such file or directory # rb_sysopen -
Exception `Errno::ENOENT' at test.rb:10 - No such file or directory # rb_sysopen -
/Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `initialize'
/Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `open'
/Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/csv.rb:641:in `open'
/Users/dave/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/csv.rb:510:in `foreach'
test.rb:in `each'
Along with other noise.
If you really want to blow your mind, inside the rescue:
puts Thread.current.backtrace
Which outputs:
test.rb:21:in `backtrace'
test.rb:21:in `rescue in meth2'
test.rb:7:in `meth2'
test.rb:4:in `meth1'
test.rb:35:in `<main>'
I have an issue that's appearing only when running rspec, my application loads and works fine locally and in production
rspec seems to think that the gem JWT belongs to my module BACKBONE
An error occurred while loading spec_helper.
Failure/Error: JWT.encode(payload, ENV['APP_SECRET'], 'HS256')
NameError:
uninitialized constant BACKBONE::JWT
# ./lib/jwt.rb:6:in `encode'
# ./lib/backbone/policies.rb:7:in `<class:Policy>'
# ./lib/backbone/policies.rb:5:in `<module:BACKBONE>'
# ./lib/backbone/policies.rb:3:in `<top (required)>'
# ./init.rb:10:in `load'
# ./init.rb:10:in `block in <module:CrewManagement>'
# ./init.rb:10:in `glob'
# ./init.rb:10:in `<module:CrewManagement>'
# ./init.rb:4:in `<top (required)>'
# ./spec/spec_helper.rb:16:in `require'
# ./spec/spec_helper.rb:16:in `<top (required)>'
files are listed in the order they are loaded into memory, it may be useful to note that I have several other applications with the same setup that are not experiencing this issue
/spec/spec_helper.rb
require 'bundler'
require 'simplecov'
SimpleCov.start
Bundler.require :default
Dotenv.load("./.env.#{(ENV['RACK_ENV'] || 'development')}")
require 'rack/test'
require './init'
# ...
/init.rb
module CrewManagement
## ...
Dir.glob('./lib/**/*.rb') { |file| load file }
## ...
end
/lib/jwt.rb
module BACKBONE
def self.encode(payload)
JWT.encode(payload, ENV['APP_SECRET'], 'HS256')
end
def self.decode(token)
JWT.decode(token, ENV['APP_SECRET'], true, algorithm: 'HS256')
end
end
/lib/backbone/policies.rb
module BACKBONE
headers = { 'Authorization' => BACKBONE.encode(app: '...') }
end
Given the following code:
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
Backup::Destination::Test.new
This works as expected, outputting:
Test
Base
However if I split things up like this:
# lib/backup.rb
require_relative 'backup/destination/base'
module Backup; end
# lib/backup/destination/base.rb
require_relative 'test'
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
# lib/backup/destination/test.rb
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
And execute with the following (from irb):
require_relative 'lib/backup'
I get this error:
NameError: uninitialized constant Backup::Destination::Base
from /lib/backup/destination/test.rb:3:in `<module:Destination>'
from /lib/backup/destination/test.rb:2:in `<module:Backup>'
from /lib/backup/destination/test.rb:1:in `<top (required)>'
from /lib/backup/destination/base.rb:1:in `require_relative'
from /lib/backup/destination/base.rb:1:in `<top (required)>'
from /lib/backup.rb:1:in `require_relative'
from /lib/backup.rb:1:in `<top (required)>'
from (irb):1:in `require_relative'
What am I missing?
Note: I couldn't post the above without adding more details. Stupid feature because in this case code is worth a thousand words. (this text allowed the question to be posted)
The problem is that you are requiring test.rb before your Base class is defined. One possible solution is to move your require to the bottom of base.rb.
Another possible solution is to remove your require from base and require both files in the correct order from backup.
Made the following changes to fix the problem:
# lib/backup.rb
require_relative 'backup/destination/base'
require_relative 'backup/destination/test'
module Backup; end
And removed the require_relative statement from lib/backup/destination/base.rb. This fixed the order of the require_relative statements. I mistakenly thought the files were required before anything was executed.
I just started Ruby. This is what I tried:
require'F:\RubymineProjects\practice122013\Coordinatev2'
class XYZCoordinate < Coordinate
attr_accessor :z
##newtotal=0
def initialize(x,y,z)
super(x,y)
#z=z
##newtotal+=1
end
def to_s
return "(##x, ##y, ##z)"
end
def XYZCoordinate.total
return "Number of 3D-coordinates are: ###newtotal"
end
end
p1=XYZCoordinate.new(0,0,0)
puts p1.to_s
p2=XYZCoordinate.new(1,5,5)
puts p2.to_s
puts XYZCoordinate.total
and this is the error I get:
C:\Ruby193\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) F:/RubymineProjects/practice122013/XYZCoordinate.rb
F:/RubymineProjects/practice122013/XYZCoordinate.rb:3:in `<top (required)>': uninitialized constant Coordinate (NameError)
(0, 0)
from -e:1:in `load'
2
from -e:1:in `<main>'
3
Can anyone help me please.
This is what I did to correct the problem.
require"F:\\RubymineProjects\\practice122013\\Coordinatev2"
require"F:\\RubymineProjects\\practice122013\\Coordinate"
I had to specify where those 2 files were.
I can start a pry session of a command line app like this
pry -r ./todo.rb
However, if I want to call the list function
pry -r ./todo.rb list
I'm getting an error message.
Without pry, I call the list function
ruby todo.rb list
This is the error message
/Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/repl_file_loader.rb:16:in `initialize': No such file: /Users/michaeljohnmitchell/Sites/todo/bin/list (RuntimeError)
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/pry_class.rb:161:in `new'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/pry_class.rb:161:in `load_file_through_repl'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/cli.rb:162:in `block in <top (required)>'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/cli.rb:65:in `call'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/cli.rb:65:in `block in parse_options'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/cli.rb:65:in `each'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/lib/pry/cli.rb:65:in `parse_options'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/gems/pry-0.9.10/bin/pry:16:in `<top (required)>'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/bin/pry:19:in `load'
from /Users/michaeljohnmitchell/.rvm/gems/ruby-1.9.2-p290#global/bin/pry:19:in `<main>'
Source Code
TODO_FILE = 'todo.txt'
def read_todo(line)
line.chomp.split(/,/)
end
def write_todo(file,name,created=Time.now,completed='')
file.puts("#{name},#{created},#{completed}")
end
command = ARGV.shift
case command
when 'new'
new_task = ARGV.shift
File.open(TODO_FILE,'a') do |file|
write_todo(file,new_task)
puts "Task added."
end
when 'list'
File.open(TODO_FILE,'r') do |file|
counter = 1
file.readlines.each do |line|
name,created,completed = read_todo(line)
printf("%3d - %s\n",counter,name)
printf(" Created : %s\n",created)
unless completed.nil?
printf(" Completed : %s\n",completed)
end
counter += 1
end
end
when 'done'
task_number = ARGV.shift.to_i
binding.pry
File.open(TODO_FILE,'r') do |file|
File.open("#{TODO_FILE}.new",'w') do |new_file|
counter = 1
file.readlines.each do |line|
name,created,completed = read_todo(line)
if task_number == counter
write_todo(new_file,name,created,Time.now)
puts "Task #{counter} completed"
else
write_todo(new_file,name,created,completed)
end
counter += 1
end
end
end
`mv #{TODO_FILE}.new #{TODO_FILE}`
end
Update
when I try
pry -r ./todo.rb -e list
I'm getting the following error
NameError: undefined local variable or method `list' for main:Object
From pry --help:
-e, --exec A line of code to execute in context before the session starts
So, if your list method is defined on main (if you don't know, it probably is), then you can do this:
pry -r ./todo.rb -e list
Update
Pry doesn't let you pass in arguments for scripts it loads (or at least it isn't documented). But all is not lost, you can call pry from your script. Just drop this at wherever you want to inspect:
require 'pry'; binding.pry
This will spawn a pry session that has access to all the local variables and methods.
I think you can use:
ruby -rpry ./todo.rb -e list