使用单例类动态覆盖/添加ActiveRecord关联

业务逻辑是这样的:用户通过连接表在船中,我想我们称该模型为票证。 但是当用户实例想要检查船上还有谁时,有一个条件是询问该用户是否有权查看船上的每个人,或者只是船上的某些人。 如果用户可以看到每个人,那么正常交易就可以了: some_user.boats.first.users返回所有用户都有该船的票证。 但是对于一些用户来说,只有人们在船上(就他们而言)是人们,就像餐厅一样。 因此,如果用户的票证被“标记”(使用acts_as_taggable样式系统)和“Dining Room”,则从some_user.boats.first.users返回的唯一用户应该是具有标记为“Dining Room”的票证的用户。

只是为了记录,我并不是试图设计一些疯狂的东西 – 我试图将这种任意分组楔入一个(大多数)存在的系统。 所以我们得到了:

 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(< :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代码,但是我找不到任何能在前面的SQL中使用下划线加上’id’前缀的地方。 我知道加载ActiveRecord类时会加载关联,我假设单例类也是如此。 耸耸肩

我还使用了alias_method_chain

 singleton = class << a_user_instance ; self ; end singleton.class_eval(< :tags, :conditions => {:'tags.id' => tags}) end alias_method_chain :tickets, :tag_filtering code ) 

但是,虽然该方法产生了所需的票证,但这些票证上的任何联接都使用类中的条件,而不是虚拟类。 some_user.boats.first.users返回所有用户。

任何类型的评论都将受到赞赏,特别是如果我用这种方法咆哮错误的树。 谢谢!

因此,对于您的下划线问题的猜测是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 

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

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

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

你使用的是什么版本的Rails? 您是否尝试升级以查看下划线问题是否已修复? 它就像找不到外键作为“tag_id”或somethin’。

我的ruby-fu是有限的,所以我不确定如何在运行时动态包含正确的方法选项。

只是为了帮助你澄清,你必须担心这两个地方。 您希望过滤用户的可查看用户,以便他们只能看到具有相同标记的用户。 你的结构是:

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

… 对?

因此,您需要将两组故障单过滤到具有current_user标记的故障单。

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

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

你的方法就是问题。 我知道目前似乎很容易破解你不必重构现有呼叫站点的东西,但我相信,如果时间过去,这将会成为困扰和复杂性的源头。

根据我的经验,睡觉的狗躺着回来咬你。 通常以未来开发人员的forms,不知道你的关联是“魔术”并使用它假设它只是pail ole rails。 他/她可能甚至没有理由写一个会暴露行为的测试用例,这会增加你只能在生产中找到bug并且客户端不满意的几率。 你现在节省的时间真的值得吗?

Austinfrombostin指明了方向。 不同的语义? 不同的名字。 规则一是始终编写代码,尽可能清楚地说出它的作用。 其他任何事情都是疯狂的道路。