Does ruby allow you to treat warnings as errors?
One reason I'd like to do this is to ensure that if heckle removing a line of code means that a warning occurs, I have the option of ensuring that the mutant get killed.
There is unfortunately no real way of doing this, at least not on most versions of Ruby out there (variations may exist), short of monitoring the program output and aborting it when a warning appears on standard error. Here's why:
Ruby defines Kernel.warn, which you can redefine to do whatever you wish (including exiting), and which you'd expect (hope) to be used consistently by Ruby to report warnings (including internal e.g. parsing warning), but
methods implemented natively (in C) inside Ruby will in turn directly invoke a native method called rb_warn from source/server.c, completely bypassing your redefinition of Kernel.warn (e.g. the "string literal in condition" warning, for example, issued when doing something like: do_something if 'string', is printed via the native rb_warn from source/parse.c)
to make things even worse, there is an additional, rb_warning native method, which can be used by Ruby to log warnings if -w or -v is specified.
So, if you need to take action solely on warnings generated by your application code's calling Kernel.warn then simply redefine Kernel.warn. Otherwise, you have exactly two options:
alter source/error.c to exit in rb_warn and rb_warning (and rb_warn_m?), and rebuild Ruby
monitor your program's standard error output for ': warning:', and abort it on match
You can finally do that by overriding Warning.warn like
module Warning
def warn(msg)
raise msg
end
end
This will turn the warning into an exception. This solution works at least since 2.4 branch.
You could also potentially use DTrace, and intercept the calls to rb_warn and rb_warning, though that's not going to produce exceptions you can rescue from somewhere. Rather, it'll just put them somewhere you can easily log them.
Related
I have a lot of old Perl code that gets called frequently, I have been writing a new module and all of a sudden I'm getting a lot of warnings in my error_log for Apache, they are for every module currently being used. e.g,
"my" variable $variable masks earlier declaration in same statement at
/path/to/module.pm line 40 (#1)
Useless use of hash element in void context at
/path/to/another/module.pm line 212 (#2)
The main layout of the codebase is one giant script that includes the modules and directs the requests to them needed to create certain pages for the website and the main script then handles static elements like menus.
My current project is separated from this main script and doesn't use it however any time I call my code using ajax, there are some other ajax calls that will use the main script and the warnings only seem to appear from those request but only when I'm calling my project.
I have grepped every module and none of them have use warnings (or -w) in them, I have also tried using no warnings 'all' in the main script and my own project but it's not doing anything.
At this point I'm out of ideas on what to do next so all help is appreciated, I'd just like to suppress the warnings, the codebase is quite old and poorly written so going and correcting each issue that causes the warns in the first place isn't do-able.
The Apache server is running mod_perl as well, if that might make a difference I have a feeling it might be something to do with CGI, but I can't seem to find any evidence.
I take it that the code gets called by running certain top-level Perl script(s).
Then use the __WARN__ hook in those script(s) to stop printing of warnings
BEGIN { $SIG{__WARN__} = sub {} };
Place this BEGIN block before the use statements so to affect modules as well.
An empty subroutine is the way to mute warnings since __WARN__ doesn't support 'IGNORE'.
See warn and %SIG in perlvar.
See this post and this post for comments and some examples.
To investigate further and track the warnings you can use Carp
BEGIN {
$SIG{__WARN__} = \&Carp::cluck; # or Carp::confess; to also die
}
which will make it print full stack traces. This can be fine-tuned as you please since we can write our own sub to be called. Or use Carp::Always.
See this post
for some more drastic measures (like overriding CORE::GLOBAL::warn)
Once you find a more precise level at which to suppress warnings then local $SIG{__WARN__} is the way to go, if possible. This is used in a post linked above, and here is another example. It is of course far better to suppress warnings only where needed instead of everywhere.
More detail
Getting stack traces in Perl?
How can I get a call stack listing in Perl?
Note that longmess is unfortunately no longer so standard and well supported.
With this minimal ruby code:
require 'debug'
puts
in a file called, e.g. script.rb
if I launch it like so: ruby -rdebug script.rb
and then press l on the debug prompt, I get the listing, as expected
if I instead run it normally as ruby script.rb
when pressing l I get:
(rdb:1) l
[-3, 6] in script.rb
No sourcefile available for script.rb
The error message seems misleading at best: the working directory hasn't changed, and the file is definitely still there!
I'm unable to find documentation on this behavior (I tried it on both jruby and mri, and the behavior is the same)
I know about 'debugger' and 'pry', but they serve a different use case:
I'm used to other scripting languages with a builtin debug module, that can let me put a statement anywhere in the code to drop me in a debug shell, inspect code, variables and such... the advantage of having it builtin it's that it is available everywhere, without having to set up an environment for it, or even when I'm on a machine that's not my own
I could obviously workaround this by always calling the interpreter with -rdebug and manually setting the breakpoint, but I find this more work than the alternative
After looking into the debug source code, I found a workaround and a fix:
the workaround can be:
trace on
next
trace off
list
this will let you get the listing without restarting the interpreter with -rdebug, with the disadvantage that you'll get some otherwise unwanted output from the tracing, and you'll be able to see it only after moving by one statement
for the fix: the problem is that the SCRIPT_LINES__ Hash lacks a value for the current file... apparently it's only set inside tracer.rb
I've changed line 161, and changed the Hash with a subclass that tracks where []= has been called from, and I wasn't able to dig up the actual code that does the work when stepping into a function that comes from a different file
Also: I haven't found a single person yet who actively uses this module (I asked both on #ruby, #jruby and #pry on freenode), and together with the fact that it uses a function that is now obsolete it leads me to be a bit pessimistic about the maintenance state of this module
nonetheless, I submitted a pull request for the fix (it's actually quite dumb and simple, but to do otherwise I'd need a deeper understanding of this module, and possibly to refactor some parts of it... but if this module isn't actively maintaned, I'm not sure that's a good thing to put effort on)
I suspect the Ruby interpreter doesn't have the ability to load the sourcefile without the components in the debug module. So, no -rdebug, no access to the sourcefile. And I agree it is a misleading error. "Debugging features not loaded" might be better.
Sometimes, we use libraries that are pretty much well debugged and are usually not a cause of an error. Still, these libraries can return errors due to our misuse of their API. In such case, the steps internal to these libraries show up within the backtrace of an error, which are just garbage from the point of view of the programmers using the library, and make it difficult to spot the cause of the error. Even some methods in the core Ruby insert some internal steps into the backtrace. For example, whenever you see a backtrace involving Enumerable#inject, there is always Enumerable#each being called from it, which shows up in the backtrace and is annoying.
What is a good way to remove from the backtrace the steps internal to certain given libraries? I am currently dong it by parsing the backtrace and filtering it by the file name. Is there a better way to do it?
When you are writing a library by yourself, is there a good way to suppress the internal steps appearing in a backtrace involving a method call that uses the library? An obvious way might be to insert a pair of rescue and raise for every method that is to be used from outside of the library, but that does not seem right.
Well...
There isn't really a better way to filter. If you can get the full filepath for the backtrace, though, you can filter by directory which can rule out all stdlibs and gems. Beyond that, it's more trouble than it's worth.
There is a much better solution for this. However, it requires that you catch all exception thrown by Ruby in your library, and then rethrow them after doing this (also do this to all your own excpetions). So wrap all your method with this:
begin
...
rescue Exception
e = $!
e.set_backtrace(caller(nesting_level))
raise e
end
The nesting_level is how many methods of this library the current method was called from. If it was called directly from user code, put 0. If it was called by one method that was called in user code, put in 1, and so on.
EDITED:
I've wrote code that uses OptionParser to handle command line input gracefully. I am facing two major hits.
Passing an empty switches '-' doesn't give an error. Of course some programs take that as valid, but mine shouldn't.
The program requires two mandatory switches, but it accepts one switch without complaining! e.g. program.ruby -f foo -b bar is the valid input and both switches are :REQUIRED. But providing only one switch passes without problem and this is not the desired behavior.
For the first case I've done this:
opts.on('-', /\A-\Z/) do
$stderr.print "Invalid empty switch"
exit 1
end
It works fine. But is this the proper way of doing it?
For the second case, I've looked around for a solution within the OptionParser.new block but I couldn't find one. e.g.
unless options.foo && options.bar
puts "Error."
exit 2
end
Doing it outside the OptionParser.new block is the normal way?
If you are using OptionParser, then yes, you need to explicitly disallow the empty switch and manually check the required parameters.
However, if you used another tool for option parsing, such as defunkt's gem choice, you could mark options as required and the invalid options (such as the empty switch) would cause the help to be printed and application to exit. I understand that in some cases it makes more sense to use OptionParser, but personally I prefer to use the more convenient tools out there.
Even though making options required is pretty easy one way or the other, I would recommend that you think your API decision through. How many command line utilities do you know, that have required options? There's a reason why command line is usually separated into options and arguments, with the former being usually optional and the latter usually required. I would stick to that established convention.
I think Thor(https://github.com/wycats/thor) can resolve your problem more efficiently.
I've seen plenty of posts providing the -W0 flag as an answer to this issue, but I don't want to suppress all warnings, just warnings of a particular value.
I'm running a non-rails app (which uses ActiveRecord, notwithstanding) on Ruby 1.8.7. I want to keep all warnings except for the following DEPRECATION WARNING:
Object#id will be deprecated; use Object#object_id
If that's not possible, I'd like to jettison all deprecation warnings. Java, at least, lets you do this. How about Ruby?
Update: I've upvoted both answers but checked the one that later searchers will expect to find here.
If there's a specific section of code that produces the warnings, you could try mixing in the Kernel module from ActiveSupport and wrap it with a silence_warnings block (example pulled straight from the RDoc):
silence_warnings do
value = do_something_that_causes_warning # no warning voiced
end
noisy_call # warning voiced
Is it absolutely necessary to suppress it? It's not like you're compiling something and have to sift through a ton of warnings all at once...
Edit: If you use read_attribute(:id), then you should avoid the waring. Thanks Jeremy!
I'm not a Rails developer, but isn't there a method that allows you to say "I want the database field id, not the id method of the object"?