2009-05-21 54 views
2

业务逻辑是这样的:用户通过一个连接表在一个船上,我想我们称这个模型为一个Ticket。但是,当用户实例想要检查船上还有其他人时,有一个条件是询问该用户是否有权查看船上的所有人或船上的某些人。如果用户可以看到每个人,则正常交易是正常的:some_user.boats.first.users返回所有用户的船票。但是对于一些用户来说,唯一在船上的人(就他们而言)就是人,比如餐厅。因此,如果用户的票证是“标记的”(使用acts_as_taggable风格系统)和“餐厅”,则唯一从some_user.boats.first.users返回的用户应该是带有标记为“餐厅”的票证的用户。使用单例类动态覆盖/添加一个ActiveRecord关联

只是为了记录,我并没有试图从getgo中设计出疯狂的东西 - 我试图将这个任意分组嵌入到(大部分)已存在的系统中。 所以我们有:

class User 
    has_many :tickets 
    has_many :boats, :through => :tickets 
end 

class Ticket 
    belongs_to :user 
    belongs_to :boat 
end 

class Boat 
    has_many :tickets 
    has_many :users, :through => :tickets 
end 

起初,我以为我可以有条件地修改虚拟类,如:

singleton = class << a_user_instance ; self ; end 
singleton.class_eval(<<-code 
    has_many :tickets, :include => :tags, :conditions => ['tags.id in (?)', [#{tag_ids.to_s(:db)}]] 
code 
) 

这得一路下跌到生成SQL,但产生的时候,它生成SQL中结束:

LEFT OUTER JOIN "tags" ON ("tags"."id" = "taggings"."tag_id") WHERE ("tickets"._id = 1069416589 AND (tags.id in (5001,4502)))

我试着挖周围的ActiveRecord的代码,但我无法找到任何地方使得w应在上面的SQL中用'下划线'加上'id'前缀。我知道当一个ActiveRecord类被加载时会加载关联,并且我认为它与单例类相同。 耸肩

我也用一个alias_method_chain像:

singleton = class << a_user_instance ; self ; end 
singleton.class_eval(<<-code 
    def tickets_with_tag_filtering 
    tags = Tag.find(etc, etc) 
    tickets_without_tag_filtering.scoped(:include => :tags, :conditions => {:'tags.id' => tags}) 
    end 
    alias_method_chain :tickets, :tag_filtering 
code 
) 

不过,虽然这种方法产生所需门票,任何加入这些门票中的类,而不是虚拟类中使用的条件。 some_user.boats.first.users返回所有用户。

任何类型的评论将被赞赏,特别是如果我用这种方法吠叫错误的树。谢谢!

回答

2

因此,一个关于你的下划线问题的猜测是,Rails在评估时根据上下文生成关联代码。作为一个单独的类会搞砸,就像这样:

"#{owner.table_name}.#{association.class.name}_id = #{association.id}" 

可以在那里得到的,并在你的单身类中定义一个类名属性,看看是否能解决问题。

总的来说,我不推荐这样做。它会产生令人痛苦的行为,以追查和无法有效地扩展。它会在代码库中创建一个地雷,以后会伤害你或者你爱的人。

相反,考虑使用named_scope声明:

class User 
    has_many :taggings, :through => :tickets 

    named_scope :visible_to, lambda { |looking_user| 
     { :include => [ :tickets, :taggings ], 
     :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
     } 
    } 
end 

虽然你可能要回去和编辑一些代码,这是很多的方式更加灵活,可以使用:

Boat.last.users.visible_to(current_user) 

很显然,限制发现,以及限制的目的是什么。由于条件是在运行时动态计算的,因此您可以处理客户端触碰到的下一个奇怪修改。说一些他们的用户有X射线的眼光和洞察力:

class User 
    named_scope :visible_to, lambda { |looking_user| 
     if looking_user.superhuman? 
     {} 
     else 
     { :include => [ :tickets, :taggings ], 
      :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
     } 
     end 
    } 
end 

通过返回一个空的哈希值,可以有效地抵消范围的影响。

+0

你是对的:我只是要实现一些非常类似的事情,编辑现有的代码,并避免把我的整个胸部放在一个无限力量的恶习中。 另外,我追踪了singleton类中的下划线问题:ActiveRecord 2.3 reflection.rb中的第241行。如果这条线是: `active_record.class_name.foreign_key` 而不是 `active_record.name.foreign_key` 一切都将正常工作。 嗨,谢谢你的帮助和建议。对此,我真的非常感激。 – narsk 2009-06-08 14:49:29

0

为什么不只是抓住船上的所有用户,并包括他们的标签。

然后运行快速筛选器以包含&仅返回与查询用户标记相同的用户。

+0

这就是我尝试使用`alias_method_chain`的方法,并且我编辑了示例代码来阐明这一点。我无法做到这一点的原因是,现有的代码很混乱,认为所有的用户总是希望'看到'船上的每个人,我试图在上游捕捉它,让睡觉的狗躺下,所有其他协会的“正义工作”。 – narsk 2009-05-22 14:15:17

0

您使用的是哪个版本的Rails?您是否尝试过升级以查看下划线问题是否已解决?这就像它无法找到作为“tag_id”或“事端”输入的外键。

我的红宝石是有限的,所以我不知道如何在运行时动态地包含正确的方法选项。

只是为了帮助您澄清,您不得不担心这两个地方。您要过滤用户的可见用户,以便他们只能看到具有相同标签的用户。您的结构是:

用户< - >门票< - >船< - >门票< - >用户

...对不对?

因此,您需要将两组票据过滤到具有current_user标签的票据集。

也许你只需要一个current_user.viewable_users()方法,然后通过它过滤所有内容?我不确定你需要保留哪些现有功能。

Blech,我不觉得我在帮你。抱歉。

+0

我使用activerecord 2.1,但它也发生在2.3.2。但是,感谢您抽出时间。 – narsk 2009-06-01 14:59:43

0

你的方法是这个问题。我知道现在似乎很适合在不需要重构现有呼叫站点的地方进行攻击,但是我相信,如果时间过去了,这会再次困扰您,将其作为错误和复杂性的来源。

睡觉的狗躺着回来咬你硬,在我的经验。通常情况下,未来的开发人员不知道你的关联是“魔术”,并假定它只是桶轨。他/她甚至可能甚至没有理由编写一个可能会暴露行为的测试用例,这会增加您仅在生产中遇到错误并且客户端不高兴时发现错误的可能性。你现在储蓄的时间真的值得吗?

Austinfrombostin正在指明方向。不同的语义?不同的名字。第一条规则总是编写代码,尽可能清楚地说明它做了什么。其他任何事情都是疯狂的道路。