Ruby on Rails: Global sabitler nerede tanımlanır?


213

İlk Ruby on Rails web uygulamamla yeni başladım. Bir sürü farklı model, görünüm, kontrolör vb. Var.

Tüm uygulamam boyunca geçerli olan gerçekten küresel sabitlerin tanımlarını yapıştırmak için iyi bir yer bulmak istiyorum. Özellikle, hem modellerimin mantığında hem de görüşlerimde alınan kararlarda uygulanırlar. Onlar hem mevcut konum bu tanımları koymak için herhangi KURU yer bulamıyor tüm benim modelleri ve ayrıca tüm görünümlerinde.

Belirli bir örnek almak için, bir sabit istiyorum COLOURS = ['white', 'blue', 'black', 'red', 'green']. Bu, hem modellerde hem de görünümlerde her yerde kullanılır. Erişilebilmesi için tek bir yerde nerede tanımlayabilirim?

Ne denedim:

  • Model.rb dosyasında en çok ilişkili oldukları sabit sınıf değişkenleri @@COLOURS = [...]. Ama onu tanımlamak için aklı başında bir yol bulamadım, böylece Card.COLOURSkludgy gibi bir şeyden ziyade görüşlerime yazabilirim Card.first.COLOURS.
  • Model üzerinde bir yöntem, benzer bir şey def colours ['white',...] end- aynı sorun.
  • Application_helper.rb'de bir yöntem - şu ana kadar yaptığım şey bu, ancak yardımcılara modellerde değil, yalnızca görünümlerde erişilebilir
  • Ben application.rb veya environment.rb bir şey denemiş olabilir düşünüyorum, ama bunlar gerçekten doğru görünmüyor (ve onlar da çalışmıyor gibi görünüyor)

Hem modellerden hem de görünümlerden erişilebilecek bir şey tanımlamanın bir yolu yok mu? Yani, modellerin ve görüşlerin ayrı olması gerektiğini biliyorum, ancak bazı alanlarda elbette aynı alana özgü bilgiye atıfta bulunmaları gereken zamanlar olacak mı?



Bunun GERÇEKTEN geç olduğunu takdir ediyorum, ancak diğer okuyucular için neden bunları sadece modelinizde tanımlamadığınızı ve denetleyicilerinizi görüşlerinize aktarmak için kullanmadığınızı merak ediyorum. Bu şekilde, denetleyici / görünüm VE model / görünüm arasında bağımlılıklar yaratmak yerine endişelerin daha net bir şekilde ayrılması gerekir.
Tom Tom

2
@TomTom: Bu sabitleri, ihtiyaç duyan her bir görünüme ve yardımcıya aktarır mısınız? Başka bir deyişle, denetleyiciyi hangi görünümlerin hangi sabitlere ihtiyaç duyduğunun farkında hale getirin? Bu daha çok bir MVC ihlali gibi görünüyor.
AlexC

Yanıtlar:


229

Modeliniz sabitler için gerçekten "sorumlu" ise, onları oraya yapıştırmalısınız. Yeni bir nesne örneği oluşturmadan bunlara erişmek için sınıf yöntemleri oluşturabilirsiniz:

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

# accessible like this
Card.colours

Alternatif olarak, sınıf değişkenleri ve bir erişimci oluşturabilirsiniz. Bununla birlikte, sınıf değişkenleri kalıtımla ve çok iş parçacıklı ortamlarda bir şekilde şaşırtıcı olabileceğinden bu önerilmez.

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

# accessible the same as above

Yukarıdaki iki seçenek, gerekirse erişimci yönteminin her çağrısında döndürülen diziyi değiştirmenize olanak tanır. Eğer gerçekten değişmez bir sabite sahipseniz, bunu model sınıfında da tanımlayabilirsiniz:

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

# accessible as
Card::COLOURS

Aşağıdaki örnekte olduğu gibi bir başlatıcıda her yerden erişilebilen genel sabitler de oluşturabilirsiniz. Renkleriniz gerçekten küreselse ve birden fazla model bağlamında kullanılıyorsa, bu muhtemelen en iyi yerdir.

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

Not: Yukarıda sabitleri tanımladığımızda, genellikle freezediziyi isteriz . Bu, örneğin yeni bir eleman ekleyerek dizinin daha sonra (yanlışlıkla) diziyi değiştirmesini önler. Bir nesne dondurulduktan sonra artık değiştirilemez.


1
Çok teşekkür ederim. Sınıf yöntemlerini tanımlamak için Ruby class-fu eksikti. Ama aslında bu durumda başlatıcı seçeneğini seviyorum, çünkü renkler birden fazla model ve görünümde kullanılıyor. Çok teşekkürler!
AlexC

21
config/initializers/my_constants.rbRotaya gidiyorsanız, sunucuyu yeniden başlatmayı unutmayın:touch tmp/restart.txt
user664833 4:12

4
def self.coloursÖrnek ideal değildir. Her aradığınızda def self.colours, dizinin yeni bir örneği döndürülür . #freezebu durumda yardımcı olmaz. En iyi uygulama onu Ruby sabiti olarak ilan etmektir, bu durumda her zaman aynı nesneyi geri alırsınız.
Zabba

@Zabba Tek bir dizinin tahsisi uygulamanız için fark edilir bir fark yaratırsa, muhtemelen ilk etapta Ruby kullanmamalısınız ... Bu, bir yöntem kullanarak ve her seferinde tamamen yeni bir dizi döndürmenin birkaç tane olabileceğini söyledi. avantajları: (1) Ruby'deki sınıf sınırınızdaki değişmez nesnelere ulaşabileceğiniz en yakın şeydir ve (2) daha sonra doğal duruma bağlı olarak dönüş değerini (örneğin DB'den renklerin okunması).
Holger

@Holger Sadece, hedeflerinizden en az biri sabit kullanılarak elde edilebilir: class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; endBununla birlikte, bir dizinin herhangi bir dilde tahsis edilmesi potansiyel olarak sorunlu olabilir; birincisi, hafızayı (iyi) bir sebep olmaksızın kullanıyor. Bir DB'den yükleme yapıyorsa ve değeri önbelleğe almak istiyorsanız, def self.coloursyöntem kullanılarak tembel olarak yüklenebilen bir sınıf örneği değişkeni de kullanılabilir. Yine de değişmezlik konusunda anlaştılar.
Zabba

70

Bazı seçenekler:

Bir sabit kullanma:

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

Sınıf örneği değişkeni kullanılarak yüklenen tembel:

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

Eğer gerçekten küresel bir sabitse ( yine de bu nitelikteki küresel sabitlerden kaçının ), config/initializers/my_constants.rbörneğin üst düzey bir sabit koymayı da düşünebilirsiniz .


1
Heh. Adil yorum - benim örnek belleğimden yazarken sözdizimi hatası :) İpucu için teşekkürler!
AlexC

2
Sonra extendsınıftaki modül böylece kullanılabilir olacak Card.COLOURS.
undefinedvariable

Kullanırken extendbenim için çalışmıyor. Kullanırken includeaşağıdaki gibi erişebilirim:Card::COLOURS
Abhi

Kesinlikle bu altına yerleştirmemelisiniz /models. Bir başlatıcı oluşturursanız çok daha iyi olur.
linkyndy

@linkyndy Ben bunu koymak için tamam olduğunu söyleyebilirim /models, ama sadece bir modül içinde, örneğin module Constants; COLOURS = ...; enddenilen bir dosyada models/constants.rb.
Kelvin

57

Rails 4.2'den itibaren config.xözelliği kullanabilirsiniz :

# 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'

Hangi olarak mevcut olacak:

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

Özel yapılandırma yüklemenin başka bir yöntemi:

# 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 ve 6'da , configurationnesneyi doğrudan özel yapılandırma için de kullanabilirsiniz config.x. Ancak, yalnızca iç içe olmayan yapılandırma için kullanılabilir:

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

Şu şekilde sunulacak:

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

2
En çok hoşuma gidiyor Rails.configuration.colours(keşke çok uzun olmasaydı)
Tom Rossi

@TomRossi Kabul ediyorum, örneğin configiyi configuration. Bir noktada bir kısayol almayı ümit edebiliriz :)
Halil Özgür

hala raylar 6'da birden çok kontrolör arasında paylaşılacak sabitleri tanımlamanın en iyi yolu nedir? Cevap için teşekkürler!
Crashalot

@Crashalot Hala dokümanlarda listeleniyor. "En iyisi"? Değişir. Onların ortak atalarında olabilir. Ya da arada ApplicationControllerbaşka bir şey yoksa. Sabit doğrudan kontrolörler ile ilgili değilse, hala küresel bir yapılandırma, vb.
Düşünürüm

@ HalilÖzgür cevap için teşekkürler. ortak bir atadaki sabitleri nasıl tanımlarsınız?
Crashalot

18

Birden fazla sınıfta bir sabit gerekiyorsa bunu config / initializers / contant.rb dosyasına her zaman tüm büyük harflere koyarım (aşağıdaki durumların listesi kesilir).

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

Model kodu dışında uygulama yoluyla kullanılabilirler:

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

Sabiti modelde kullanmak için, sabiti kullanılabilir yapmak üzere attr_accessor komutunu kullanın.

class Customer < ActiveRecord::Base
    attr_accessor :STATES

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

1
güzel, config/initializers/constants.rbmuhtemelen daha iyi bir seçim olsa olurdu
Adit Saxena

Bunu da kullanıyorum, ancak son zamanlarda bu sabitlerin application.rb'de erişilemediği sorunuyla karşılaştım
Zia Ul Rehman Mughal

sabitlerim çalışıyordu ama bir sebepten dolayı durdu (Bir şekilde dosyam başlatıcılardan çıkmış gibi). Bu cevabı kontrol ettikten sonra yakından baktım ve onları geri taşıdım ve Şimdi çalışıyor. Teşekkürler
Muhammed Nasir Şemshad

Ben attr_accessor gerekli olduğunu sanmıyorum. Belirli bir Rails sürümünden bahsediyor musunuz?
Mayuresh Srivastava

16

Uygulama genelindeki ayarlar ve global sabitler için Settingslogic kullanmanızı öneririm . Bu ayarlar YML dosyasında saklanır ve modellere, görünümlere ve denetleyicilere erişilebilir. Ayrıca, tüm ortamlarınız için farklı ayarlar oluşturabilirsiniz:

  # 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

Görünümde bir yerde (bu tür şeyler için yardımcı yöntemleri tercih ederim) veya bir modelde, örneğin, renk dizisi için alabilirsiniz Settings.colors.split(/\s/). Çok esnektir. Ve bir bisiklet icat etmenize gerek yok.


7

Bir sınıf yöntemi kullanın:

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

Sonra Model.coloursbu diziyi döndürür. Alternatif olarak, bir başlatıcı oluşturun ve ad alanı çakışmalarını önlemek için sabitleri bir modüle sarın.


4

Hepsini tek bir yerde sabit tutmaya çalışın, Uygulamamda başlatıcıların içinde sabitler klasörü oluşturdum:

resim açıklamasını buraya girin

ve genellikle bu dosyalarda sabit kalıyorum.

Durumunuzda sabitler klasörü altında dosya oluşturabilirsiniz. colors_constant.rb

colors_constant.rb

resim açıklamasını buraya girin

Sunucuyu yeniden başlatmayı unutma


1
Burada bulduğum en iyi cevap bu. Teşekkür ederim.
Preston

3

Sabitlerinizi tek bir yerde tanımlamak istiyorsanız başka bir seçenek:

module DSL
  module Constants
    MY_CONSTANT = 1
  end
end

Ancak, onlara tam nitelikli bir şekilde erişmek zorunda kalmadan küresel olarak görünür olmasını sağlayın:

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

3

Uygulama çapında küresel sabitleri koymak için ortak bir yer içerisindedir 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

Genellikle raylar programımda bir 'arama' modeli / tablo var ve sabitler için kullanın. Sabitlerin farklı ortamlar için farklı olması çok yararlıdır. Ayrıca, bunları genişletmek için bir planınız varsa, daha sonraki bir tarihte 'sarı' eklemek istediğinizi varsayalım, arama tablosuna yeni bir satır ekleyebilir ve bununla işinizi yapabilirsiniz.

Bu tabloyu değiştirmek için yöneticiye izin verirseniz, bunlar bakım için size gelmez. :) KURU.

Taşıma kodum şöyle:

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

Önceden doldurmak için seeds.rb kullanıyorum.

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

1

Global değişken config/initializersdizinde bildirilmelidir

COLOURS = %w(white blue black red green)

Teşekkürler! Diğerleri bundan daha önce bahsetti. Holger'in cevabının son satırı ve Zabba bu tekniğe de değiniyor, ancak Zabba buna karşı uyarıyor.
AlexC

0

ENV['some-var']Durumunuza göre, bazı çevresel değişkenleri tanımlayabilir ve yakut koduyla getirebilirsiniz , bu çözüm sizin için uygun olmayabilir, ancak umarım başkalarına yardımcı olabilir.

Örnek: farklı dosyaları oluşturabilir .development_env, .production_env, .test_envve uygulama ortamları göre yerleştirin, bu gen kontrol dotenv-rayları şunlara ait bu otomatikleştirmek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.