2010-10-20 59 views
1

我有一个模型,它看起来是这样的:Rails:将模型添加到模型以基于current_user执行检查?

class Comment < ActiveRecord::Base 
    ... 
    #allow editing comment if it is moderated and the user passed-in 
    #is the one that owns the comment 
    def can_edit?(user) 
    moderated? and user.Type == User and user.id == self.user_id 
    end 
    ... 
end 

并在View的电话:

<%= link_to 'Show Comment', @comment if @comment.can_show?(current_user) %> 

我需要写在许多不同的车型很多这样的方法 - 排序验证检查来看看是否允许current_user 在模型上做些什么。

但它感觉很累赘 - 特别是需要检查传入的user确实是User类型的对象。

什么是干净的,最好的做法,做这种事情?我在正确的轨道上吗? (即我应该加入这种方法的模型或其他地方)

注意

我使用范围的查询来获得的评论和其他车型,但在某些情况下,我不能范围查询所以我必须使用can_xxxx?方法

Ps。我在做什么被认为是“胖胖的模特”?

回答

1

创建一个module包含所有需要授权的类的授权方法和include模块。

将名为authorization.rb的文件添加到app/models目录。

module Authorization 

    def can_edit?(user) 
    moderated? and user.is_a?(User) and user.id == self.user_id 
    end 

    def self.included(base) 
    base.send(:extend, ClassMethods) 
    end 

    module ClassMethods 
    # add your class methods here. 
    end 
end 

一个名为authorization.rb文件添加到config/initializers目录。现在

%w(
Comment 
Post 
).each do |klass| 
    klass.constantize.include(Authorization) 
end 

CommentPost车型将拥有所有的授权方法。

其他方法是使用您当前的named_scope。

class Post 
    named_scope :accessible, lambda { |user| 
    { 
     :conditions => { :user_id => user.id, :moderated => true} 
    } 
    } 
end 

Post控制器动作

class PostsController 

    def index 
    @posts = Post.acessible(current_user) 
    # process data 
    end 

    def show 
    # throws record not found when the record is not accessible. 
    @post = Post.acessible(current_user).find(params[:id]) 
    # process data 
    end 

end 

我喜欢这种方法,因为它使用相同的逻辑用于访问对象的数组或一个单独的对象。

您可以将named_scope添加到模块,以避免重复定义:

module Authorization 
    def self.included(base) 
    base.named_scope :accessible, lambda { |user| 
     { 
     :conditions => { :user_id => user.id, :moderated => true} 
     } 
    } 
    end 

    module ClassMethods 
    # add your class methods here. 
    end 
end 

确保包括在模块中所需的类正如前面建议。

+0

感谢*很多*。这种方法感觉更清洁。我想现在对于你的技术有很多深入的理解! – Zabba 2010-10-21 15:34:23

1

我不认为你在做什么是必然的错误。我看到三种简化方法:

1)跟踪self.user以及self.user_id。然后,您可以说:

def can_show?(user) 
    moderated ? and user == self.user 
end 

请注意,这可能会增加DB查找时间和/或内存占用量的开销。

2)使用#is_a?为了检查祖先,而不是刚下课的平等:

def can_show?(user) 
    moderated ? and user.is_a?(User) and user.id == self.user_id 
end 

3)如果传递一个非用户是错误的,你可能想提高一个错误,当发生这种情况:

def can_show?(user) 
    raise "expected User, not #{ user.class.to_s }" unless user.is_a?(User) 
    moderated ? and user.id == self.user_id 
end 

至于Q2,我没有听说过“胖模特”的术语。它在任何地方被引用?

+0

我在网上看了一些最佳实践提到“胖模型和瘦控制器” – Zabba 2010-10-21 00:09:41

+0

我如何跟踪评论模型中的用户?它确实有一个user_id字段。 – Zabba 2010-10-21 00:14:39

1

重新:脂肪模型和瘦控制器

这是在控制器逻辑推到模型中而不是它(或更糟的是,视图)的想法。

一大好处是帮助测试;也是在模型中放置更多逻辑的重点,而不是控制器。请记住,让控制器适用于多个模型并不罕见。

将逻辑放入模型而不是控制器通常意味着业务规则正在被烘焙到模型中 - 这正是它们所属的地方。

一个可能的缺点是控制器可用的任何信息在模型中不可用需要显式传递到模型的方法或使用模型的实例变量“设置”。

您需要将当前用户传入模型的示例说明了此问题。

总的来说,我和其他许多人都发现,胖胖的模特往往比没有更好。