Getting Current Line of Code in Ruby

Getting current line of code in Ruby

You can use __LINE__ variable. See this https://stackoverflow.com/a/2496240/100466 answer also.

How to get current file and line number in Ruby?

You'll have to use caller

def mylog(str)
caller_line = caller.first.split(":")[1]
puts "#{__FILE__} : #{caller_line} : #{str}"
end

You'll probably want to know the file from which mylog was called too...

def mylog(str)
caller_infos = caller.first.split(":")
puts "#{caller_infos[0]} : #{caller_infos[1]} : #{str}"
end

How to find out from which line number the method was called in Ruby?

You can use caller_locations which has been added recently. It returns an array of Location objects. See http://ruby-doc.org/core-2.2.3/Thread/Backtrace/Location.html for details.

No need to parse the return of caller. Hooray.

To add on to this caller_locations.first or caller_locations(0) gets the last method location, increment the parameter to pull specific steps.

Ruby CSV - get current line/row number

Because of changes in CSV in current Rubies, we need to make some changes. See farther down in the answer for the original solution with Ruby prior to 2.6. and the use of with_index which continues to work regardless of the version.

For 2.6+ this'll work:

require 'csv'

puts RUBY_VERSION

csv_file = CSV.open('test.csv')
csv_file.each do |csv_row|
puts '%i %s' % [csv_file.lineno, csv_row]
end
csv_file.close

If I read:

Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00
1996,Jeep,Grand Cherokee,"MUST SELL!\nair, moon roof, loaded",4799.00

The code results in this output:

2.6.3
1 ["Year", "Make", "Model", "Description", "Price"]
2 ["1997", "Ford", "E350", "ac, abs, moon", "3000.00"]
3 ["1999", "Chevy", "Venture \"Extended Edition\"", "", "4900.00"]
4 ["1999", "Chevy", "Venture \"Extended Edition, Very Large\"", "", "5000.00"]
5 ["1996", "Jeep", "Grand Cherokee", "MUST SELL!\\nair, moon roof, loaded", "4799.00"]

The change is because we have to get access to the current file handle. Previously we could use the global $., which always had a possibility of failure because globals can get stomped on by other sections of called code. If we have the handle of the file being opened, then we can use lineno without that concern.


$.

Ruby prior to 2.6 would let us do this:

Ruby has a magic variable $. which is the line number of the current file being read:

require 'csv'

CSV.foreach('test.csv') do |csv|
puts $.
end

with the code above, I get:

1
2
3
4
5

$INPUT_LINE_NUMBER

$. is used all the time in Perl. In Ruby, it's recommended we use it the following way to avoid the "magical" side of it:

require 'english'

puts $INPUT_LINE_NUMBER

If it's necessary to deal with embedded line-ends in fields, it's easily handled by a minor modification. Assuming a CSV file "test.csv" which contains a line with an embedded new-line:

Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00
1999,Chevy,"Venture ""Extended Edition, Very Large""","",5000.00

with_index

Using Enumerator's with_index(1) makes it easy to keep track of the number of times CSV yields to the block, effectively simulating using $. but honoring CSV's work when reading the extra lines necessary to deal with the line-ends:

require 'csv'

CSV.foreach('test.csv', headers: true).with_index(1) do |row, ln|
puts '%-3d %-5s %-26s %s' % [ln, *row.values_at('Make', 'Model', 'Description')]
end

Which, when run, outputs:

$ ruby test.rb
1 Ford E350 ac, abs, moon
2 Chevy Venture "Extended Edition"
3 Jeep Grand Cherokee MUST SELL!
air, moon roof, loaded
4 Chevy Venture "Extended Edition, Very Large"

Ruby - Read file and print line number

Ruby, like Perl, has the special variable $. which contains the line number of a file.

File.open("file.txt").each do |line|
puts line, $.
end

Prints:

#Hello
1
#My name is John Smith
2
#How are you?
3

Strip the \n from line if you want the number on the same line:

File.open("file.txt").each do |line|
puts "#{line.rstrip} #{$.}"
end

#Hello 1
#My name is John Smith 2
#How are you? 3

As stated in comments, rather than use File.open you can use File.foreach with the benefit of autoclose at the end of the block:

File.foreach('file.txt') do |line|
puts line, $.
end
# same output...

Ruby script to check how many lines of code are in an application

Generally I wouldn't - I'd use a shell utility. Seriously.

Also, do you want to include comments? Etc. etc.

There's a very good answer to this, not using Ruby here: How to count all the lines of code in a directory recursively?

If I were approaching this with the intent of actually doing it I'd do this:

Dir['**/*.*'].each do |fname|
puts "#{fname} #{linecount(fname)}"
end

Linecount would be something like

def linecount(fname)
File.open(fname, "r") do |f|
return f.readlines.length
end
end

This is a nasty hack by the way, because it reads the whole file into memory just to see how long the array is, you might want to get each line and count them as you read through.

You could get rid of blanks or lines that are only comments too by only incrementing the counter.

Something like

lines = f.readlines
lines.delete_if { |l| l =~ /^[[:space:]]*#|^$/ }
return lines.length

I haven't tested that regexp by the way.

As an exercise for the reader - work out what the 'if' statement is you need to put on the end of the puts to stop it barfing on directories :D

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:

Opal error stack screenshot

The line numbers of the source file are available:

Opal error stack trace


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


Related Topics



Leave a reply



Submit