业务逻辑是这样的:用户通过联接表在船中,我想我们称该模型为票证。但是当用户实例想要检查船上还有谁时,有一个条件是询问该用户是否有权查看船上的每个人,或者只是船上的某些人。如果用户可以看到每个人,那么正常交易就可以了:some_user.boats.first.users
会返回所有拥有该船票的用户。但是对于一些用户来说,只有人们在船上(就他们而言)是人们,就像餐厅一样。因此,如果用户的票证被“标记”(使用acts_as_taggable
样式系统)和“餐厅”,则从some_user.boats.first.users
返回的唯一用户应该是带有标记为“餐厅”的票证的用户。
仅仅是为了记录,我不是试图设计一些疯狂的东西 - 我试图将这种任意分组楔入一个(大多数)存在的系统。 所以我们得到了:
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代码,但是我找不到任何能在前面的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
会返回所有用户。
任何类型的评论都会受到赞赏,特别是如果我用这种方法咆哮错误的树。谢谢!
答案 0 :(得分: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
通过返回空哈希,可以有效地抵消范围的影响。
答案 1 :(得分:0)
为什么不抓住船上的所有用户并添加他们的标签。
然后运行快速过滤器以包含&amp;仅返回与查询用户具有相同标记的用户。
答案 2 :(得分:0)
您使用的是什么版本的Rails?您是否尝试升级以查看下划线问题是否已修复?它就像找不到外键作为“tag_id”或某些东西。
我的ruby-fu是有限的,所以我不确定如何在运行时动态包含正确的方法选项。
只是为了帮助你澄清,你必须担心这两个地方。您希望过滤用户的可查看用户,以便他们只能看到具有相同标记的用户。你的结构是:
用户&lt; - &gt;门票&lt; - &gt;船&lt; - &gt;门票&lt; - &gt;用户
......对吗?
因此,您需要将两组故障单过滤到具有current_user标记的故障单。
也许您只需要一个current_user.viewable_users()方法,然后通过它过滤所有内容?我不确定你需要保留哪些现有功能。
布莱克,我觉得我根本不在帮助你。遗憾。答案 3 :(得分:0)
你的方法就是问题所在。我知道目前似乎很容易破解你不必重构现有呼叫站点的东西,但我相信,如果时间过去,这将会成为困扰和复杂性的源头。
根据我的经验,睡觉的狗躺着回来咬你。通常以未来开发人员的形式,不知道你的关联是“魔术”,并使用它假设它只是pail ole rails。他/她可能甚至没有理由写一个会暴露行为的测试用例,这会增加你只能在生产中找到bug并且客户不满意的几率。你现在攒钱真的值得吗?Austinfrombostin指明了方向。不同的语义?不同的名字。规则一是始终编写代码,尽可能清楚地说出它的作用。其他任何事情都是疯狂的道路。