我刚刚开始使用我的第一个Ruby on Rails web应用程序。我有很多不同的模型,视图,控制器等等。

我想找到一个好地方粘真正的全局常数的定义,适用于我的整个应用程序。特别是,它们适用于我的模型的逻辑,并在我的视图中所做的决定。我找不到任何DRY的地方来放置这些定义,它们既适用于我的所有模型,也适用于我的所有视图。

举个具体的例子,我想要一个常量colors = ['white', 'blue', 'black', 'red', 'green']。这在模型和视图中都被广泛使用。我可以在哪里只在一个地方定义它,以便它是可访问的?

我的尝试:

Constant class variables in the model.rb file that they're most associated with, such as @@COLOURS = [...]. But I couldn't find a sane way to define it so that I can write in my views Card.COLOURS rather than something kludgy like Card.first.COLOURS. A method on the model, something like def colours ['white',...] end - same problem. A method in application_helper.rb - this is what I'm doing so far, but the helpers are only accessible in views, not in models I think I might have tried something in application.rb or environment.rb, but those don't really seem right (and they don't seem to work either)

是否没有办法定义从模型和视图都可以访问的东西?我的意思是,我知道模型和视图应该是分开的,但在某些领域,它们肯定会有需要引用相同的领域特定知识的时候?


当前回答

如果一个常量在多个类中需要,我会把它放在config/initializers/constant中。Rb总是全大写(下面的状态列表被截断)。

STATES = ['AK', 'AL', ... 'WI', 'WV', 'WY']

除了在模型代码中,它们可以在整个应用程序中使用:

    <%= form.label :states, %>
    <%= form.select :states, STATES, {} %>

要在模型中使用该常量,请使用attr_accessor使该常量可用。

class Customer < ActiveRecord::Base
    attr_accessor :STATES

    validates :state, inclusion: {in: STATES, message: "-- choose a State from the drop down list."}
end

其他回答

如果你的模型真的对常数“负责”,你应该把它们放在那里。你可以创建类方法来访问它们,而不需要创建新的对象实例:

class Card < ActiveRecord::Base
  def self.colours
    ['white', 'blue']
  end
end

# accessible like this
Card.colours

或者,您可以创建类变量和访问器。然而,不鼓励这样做,因为类变量在继承和多线程环境中可能会令人惊讶。

class Card < ActiveRecord::Base
  @@colours = ['white', 'blue'].freeze
  cattr_reader :colours
end

# accessible the same as above
Card.colours

如果需要,上面的两个选项允许您在每次调用访问器方法时更改返回的数组。如果你有一个真正不可改变的常数,你也可以在模型类中定义它:

class Card < ActiveRecord::Base
  COLOURS = ['white', 'blue'].freeze
end

# accessible as
Card::COLOURS

您还可以创建全局常量,可以从初始化器中的任何位置访问,如下例所示。这可能是最好的地方,如果你的颜色真的是全球性的,并在多个模型环境中使用。

# put this into config/initializers/my_constants.rb
COLOURS = ['white', 'blue'].freeze

# accessible as a top-level constant this time
COLOURS

注意:当我们在上面定义常量时,通常我们想要冻结数组。这可以防止其他代码以后(无意中)修改数组,例如添加一个新元素。一旦一个对象被冻结,它就不能再改变了。

使用类方法:

def self.colours
  ['white', 'red', 'black']
end

然后模型。colors将返回该数组。或者,创建初始化式并将常量包装在模块中,以避免名称空间冲突。

另一个选择,如果你想在一个地方定义你的常数:

module DSL
  module Constants
    MY_CONSTANT = 1
  end
end

但是仍然使它们在全局可见,而不必以完全合格的方式访问它们:

DSL::Constants::MY_CONSTANT # => 1
MY_CONSTANT # => NameError: uninitialized constant MY_CONSTANT
Object.instance_eval { include DSL::Constants }
MY_CONSTANT # => 1

根据你的情况,你也可以定义一些环境变量,在ruby代码中通过ENV['some-var']来获取,这个方法可能不适合你,但是我希望它能帮助到其他人。

例如:你可以创建不同的文件。development_env, .production_env, .test_env,并根据你的应用程序环境加载它,检查这个gen dotenv-rails,它为你的应用程序自动化。

在我的应用程序中,我在初始化器中创建了常量文件夹,如下所示:

我通常在这些文件中保持不变。

在您的情况下,您可以在constants文件夹下创建文件colors_constant.rb

colors_constant.rb

别忘了重启服务器