我正在尝试将Redis集成到Rails应用程序中以取代“has_many through”关系。 我想无缝地执行此操作,因此我们无需在应用程序中更改代码。 我的想法是覆盖某些类属性的不同方法(例如,类Speaker的followers属性),以便在使用它们时能够创建自定义行为: 以下是我想要达到的行为:
s = Speaker.new
s.followers # calls custom getter and returns [User1, User2]
s.followers << User.create
s.followers # calls custom getter and returns [User1, User2, User3]
这是我的想法受到Overriding instance variable array's operators in Ruby
的启发class Speaker < ActiveRecord::Base
attr_accessor :followers
def initialize
super
@followers = []
class << @followers
def <<(u)
puts "setter #{u.id}"
$redis.set "speaker#{self.id}followers", u.id
super(u.id)
end
end
end
def followers
puts "getter"
user_ids = $redis.get "speaker#{self.id}followers"
User.find_all user_ids
end
end
问题是关注者getter的实现覆盖了“def&lt;&lt;(val)”
的实现如果未定义吸气剂“def followers”:
s.followers
# []
s.followers << User.create
# "setter 1"
# [1]
s.followers
# [1]
s.followers << User.create
# "setter 2"
# [1, 2]
s.followers
# [1, 2]
如果定义了吸气剂“def参与者”:
s.followers << User.create
# ["My", "Custom", "Array", User1]
s.followers
# ["My", "Custom", "Array"]
s.followers << User.create
# ["My", "Custom", "Array", User2]
s.followers
# ["My", "Custom", "Array"]
我怎样才能获得getter和setter“&lt;&lt;”一起工作?
答案 0 :(得分:0)
def followers<<(val)
那不行。原因是:
foo.followers << "abc"
# is actually 2 methods!
foo.followers().<<("abc")
因此#followers
需要返回具有重写#<<
方法的类的对象。
rails关联返回关联代理对象的方式很多。
# Book has_many Pages
book.pages # an assoc proxy
book.pages << Page.new # calls #<< on the assoc proxy
Book#pages<<
不存在。 Book#pages
返回ActiveRecord::HasManyAssociationProxy
(或类似的)实例,然后实现#<<
实例方法。
答案 1 :(得分:0)
你的问题是getter返回一个新数组。您修改了@followers
数组的单例类,但是没有在getter中使用它:
def followers
puts 'getter'
['a','new','array']
end
如果你想拥有一个自定义getter,那么你需要确保getter返回@followers(不更改底层引用),或者你需要重新装饰数组。
然而,AlexWayne建议的是正确的方法。返回处理redis详细信息的代理对象:
class FollowersList < SimpleDelegator
def initialize(assoc)
@assoc = assoc
super(_followers)
end
def _reload
__setobj__ _followers
self
end
def _followers
user_ids = $redis.get key
User.find_all user_ids
end
def _key
"speaker#{@assoc.id}followers"
end
# implement your overrides. The _reload method is to force the list to sync
# with redis again, there are other ways to do this that wouldn't do the query
# again
def <<(val)
$redis.lpush key, val.id
_reload
end
#etc
end