Ruby on Rails : 전역 상수를 정의 할 위치는?


213

첫 번째 Ruby on Rails webapp을 시작했습니다. 다양한 모델, 뷰, 컨트롤러 등이 있습니다.

전체 응용 프로그램에 적용되는 진정한 글로벌 상수의 정의를 고수 할 수있는 좋은 장소를 찾고 싶습니다. 특히, 그들은 내 모델의 논리와 내 견해에서 내린 결정 모두에 적용됩니다. 이 정의를 모든 모델과 모든 뷰에서 사용할 수있는 곳에 DRY 장소를 찾을 수 없습니다 .

구체적인 예를 들어, 상수를 원합니다 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에서 무언가를 시도했을 수도 있지만 실제로는 올바르지 않습니다 (그리고 작동하지 않는 것 같습니다)

모델과 뷰에서 액세스 할 수있는 항목을 정의 할 방법이 없습니까? 내 말은, 모델과 뷰가 분리되어야한다는 것을 알고 있지만, 어떤 도메인에서는 반드시 동일한 도메인 별 지식을 참조해야하는 경우가 있습니까?



정말 늦었지만, 다른 독자들에게 왜 모델에서 그것들을 정의하고 컨트롤러를 사용하여 뷰에 전달하지 않았는지 궁금합니다. 이런 식으로 컨트롤러 / 뷰와 모델 / 뷰 사이의 종속성을 생성하는 대신 우려 사항을보다 명확하게 분리 할 수 ​​있습니다.
Tom Tom

2
@ TomTom :이 상수를 각보기와 도우미에 전달해야합니까? 다시 말해, 어떤 뷰가 어떤 상수를 필요로하는지 컨트롤러에 알리십시오. 그것은 MVC를 위반하는 것 같습니다.
AlexC

답변:


229

상수에 대해 모델이 실제로 "책임"인 경우 해당 값을 고정해야합니다. 새 객체 인스턴스를 만들지 않고 클래스 메서드를 만들어 액세스 할 수 있습니다.

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

위의 두 옵션을 사용하면 필요한 경우 접근 자 메서드를 호출 할 때마다 반환 된 배열을 변경할 수 있습니다. 진정으로 변경 불가능한 상수가있는 경우 모델 클래스에서 정의 할 수도 있습니다.

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 클래스가 누락 된 것 같습니다. 그러나 색상은 여러 모델과 뷰에서 사용되므로 실제로이 경우 초기화 옵션이 마음에 듭니다. 많은 감사합니다!
AlexC

21
config/initializers/my_constants.rb경로로 가는 경우 서버를 다시 시작해야합니다.touch tmp/restart.txt
user664833

4
def self.colours예는 이상적이지 않습니다. 전화 할 때마다 def self.colours, 배열의 새로운 인스턴스가 반환됩니다 . #freeze이 경우 도움이되지 않습니다. 가장 좋은 방법은 루비 상수로 선언하는 것입니다.이 경우 항상 같은 객체를 다시 얻게됩니다.
Zabba

@Zabba 단일 배열 할당이 앱에서 눈에 띄는 차이를 만드는 경우, 처음에는 루비를 사용하지 않아야합니다 ... 즉, 메소드를 사용하고 매번 완전히 새로운 배열을 반환 할 때마다 부부가있을 수 있습니다 장점 : (1) Ruby에서 클래스 경계에있는 불변 객체에 접근 할 수있는 가장 가까운 것입니다. (2) 나중에 고유 한 상태를 기반으로 반환 값을 조정할 수있는 클래스에서 균일 한 인터페이스를 유지합니다 (예 : 인터페이스를 변경하지 않고 DB에서 색상을 읽음).
Holger

@Holger 단지 하나 이상의 목표를 사용하여 목표를 달성 할 수 있습니다. class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; end즉, 어떤 언어로든 배열을 할당하는 것은 잠재적으로 문제가 될 수 있습니다. 우선, (좋은) 이유없이 메모리를 사용하고 있습니다. DB에서로드하고 값을 캐시하려는 경우 클래스 인스턴스 변수를 사용할 수도 있습니다 def self.colours. 이 메소드 는 메소드 를 사용하여 지연로드 될 수 있습니다 . 불변성 측면에 대해 동의했습니다.
Zabba

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.
undefinedvariable

extend나를 위해 작동하지 않을 때 . 사용하면 다음 include과 같이 액세스 할 수 있습니다.Card::COLOURS
Abhi

당신은 확실히 아래에 배치해서는 안됩니다 /models. 이니셜 라이저를 만들면 훨씬 좋습니다.
linkyndy

@linkyndy 나는 그것을 안에 넣는 것이 좋다고 말하지만 /models모듈 안에 예를 들어 module Constants; COLOURS = ...; end파일 과 같은 경우에만 가능하다 models/constants.rb.
Kelvin

57

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 5 & 6 에서는, configuration이외에도 커스텀 구성을 위해 객체를 직접 사용할 수 있습니다 config.x. 그러나 중첩되지 않은 구성에만 사용할 수 있습니다.

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

다음과 같이 사용할 수 있습니다.

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

2
내가 좋아하는 Rails.configuration.colours(내가 너무 오래하지 않았다 할지라도) 최고
톰 로시

@TomRossi 동의합니다. 예를 들면 다음 config과 같습니다 configuration. 우리는 : 어떤 시점에서 바로 가기를 얻을 수 있도록 노력하겠습니다 수도
할릴 오즈

이것은 여러 컨트롤러에서 공유 할 상수를 정의하는 레일 6에서 여전히 가장 좋은 방법입니까? 답변 해주셔서 감사합니다!
Crashalot

@Crashalot 그것은 여전히 ​​문서에 나열되어 있습니다. "최고"? 때에 따라 다르지. 공통 조상일 수 있습니다. 또는 ApplicationController그 사이에 다른 것이 없다면. 상수가 컨트롤러와 직접 관련이 없다면 여전히 글로벌 구성 등을 고려할 것입니다.
Halil Özgür

@ HalilÖzgür 답장을 보내 주셔서 감사합니다. 공통 조상에서 상수를 어떻게 정의합니까?
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에 접근하지 못하도록 문제를 통해 온
지아 UL 레흐만 무굴을

내 상수가 작동했지만 어떤 이유로 든 중지되었습니다 (어쨌든 내 파일이 초기화 프로그램에서 벗어났습니다). 이 답변을 확인한 후에 나는 자세히 살펴보고 다시 움직였습니다. 감사합니다
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

한곳에서 모든 것을 일정하게 유지하십시오. 응용 프로그램에서 다음과 같이 이니셜 라이저 안에 상수 폴더를 만들었습니다.

여기에 이미지 설명을 입력하십시오

그리고 나는 보통이 파일들에서 모든 것을 일정하게 유지합니다.

귀하의 경우 상수 폴더 아래에 파일을 다음과 같이 만들 수 있습니다 colors_constant.rb

colors_constant.rb

여기에 이미지 설명을 입력하십시오

서버를 다시 시작하는 것을 잊지 않았다


1
이것이 내가 찾은 최고의 답변입니다. 감사합니다.
약속 Preston

3

한 곳에서 상수를 정의하려는 경우 다른 옵션 :

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

나는 일반적으로 레일 프로그램에 '조회'모델 / 테이블을 가지고 상수에 사용합니다. 상수가 환경에 따라 다를 경우 매우 유용합니다. 또한 확장 계획이있는 경우 나중에 '노란색'을 추가하고 싶다면 조회 테이블에 새 행을 추가하고 완료하면됩니다.

관리자에게이 테이블을 수정할 수있는 권한을 부여하면 유지 관리를 위해 제공되지 않습니다. :) 건조합니다.

마이그레이션 코드는 다음과 같습니다.

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');

1

전역 변수는 config/initializers디렉토리에 선언해야합니다

COLOURS = %w(white blue black red green)

감사! 다른 사람들은 이미 이것을 언급했습니다. 그것은 홀거의 대답의 마지막 줄이며 Zabba는이 기술에 대해서도 언급하지만 Zabba는 그것에 대해 경고합니다.
AlexC

0

조건에 따라 일부 환경 변수를 정의하고 다음을 통해 가져올 수 있습니다 ENV['some-var'] 하고 루비 코드로 수도 있습니다.이 솔루션은 적합하지 않을 수 있지만 다른 사람들에게 도움이되기를 바랍니다.

예 : 다른 파일을 생성 하여 애플리케이션 환경에 따라로드 할 수 .development_env있으며 .production_env, 이를 자동화하는 .test_env이 gen dotenv-rail 을 확인 하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.