我之前有一些过滤器,我用它来控制资源级别的资源访问。基本思路如下:
user
或admin
,可以根据“访问权限”表访问特定资源。admin
,owner
,特定用户或所有人的资源/方法。一些代码示例最能说明这一点。我们有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])
有没有人知道我在这里做的事情有点优雅?
答案 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授权插件正在做什么,特别是cancan和declarative_authorization。
答案 1 :(得分:2)
嗯,你可以做以下两件事:
1-首先删除另外两个private
语句,第一个就足够了。请记住private
,protected
和public
只是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