Делать generic before_filters менее уродливым? - PullRequest
1 голос
/ 18 декабря 2009

У меня есть несколько фильтров до этого, которые я использую для контроля доступа к ресурсам на уровне ресурсов. Основная идея заключается в следующем:

  1. Пользователь может быть user или admin и может иметь доступ к определенным ресурсам на основе таблицы «доступов».
  2. Ресурсы / методы могут быть ограничены в доступе 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

Это все отлично , насколько я могу судить, с точки зрения кодирования. Но это так чертовски уродливо! В частности строки:

 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 ]

5 голосов
/ 18 декабря 2009

Я не знаю, есть ли действительно чистый способ сделать это, но вот несколько советов:

Сначала создайте контроллер 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 .

2 голосов
/ 18 декабря 2009

Ну, вы можете сделать 2 следующие вещи:

1- Сначала удалите 2 других оператора 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
1 голос
/ 18 декабря 2009

Объединяя оба ваших ответа, у меня остается следующий довольно чистый код, который я действительно должен поместить в отдельный плагин.

 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...