使通用的before_filters稍微不那么难看?

时间:2009-12-18 18:00:04

标签: ruby-on-rails ruby

我之前有一些过滤器,我用它来控制资源级别的资源访问。基本思路如下:

  1. 用户可以是useradmin,可以根据“访问权限”表访问特定资源。
  2. 可以限制访问adminowner,特定用户或所有人的资源/方法。
  3. 一些代码示例最能说明这一点。我们有4个应用程序级方法,使用before_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
    

    从编码的角度来看,这就是 fine 。但它真是太神奇了!特别是行:

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

    OR

     instance_variable_set "@#{class_name}".to_sym, class_name.capitalize.constantize.find(params[:id])
    

    有没有人知道我在这里做的事情有点优雅?

3 个答案:

答案 0 :(得分:5)

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

首先,创建一个控制器ResourceController并让所有相关的控制器继承它。 (如果此授权适用于所有控制器,则可以使用ApplicationController。)

现在,在名为model_name的超类中实现一个私有方法(就像您的class_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

除了始终使用instance_variable_get/set或类似内容之外,我没有看到使用@object的快捷方式。但是如果你不想这样做,那些线条现在变得更简单了:

instance_variable_set current_object_var_name, current_object
obj = instance_variable_get(current_object_var_name)

此时,您的代码应该更具可读性,并且更加漂亮。

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

答案 1 :(得分:2)

嗯,你可以做以下两件事:

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

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

答案 2 :(得分:1)

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

 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