What are some tips for debugging Ruby erb files? - ruby

Currently, when I get an error on an erb template (for use with HTTPServer/cgi) I do the following:
If it's a small change, revert, save and retest.
For a large change or new file, delete or comment 1/2 the code, and retest. Perform a binary search until I've deleted/found the broken code.
The call stack doesn't seem to correspond to anything in my .rhtml file.
(erb):6:in `block in <main>'
/opt/local/lib/ruby/1.9.1/erb.rb:753:in `eval'
/opt/local/lib/ruby/1.9.1/erb.rb:753:in `result'
bin/correct.rb:45:in `block in <main>'
/opt/local/lib/ruby/1.9.1/webrick/httpservlet/prochandler.rb:26:in `call'

Not sure if this is applicable to this problem but maybe it will help someone. I'm using rails 5 and if you put
<% debugger %>
in your html.erb file it will pause the terminal window that your rails server is running in. From there you can debug whatever params or variables your html.erb file has.

As Daniel said, most times the error message will help you to quickly find where the error is.
There are indeed some occasions on which it doesn't.
The dumber, faster way to do that binary search is to just insert a wrong line, like
<%= the_error_is_after_this_line %>
and then move the line until you find the exact row.
I'm not one of those bright programmers who can write tons of lines per time which just work; i usually develop by small steps and reload the page on browser each time.
That said, the better way to avoid hard to debug views (or methods, or whatever) is to write simple, short ones. My rule of thumb is that I must be able to read the whole view (or method) in the editor window, unless it's just plain html.
Always use helpers and partial views. Can you count more than two () or [] in a line of your erb view? If yes, use a helper.
Can you count more than two or three blocks in your view? Use some partials.

In general Erb errors tell you where they occurred. For instance here your error is on line 6 of the erb file. You have omitted the error message that came with the backtrace, but that usually tells you what kind of error to look for. For instance, in my simple test here:
NameError: undefined local variable or method `asdf' for main:Object
from (erb):7
from (irb):6
It is clear enough what is going wrong and where.
Can you post more information about your error and the erb that caused it?

On Rails 5 you can find the gem 'byebug' by default at Gemfile:
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
Then you can use byebug on your controller, put it wherever you want and many times you need, it functions like a 'breakpoint' and finally run your server $ rails server
class UsersController < ApplicationController
byebug
end
At command line write help for options, normally use letter 'c' to continue to the next breakpoint, or the letter 'n' to advance step by step and ctrl+d to exit.
(byebug) help
break -- Sets breakpoints in the source code
catch -- Handles exception catchpoints
condition -- Sets conditions on breakpoints
continue -- Runs until program ends, hits a breakpoint or reaches a line
debug -- Spawns a subdebugger
delete -- Deletes breakpoints
disable -- Disables breakpoints or displays
display -- Evaluates expressions every time the debugger stops
down -- Moves to a lower frame in the stack trace
edit -- Edits source files
enable -- Enables breakpoints or displays
finish -- Runs the program until frame returns
frame -- Moves to a frame in the call stack
help -- Helps you using byebug
history -- Shows byebug's history of commands
info -- Shows several informations about the program being debugged
interrupt -- Interrupts the program
irb -- Starts an IRB session
kill -- Sends a signal to the current process
list -- Lists lines of source code
method -- Shows methods of an object, class or module
next -- Runs one or more lines of code
pry -- Starts a Pry session
quit -- Exits byebug
restart -- Restarts the debugged program
save -- Saves current byebug session to a file
set -- Modifies byebug settings
show -- Shows byebug settings
skip -- Runs until the next breakpoint as long as it is different from the current one
source -- Restores a previously saved byebug session
step -- Steps into blocks or methods one or more times
thread -- Commands to manipulate threads
tracevar -- Enables tracing of a global variable
undisplay -- Stops displaying all or some expressions when program stops
untracevar -- Stops tracing a global variable
up -- Moves to a higher frame in the stack trace
var -- Shows variables and its values
where -- Displays the backtrace
(byebug)
Other option to display the debug(params):
In the app/views/layouts/application.html.erb file under render footer and above put the next:
<%= debug(params) if Rails.env.development? %>
Finally I share this options as I know as a newbie in Ruby on Rails. Hope this helps.
Source of some help: https://rubyplus.com/articles/3631-Debugging-using-ByeBug-Gem-in-Rails-5

Related

What could be causing ruby NoMethodError backtrace to be so slow?

I have a pretty large ruby (non-rails) application that I'm developing. It's reasonably fast considering how large and complex it is (go ruby!), but sometimes I fat finger a method name and get the NoMethodError.
And usually when this happens, the application hangs for like 20 to 30 seconds to just print out the backtrace.
Specifically, if I do something like this:
puts "about to crash!"
Array.new().inspekt # NoMethodError here
I see the "about to crash!" right away, and then 20s or so nothing seems to happen before I finally get the NoMethodError and backtrace.
At first I thought it might be the "did you mean" gem, so I turned that off with --disable-did_you_mean on the command line, and that turned off the "did you mean" suggestions, but nothing sped up the backtrace.
What's interesting is that this is only for NoMethodError.
If I cause some other exception, such as:
puts "about to crash!"
a = 3/0
Then I see the backtrace immediately.
And to make things even weirder, if I interrupt the process right after the "about to crash!" (such as with a ctrl-c on unix) then I immediately get the NoMethodError and it's backtrace. So it has the information - but ruby is stuck on trying to clean something up perhaps, something that only gets cleaned up on NoMethodError?
Info: ruby 2.7.0
OS: CentOS Linux release 7.5.1804
UPDATE - to responses so far:
Everyone seems to be concerned about the backtrace and profiling the ruby code.
Except the slowdown is NOT happening there. There are NO LINES OF RUBY CODE that are executed during the slowdown. All of the lines prior to this, "in the backtrace" are already executed and in a matter of a second or so. Then the system hangs, between the puts and the NoMethodError. There is no ruby code in between to profile, so any profiler that is looking at code written in my ruby script isn't going to help. The slowdown is something internal to ruby and is not in my code, unless I'm terribly confused about what's happening.
To be very clear:
Line 10042: puts "HERE" # Happens at ~1s
Line 10043: Array.new().inspekt # Happens at ~20-30s
There is no code between those lines to profile. The 20-30s is not happening in any code before line 10042 executes, so profiling that will not help.
I do have other Fibers that are paused. But there is no code here that yields to them. Is it possible that there's some strange built-in yield code that attempts to run other (paused) fibers when an exception is hit? I can't think of a reason you'd ever want this behavior, and many reasons why it would be catastrophic, but I can't think of anything else that would cause this problem (that is also killable with a ctrl-c!)
I would try to debug the full backtrace in there to see what is actually happening
begin
puts "about to crash!"
Array.new().inspekt
rescue => e
puts e.backtrace
raise # raise anyway
end
In my case I get 20 lines of backtrace with ruby 2.6.3 and irb, if that doesn't really tell you anything interesting I would then do the tedious work of measuring each runtime by modifying each file of the backtrace and printing the times at each step, debugging yay!

Byebug terminal output is inconsistent and buggy

Whenever I drop a debugging breakpoint with byebug, I am noticing inconsistencies with the text that I type. For instance, I am able to type the first 2-3 characters. However, after the first 2-3 characters, the terminal starts adding random square brackets and other characters. For instance when I type env, Byebug outputs: eenv, or when I try to access a key in env Byebug outputs something even worse: ^[[Aenv['QUERY_STRING'']^[[.
Has anyone ran into this problem with Byebug?
A similar issue occurred to me while working in ruby on rails, using multiple workers. I am making the assumption here that OP is using rails because env and query strings are mentioned in the output.
So, if you are running multiple workers while debugging, they can cause this sort of behaviour, because code is still being executed after the byebug breakpoint is hit, and more importantly: while you try to type letters in the terminal, causing input to get jumbled. Debugging middleware can exacerbate this behaviour, since middleware is always hit, causing this issue to happen only while debugging certain components but not for an example controllers.
Ensure that only one worker is hitting your breakpoint at a time. This could be done by setting your development environment to never run more than 1 worker at least for the time being
This is a parallelization issue. Either with multiple workers while running rails server, OR in my case it was:
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors) # <--- this is a madness freaking root
end
byebug (and any bb-based gem) terminal input going nuts after hitting a break point in this case

How can I detect how many levels down Pry has gone

If I'm at a Bash prompt and I type bash I can then detect that I'm at the 2nd level by typing echo $SHLVL.
Suppose I'm in a pry session and I type pry again. How can I detect that I'm at the 2nd level down? How can I detect the Pry level? Nothing listed in (Pry.methods - Object.methods).sort seems to have anything useful.
This is for testing some code for a pry-related project, where I need detect the level.
If you call caller within a pry session within a pry session, then you will see a list of commands. Among them, you should be able to find the part that corresponds to a nested pry call. Find the crucial line that is related to each pry session call, and you will know your level. As far as I checked, you should find two occurrences of a line like:
"/usr/local/lib/ruby/gems/2.2.0/gems/pry-0.10.1/lib/pry/repl.rb:67:in `repl'"
Count such lines.

Take over console output using ruby

When I run vim or top from a console they are able to take over rendering the whole console. When I quit I'm then returned to the console.
Is it possible to do this from ruby? As a simple example, how would I do the following
# Rakefile
task :clock do
loop do
console.render Time.now
sleep 1
end
end
when I run this the console would be cleared and the first line would show the time. When I quit I'd then continue the console session as it was before I ran rake clock.
Update
Having checked the tictactoe example for ruby curses here's an implementation of the clock example. I've shown the clock on random lines to demonstrate refreshing the whole console.
#!/usr/bin/env ruby
require 'curses'
loop do
Curses.clear
Curses.setpos(rand * 10, 0)
Curses.addstr(Time.now.to_s);
Curses.refresh
sleep 1
end
You're looking for the Ruby curses library which gives you full control over the screen: positioning, color, &c.
It's not a well document library, but a Stackoverflow search for "[ruby] curses" will give you links to examples.

How could I pack a rack web-application includes infinite loop

I want to pack a rack web-application in order to distribute it, In which a infinite loop resides. So it won't stop until my ctrl-c. But it seems ocra will only pack it when it ends 'naturally', and ctrl-c stopped the process.
have been tring use exit or abort in callmethod of object being passed to rake. after which the whole process do not end, some trace info appears though.
it is possible to invoke rake.run in a thread, and end application after given time. But I do not want to distribute a suicide version. so is there some more eligible and controllable way to normally end it ?
not sure if this is a insane question, but thanks in advance.
According to the OCRA docu, OCRA sets an environment variable OCRA_EXECUTABLE when being run.
So you could check for that environment var in your code and break the loop if OCRA is running, e.g.:
while true
break if ENV.has_key? 'OCRA_EXECUTABLE'
...
end

Resources