Quick metaprogramming question: collecting arguments across

Hi all,

Quick question: is there a better way to write Foo.contains below?
Forcing the lexical scoping like this feels wrong.

class Foo
def self.contains *o
@contents ||= []
@contents += o
contents = @contents
define_method(:contents) { contents }
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents # => [:goat, :boat, :stoat]

Thanks!

William M. wrote:

Hi all,

Quick question: is there a better way to write Foo.contains below?
Forcing the lexical scoping like this feels wrong.

Maybe:

class Foo
def contents
self.class.contains
end
def self.contains *o
@contents ||= []
return @contents if o.empty?
@contents += o
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents # => [:goat, :boat, :stoat]

T.

On 7/6/06, William M. [email protected] wrote:

define_method(:contents) { contents }
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents # => [:goat, :boat, :stoat]

How about:

class Foo
class << self
def contains *o
@contents ||= []
@contents += o
end
attr_accessor :contents
end
def contents
@contents ||= self.class.contents
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents #=> [:goat, :boat, :stoat]

On 7/7/06, Phil T. [email protected] wrote:

@contents += o
p Bar.new.contents # => [:goat, :boat, :stoat]
attr_accessor :contents

end

Phil

A tiny refinement (in the golfing mood):

class Foo
class << self
def contains(*args)
(@contents ||= []).push(*args)
end
end
def contents
self.class.contains
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.contains
p Bar.new.contents

END
[:goat, :boat, :stoat]
[:goat, :boat, :stoat]

Regards,
Sean

On 7/6/06, Phil T. [email protected] wrote:

contents = @contents

end
def contents
@contents ||= self.class.contents
end

Actually, after thinking about it a bit more, I’d probably go with:

 def contents
   self.class.contents
 end

end

Phil

2006/7/7, William M. [email protected]:

define_method(:contents) { contents }

end
end

Any reason why you cannot simply use a normal attribute accessor?

class Foo
class <<self
attr_reader :contents

def contains *o
  (@contents ||= []).concat o
end

end
end

Note, you may want to use a Set to avoid duplicates.

Kind regards

robert

On Fri, 7 Jul 2006, William M. wrote:

define_method(:contents) { contents }
end
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents # => [:goat, :boat, :stoat]

if the behaviour is useful - abstract it:

harp:~ > cat a.rb
module Container
module ClassMethods
def contents() @contents ||= [] end
def contains(*list) contents.push *list.flatten end
end
def contents() self.class.contents end
def self.included other
other.extend ClassMethods
super
end
end

class Foo
include Container
end

class Bar < Foo
contains :goat, :boat
contains :stoat
end

p Bar.new.contents # => [:goat, :boat, :stoat]

harp:~ > ruby a.rb
[:goat, :boat, :stoat]

regards.

-a

Excerpts from William M.'s mail of 6 Jul 2006 (PDT):

Quick question: is there a better way to write Foo.contains below?
Forcing the lexical scoping like this feels wrong.

Thanks for all the replies. A case of overlooking the obvious solution.