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,793
  • 6
  • 42
  • 51

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,197
  • 4
  • 83
  • 109
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,676
  • 2
  • 30
  • 23
  • I don't want to use the Foo class though. I want to user foo.users << x – Matthew Stopa Mar 02 '12 at 16:49
  • @JohnBaker, `foo.users << obj` is equivalent to `foo.users.send :<<, obj`. You should simply return Foo's array of users; the [`Array#<<`](http://ruby-doc.org/core-1.9.3/Array.html#method-i-3C-3C) method will push additional users into the array. – Matheus Moreira Mar 02 '12 at 17:42
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
  • 6,626
  • 3
  • 22
  • 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
  • 46,608
  • 11
  • 99
  • 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
  • 34,849
  • 13
  • 91
  • 116
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
  • 64,083
  • 21
  • 152
  • 198