tags:

views:

72

answers:

3

Hello, I have a skeleton class:

class Foo
   def bar
    # returns some sort of array
   end
end

but how can one add the 'writer' method to 'bar' so to enable the Array#push behavior?

Foo.new.bar<<['Smile']
_.bar #=> ['Smile']

EDITED: I should expand my question further. There are two classes. Foo, and Bar, much like the ActiveRecord has_many relation where Foo has_many Bars

But I am actually storing the ids of Bar inside a method of Foo. I name that method bar_ids

so @foo = Foo.new(:bar_ids => [1,2,3])

As you can imagine, if I ever want to look up what Bars belong to @foo, I have to actually do something like Bar.where(:id => @foo.bar_ids)

So I decided to make another method just named bar to do just that class Foo #... def bar Bar.where(:id => bar_ids) end end

That worked out. now I can do @foo.bar #=> all the bars belonging to @foo

Now I also want to have that kind of push method like ActiveRecord associations, just to cut out the "id" typing when associating another bar object to a foo object

Currently, this works: @foo.bar_ids << Bar.new.id @foo.save

But I want: @foo.bar << Bar.new #where the new bar's id will get pushed in the bar_ids method of @foo @foo.save

Thanks for all of your help, I really appreciate your thoughts on this!

+1  A: 

Return an object that has the << method defined.

Tass
So base on my example, bar /does/ return an array which does have the << defined, so I am done?
Nik
You are done if you always return the same array object of course. Otherwise, the `_.bar #=> ['Smile']` won't work
David
A: 

Unless I'm misunderstanding what you're wanting, why not just make the bar method a getter for an internal array member?

class Foo
  attr_reader :bar

  def initialize
    @bar = []
  end
end

f = Foo.new
f.bar << 'abc'
# f.bar => ['abc']
Daniel Vandersluis
+1  A: 
class Foo
   attr_reader :bar
   def initialize
     @bar = Array.new
     def @bar.<< arg
       self.push arg.id
     end
   end

end

class Bar
  attr_accessor :id
  def initialize id
    self.id = id
  end
end


f = Foo.new
bars = (1..5).map{|i| Bar.new i}

f.bar << bars[2]
f.bar << bars[4]

p f.bar  #=> [3, 5]
ormuriauga
@Ormuriauga. That looks pretty cool, would you be kind enough to explain why the def @bar.<< arg is in side the initialize?
Nik
@Nik, yes ormuriauga's solution is pretty cool. The `def @bar.<<` is in the `initialize` method for `Foo` as he's customizing the `@bar` (`Array`) object there (by overriding the normal array `<<` method with a singleton method).
banister
Nik
thanks @banister +1, do you need a more thorough explanation @nik?
ormuriauga
from what I understand, because @bar is now defined as an array, it automatically 'inherits' the << method unto itself, but we want to not use that predefined << and to define our own <<. That's so far what I figure, could you point me to the right direction where I can look up why this needs to be done (the act of overriding) inside the initialize method? Thanks!
Nik
The overriding of `<<`, when done like this, needs to be in the initializer because we are overriding it on a single Array instance and then it makes sense to that first thing when we create it.
ormuriauga
But I would in fact write a separate class to do this and not use Array like this; I just wanted to give you a quick answer. When we override `<<` like this we are overriding it on the newly created object `@bar` *only*, not on other instances of Array. What that means is that we define the function on `@bar`'s metaclass (http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html). But like I said, this is not a very robust solution in this case, only a quick hack.
ormuriauga
I see, I am reading up on your link now. Thank You Very Much!
Nik