Ruby: overloading indexed assignment to instance attributes
For the instances of a class that I'm writing (an ActiveRecord model), I'd like to be able to overload assignments like this:
m.rank['foo'] = 1
m.rank['bar'] = 2
In other words, I don't want the numbers to be written into an actual @rank
hash, but rather I'd like to have some setter method called with 'foo'
and 1
as its parameters.
A naive way to get this functionality wo开发者_StackOverflow中文版uld be to define set_rank(key, rank)
and get_rank(key)
, but the syntax isn't very sugary. For nicer syntax, one could define a helper class that defines []
and []=
, and have the rank
method return an instance of that class.
Is there an established pattern to write this in a concise manner? Or is it a bad idea in the first place, and I should just use set_rank
and get_rank
?
You can achieve this by initialising your rank attribute like so:
@rank = {}
def @rank.[] key
puts "Retrieving key #{key}" #put code to execute when key is retrieved here
begin
self.fetch key
rescue
puts "Key not found" #put code to execute when key is not found here
nil
end
end
def @rank.[]= key, val
puts "Setting key #{key} to #{val}" #put code to execute when key is set here
self.store key, val
end
You will need to do this in your initialize
method and use attr_reader :rank
(recommended) or attr_accessor :rank
.
Explanation
This uses a feature of Ruby called singleton methods. Singleton methods are where you define methods on specific objects rather than on all instances of a class. You can define singleton methods on any object. The syntax is, as above, def identifier.method ... end
.
I think it's pretty ugly and totally useless. My solution:
class Test
attr_accessor :rank
def initialize
@rank = Rank.new
end
def meth(key, rank)
print key, " ", rank
end
class Rank
def []=(key, rank)
@test = Test.new
@test.meth(key, rank)
end
end
end
m = Test.new
m.rank["key"] = "rank" # => key rank
精彩评论