Rails Metaprograming / Refactoring / DRYing控制器

时间:2014-08-12 15:47:19

标签: ruby ruby-on-rails-4 metaprogramming

我有一个带有多个动作的控制器。许多人遵循这种模式:

def favorites
  @favorites = Favorite.where(organization_id: @resource.id).page(params[:page]).per(50)
end

这不仅仅是收藏夹,而且还有下载,搜索,列表等,它们都非常相似,我想创建一个我可以在before_filter中调用的方法。像这样:

def set_instance_variable
  subject = __method__
  class = __method__.singularize.constantize
  instance_variable = self.class.instance_variable_set("@#{subject}", "#{class}.where(organization_id: @resource.id).page(params[:page]).per(50)")
end

这里的语法可能有些偏差,但我知道这不起作用,因为__method__将始终是set_instance_variable而不是调用它的父方法。

有没有办法根据定义它们的方法动态设置实例变量?上面这个例子是否在正确的轨道上?

1 个答案:

答案 0 :(得分:1)

我喜欢CanCan库处理这个问题的方式。使用CanCan,您可以在控制器顶部调用类方法:

load_resource

CanCan然后看:

  • 您决定是否需要收藏品或单一资源的行动,

  • 用于确定要加载的类的控制器的名称

  • 添加范围的授权规则,例如您的organization_id限制(cancan也是用于定义这些范围的库)

我认为分页和资源加载是分开的,你不应该把它们放在同一个方法中。我正在拍摄这样的界面:

class FavoritesController
  load_resource
  paginate_resource only: [:index]

  def show
    # @favorite loaded here
  end

  def index
    # @favorites loaded and paginated here
  end
end

https://github.com/CanCanCommunity/cancancan/blob/develop/lib/cancan/controller_resource.rb#L29

如果在您的应用程序中使用非平静资源更有意义,那么您无法重新使用基于约定的事物cancan,而是必须定义您自己的函数。考虑这样的事情:

def favorites
  @favorites = load_resource Favorite
end


private

def load_resource(klass)
  klass.where(organization_id: @resource.id).page(params[:page]).per(50)
end