11

I'm trying to do something like:

account.users << User.new

But I need users to be a method on an account. So I've tried things like:

def users<<(obj)

But I've had no luck with that. Is this even possible to do in Ruby? I would assume so because the ActiveRecord relationships seem to work this way in Rails.

Matthew Stopa
  • 3,734
  • 6
  • 39
  • 50

6 Answers6

10

Check this answer: Rails: Overriding ActiveRecord association method

[this code is completely from the other answer, here for future searchers]

has_many :tags, :through => :taggings, :order => :name do
    def << (value)
      "overriden" #your code here
    end     
  end
Community
  • 1
  • 1
Jesse Wolgamott
  • 40,057
  • 4
  • 80
  • 107
5

It seems like you might not be describing your actual problem, but to answer your question -- yes you can override the << operator:

class Foo
  def <<(x)
    puts "hi! #{x}"
  end
end

f = Foo.new
=> #<Foo:0x00000009b389f0>
> f << "there"
hi! there
muffinista
  • 6,588
  • 2
  • 28
  • 23
3

I assume you have a model like this:

class Account < ActiveRecord::Base
  has_and_belongs_to_many :users
end

To override Account#users<<, you need to define it in a block that you pass to has_and_belongs_to_many:

class Account < ActiveRecord::Base
  has_and_belongs_to_many :users do
    def <<(user)
      # ...
    end
  end
end

You can access the appropriate Account object by referring to proxy_association.owner:

def <<(user)
  account = proxy_association.owner
end

To call the original Account#users<<, call Account#users.concat:

def <<(user)
  account = proxy_association.owner
  # user = do_something(user)
  account.users.concat(user)
end

For more details, see this page: Association extensions - ActiveRecord

Miscreant
  • 5,806
  • 3
  • 20
  • 21
1

In this case it's the << of you class of you User. So can be an Array or a AssociationProxy.

The must simplest is create a new method to do what you want.

You can override the method by instance instead.

account.users.instance_eval do
  def <<(x)
    put 'add'
  end
end

account.users << User.new
# add

But you need do that all the time before you add by <<

shingara
  • 45,722
  • 11
  • 98
  • 105
0

users would return an object that has overridden << operator like Array, IO, String, or any type you create. You override like this:

class SomeType
  def <<(obj)
    puts "Appending #{obj}"
  end
end
Linuxios
  • 33,279
  • 13
  • 86
  • 114
0

If you are trying to perform an action upon adding an User to the users collection, you can use association callbacks instead of over-riding <<(as there are many ways to add an object to an association).

class Account
  has_many :users, :after_add => :on_user_add

  def on_user_add(user)
    p "Added user : #{user.name} to the account: #{name}"
  end
end
Harish Shetty
  • 63,225
  • 21
  • 147
  • 197