Joel VanderWerf
11/23/2006 2:26:00 AM
Victor "Zverok" Shepelev wrote:
> Hi all.
>
> I have some huge class with aspects of functionality spreaded in several
> source files.
>
> And in some of those aspects there is the same picture:
> ---
> class MyClass
> def push_something(obj)
> @something_list ||= []
> @something_list << obj
> end
>
> def use_something(i)
> (@something_list ||= [])[i]
> end
> end
> ---
>
> Then I note (through profiler) various push_XXX spend too much time in
> checking is @XXX_list initialized. Then I change it:
>
> ---
> class MyClass
> alias :initialize_without_something :initialize
>
> def initialize(*arg)
> initialize_without_something(*arg)
> @something_list = []
> end
>
> def push_something(obj)
> @something_list << obj
> end
>
> def use_something(i)
> @something_list[i]
> end
> end
> ---
>
> Now push_XXX and use_XXX work cleaner and faster, but all those initialize
> aliases (now I have 5 or 6) don't seem to be very elegant.
>
> Is there better solution?
>
> (to be clear, all those push_XXX are NOT similar - some of them push object
> to hashes, others to arrays, params count differ and so on - so, they can't
> be generated at once through metaprogramming)
>
> Thanks.
>
> V.
>
I don't know how this will profile--it has the disadvantage of
generating more singletons than you might want, and there is the extra
overhead of an attr_reader rather than just @something_list.
class MyClass
def method_missing(m,*)
if m == :something_list
@something_list = []
class << self
attr_reader :something_list
end
something_list
else
super
end
end
def push_something(obj)
something_list << obj
end
def use_something(i)
something_list[i]
end
end
mc = MyClass.new
mc.push_something 3
p mc # ==> #<MyClass:0xb7d02114 @something_list=[3]>
Also, the method_missing def would need to know about each such attr,
which seems to contradict your goal of distributing that information to
lots of files. A little metaprogramming would help with that, since with
this approach you only need to metaprogram the accessors, and not all
the push_, pop_ etc. methods. Define a class method that registers
things like "something_list", and the method_missing can lookup in the
registered methods.
If you can tolerate a register method as well as the accessor overhead,
then this gives me another idea, which doesn't generate singletons:
class MyClass
@reg_methods_defined = false
@reg = {}
class << self
attr_reader :reg
def define_reg_methods
unless @reg_methods_defined
@reg.each do |m,|
attr_reader m
end
@reg_methods_defined = true
end
end
end
def initialize
self.class.define_reg_methods
self.class.reg.each do |m, (iv, bl)|
instance_variable_set(iv, bl.call)
end
end
def self.register m, &bl
@reg[m] = ["@#{m}", bl]
end
end
class MyClass
register :something_list do
[]
end
def push_something(obj)
something_list << obj
end
def use_something(i)
something_list[i]
end
end
mc = MyClass.new
mc.push_something 3
p mc # ==> #<MyClass:0xb7d65a7c @something_list=[3]>
Getting this to work for subclasses as well is left as an exercise to
the reader ;)
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407