for a number of different cases, Ruby exceptions in a HAML file report an incorrect line number #485

Closed
charleseff opened this Issue Jan 24, 2012 · 3 comments

Comments

Projects
None yet
4 participants

Using: Rails 3.1.0, Ruby 1.9.3

To reproduce, run this:

rails new haml_exception_test
cd haml_exception_test
echo "gem 'haml'" >> Gemfile
echo "gem 'hpricot'" >> Gemfile
echo "gem 'ruby_parser'" >> Gemfile
bundle
rails generate scaffold block
rake db:migrate
rm -f app/views/blocks/index.html.erb
cat > app/views/blocks/index.html.haml <<END
:ruby
  on_line_2
END
rails server

, then go to http://localhost:3000/blocks, which outputs:

NameError in Blocks#index

Showing /Users/change/Development-test/haml_exception_test/app/views/blocks/index.html.haml where line #1 raised:

undefined local variable or method `on_line_2' for #<#<Class:0x00000103afe868>:0x00000103b081b0>

Notice that the exception reports line #1 even though the exception was on line 2.

Member

mattwildig commented Nov 20, 2012

This is caused by the :ruby filter, and I don’t think it’s easy to fix.

When the source Haml is compiled, the lines in the generated Ruby are made to correspond to the lines in the original Haml as much as possible, so that if an error occurs the line reported as the cause of the error will match the location in the Haml, even though it is really referring to the line in the generated Ruby.

The problem with the :ruby filter is that the Ruby code in the filter needs to be ‘wrapped’ with extra code before and after before it is added to the generated code, so there are two extra lines of code to fit in.

For example if the Haml is something like:

:ruby
  here_is
  some_of(my)
  ruby.code

then the generated Ruby code looks something like:

added_first_line
here_is
some_of(my)
ruby.code
added_last_line

If this was added like this, then added_first_line would be a the same line number as :ruby, and all the Ruby from the Haml template would be at the correct place in the generated Ruby. However all the remaining lines in the template would be at the wrong position in the generated code since added_last_line would push them all down one.

What actually gets added to the generated code looks like this, using a ; to join added_first_line with the first line from the filter (the code is in haml/filters.rb):

added_first_line; here_is
some_of(my)
ruby.code
added_last_line

Now it all fits into the number of lines available, so the rest of the template appears at the right place, but the code within the :ruby filter itself appears at one line less than it should.

The same trick of joining lines using a semicolon can’t be used on the last line, because the last line in the filter could be a comment (or end with one):

added_first_line
here_is
some_of(my)
ruby.code #a comment here;added_last_line

This would leave all the generated lines in the right place, but breaks everything by commenting out the filter tidy up code.

If Ruby had something like C’s #line directive then this would be fairly simple to fix. Ruby does let you specify the line as a parameter to eval, so it might be possible to get something working by treating the code as a string and passing it to eval.

If you’re using :ruby a lot and it’s important to get the line numbers right you might want to look at implementing a custom filter using eval, but I don’t think it’s worth the complexity and performance costs to change the current implementation.

Member

mattwildig commented Nov 20, 2012

As an aside, you don’t need to create a Rails app to demonstrate this:

$ haml
:ruby
  on_line_2
^D
Exception on line 1: undefined local variable or method `on_line_2' for #<Object:0x00000100b83cc8>
  Use --trace for backtrace.

(Thats CTRL-D/end of file, or you could just create the file).

norman closed this Feb 26, 2013

gam3 commented Aug 22, 2015

There is a ruby line directive pull request at ruby/ruby#911.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment