1

我正在尝试将 Redis 集成到 Rails 应用程序中以替换“has_many through”关系。我想无缝地做到这一点,这样我们就不必在整个应用程序中更改代码。我的想法是覆盖某些类属性(例如 Speaker 类的 follower 属性)的不同方法,以便能够在使用它们时创建自定义行为:以下是我想要达到的行为:

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]

这是我的想法,灵感来自Ruby 中的 Overriding instance variable array's operator

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 <<(val)”的实现

如果没有定义吸气剂“def 追随者”:

s.followers
# []
s.followers << User.create 
# "setter 1"
# [1]
s.followers
# [1]
s.followers << User.create 
# "setter 2"
# [1, 2]
s.followers
# [1, 2]

如果定义了 getter“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 "<<" 一起工作?

4

2 回答 2

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(或类似的东西),然后实现#<<实例方法。

于 2013-02-07T22:00:17.710 回答
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
于 2013-02-07T22:24:46.200 回答