Ruby on Rails模式,用于设置类默认值并允许对子类进行覆盖

时间:2009-07-02 06:17:08

标签: ruby-on-rails ruby design-patterns structure

基本上我想实现一个简单的Rails扩展来定义控制器中方法的严重性,以便我可以适当地限制它们的使用。例如,我在一个抽象的超类中定义了默认的restful动作:

view_methods :index, :show
edit_methods :new, :create, :edit, :update
destroy_methods :destroy

然后我将进入一个非抽象的控制器调用:

edit_methods :sort

将该特定控制器上的sort方法添加为编辑级别方法。

然后我可以使用before_filter来检查当前正在执行的操作的级别,如果我的逻辑确定当前用户无法执行,则中止它。

麻烦的是,我在弄清楚如何设置这种结构时遇到了麻烦。到目前为止,我尝试过这样的事情:

class ApplicationController

  @@view_methods = Array.new
  @@edit_methods = Array.new
  @@destroy_methods = Array.new

  def self.view_methods(*view_methods)
    class_variable_set(:@@view_methods, class_variable_get(:@@view_methods) << view_methods.to_a)
  end

  def self.edit_methods(*edit_methods)
    class_variable_set(:@@edit_methods, self.class_variable_get(:@@edit_methods) << edit_methods.to_a)
  end

  def self.destroy_methods(*destroy_methods)
    @@destroy_methods << destroy_methods.to_a
  end

  def self.testing
    return @@edit_methods
  end


  view_methods :index, :show
  edit_methods :new, :create, :edit, :update
  destroy_methods :destroy

end

上述三种方法的目的不同,只是为了向您展示我的尝试。第三个工作,但无论我测试什么控制器,返回相同的结果。可能是因为类变量存储在应用程序控制器中,因此全局更改。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:3)

问题是您的类变量是继承的,但指向Array的同一个实例。如果您更新一个,它也将在继承Array

的所有类上更新

ActiveSupport通过使用多种方法扩展Class类来定义可继承的类属性,为此问题提供solution。它们在Rails内部随处使用。一个例子:

class ApplicationController
  class_inheritable_array :view_method_list
  self.view_method_list = []

  def self.view_methods(*view_methods)
    self.view_method_list = view_methods  # view_methods are added
  end

  view_methods :index, :show
end

现在,您可以在ApplicationController中设置默认值并稍后覆盖它们。

class MyController < ApplicationController
  view_method :my_method
end

ApplicationController.view_method_list #=> [:index, :show]
MyController.view_method_list #=> [:index, :show, :my_method]

您甚至可以在控制器上使用view_method_list作为实例方法(例如MyController.new.view_method_list)。

在您的示例中,您没有定义从列表中删除方法的方法,但其目的是执行以下操作(如果您需要):

# given the code above...
class MyController
  self.view_method_list.delete :show
end
MyController.view_method_list #=> [:index, :my_method]

答案 1 :(得分:0)

我把它变成了这样一个插件:

module ThreatLevel

  def self.included(base)
    base.send :extend, ClassMethods
  end

  module ClassMethods
    def roger_that!
      class_inheritable_array :view_method_list, :edit_method_list, :destroy_method_list

      self.view_method_list = Array.new
      self.edit_method_list = Array.new
      self.destroy_method_list = Array.new

      def self.view_methods(*view_methods)
        self.view_method_list = view_methods
      end

      def self.edit_methods(*edit_methods)
        self.edit_method_list = edit_methods
      end

      def self.destroy_methods(*destroy_methods)
        self.destroy_method_list = destroy_methods
      end

      view_methods :index, :show
      edit_methods :new, :create, :edit, :update
      destroy_methods :destroy
    end
  end
end

ActionController::Base.send :include, ThreatLevel

打电话给roger_that!在你想让它生效的super_controller上就可以了。xt