Ruby on rails 让通用的Pre_过滤器不那么难看?

Ruby on rails 让通用的Pre_过滤器不那么难看?,ruby-on-rails,ruby,Ruby On Rails,Ruby,我有一些Prefore过滤器,用于按资源级别控制对资源的访问。基本思路如下: 用户可以是用户或管理员,并且可以基于“访问”表访问特定资源 对管理员、所有者、特定用户或所有人的访问可以限制资源/方法 一些代码示例最好地说明了这一点。我们有4种应用程序级方法,它们通过before\u filter添加到调用链中。下面是示例控制器类的顶部: before_filter :require_user before_filter :get_object, :only=>[:show, :edit, :

我有一些Prefore过滤器,用于按资源级别控制对资源的访问。基本思路如下:

  • 用户可以是
    用户
    管理员
    ,并且可以基于“访问”表访问特定资源
  • 管理员
    所有者
    、特定用户或所有人的访问可以限制资源/方法
  • 一些代码示例最好地说明了这一点。我们有4种应用程序级方法,它们通过
    before\u filter
    添加到调用链中。下面是示例控制器类的顶部:

    before_filter :require_user
    before_filter :get_object, :only=>[:show, :edit, :update, :destroy]
    before_filter :require_access, :only=>[:show]
    before_filter :require_owner, :only=>[:edit, :update, :destroy]
    
    如您所见,首先我们要求用户登录以访问此控制器中的任何方法。下面是3个方法(在application.rb中定义),以便您可以看到它们的外观:

     private
     def get_object
       begin
         class_name = controller_name.gsub("Controller","").downcase.singularize
         instance_variable_set "@#{class_name}".to_sym, class_name.capitalize.constantize.find(params[:id])
       rescue
         flash[:error] = "You do not have access to that #{class_name}."
         redirect_to "/" and return
       end
     end
    
     private
     def require_owner
       class_name = controller_name.gsub("Controller","").downcase.singularize
       accessable = instance_variable_get("@#{class_name.downcase}")
       unless accessable.user == current_user
         flash[:error] = "You do not have access to that #{class_name.downcase}."
         redirect_to "/" and return
       end
     end
    
     private
     def require_access
       class_name = controller_name.gsub("Controller","").downcase.singularize
       accessable = self.instance_variable_get("@#{class_name.downcase}")
       unless current_user.has_access?(accessable)
         flash[:error] = "You do not have access to that #{class_name.downcase}."
         redirect_to "/" and return
       end
     end
    
    就我所知,从编码的角度来看,这一切都很好。但它真他妈的丑!特别是以下几行:

     class_name = controller_name.gsub("Controller","").downcase.singularize
     obj = instance_variable_get("@#{class_name.downcase}")
    


    有人知道我在这里做什么更优雅一点吗?

    好吧,你可以做以下两件事:

    1-首先删除另外两条
    private
    语句,第一条就足够了。请记住,
    private
    protected
    public
    只是Ruby类中定义的其他方法

    2-最好重构代码以在其方法中设置对象创建:

    def create_object
      class_name = controller_name.gsub("Controller","").downcase.singularize
      obj = instance_variable_get("@#{class_name.downcase}")
    end
    
    def locate_object
     instance_variable_set "@#{class_name}".to_sym class_name.capitalize.constantize.find(params[:id])
    end
    

    你可以做以下两件事:

    1-首先删除另外两条
    private
    语句,第一条就足够了。请记住,
    private
    protected
    public
    只是Ruby类中定义的其他方法

    2-最好重构代码以在其方法中设置对象创建:

    def create_object
      class_name = controller_name.gsub("Controller","").downcase.singularize
      obj = instance_variable_get("@#{class_name.downcase}")
    end
    
    def locate_object
     instance_variable_set "@#{class_name}".to_sym class_name.capitalize.constantize.find(params[:id])
    end
    
    我不知道是否有真正干净的方法可以做到这一点,但这里有一些建议:

    首先,创建一个控制器
    ResourceController
    ,并让所有相关控制器从中继承。(如果此授权适用于所有控制器,则只需使用
    ApplicationController

    现在,在名为
    model\u name
    的超类中实现一个私有方法(就像您的
    class\u name
    ),这样您就不必在每次需要时都派生它。而且,您应该能够通过简单地执行以下操作来推导:

    def model_name
      controller_name.classify
    end
    
    您还可以在返回实际类的超类中实现
    model
    方法:

    def model
      model_name.constantize
    end
    
    此时,您还可以添加如下内容:

    def current_object
      model.find(params[:id])
    end
    
    def current_object_var_name
      "@#{model_name.underscore}"
    end
    
    除了总是使用
    @object
    或类似的东西之外,我看不到使用
    实例变量\u get/set
    的快捷方法。但如果您不想这样做,那么这些行现在就简单一点:

    instance_variable_set current_object_var_name, current_object
    obj = instance_variable_get(current_object_var_name)
    
    在这一点上,您的代码应该更具可读性,并且更加美观

    您可能还想了解一些最新的Rails授权插件正在做什么,尤其是和。

    我不知道是否有一种真正干净的方法可以做到这一点,但这里有一些建议:

    首先,创建一个控制器
    ResourceController
    ,并让所有相关控制器从中继承。(如果此授权适用于所有控制器,则只需使用
    ApplicationController

    现在,在名为
    model\u name
    的超类中实现一个私有方法(就像您的
    class\u name
    ),这样您就不必在每次需要时都派生它。而且,您应该能够通过简单地执行以下操作来推导:

    def model_name
      controller_name.classify
    end
    
    您还可以在返回实际类的超类中实现
    model
    方法:

    def model
      model_name.constantize
    end
    
    此时,您还可以添加如下内容:

    def current_object
      model.find(params[:id])
    end
    
    def current_object_var_name
      "@#{model_name.underscore}"
    end
    
    除了总是使用
    @object
    或类似的东西之外,我看不到使用
    实例变量\u get/set
    的快捷方法。但如果您不想这样做,那么这些行现在就简单一点:

    instance_variable_set current_object_var_name, current_object
    obj = instance_variable_get(current_object_var_name)
    
    在这一点上,您的代码应该更具可读性,并且更加美观


    您可能还想了解一些最新的Rails授权插件正在做什么,特别是。

    结合您的两个答案,我留下了以下相当干净的代码,我真的应该将其放入一个单独的插件中

     private
     def get_resource
       begin
         instance_variable_set current_object_var_name.to_sym, model_name.constantize.find(params[:id])
       rescue
         flash[:error] = "You do not have access to that #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def require_owner
       unless resource.user == current_user
         flash[:error] = "You do not have access to that  #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def require_access
       unless current_user.has_access?(resource)
         flash[:error] = "You do not have access to that #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def resource
       instance_variable_get(current_object_var_name)
     end
    
     def model_name
       @model_name ||= controller_name.classify
     end
    
     def current_object_var_name
       "@#{model_name.underscore}"
     end
    

    结合你的两个答案,我留下了以下相当干净的代码,我真的应该把它们放在一个单独的插件中

     private
     def get_resource
       begin
         instance_variable_set current_object_var_name.to_sym, model_name.constantize.find(params[:id])
       rescue
         flash[:error] = "You do not have access to that #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def require_owner
       unless resource.user == current_user
         flash[:error] = "You do not have access to that  #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def require_access
       unless current_user.has_access?(resource)
         flash[:error] = "You do not have access to that #{model_name}."
         redirect_to "/" and return
       end
     end
    
     def resource
       instance_variable_get(current_object_var_name)
     end
    
     def model_name
       @model_name ||= controller_name.classify
     end
    
     def current_object_var_name
       "@#{model_name.underscore}"
     end
    

    哦,太好了,这些确实很好用。仅此而已,它应该是
    controller\u name.gsub(“controller”,”)。分类
    对吗?嗯……在我的controllers中,
    controller\u name
    方法为您去掉了“controller”一词。换句话说,如果控制器是
    fastcarscocontroller
    ,则
    controller\u name
    返回字符串
    “fast\u cars”
    。试着把它放在一个视图中:
    ,看看它到底给你带来了什么。哦,我明白了。在过去,我使用了params[:controller],但我没有意识到controller_name并没有产生相同的结果。哦,太好了,这些确实工作得很好。仅此而已,它应该是
    controller\u name.gsub(“controller”,”)。分类
    对吗?嗯……在我的controllers中,
    controller\u name
    方法为您去掉了“controller”一词。换句话说,如果控制器是
    fastcarscocontroller
    ,则
    controller\u name
    返回字符串
    “fast\u cars”
    。试着把它放在一个视图中:
    ,看看它到底给你带来了什么。哦,我明白了。在过去,我使用了params[:controller],但我没有意识到controller_name不会产生相同的结果。