ChatGPT解决这个技术问题 Extra ChatGPT

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

我刚刚开始使用我的第一个 Ruby on Rails webapp。我有一堆不同的模型、视图、控制器等等。

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

举一个具体的例子,我想要一个常量 COLOURS = ['white', 'blue', 'black', 'red', 'green']。这在模型和视图中随处可见。我可以在哪里只在一个地方定义它以便可以访问它?

我试过的:

model.rb 文件中与它们最相关的常量类变量,例如@@COLOURS = [...]。但是我找不到一种理智的方式来定义它,以便我可以在我的视图中编写 Card.COLOURS 而不是像 Card.first.COLOURS 这样的笨拙的东西。

模型上的一种方法,例如 def colors ['white',...] end - 同样的问题。

application_helper.rb 中的一个方法 - 这是我目前正在做的事情,但是助手只能在视图中访问,而不是在模型中

我想我可能在 application.rb 或 environment.rb 中尝试过一些东西,但这些看起来并不正确(而且它们似乎也不起作用)

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

我很感激这真的太晚了,但对于其他读者,我想知道为什么你不只是在你的模型中定义它们并使用你的控制器将它们传递给你的视图。通过这种方式,您可以更清晰地分离关注点 - 而不是在控制器/视图和模型/视图之间创建依赖关系。
@TomTom:将这些常量传递给需要它们的每个视图和助手?换句话说,让控制器知道哪些视图需要哪些常量?这听起来更像是对 MVC 的违反。

H
Holger Just

如果您的模型真的对常量“负责”,那么您应该将它们粘贴在那里。您可以创建类方法来访问它们,而无需创建新的对象实例:

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

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


非常感谢。看起来我错过了定义类方法的 Ruby class-fu。但我实际上喜欢这种情况下的初始化选项,因为颜色用于多个模型和视图中。非常感谢!
如果走 config/initializers/my_constants.rb 路线,请记住重新启动服务器:touch tmp/restart.txt
def self.colours 示例并不理想。每次调用 def self.colours将返回一个新的数组实例#freeze 在这种情况下无济于事。最佳实践是将其声明为 Ruby 常量,在这种情况下,您将始终返回相同的对象。
@Zabba 如果单个数组的分配对您的应用程序产生了显着影响,那么您可能首先不应该使用 Ruby ......也就是说,每次使用一个方法并返回一个全新的数组可能会有几个优点:(1)它是最接近 Ruby 类边界上不可变对象的东西,(2)您在类上保持统一接口,以后可以根据固有状态调整返回值(例如,通过从数据库中读取颜色)而不更改界面。
@Holger Just,至少您的一个目标仍然可以使用常量来实现:class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; end 也就是说,以任何语言分配数组都可能存在问题;一方面,它没有(好的)理由使用内存。如果从数据库加载,并且想要缓存值,也可以使用类实例变量,可以使用 def self.colours 方法延迟加载。虽然同意不变性方面。
Z
Zabba

一些选项:

使用常量:

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

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

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

如果它是一个真正的全局常量(避免这种性质的全局常量),您还可以考虑在 config/initializers/my_constants.rb 中放置一个顶级常量。


呵呵。公平的评论 - 从我的例子中输入内存时出现语法错误 :) 感谢您的提示!
然后 extend 类中的模块,以便它可以与 Card.COLOURS 一起使用。
使用 extend 时,它对我不起作用。使用 include 时,我可以访问:Card::COLOURS
您绝对不应将其放在 /models 下。如果你创建一个初始化器会好很多。
@linkyndy 我会说可以将它放在 /models 下,但前提是它位于模块内,例如 module Constants; COLOURS = ...; end 在名为 models/constants.rb 的文件中。
H
Halil Özgür

从 Rails 4.2 开始,您可以使用 config.x 属性:

# config/application.rb (or config/custom.rb if you prefer)
config.x.colours.options = %w[white blue black red green]
config.x.colours.default = 'white'

这将作为:

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

另一种加载自定义配置的方法:

# config/colours.yml
default: &default
  options:
    - white
    - blue
    - black
    - red
    - green
  default: white
development:
  *default
production:
  *default
# config/application.rb
config.colours = config_for(:colours)
Rails.configuration.colours
# => {"options"=>["white", "blue", "black", "red", "green"], "default"=>"white"}
Rails.configuration.colours['default']
# => "white"

在 Rails 56,除了 config.x 之外,您还可以直接使用 configuration 对象进行自定义配置。但是,它只能用于非嵌套配置:

# config/application.rb
config.colours = %w[white blue black red green]

它将以下列形式提供:

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

我最喜欢Rails.configuration.colours(虽然我希望它不要那么长)
@TomRossi 我同意,例如 configconfiguration 一样好。我们可能希望在某个时候获得一条捷径:)
这仍然是 rails 6 中定义要在多个控制器之间共享的常量的最佳方式吗?感谢你的回答!
@Crashalot 它仍然列在文档中。 “最好的”?这取决于。它可以在他们共同的祖先。或者在 ApplicationController 中,如果两者之间没有其他内容。如果常量与控制器没有直接关系,我仍然会考虑全局配置等。
@HalilÖzgür 感谢您的回复。您如何定义共同祖先中的常量?
j
jacklin

如果一个以上的类需要一个常量,我将它放在 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

不错,config/initializers/constants.rb 可能会是更好的选择
我也使用它,但最近遇到了在 application.rb 中无法访问这些常量的问题
我的常量正在工作,但由于某种原因停止了(因为我的文件以某种方式移出了初始化程序)。检查此答案后,我仔细查看并将它们移回并现在开始工作。谢谢
我认为不需要 attr_accessor 。您是在谈论任何特定的 Rails 版本吗?
R
Riccardo

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

  # app/config/application.yml
  defaults: &defaults
    cool:
      sweet: 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/)。它非常灵活。而且您不需要发明自行车。


G
Ghanshyam Anand

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

https://i.stack.imgur.com/UODRT.png

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

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

颜色常量.rb

https://i.stack.imgur.com/2X2Ny.png

别忘了重启服务器


S
Steve Ross

使用类方法:

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

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


G
Greg Hurrell

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

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

D
Dennis

放置应用程序范围的全局常量的常见位置是在 config/application 中。

module MyApp
  FOO ||= ENV.fetch('FOO', nil)
  BAR ||= %w(one two three)

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

S
SriSri

我通常在我的 rails 程序中有一个“查找”模型/表,并将其用于常量。如果常数在不同的环境中有所不同,这将非常有用。此外,如果您有扩展它们的计划,假设您想在以后添加“黄色”,您可以简单地在查找表中添加一个新行并完成它。

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

这是我的迁移代码的样子:

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

我使用 seed.rb 来预填充它。

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

b
bird

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

COLOURS = %w(white blue black red green)

谢谢!其他人已经提到了这一点。这是 Holger 回答的最后一行,Zabba 也提到了这种技术,尽管 Zabba 对此提出了警告。
f
fangxing

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

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


H
Hà Tiến Đạt

我认为您可以使用 gem config

https://github.com/rubyconfig/config

易于处理和编辑