Dynamically replacing methods for efficiency

H

Hal Fulton

I have an idea here, but I'm afraid of crossing the line from
cleverness to madness.

Imagine you have a class with methods whose behavior changes
based on various attributes.

For example, I'll name this method "action"; suppose that in
general, it just calls do_this and do_that.

def action
do_this
do_that
end

But now suppose we enable things like a debug mode and a
trace mode and logging and a configurable delay and such:

def action
STDERR.puts "Entering action" if @trace
STDERR.puts "before do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: Useful info" if @log
sleep(@delay) if @delay
do_this
STDERR.puts "Returned from do_this" if @trace
STDERR.puts "after do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: More useful info" if @log
do_that
STDERR.puts "Returned from do_that" if @trace
STDERR.puts "after do_that: foo = #@foo" if @debug
@log.puts "#{Time.now}: Still more useful info" if @log
end

Here we've added ten if-statements. If #action is called
frequently, performance will degrade even if these special
attributes are not set.

But suppose for example we fixed the "trace" setter so that
when it was set, it aliased "action_trace" to "action",
where the former is something like:

def action_trace
STDERR.puts "Entering action"
do_this
STDERR.puts "Returned from do_this"
do_that
STDERR.puts "Returned from do_that"
end

There would also be versions like: action_log, action_debug,
not to mention (here's where it gets hairy) things like
action_debug_log, action_debug_trace, action_debug_trace_log,
and so on.

On the one hand, I appreciate Ruby because I *can* do this
sort of thing. On the other hand, if the logic for #action
changes, how many pieces of code will I find myself modifying?

Another possibility is: When a special attribute is changed,
write the method code to a file and load that file. This at
least would keep all the logic in one place.

Please: Express your opinions.


Hal
 
L

Lyle Johnson

Imagine you have a class with methods whose behavior changes
based on various attributes.

Please: Express your opinions.

I have a nagging feeling that aspect-oriented programming figures in
to this discussion somehow, if only I knew more about it.
 
H

Hal Fulton

Lyle said:
I have a nagging feeling that aspect-oriented programming figures in
to this discussion somehow, if only I knew more about it.

That is probably true, but I am not necessarily looking for a major
paradigm shift in my code.


Hal
 
F

Florian Gross

Hal said:
For example, I'll name this method "action"; suppose that in
general, it just calls do_this and do_that.

def action
do_this
do_that
end

But now suppose we enable things like a debug mode and a
trace mode and logging and a configurable delay and such:

def action
STDERR.puts "Entering action" if @trace
STDERR.puts "before do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: Useful info" if @log
sleep(@delay) if @delay
do_this
STDERR.puts "Returned from do_this" if @trace
STDERR.puts "after do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: More useful info" if @log
do_that
STDERR.puts "Returned from do_that" if @trace
STDERR.puts "after do_that: foo = #@foo" if @debug
@log.puts "#{Time.now}: Still more useful info" if @log
end

Here we've added ten if-statements. If #action is called
frequently, performance will degrade even if these special
attributes are not set.

Maybe something like this will work reasonably?

def initialize
@action_hooks = Hash.new do |hash, method|
hash[method] = Hash.new do |type_hash, type|
type_hash[type] = Array.new
end
end
end

def call_hook(method, type, *more)
@action_hooks[method][type].each { |hook| hook.call(*more) }
end

def register_hook(method, type, &block)
@action_hooks[method][type] << block
end

def action
call_hook:)action, :before_do_this)
do_this
call_hook:)action, :before_do_that)
do_that
call_hook:)action, :before_end)
end

I suppose that someway it could be factored out to a reusable Mix-In.

Regards,
Florian Gross
 
A

Adam Fields

Hal said:
For example, I'll name this method "action"; suppose that in
general, it just calls do_this and do_that.

def action
do_this
do_that
end

But now suppose we enable things like a debug mode and a
trace mode and logging and a configurable delay and such:

def action
STDERR.puts "Entering action" if @trace
STDERR.puts "before do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: Useful info" if @log
sleep(@delay) if @delay
do_this
STDERR.puts "Returned from do_this" if @trace
STDERR.puts "after do_this: foo = #@foo" if @debug
@log.puts "#{Time.now}: More useful info" if @log
do_that
STDERR.puts "Returned from do_that" if @trace
STDERR.puts "after do_that: foo = #@foo" if @debug
@log.puts "#{Time.now}: Still more useful info" if @log
end

Here we've added ten if-statements. If #action is called
frequently, performance will degrade even if these special
attributes are not set.

Maybe something like this will work reasonably?

def initialize
@action_hooks = Hash.new do |hash, method|
hash[method] = Hash.new do |type_hash, type|
type_hash[type] = Array.new
end
end
end

def call_hook(method, type, *more)
@action_hooks[method][type].each { |hook| hook.call(*more) }
end

def register_hook(method, type, &block)
@action_hooks[method][type] << block
end

def action
call_hook:)action, :before_do_this)
do_this
call_hook:)action, :before_do_that)
do_that
call_hook:)action, :before_end)
end

I suppose that someway it could be factored out to a reusable Mix-In.

I'm totally new to ruby, but it seems like blocks are the right way to
go for this.

Maybe something like this:

---------------------------------------------------------

def do_this
print "this" + "\n"
end

def do_that
print "that" + "\n"
end

def hook
yield
end

action = "trace"
def hook
print "hooked" + "\n"
yield
end if action == "trace"

hook {do_this}
hook {do_that}
 
A

Ara.T.Howard

Imagine you have a class with methods whose behavior changes
based on various attributes.
Please: Express your opinions.

look at my source for arrayfields - i do exactly this. the general idea is as
follows

only two methods are added to Array, #fields, and #fields=. these obviously
get/set the @fields memeber of an array. however, #fields= is special and
extends (if not already done) the object itself with a module containing code
which overrides some of the index-y array operations with ones that work with
strings, symbols, or whatever the method used to (super). here some of the
relevant bits (simplified a little):

here we see one method only added to class Array. this does not affect any
existing or future arrays in the program since none of them use this method:

class Array
def fields= fields
extend ArrayFields unless defined? @fieldset

@fieldset =
if ArrayFields::FieldSet === fields
fields
else
ArrayFields::FieldSet.new fields
end
end
end

in ArrayFields we see things like:

module ArrayFields
def [](idx, *args)
if @fieldset and (String === idx or Symbol === idx)
pos = @fieldset.pos idx
return nil unless pos
super(pos, *args)
else
super
end
end
end

so all the additional functionality is contrained to this module. this has the
quite important advantage that if you do this

require 'arrayfields'

a0 = [0,1,2]
a1 = [3,4,5]

a0.fields = %w(zero one two)

p a0['zero']
p a1[0]

only a0 will see a performance hit. i think this could apply to your
scenario.

cheers.

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| A flower falls, even though we love it;
| and a weed grows, even though we do not love it.
| --Dogen
===============================================================================
 
J

jim

* Lyle Johnson said:
I have a nagging feeling that aspect-oriented programming figures in
to this discussion somehow, if only I knew more about it.

Sounds like adaptive programming (used alot in mobile
devices) that is discussed in this issue of IEEE Sprectrum.

The methods could change depending upon location or available
resources. Usefull for memory constrained devices.

Jim
 
S

Sean O'Dell

Imagine you have a class with methods whose behavior changes
based on various attributes.

Perhaps "states" would apply here. Check out [ruby-talk:103859].

Sean O'Dell
 
R

Robert Klemme

Hal Fulton said:
I have an idea here, but I'm afraid of crossing the line from
cleverness to madness.

Imagine you have a class with methods whose behavior changes
based on various attributes.

For example, I'll name this method "action"; suppose that in
general, it just calls do_this and do_that.

def action
do_this
do_that
end

But now suppose we enable things like a debug mode and a
trace mode and logging and a configurable delay and such:

Please: Express your opinions.

While there were other suggestions let me point out that some future version
(or was it 1.9.x?) will include hooks that can be put before, after or
around a method invocation. I don't remember the exact thread but I do
remember that there was quite some discussion about this - probably three
months ago.

Kind regards

robert
 

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,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top