Ruby on Rails:グローバル定数はどこで定義するのですか?


213

最初のRuby on Rails Webアプリケーションを始めたばかりです。さまざまなモデル、ビュー、コントローラーなどがたくさんあります。

アプリ全体に適用される、真にグローバルな定数の定義を固執するのに適した場所を見つけたいと思っています。特に、それらは私のモデルのロジックと私の見解で行われた決定の両方に適用されます。これらの定義を私のすべてのモデルとすべての私の見解の両方で利用できる場所に置くためのDRYの場所を見つけることができません。

特定の例をとるには、定数が必要ですCOLOURS = ['white', 'blue', 'black', 'red', 'green']。これは、モデルとビューの両方で、至る所で使用されています。アクセスできるように、1つの場所でどこに定義すればよいですか?

私が試したこと:

  • それらが最も関連付けられているmodel.rbファイル内の定数クラス変数(など)@@COLOURS = [...]。しかし、私はそれを明確に定義する方法を見つけることができなかったので、私は自分の意見Card.COLOURSでのような何か不器用なものではなく書くことができますCard.first.COLOURS
  • モデルのメソッドのようなものdef colours ['white',...] end-同じ問題。
  • application_helper.rbのメソッド-これは私がこれまで行っていることですが、ヘルパーはモデルではなくビューでのみアクセスできます
  • 私はapplication.rbまたはenvironment.rbで何かを試したかもしれないと思いますが、それらは本当に正しく見えません(そしてそれらもどちらも動作しないようです)

モデルとビューの両方からアクセスできるように何かを定義する方法はありませんか?つまり、モデルとビューを分離する必要があることはわかっていますが、確かに一部のドメインでは、同じドメイン固有の知識を参照する必要がある場合がありますか?



これが本当に遅いことを感謝しますが、他の読者にとっては、モデルでそれらを定義せず、コントローラーを使用してビューにそれらを渡さなかったのはなぜでしょうか。このようにして、コントローラー/ビューとモデル/ビューの間に依存関係を作成するのではなく、問題をより明確に分離できます。
トムトム

2
@TomTom:これらの定数を各ビューとそれらを必要とするヘルパーに渡しますか?言い換えれば、どのビューがどの定数を必要とするかをコントローラーに認識させますか?それはMVCの違反のように聞こえます。
AlexC 2014年

回答:


228

モデルが定数に対して本当に「責任がある」場合、定数をそこに固定する必要があります。新しいオブジェクトインスタンスを作成せずに、クラスメソッドを作成してそれらにアクセスできます。

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

# accessible like this
Card.colours

または、クラス変数とアクセサーを作成できます。ただし、クラス変数は継承やマルチスレッド環境で一種の意外な動作をする可能性があるため、これはお勧めできません。

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

# accessible the same as above

上記の2つのオプションを使用すると、必要に応じて、アクセサーメソッドを呼び出すたびに返される配列を変更できます。真に変更できない定数がある場合は、モデルクラスで定義することもできます。

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

注:上記の定数を定義する場合、多くの場合freeze、配列を使用します。これにより、他のコードが新しい要素を追加するなどして後で(偶然に)配列を変更することを防ぎます。オブジェクトが凍結されると、変更できなくなります。


1
どうもありがとうございました。クラスメソッドを定義するためのRuby class-fuが欠けていたようです。ただし、この場合、色は複数のモデルとビューで使用されるため、実際にはイニシャライザオプションが好きです。どうもありがとう!
AlexC 2010年

21
行く場合config/initializers/my_constants.rbのルートを、サーバを再起動することを忘れないでください:touch tmp/restart.txt
user664833

4
このdef self.colours例は理想的ではありません。あなたが呼び出すたびにdef self.colours配列の新しいインスタンスが返されます#freezeこの場合は役に立ちません。ベストプラクティスは、Ruby定数として宣言することです。その場合、常に同じオブジェクトが返されます。
Zabba 2016年

@Zabba単一の配列の割り当てによってアプリに顕著な違いが生じる場合は、おそらく最初からRubyを使用するべきではありません...つまり、メソッドを使用して、毎回完全に新しい配列を返すと、利点:(1)Rubyのクラス境界で不変オブジェクトに到達できる最も近いものであり、(2)固有の状態(たとえば、インターフェイスを変更せずにDBから色を読み取る)。
Holger

@ホルガーただ、少なくとも1つの目的は定数を使用して達成できます。class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; endつまり、任意の言語で配列を割り当てると問題が発生する可能性があります。1つは、理由もなくメモリを使用しています。DBからロードし、値をキャッシュしたい場合は、def self.coloursメソッドを使用して遅延ロードできるクラスインスタンス変数を使用することもできます。しかし、不変性の側面については同意しました。
Zabba 2016年

70

いくつかのオプション:

定数を使用:

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たとえば、最上位の定数を配置することも検討できます。


1
へえ。公正なコメント-メモリからタイプするときの構文エラー私の例:)ヒントをありがとう!
AlexC

2
次にextend、モジュールをクラスで使用して、で使用できるようにしCard.COLOURSます。
未定義

使っextendていないときは効かない。使用している場合include:私は次のようにアクセスすることができますCard::COLOURS
Abhi

これは絶対に下に配置しないでください/models。イニシャライザを作成する方がはるかに優れています。
linkyndy 2015年

@linkyndy私はそれをに入れても大丈夫だと思います/modelsが、それがモジュール内にある場合に限りmodule Constants; COLOURS = ...; endますmodels/constants.rb
ケルビン

56

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ではconfiguration、に加えて、オブジェクトを直接カスタム構成に使用できますconfig.x。ただし、ネストされていない構成でのみ使用できます。

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

次のように利用できます。

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

1
私のようなRails.configuration.colours最高の(私はそれはそう長くはなかったたいけれども)
トム・ロッシ

@TomRossi同意する、たとえばconfigと同じくらい良いconfiguration
いつか

これは、複数のコントローラー間で共有される定数を定義するレール6でまだ最善の方法ですか?答えてくれてありがとう!
Crashalot

18

定数が複数のクラスで必要な場合は、config / initializers / contant.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

1
いいですが、config/initializers/constants.rbおそらくより良い選択でしょう
Adit Saxena

私もこれを使用していますが、最近これらの定数がapplication.rbでアクセスできないという問題に遭遇しました
Zia Ul Rehman Mughal

私の定数は機能していましたが、何らかの理由で停止しました(どういうわけか、私のファイルは初期化子の外に移動しました)。この回答を確認した後、私はよく調べてそれらを戻し、現在動作しています。ありがとう
Muhammad Nasir Shamshad

attr_accessorは必要ないと思います。特定のRailsバージョンについて話していますか?
Mayuresh Srivastava

16

アプリケーション全体の設定とグローバル定数の場合は、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/)。それは非常に柔軟です。そして、あなたは自転車を発明する必要はありません。


7

クラスメソッドを使用します。

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

次にModel.colours、その配列を返します。または、イニシャライザを作成し、定数をモジュールにラップして、名前空間の競合を回避します。


4

すべての定数を1か所に保つようにします。私のアプリケーションでは、次のようにイニシャライザ内に定数フォルダを作成しました。

ここに画像の説明を入力してください

そして、私は通常これらのファイルですべてを一定に保ちます。

あなたの場合、あなたは定数フォルダの下にファイルを作成することができます colors_constant.rb

colors_constant.rb

ここに画像の説明を入力してください

サーバーを再起動することを忘れないでください


1
これが私がここで見つけた最良の答えです。ありがとうございました。
プロストンプレストン

3

定数を1か所で定義する場合の別のオプション:

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

3

アプリケーション全体のグローバル定数を配置する一般的な場所は内部config/applicationです。

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

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

2

私は通常、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

seeds.rbを使用して、事前に入力します。

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

1

グローバル変数はconfig/initializersディレクトリで宣言する必要があります

COLOURS = %w(white blue black red green)

ありがとう!他の人はすでにこれについて言及しています。これはHolgerの回答の最後の行です。Zabbaは警告を発していますが、この手法についても言及しています。
AlexC 2017

0

状況に応じて、いくつかの環境変数を定義しENV['some-var']、Rubyコードで取得することもできます。このソリューションはあなたに合わないかもしれませんが、それが他の人に役立つことを願っています。

例:あなたは別のファイルを作成することができ.development_env.production_env.test_envおよびアプリケーション環境を応じてそれを読み込む、この世代のチェックdotenvレールあなたのためにこれを自動化します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.