Rake, Hoe, Meta-Programming, and Warnings (oh my!)

T

Tim Pease

Is there a way to disable specific warnings from within a Ruby program
(not from the command line)?

The warnings are coming from some meta-programming code that is
redefining instance methods on the fly based on user input. So maybe a
better question would be, is there a way to redefine methods without
causing Ruby to spew out a warning each time the method is redefined.

This is the result of using Hoe (thanks Ryan, Eric, and other sun
deprived Rubyists) to run my unit tests. Hoe adds the -w flag to the
Ruby interpreter when it runs tests. This is a good thing as it found
several minor flaws in my code. But redefining methods is at the heart
of what I'm trying to do.


Example:

class Pattern
def self.create_date_format_methods( pf )
module_eval <<-CODE
def pf.format_date
Time.now.strftime "#{pf.date_pattern}"
end
CODE
end
end

When running unit tests with the -w flag ...

(eval):1: warning: redefine format_date



Any thoughts on how to suppress the warning and/or change my
meta-programming so the warning does not show up?

Blessings,
TwP
 
F

Florian Frank

Tim said:
class Pattern
def self.create_date_format_methods( pf ) old, $VERBOSE = $VERBOSE, nil
module_eval <<-CODE
def pf.format_date
Time.now.strftime "#{pf.date_pattern}"
end
CODE
ensure
$VERBOSE = old
 
D

Daniel Berger

Tim said:
Is there a way to disable specific warnings from within a Ruby program
(not from the command line)?

The warnings are coming from some meta-programming code that is
redefining instance methods on the fly based on user input. So maybe a
better question would be, is there a way to redefine methods without
causing Ruby to spew out a warning each time the method is redefined.

Sure - redefine Kernel#warn. ;)

Seriously, I think what we need are structured warnings. I brought
this up way back in 2002: http://tinyurl.com/yfko6z

I'm certainly not the first to think of it (Perl lets you disable
specific kinds of warnings, it's been brought up on IRC several times
over the years), but some sort of structured warning that programmers
could specifically raise and/or ignore is something I've wanted for a
long time.

I would think it would just be a matter of copying exception.c, ripping
out the longjmp/exit parts of it, defining a warning hierarchy, and
adding a Warning.ignore class method. Of course, it probably isn't
that simple.

Any, I imagine instead of doing this within your code:

warn, "redefinition of constant FOO"

Internally, you would do this:

raise RedefinedWarning, "redefinition of constant FOO"

And then, a programmer could do this:

RedefinedWarning.ignore # ignore all redefintion warnings

And, I think Sean originally suggested allowing a block syntax, so you
could temporarily ignore warnings:

RedefinedWarning.ignore do
# Ignore redefinition warnings within this block only
end

Back to your question, though, I suppose we would have add a
Warning.disable in order to prevent the warning from ever being
generated in the first place:

RedefinedWarning.disable do
def some_method
# Pretend you didn't notice that I've redefined this method
end
end

Whether that's good practice or not, I leave to the viewing audience.

Probably a much longer answer than you wanted, but there you go.

Regards,

Dan
 
T

Tim Pease

Sure - redefine Kernel#warn. ;)

Seriously, I think what we need are structured warnings. I brought
this up way back in 2002: http://tinyurl.com/yfko6z

I'm certainly not the first to think of it (Perl lets you disable
specific kinds of warnings, it's been brought up on IRC several times
over the years), but some sort of structured warning that programmers
could specifically raise and/or ignore is something I've wanted for a
long time.

I would think it would just be a matter of copying exception.c, ripping
out the longjmp/exit parts of it, defining a warning hierarchy, and
adding a Warning.ignore class method. Of course, it probably isn't
that simple.

Any, I imagine instead of doing this within your code:

warn, "redefinition of constant FOO"

Internally, you would do this:

raise RedefinedWarning, "redefinition of constant FOO"

And then, a programmer could do this:

RedefinedWarning.ignore # ignore all redefintion warnings

And, I think Sean originally suggested allowing a block syntax, so you
could temporarily ignore warnings:

RedefinedWarning.ignore do
# Ignore redefinition warnings within this block only
end

Back to your question, though, I suppose we would have add a
Warning.disable in order to prevent the warning from ever being
generated in the first place:

RedefinedWarning.disable do
def some_method
# Pretend you didn't notice that I've redefined this method
end
end

Whether that's good practice or not, I leave to the viewing audience.

Probably a much longer answer than you wanted, but there you go.

Long answers are usually the most educational ones (or the most inflammatory).

I can understand why this would cause a warning -- if someone
inadvertently redefined a method, this would be a nice check. However,
this is my desired behavior <sigh>.

As Florian mentioned, redefining $VERBOSE will suppress the warnings.
It "feels" a little kludgy to me, though.

Hmmm ... maybe Matz doesn't want the performance hit of having to
perform a warning table lookup for each line of code parsed. That
would be one good reason to disallow fine grained warning control.

How's the snow down in your neck of the woods?

Blessings,
TwP
 
J

James Edward Gray II

The warnings are coming from some meta-programming code that is
redefining instance methods on the fly based on user input. So maybe a
better question would be, is there a way to redefine methods without
causing Ruby to spew out a warning each time the method is redefined.

Sure. Watch:

#!/usr/bin/env ruby -w

class Test
def override
"original"
end
end

Test.new.override # => "original"

class Test
def override # !> method redefined; discarding old override
"warning"
end
end

Test.new.override # => "warning"

class Test
undef :eek:verride
def override
"no warning"
end
end

Test.new.override # => "no warning"

class Test
alias_method :eek:ld_override, :eek:verride
def override
"alias avoids warning too"
end
end

Test.new.override # => "alias avoids warning too"

__END__

Hope that helps.

James Edward Gray II
 
T

Tim Pease

Sure. Watch:

#!/usr/bin/env ruby -w

class Test
def override
"original"
end
end

Test.new.override # => "original"

class Test
def override # !> method redefined; discarding old override
"warning"
end
end

Test.new.override # => "warning"

class Test
undef :eek:verride
def override
"no warning"
end
end

Test.new.override # => "no warning"

class Test
alias_method :eek:ld_override, :eek:verride
def override
"alias avoids warning too"
end
end

Test.new.override # => "alias avoids warning too"

__END__

Hope that helps.

Hey James! That did the trick.

I could have sworn that I tried undef before, but in my current sleep
deprived state I'm not too sure about it. Hmmm ... I most likely did
try undef, but it chucked an exception at me when I tried to undef a
non-existent method.

Now using undef along with method_defined?

All is well :)

Blessings,
TwP
 
M

Mike Harris

Tim said:
Is there a way to disable specific warnings from within a Ruby program
(not from the command line)?

The warnings are coming from some meta-programming code that is
redefining instance methods on the fly based on user input. So maybe a
better question would be, is there a way to redefine methods without
causing Ruby to spew out a warning each time the method is redefined.

This is the result of using Hoe (thanks Ryan, Eric, and other sun
deprived Rubyists) to run my unit tests. Hoe adds the -w flag to the
Ruby interpreter when it runs tests. This is a good thing as it found
several minor flaws in my code. But redefining methods is at the heart
of what I'm trying to do.


Example:

class Pattern
def self.create_date_format_methods( pf )
module_eval <<-CODE
def pf.format_date
Time.now.strftime "#{pf.date_pattern}"
end
CODE
end
end

When running unit tests with the -w flag ...

(eval):1: warning: redefine format_date



Any thoughts on how to suppress the warning and/or change my
meta-programming so the warning does not show up?

Blessings,
TwP
I understand that this isn't identical to what you're doing, but it
demonstrates how define_method won't cause a warning.

class Foo
define_method:)bar) do
puts "bar"
end
def cat
puts "cat"
end
end

Foo.new.bar
Foo.new.cat

class Foo
define_method:)bar) do
puts "bar2"
end
def cat
puts "cat2"
end
end

Foo.new.bar
Foo.new.cat
ruby -w foo.rb
bar
cat
foo.rb:17: warning: method redefined; discarding old cat
bar2
cat2
 
T

Tim Pease

I understand that this isn't identical to what you're doing, but it
demonstrates how define_method won't cause a warning.

class Foo
define_method:)bar) do
puts "bar"
end
def cat
puts "cat"
end
end

Foo.new.bar
Foo.new.cat

class Foo
define_method:)bar) do
puts "bar2"
end
def cat
puts "cat2"
end
end

Foo.new.bar
Foo.new.cat

bar
cat
foo.rb:17: warning: method redefined; discarding old cat
bar2
cat2

There is going to be a performance hit with define_method. Since the
method body is a block, each time it is called a Binding to the
instance will have to be created in which the block will be run.

$ cat a.rb

require 'benchmark'

class Object
def method() end
define_method:)method1) {}
end

this_many = 1_000_000
::Benchmark.bm(10) do |bm|
bm.report('method :') {this_many.times {method()}}
bm.report('method1 :') {this_many.times {method1()}}
end


$ ruby a.rb
user system total real
method : 0.400000 0.000000 0.400000 ( 0.400000)
method1 : 1.272000 0.000000 1.272000 ( 1.282000)



It is a factor of 3 slower :(

Blessings,
TwP
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top