Catching line numbers in ruby exceptions
p e.backtrace
I ran it on an IRB session which has no source and it still gave relevant info.
=> ["(irb):11:in `foo'",
"(irb):17:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52"]
If you want a nicely parsed backtrace, the following regex might be handy:
p x.backtrace.map{ |x|
x.match(/^(.+?):(\d+)(|:in `(.+)')$/);
[$1,$2,$4]
}
[
["(irb)", "11", "foo"],
["(irb)", "48", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", nil]
]
( Regex /should/ be safe against weird characters in function names or directories/filenames )
( If you're wondering where foo camefrom, i made a def to grab the exception out :
>>def foo
>> thisFunctionDoesNotExist
>> rescue Exception => e
>> return e
>>end
>>x = foo
>>x.backtrace
Is it possible to get the line number that threw an error?
Just take the backtrace:
begin
. . .
# error occurs here
. . .
rescue => error
puts "Error: " + error.message
puts error.backtrace
end
To get only the line number - just parse it out of the backtrace via a regex.
More information can be found here: Catching line numbers in ruby exceptions
Get the error line in a Ruby Opal code
Opal has source map support, to facilitate this kind of source level of debugging. I will not go into details about sourcemaps, but HTML5Rocks has a great article that covers the topic in depth.
Here is the minimal boilerplate to set that up with Opal:
Let index.rb
be our source file:
class Test
def initialize
end
def crash
print x
end
end
Test.new.crash
Since you would rather not use a lot of extraneous utilties, let us directly use the Opal API. Create a file builder.rb
which will compile the file above:
require 'opal'
Opal::Processor.source_map_enabled = true
Opal.append_path "."
builder = Opal::Builder.new.build('index')
# Write the output file containing a referece to sourcemap
# which we generate below : this will help the browser locate the
# sourcemap. Note that we are generating sourcemap for only code and not
# the entire Opal corelib.
#
File.binwrite "build.js", "#{builder.to_s}\n//# sourceMappingURL=build.js.map"
File.binwrite "build.js.map", builder.source_map.to_s
File.binwrite "opal_lib.js", Opal::Builder.build('opal_lib')
Also create an opal_lib.rb
file containing only:
require 'opal'
Finally create an index.html
which will allow us to run the script in browser.
<!DOCTYPE html>
<html>
<head>
<script src="opal_lib.js"></script>
<script src="build.js"></script>
</head>
<body>
</body>
</html>
Now to actually compile your file, run:
ruby builder.rb
This will generate compiled javascript files opal_lib.js
and build.js
which are referenced by our index.html
file. Now just open index.html
in your browser. You will get a full call-stack and source view:
The line numbers of the source file are available:
As an alternative to using the browser, you can also use Node.js for the same purpose. This requires you have Node.js
and npm
installed. You will also need to install npm module source-map-support
npm install source-map-support
Now you can open the node repl and enter the following:
require('source-map-support').install();
require('./opal_lib');
require('./build');
You will get a stack trace with correct source line numbers :
/home/gaurav/Workspace/opal-playground/opal_lib.js:4436
Error.captureStackTrace(err);
^
NoMethodError: undefined method `x' for #<Test:0x102>
at OpalClass.$new (/home/gaurav/Workspace/opal-playground/opal_lib.js:4436:15)
at OpalClass.$exception (/home/gaurav/Workspace/opal-playground/opal_lib.js:4454:31)
at $Test.$raise (/home/gaurav/Workspace/opal-playground/opal_lib.js:4204:31)
at $Test.Opal.defn.TMP_1 (/home/gaurav/Workspace/opal-playground/opal_lib.js:3032:19)
at $Test.method_missing_stub [as $x] (/home/gaurav/Workspace/opal-playground/opal_lib.js:886:35)
at $Test.$crash (/home/gaurav/Workspace/opal-playground/index.rb:8:11)
at /home/gaurav/Workspace/opal-playground/index.rb:13:10
at Object.<anonymous> (/home/gaurav/Workspace/opal-playground/index.rb:13:10)
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
I recommend that you use bundler for gem management. Here is the Gemfile
for fetching Opal master:
source 'http://production.cf.rubygems.org'
gem 'opal', github: 'opal/opal'
To compile you will have to run:
bundle install
bundle exec ruby builder.rb
Sprockets integration / rack integration that others have mentioned use the same API underneath, abstracting away the plumbing.
Update:
Since we have the correct line numbers in stack, it is fairly to programatically parse the stack and extract this line number into a variable:
require('./opal_lib');
require('source-map-support').install();
var $e = null;
try {
require('./build');
} catch (e) {
$e = e;
}
var lines = e.split('\n').map(function(line){ return line.match(/^.*\((\S+):(\d+):(\d+)\)/) })
var first_source_line;
for (var i = 0; i < lines.length ; i++) {
var match = lines[i];
if (match == null) continue;
if (match[1].match(/index.rb$/) {
first_source_line = match;
break;
}
}
var line_number;
if (first_source_line) line_number = first_source_line[2] // ==> 8
And of course you can do it ruby as well (but if you are running this in browser you will have to include source-map-support here as well):
class Test
def initialize
end
def crash
print x
end
end
line_num = nil
begin
Test.new.crash
rescue => e
if line = e.backtrace.map{|line| line.match(/^.*\((\S+):(\d+):(\d+)\)/) }.compact.find{|match| match[1] =~ /index.rb$/ }
line_num = line[2]
end
end
puts "line_num => #{line_num}" # ==> 8
How to do exception handling for undefined local variables?
You can write
d = a + e rescue nil
which would catch the exception and assigns d = nil
.
Or
begin
d = a + e
rescue
end
Which only catches the exception and does nothing else because of the empty rescue block.
But I would consider this a bad practice. Because apart from in the console you should never run into this issue in real-life. This error is trivial and should be noticed and fixed before the app gets into production.
Ruby - Random number in range with exceptions
shuffle
will permute the entire array, which is potentially slow for large arrays. sample
is a much faster operation
(1..99999).to_a.sample(3)
For benchmarking purposes:
> require 'benchmark'
> arr = (0..99999).to_a; 0
> Benchmark.realtime { 10_000.times { arr.sample(3) } }
=> 0.002874
> Benchmark.realtime { 10_000.times { arr.shuffle[0,3] } }
=> 18.107669
Related Topics
Best Way to Generate Order Numbers for an Online Store
How to Update Gems on Production Server
Namespacing Thor Commands in a Standalone Ruby Executable
Run Ruby Script in the Background
What's the Variable Scope Within 'Class_Eval' String
Generate a Migration File from Schema.Rb
How to Use Ruby Minitest::Spec with Rails for API Integration Tests
How to Replace Multiple Newlines in a Row with One Newline Using Ruby
Are Bundle Exec and Require 'Bundler/Setup' Equivalent
Ruby Singleton Methods with (Class_Eval, Define_Method) VS (Instance_Eval, Define_Method)
Ruby Methods That Either Yield or Return Enumerator
Run Two Different Rails Application on One Dedicated Server
Best Way to Cache a Response in Sinatra
Creating Categories on Jekyll Driven Site