Ruby on Rails:在哪里定义全局常量?

Ruby on Rails: Where to define global constants?

我刚刚开始使用我的第一个RubyonRails webapp。我有很多不同的模型、视图、控制器等等。

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

举个具体的例子,我需要一个常数COLOURS = ['white', 'blue', 'black', 'red', 'green']。无论是在模型还是视图中,这都被广泛使用。我在哪里可以只在一个地方定义它,这样它就可以被访问?

我试过的:

  • model.rb文件中最相关的常量类变量,如@@COLOURS = [...]。但是我找不到一个合理的方法来定义它,这样我就可以在我的视图中写下Card.COLOURS,而不是像Card.first.COLOURS这样的笨拙的东西。
  • 模型上的一种方法,类似于def colours ['white',...] end一样的问题。
  • application_helper.rb中的一个方法-这是我目前正在做的,但是这些助手只能在视图中访问,而不能在模型中访问。
  • 我想我可能在application.rb或environment.rb中尝试过一些方法,但这些方法似乎并不正确(它们也似乎不起作用)。

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


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

1
2
3
4
5
6
7
8
class Card < ActiveRecord::Base
  def self.colours
    ['white', 'blue']
  end
end

# accessible like this
Card.colours

或者,您可以创建类变量和访问器。但是,这是不鼓励的,因为类变量在继承和多线程环境中可能会表现得令人吃惊。

1
2
3
4
5
6
class Card < ActiveRecord::Base
  @@colours = ['white', 'blue']
  cattr_reader :colours
end

# accessible the same as above

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

1
2
3
4
5
6
class Card < ActiveRecord::Base
  COLOURS = ['white', 'blue'].freeze
end

# accessible as
Card::COLOURS

您还可以创建全局常量,这些常量可以从初始值设定项中的任何位置访问,如下例所示。如果您的颜色确实是全球性的,并且在多个模型环境中使用,那么这可能是最好的地方。

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

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


一些选项:

使用常量:

1
2
3
class Card
  COLOURS = ['white', 'blue', 'black', 'red', 'green', 'yellow'].freeze
end

使用类实例变量延迟加载:

1
2
3
4
5
class Card
  def self.colours
    @colours ||= ['white', 'blue', 'black', 'red', 'green', 'yellow'].freeze
  end
end

如果它是一个真正的全局常量(不过,请避免使用这种性质的全局常量),您也可以考虑将例如,config/initializers/my_constants.rb中的顶级常量。


从Rails 5.0开始,您可以直接使用configuration对象进行自定义配置:

config/application.rb中(如果您愿意,也可以选择config/custom.rb)

1
config.colours = %w(white blue black red green)

它将提供:

1
Rails.configuration.colours # => ["white","blue","black","red","green"]

注意:对于4.2版,需要使用config.x属性:

1
config.x.colours = %w(white blue black red green)

其可用形式为:

1
Rails.configuration.x.colours # => ["white","blue","black","red","green"]

如果在多个类中需要一个常量,我将它放在config/initializers/contant.rb中,并始终保持所有大写(下面的状态列表被截断)。

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

它们通过应用程序提供,但在模型代码中除外,例如:

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

要在模型中使用常量,请使用attr_访问器使该常量可用。

1
2
3
4
5
class Customer < ActiveRecord::Base
    attr_accessor :STATES

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


对于应用程序范围的设置和全局常量,我建议使用settingslogic。这些设置存储在YML文件中,可以从模型、视图和控制器访问。甚至更多。。您可以为所有环境创建不同的设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  # app/config/application.yml
  defaults: &defaults
    cool:
      saweet: nested settings
    neat_setting: 24
    awesome_setting: <%="Did you know 5 + 5 = #{5 + 5}?" %>

    colors:"white blue black red green"

  development:
    <<: *defaults
    neat_setting: 800

  test:
    <<: *defaults

  production:
    <<: *defaults

在视图中的某个地方(我更喜欢这种类型的辅助方法),或者在您可以得到的模型中,例如,颜色数组Settings.colors.split(/\s/)。它非常灵活。你不需要发明自行车。


使用类方法:

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

然后,Model.colours将返回该数组。或者,创建初始值设定项并将常量包装在模块中以避免命名空间冲突。


config/application中,放置应用程序范围的全局常量是一个常见的地方。

1
2
3
4
5
6
7
8
module MyApp
  FOO ||= ENV.fetch('FOO', nil)
  BAR ||= %w(one two three)

  class Application < Rails::Application
    config.foo_bar = :baz
  end
end

另一个选项,如果要在一个位置定义常量:

1
2
3
4
5
module DSL
  module Constants
    MY_CONSTANT = 1
  end
end

但仍然可以使它们在全球范围内可见,而无需以完全合格的方式访问它们:

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

尝试将所有常量保持在一个位置,在我的应用程序中,我在初始值设定项中创建了常量文件夹,如下所示:

enter image description here

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

在您的例子中,您可以在constants文件夹下创建文件作为colors_constant.rb

颜色_常量.rb

enter image description here

别忘了重启服务器


我通常在Rails程序中有一个"查找"模型/表,并将其用于常量。如果常数在不同的环境中会有所不同,那么它是非常有用的。此外,如果您计划扩展它们,比如说希望在以后的日期添加"黄色",则只需向查阅表格中添加新行即可完成。

如果您授予管理员修改此表的权限,他们将不会到您这里进行维护。)干燥。

以下是我的迁移代码的外观:

1
2
3
4
5
6
7
8
9
10
class CreateLookups < ActiveRecord::Migration
  def change
    create_table :lookups do |t|
      t.string :group_key
      t.string :lookup_key
      t.string :lookup_value
      t.timestamps
    end
  end
end

我使用seeds.rb预先填充它。

1
Lookup.find_or_create_by_group_key_and_lookup_key_and_lookup_value!(group_key: 'development_COLORS', lookup_key: 'color1', lookup_value: 'red');

根据您的情况,您还可以定义一些环境变量,并通过Ruby代码中的ENV['some-var']获取它,这个解决方案可能不适合您,但我希望它可以帮助其他人。

示例:您可以创建不同的文件.development_env.production_env.test_env并根据您的应用程序环境加载它,请检查为您自动执行此操作的gen dotenv rails。


全局变量应在config/initializers目录中声明。

1
COLOURS = %w(white blue black red green)