Global rescue - DRY (don't repeat yourself)

J

Jan Roslind

I am trying to implement report to a bug management system in rails.
Want to report error stack, local and instance variables for every
method and has come to following "global" rescue clause:

def xxx
begin
...
...
rescue => e
SomeClass.some_method binding, self # appends variable info to a
class variable
raise
end
end # xxx

The problem it is not wery DRY to repeat the above rescue for every
method in my application.


Have tried with:

def xxxx
begin
...
...
load 'global_rescue.rb' # rescue => e ; SomeClass.some_method binding,
self ; raise
end # xxx

without success. No errors message but also no capture of variable
information for xxx. Can't use include. Works only for models :=(


Any suggestions?
 
R

Raveendran .P

Hi,


lib_error.rb:

class Library
def self.error
puts "Oops. something went wrong"
end
end


original_code.rb:

# require the file first line of code instead of calling in mid of
programs

require 'lib_error.rb'

begin
puts fg
rescue
Library.error
end


begin
puts fdg
rescue
Library.error
end

Run the code:

Oops. something went wrong
Oops. something went wrong

I hope it clears ur doubt. If need more details then update here asap.

Thanks,
P.Raveendran
http://raveendran.wordpress.com
 
J

Jan Roslind

Raveendran said:
....
begin
puts fdg
rescue
Library.error
end
.....

Sorry, but I ends up adding

rescue
Library.error binding, self

to every methods. Not wery DRY. Problem if I want to change rescue
clause later (add a argument)

Regards
Jan
 
M

Martin Boese

Sorry, but I ends up adding

rescue
Library.error binding, self

to every methods. Not wery DRY. Problem if I want to change rescue
clause later (add a argument)

Regards
Jan


Hmm.. what about:


class Code
def self.try(&block)
begin
block.call
rescue
puts "Errors: #{$!}"
end
end
end


Code.try do
a = 1123
bad_code
end



Cheers,
Martin
 
J

Jan Roslind

Martin Boese wrote:
....
Hmm.. what about:


class Code
def self.try(&block)
begin
block.call
rescue
puts "Errors: #{$!}"
end
end
end


Code.try do
a = 1123
bad_code
end

Hi Martin.
Thanks for your suggestion. It Works.
But how do I access bindings (to find local variables) and self (to find
instance variables) from Code.try method.
Regards Jan
 
M

Martin Boese

Hi Martin.
Thanks for your suggestion. It Works.
But how do I access bindings (to find local variables) and self (to
find instance variables) from Code.try method.
Regards Jan

Local and instance variables probably work as you expect:

local = 'test'
@instance = 'instance'

Code.try do
"#{local} #{@instance} #{self.class.name}"
end
=> "test instance Object"
 
J

Jan Roslind

Sorry - my fault - I should have included a running demo:


class VariableStack

@@variables = []

def self.save_variables (xbinding, xself)
@@variables << [ caller[0], xbinding, xself ]
end # self.save_variables

def self.get_instance_variables (xself)
xself.instance_variables.collect do |name|
value = xself.instance_variable_get(name)
"#{name}=#{value}"
end
end # self.get_instance_variables

def self.dump_variables
puts "Variable dump:"
lng = @@variables.size
0.upto(lng-1) do |i|
xcaller, xbinding, xself = @@variables
xlocal_variables = eval("local_variables", xbinding).collect {
|a| "#{a}=#{eval(a, xbinding)}" }.join(', ')
xinstance_variables =
VariableStack.get_instance_variables(xself).join(', ')
puts "variable_stack/#{i}:"
puts " caller = #{xcaller}"
puts " local variables = #{xlocal_variables}"
puts " instance variables = #{xinstance_variables}"
end # each variables
@@variables = []
end # self.dump_variables

end # VariableStack


class SomeClass1

def self.test1
begin
@some_instance_var1 = 1
some_local_var2 = 0
some_local_var3 = 3
division_by_with_zero = @some_instance_var1 / 0
rescue => e ; VariableStack.save_variables(binding, self) ; raise #
generel rescue clause - used 10.000 times - not wery DRY
end
end # self.test1

end # SomeClass1


class SomeClass2

def self.test2
begin
@some_instance_variable4 = 4
some_local_var5 = 5
SomeClass1.test1
rescue => e ; VariableStack.save_variables(binding, self) ; raise #
generel rescue clause - used 10.000 times - not wery DRY
end
end # self.test2

end # SomeClass2


# rails session, batch job or demon proces
begin
@some_instance_variable6 = 6
some_local_var7 = 7
SomeClass2.test2
rescue => e
puts "error = #{e.message.to_s}"
puts "backtrace = #{e.backtrace}"
VariableStack.dump_variables
# to-do: bug management system: save error report in database or send
error report as mail
end

Output:

error = divided by 0
backtrace = test.rb:41:in `/'test.rb:41:in `test1'test.rb:55:in
`test2'test.rb:67
Variable dump:
variable_stack/0:
caller = test.rb:42:in `test1'
local variables = some_local_var2=0, some_local_var3=3,
division_by_with_zero=, e=divided by 0
instance variables = @some_instance_var1=1
variable_stack/1:
caller = test.rb:56:in `test2'
local variables = some_local_var5=5, e=divided by 0
instance variables = @some_instance_variable4=4


My problem is that I don't want to repeat

rescue => e ; VariableStack.save_variables(binding, self) ; raise

for every method in my application. It would be much better with

load 'global_rescue_clause.rb', but that does not work.


Is there a way to include "row" ruby source code in ruby method, or an
more OO way to get a variable stack with local and instance variables?


Regards
Jan
 
J

Jan Roslind

Is there a way to include "raw" ruby source code in ruby method, or an
more OO way to get a variable stack with local and instance variables?

Regards
Jan
 
C

Caleb Clausen

Is there a way to include "raw" ruby source code in ruby method, or an
more OO way to get a variable stack with local and instance variables?

It seems to me that a macro is the best way to get to almost what you
want. Take a look at this: http://github.com/coatl/rubymacros and/or
try typing 'gem install rubymacros' in a terminal.

Here's a macro that injects a rescue clause into method definitions:

macro rescuing_vars(method)
method.body=:(
begin
^method.body
rescue => e; VariableStack.save_variables(binding, self) ; raise
end
)
return method
end

You would then use it like this:

rescuing def foo
bar baz, quux
end
#try to think of the 'rescuing' here as a
#declaration modifier, like 'static', 'inline', or 'const' in C++

which the above macro turns into:

def foo
begin
bar baz, quux
rescue => e; VariableStack.save_variables(binding, self) ; raise
end
end

You still have to go and mark each method in your program with the
rescuing tag, which is why I say it's only almost what you want. On
the plus side, you get control over which methods have their behavior
changed in this way.

You could also accomplish more or less the same thing if you keep your
method bodies in strings which get passed to eval, or blocks which get
passed to instance_eval, but that looks even less like normal ruby
code. On the other hand, you wouldn't need a special library
(rubymacros) that makes strange changes to ruby's syntax...

There may also be a better way to implement your algorithm without all
the metaprogramming magic. Sometimes when you think you need something
really strange that doesn't seem to exist, it's because you're
thinking about the problem in the wrong way.
 

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

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top