Rubyish inst.var initializations

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.

Hi --

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)

In your second example, what purpose does initialize_without_something
serve? I'm wondering why you don't just do:

   def initialize
     @something_list =
   end

and then the other methods.

David

···

On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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 :wink:

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

i think metaprogramming is perfect: define access to the ivars (containers) to
initialize the ivar and then __redefine__ the method in-place for direct
access thereafter :

     harp:~ > cat a.rb
     class Module
       def init_attr a, &init
         ivar = "@#{ a }"
         this = self
         define_method a do
           begin
             instance_variable_set ivar, instance_eval(&init)
           ensure
             this.module_eval{ define_method(a){ instance_variable_get ivar } }
           end
         end
       end
     end

     class C
       init_attr(:list){ Array.new }
       def push(val) list.push val end
     end

     obj = C.new
     p obj.list
     obj.push 42
     p obj.list

     harp:~ > ruby a.rb
     
     [42]

to clean it up pull 'init_attr' into it's own module and extend only those
classes that need this functionality with the module.

regards.

-a

···

On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote:

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)

--
my religion is very simple. my religion is kindness. -- the dalai lama

class MyClass
    def something_list
      @something_list ||=
    end
  
    def push_something(obj)
      something_list << obj
    end
  end

Optionally you could make #something_list private if you don't wish to
expose it to the outside.

Cheers,
Daniel

···

On Thu, 2006-11-23 at 10:04 +0900, 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)

dblack@wobblini.net

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)

In your second example, what purpose does initialize_without_something
serve? I'm wondering why you don't just do:

  def initialize
    @something_list =
  end

and then the other methods.

Because it would hide initialization code defined in other files for the
same class.

class A
def initialize; p 1 end
end

class A
def initialize; p 2 end
end

class A
def initialize; p 3 end
end

A.new # => 3

V.

···

From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
Sent: Thursday, November 23, 2006 3:31 AM

On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote:

since my first method was slightly flawed, here's the above plus a fix:

   harp:~ > cat a.rb
   module InitAttr
     def init_attr a, &init
       ivar = "@#{ a }"
       define_method a do
         this = class << self; self; end
         begin
           instance_variable_set ivar, instance_eval(&init)
         ensure
           this.module_eval{ define_method(a){ instance_variable_get ivar } }
         end
       end
     end
   end

   class C
     extend InitAttr
     init_attr(:list){ Array.new }
     def push(val) list.push val end
   end

   obj = C.new
   p obj.list
   obj.push 42
   p obj.list
   p C.new.list

   harp:~ > ruby a.rb
   
   [42]
   

-a

···

On Thu, 23 Nov 2006 Ara.T.Howard@noaa.gov wrote:

to clean it up pull 'init_attr' into it's own module and extend only those
classes that need this functionality with the module.

--
my religion is very simple. my religion is kindness. -- the dalai lama

<snip smart code>

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 :wink:

to do exactly this, without any extract class instance vars, plus proper
behaviour in subclasses, and not too many extract singletons, one can simply
use attributes.rb (from the metakoans.rb rubyquiz):

     harp:~ > cat a.rb
     require 'set'
     require 'rubygems'
     require 'attributes' ### gem install attributes - it's __only__ 42 lines!

     class C
       attribute('array'){ Array.new }
       attribute('set'){ Set.new }

       def initialize
         self.class.attributes.each{|a| send a} # force init
       end

       def array_push(obj) @array << obj end
       def set_push(obj) @set << obj end
     end

     class B < C; end

···

On Thu, 23 Nov 2006, Joel VanderWerf wrote:

     #
     # works for objects
     #
       c = C.new
       c.array_push 42
       c.set_push 42
       p c.array
       p c.set

     #
     # even in subclasses
     #
       b = B.new
       p b.array
       p b.set

     harp:~ > ruby a.rb
     [42]
     #<Set: {42}>
     
     #<Set: {}>

if you hate installing/depending-on gems then just steal the code: it's so
short you can inline it!

regards.

-a
--
my religion is very simple. my religion is kindness. -- the dalai lama

Daniel Schierbeck wrote:
...

Then I note (through profiler) various push_XXX spend too much time in
checking is @XXX_list initialized. Then I change it:

...

Sounds like Victor was interested in avoiding the ||= in each access. The following still does that check, though it is nice clean code, and preferable unless profiling shows it to be a bottleneck:

···

On Thu, 2006-11-23 at 10:04 +0900, Victor "Zverok" Shepelev wrote:

  class MyClass
    def something_list
      @something_list ||=
    end
      def push_something(obj)
      something_list << obj
    end
  end

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Right. And Ara's solution is completely great (I've already said this
yesterday, but don't see those mail in list - interesting, why?).

V.

···

-----Original Message-----
From: Joel VanderWerf [mailto:vjoel@path.berkeley.edu]
Sent: Thursday, November 23, 2006 8:55 PM
To: ruby-talk ML
Subject: Re: Rubyish inst.var initializations

Daniel Schierbeck wrote:

On Thu, 2006-11-23 at 10:04 +0900, Victor "Zverok" Shepelev wrote:

...

Then I note (through profiler) various push_XXX spend too much time in
checking is @XXX_list initialized. Then I change it:

...

Sounds like Victor was interested in avoiding the ||= in each access.
The following still does that check, though it is nice clean code, and
preferable unless profiling shows it to be a bottleneck:

  class MyClass
    def something_list
      @something_list ||=
    end

    def push_something(obj)
      something_list << obj
    end
  end

Frankly, I believe this is not a too good idea. There should be one main place (i.e. file) responsible for this class's definition and all other places should only add to that class. Changing initialize's signature would certainly be not a good idea either.

If you feel, you have to initialize additional fields, then this is a cleaner solution - and also more modular:

class Foo
   def initialize(a,b,c)
     super
     @a=a
     @b=b
   end
end

module Mixin
   def initialize(*a,&b)
     super
     @foo =
   end
end

Then you can safely do in another place:

class Foo
   include Mixin
end

and construction will still work ok plus you can use that module in several places.

Another clean solution:

class Foo
   def use_sth() @something ||= end
   def push_sth(x) something << x end
end

Btw, you can make your push_something more efficient by using a single statement:

def push_sth(x)
   (@sth ||= ) << x
end

Kind regards

  robert

···

On 23.11.2006 02:36, Victor "Zverok" Shepelev wrote:

From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
dblack@wobblini.net
Sent: Thursday, November 23, 2006 3:31 AM

On Thu, 23 Nov 2006, 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)

In your second example, what purpose does initialize_without_something
serve? I'm wondering why you don't just do:

  def initialize
    @something_list =
  end

and then the other methods.

Because it would hide initialization code defined in other files for the
same class.

class A
def initialize; p 1 end
end

class A
def initialize; p 2 end
end

class A
def initialize; p 3 end
end

A.new # => 3

From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
dblack@wobblini.net
Sent: Thursday, November 23, 2006 3:31 AM

Hi all.

I have some huge class with aspects of functionality spreaded in

several

source files.

Frankly, I believe this is not a too good idea. There should be one
main place (i.e. file) responsible for this class's definition and all
other places should only add to that class. Changing initialize's
signature would certainly be not a good idea either.

If you feel, you have to initialize additional fields, then this is a
cleaner solution - and also more modular:

class Foo
  def initialize(a,b,c)
    super
    @a=a
    @b=b
  end
end

module Mixin
  def initialize(*a,&b)
    super
    @foo =
  end
end

Then you can safely do in another place:

class Foo
  include Mixin
end

Robert, thanks. It's a REALLY good point.
I've completely haven't think about mixin's initialize. Would guess it.

V.

···

From: Robert Klemme [mailto:shortcutter@googlemail.com]
Sent: Friday, November 24, 2006 9:51 PM

On 23.11.2006 02:36, Victor "Zverok" Shepelev wrote:

On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote: