Obj.attr(qualifier) = value -- possible?


#1

I want to write a method that can be called like this

obj.attr(qualifier) = value

So far, I don’t see how to achieve this. I’ve tried

class C
def attr=(qualifier, value)

end
end

as well as

class D
def attr(qualifier)
Proxy.new(self, qualifier)
end
class Proxy
def =(value)

end
end
end

Neither is valid Ruby, apparently. Is there another, working way?

Michael


#2

On Fri, May 15, 2009 at 3:00 PM, Michael S. removed_email_address@domain.invalid
wrote:

end

end
end
end

Neither is valid Ruby, apparently. Is there another, working way?

I don’t think you can get the calling syntax you want. The best you
can do I think is

obj.attr = qualifier, value


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale


#3

Michael S. wrote:

end
end
end
end

Neither is valid Ruby, apparently. Is there another, working way?

Michael

I’m pretty sure that’s impossible – there is no #() method, or #()=.
But there is #[]=, and you can adapt your Proxy approach to use it:

class D
def attr
Proxy.new(self)
end
def handle qualifier, value
puts “handling #{qualifier.inspect}, #{value.inspect}”
end

class Proxy
def initialize obj
@obj = obj
end
def []=(qualifier, value)
@obj.send(:handle, qualifier, value)
end
end
end

d = D.new
d.attr[“some qualifier”] = “some value”


#4

Joel VanderWerf wrote:

I’m pretty sure that’s impossible – there is no #() method, or #()=.
But there is #[]=, and you can adapt your Proxy approach to use it:

That’s a good suggestion, but unfortunately, it doesn’t fit in my case.
I’m trying to do this stuff in an extension module for a Rails has_many
:through association. It looks roughly like this

class Role < ActiveRecord::Base
validates_presence_of :type # actor, director, …
end

class Movie < ActiveRecord::Base
has_many :roles
has_many :participants, :through => :roles do
def as(role)
self.scoped(…) # joins and conditions omitted
end
end
end

This allows me to write code like

movie.participants.as(‘actor’)

On top of that, I’d like to be able to write

movie.participants.as(‘actor’) = params[:movie][:actors]

and that’s where I’m stuck.

Michael


#5

Rick Denatale wrote:

On Fri, May 15, 2009 at 3:00 PM, Michael S. removed_email_address@domain.invalid
wrote:

�end
� � �…
� �end
�end
end

Neither is valid Ruby, apparently. Is there another, working way?

I don’t think you can get the calling syntax you want. The best you
can do I think is

obj.attr = qualifier, value

Well, you could always go with obj.attr[qualifier] = value

irb(main):004:0> class Ooo
irb(main):005:1> attr_reader :attr
irb(main):006:1> def initialize
irb(main):007:2> @attr = {}
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> obj = Ooo.new
=> #<Ooo:0x6fe6b8 @attr={}>
irb(main):011:0> obj.attr[:happy] = :sad
=> :sad
irb(main):012:0> obj.attr
=> {:happy=>:sad}
irb(main):013:0> obj.attr[:happy]
=> :sad
irb(main):014:0>

This example used a hash because it’s easy, buy could make @attr be any
class that accepts the []= method, including a class of your own.


#6

On Fri, May 15, 2009 at 3:55 PM, Michael S. removed_email_address@domain.invalid
wrote:

validates_presence_of :type # actor, director, …

This allows me to write code like

movie.participants.as(‘actor’)

On top of that, I’d like to be able to write

movie.participants.as(‘actor’) = params[:movie][:actors]

and that’s where I’m stuck.

If you’re willing to use a block you could do

movie.participants.as(‘actor’) { params[:movie][:actors] }

and then:

def as(role, &block)
value = yield
end

-A


GMU/IT d- s: a32 C++(++++)$ UL@ P— L+(++) !E W+++$ !N o? K? w— !O
M++ V PS+ PE Y PGP t+ !5 X- R tv b++ DI+ D++ G- e++ h---- r+++ y++++**

http://anthony.mp


#7

Anthony E. wrote:

On Fri, May 15, 2009 at 3:55 PM, Michael S.
removed_email_address@domain.invalid wrote:
[…]
If you’re willing to use a block you could do

movie.participants.as(‘actor’) { params[:movie][:actors] }

and then:

def as(role, &block)
value = yield
end

Another interesting suggestion. But I think I use

def replace(role, value)

end

as there’s a precedent in the Rails association methods for #replace and
the generated #replace is not useful in this case anyway.

Michael


#8

On 15.05.2009 21:50, Michael S. wrote:

This allows me to write code like

movie.participants.as(‘actor’)

On top of that, I’d like to be able to write

movie.participants.as(‘actor’) = params[:movie][:actors]

and that’s where I’m stuck.

The only way to modify behavior of the assignment operator in Ruby is
the route through []= AFAIK. If you want to use “=” you will have to do
down that route. But I see you have got your working solution already.
Just wanted to make sure expectations are not unrealistic. :slight_smile:

Kind regards

robert


#9

Michael S. removed_email_address@domain.invalid writes:

I want to write a method that can be called like this

obj.attr(qualifier) = value

So far, I don’t see how to achieve this.

Me neither. And the following:

obj = Foo.new
obj.attr(qualifier).assign(value)

seems inferior to:

obj.attr_assign(qualifier, value)