我有一个大型Rails应用程序,通过单表继承有34种不同类型的用户。当应用程序最初设计时,假设用户将根据类型具有不同的行为。这个假设是错误的,所以我想重构。
问题是你如何重构用户STI?
User#is_employee?
,User.is_contractor?
)User#type
重命名为User#user_type
,然后根据字段只是为了澄清,用户需要一个“类型”出于ACL的原因,但是对于STI来说,它们实际上只是空模型,而STI会导致问题,屁股中的一般痛苦等等。
答案 0 :(得分:3)
听起来你真正需要的是角色而不是用户类型。我发现当你最终得到很多用户类型时通常会出现这种情况,因为有些用户有多个角色。
一个简单的ACL模型是:用户有很多角色,每个角色都有很多权限。 用户的权限集是用户所属的所有角色的所有权限集。
在更复杂的情况下,权限通常是计算属性而不是数据库中的行,原因很简单,权限有时是目标和基于时间(!)。
@user.can_view_payroll_info_for?(@employee, 2.years.ago) ==> true
@user.can_view_payroll_info_for?(@employee, Time.now) ==> false
大多数情况下,角色的分辨率与您需要的一样高:
@john.has_role(:content_author) ==> true
@john.has_role(:moderator) ==> true
@benny.has_role(:content_author) ==> true
@benny.has_role(:moderator) ==> false
@michael.has_role(:moderator) ==> true
@michael.has_role(:content_author) ==> false
如果您希望将角色规范化,则可以像为用户或连接表的逗号分隔(验证)字符串'角色'列一样简单地实现它。
答案 1 :(得分:0)
所有34个型号都是空的吗?
如果他们能做到:
self.inheritance_column = nil
在用户模型的顶部,您只需检查类型:
if @user.type == "Employee"
# do something
end
答案 2 :(得分:0)
很难回答这个问题而不知道你的“ACL原因”将以何种方式使用该类型,但是......
根据我的经验,我总是和User#user_type
一起去。总而言之,这些案件非常罕见,这很好。虽然我只有一些用户类型。另一个选项(可能是你用第二个选项提到的)是使用method_missing来处理字符串匹配,并允许它表现得像选项1。
例如:
def method_missing(method, *args, &block)
if(method.to_s.starts_with?('is_'))
self.user_type == method.to_s.gsub("is_", "").gsub("?", "")
end
end
如果is_contractor?
是承包商,则user_type
会返回。
请注意,提供的代码段未经测试,并且链接的gsub看起来非常糟糕。我确信这可以写成一个很好的小正则表达式或其他一些聪明的方式。