Ruby on Rails: Nơi xác định hằng số toàn cầu?


213

Tôi mới bắt đầu với ứng dụng web Ruby on Rails đầu tiên của mình. Tôi đã có một loạt các mô hình, khung nhìn, bộ điều khiển khác nhau, v.v.

Tôi muốn tìm một nơi tốt để gắn các định nghĩa về các hằng số thực sự toàn cầu, áp dụng trên toàn bộ ứng dụng của tôi. Cụ thể, họ áp dụng cả trong logic của các mô hình của tôi và trong các quyết định đưa ra theo quan điểm của tôi. Tôi không thể tìm thấy bất kỳ vị trí DRY nào để đặt các định nghĩa này khi chúng có sẵn cho tất cả các mô hình của tôi và trong tất cả các chế độ xem của tôi.

Để lấy một ví dụ cụ thể, tôi muốn một hằng số COLOURS = ['white', 'blue', 'black', 'red', 'green']. Điều này được sử dụng ở mọi nơi, trong cả mô hình và quan điểm. Tôi có thể định nghĩa nó ở đâu tại một nơi để có thể truy cập được?

Những gì tôi đã thử:

  • Các biến lớp không đổi trong tệp model.rb mà chúng được liên kết nhiều nhất, chẳng hạn như @@COLOURS = [...]. Nhưng tôi không thể tìm ra một cách lành mạnh để định nghĩa nó để tôi có thể viết theo quan điểm của mình Card.COLOURSchứ không phải là một cái gì đó không phù hợp Card.first.COLOURS.
  • Một phương pháp trên mô hình, một cái gì đó giống như def colours ['white',...] end- cùng một vấn đề.
  • Một phương thức trong application_helper.rb - đây là những gì tôi đang làm cho đến nay, nhưng những người trợ giúp chỉ có thể truy cập được trong các khung nhìn, không phải trong các mô hình
  • Tôi nghĩ rằng tôi có thể đã thử một cái gì đó trong application.rb hoặc môi trường.rb, nhưng những thứ đó không thực sự đúng (và dường như chúng cũng không hoạt động)

Có cách nào để xác định bất cứ điều gì có thể truy cập cả từ mô hình và từ chế độ xem không? Ý tôi là, tôi biết các mô hình và khung nhìn nên tách biệt, nhưng chắc chắn trong một số lĩnh vực sẽ có lúc chúng cần tham khảo cùng một kiến ​​thức cụ thể về tên miền?



Tôi đánh giá cao rằng điều này thực sự muộn, nhưng đối với những độc giả khác, tôi tự hỏi tại sao bạn không chỉ định nghĩa chúng trong mô hình của mình và sử dụng bộ điều khiển của bạn để chuyển chúng đến chế độ xem của bạn. Theo cách này, bạn sẽ có một mối quan tâm rõ ràng hơn - thay vì tạo ra sự phụ thuộc giữa bộ điều khiển / khung nhìn VÀ mô hình / khung nhìn.
Tom Tom

2
@TomTom: Truyền các hằng số này vào từng chế độ xem và người trợ giúp cần chúng? Nói cách khác, làm cho bộ điều khiển nhận biết các khung nhìn nào cần hằng số nào? Nghe có vẻ như vi phạm MVC hơn.
AlexC

Câu trả lời:


228

Nếu mô hình của bạn thực sự "có trách nhiệm" với các hằng số, bạn nên dán chúng ở đó. Bạn có thể tạo các phương thức lớp để truy cập chúng mà không cần tạo một đối tượng mới:

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

# accessible like this
Card.colours

Ngoài ra, bạn có thể tạo các biến lớp và một phụ kiện. Tuy nhiên, điều này không được khuyến khích vì các biến lớp có thể gây ngạc nhiên với tính kế thừa và trong môi trường đa luồng.

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

# accessible the same as above

Hai tùy chọn ở trên cho phép bạn thay đổi mảng trả về trên mỗi lần gọi phương thức truy cập nếu được yêu cầu. Nếu bạn có một hằng số thực sự không thể thay đổi, bạn cũng có thể định nghĩa nó trên lớp mô hình:

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

# accessible as
Card::COLOURS

Bạn cũng có thể tạo các hằng số toàn cầu có thể truy cập từ mọi nơi trong trình khởi tạo như trong ví dụ sau. Đây có lẽ là nơi tốt nhất, nếu màu sắc của bạn thực sự toàn cầu và được sử dụng trong nhiều bối cảnh mô hình.

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

Lưu ý: khi chúng ta xác định các hằng số ở trên, chúng ta thường muốn freezemảng. Điều đó ngăn chặn các mã khác sau này (vô tình) sửa đổi mảng bằng cách thêm một phần tử mới. Khi một đối tượng bị đóng băng, nó không thể thay đổi được nữa.


1
Cảm ơn rât nhiều. Có vẻ như tôi đã thiếu lớp Ruby-fu để định nghĩa các phương thức lớp. Nhưng tôi thực sự thích tùy chọn khởi tạo trong trường hợp này, vì màu sắc được sử dụng trong nhiều mô hình và chế độ xem. Cảm ơn nhiều!
AlexC

21
Nếu đi theo config/initializers/my_constants.rbtuyến đường, hãy nhớ khởi động lại máy chủ:touch tmp/restart.txt
user664833

4
Các def self.coloursví dụ là không lý tưởng. Mỗi lần bạn gọi def self.colours, một thể hiện mới của mảng sẽ được trả về . #freezesẽ không giúp đỡ trong trường hợp này. Thực hành tốt nhất là khai báo nó như một hằng số Ruby, trong trường hợp đó bạn sẽ luôn lấy lại cùng một đối tượng.
Zabba

@Zabba Nếu việc phân bổ một mảng duy nhất tạo ra sự khác biệt đáng chú ý cho ứng dụng của bạn, có lẽ bạn không nên sử dụng Ruby ở nơi đầu tiên ... Điều đó có nghĩa là, sử dụng một phương thức và trả về một mảng hoàn toàn mới mỗi lần có thể có một cặp về lợi thế: (1) đó là điều gần nhất mà bạn có thể có được đối với các đối tượng bất biến trên ranh giới lớp của bạn trong Ruby và (2) bạn giữ giao diện thống nhất trên lớp với khả năng điều chỉnh giá trị trả về sau dựa trên trạng thái vốn có (ví dụ: đọc màu từ DB) mà không thay đổi giao diện.
Holger Chỉ cần

@Holger Chỉ cần, ít nhất một trong những mục tiêu của bạn vẫn có thể đạt được bằng cách sử dụng hằng số: class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; endĐiều đó có nghĩa là việc phân bổ một mảng trong bất kỳ ngôn ngữ nào có thể có vấn đề; đối với một, nó đang sử dụng bộ nhớ không có lý do (tốt). Nếu tải từ DB và muốn lưu trữ giá trị, người ta cũng có thể sử dụng biến thể hiện của lớp, có thể được tải lười biếng bằng cách sử dụng def self.coloursphương thức. Đồng ý về khía cạnh bất biến mặc dù.
Zabba

70

Một số tùy chọn:

Sử dụng hằng số:

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

Lười tải bằng biến thể hiện của lớp:

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

Nếu đó là một hằng số toàn cầu thực sự ( tuy nhiên, tránh các hằng số toàn cầu có tính chất này ), bạn cũng có thể xem xét đưa một hằng số cấp cao nhất vào config/initializers/my_constants.rbví dụ.


1
Heh. Nhận xét công bằng - lỗi cú pháp khi gõ từ bộ nhớ ví dụ của tôi :) Cảm ơn vì mẹo!
AlexC

2
Sau đó, extendmô-đun trong lớp để nó sẽ có sẵn với Card.COLOURS.
không xác định được

Khi sử dụng extendnó không làm việc cho tôi. Khi sử dụng includetôi có thể truy cập như:Card::COLOURS
Abhi

Bạn chắc chắn KHÔNG nên đặt cái này bên dưới /models. Sẽ tốt hơn rất nhiều nếu bạn tạo một trình khởi tạo.
linkyndy

@linkyndy Tôi muốn nói rằng bạn có thể đặt nó bên dưới /models, nhưng chỉ khi nó nằm trong một mô-đun, ví dụ như module Constants; COLOURS = ...; endtrong một tệp được gọi models/constants.rb.
Kelvin

56

Kể từ Rails 4.2, bạn có thể sử dụng thuộc config.xtính:

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

Mà sẽ có sẵn như:

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

Một phương pháp tải cấu hình tùy chỉnh khác:

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

Trong Rails 5 & 6 , bạn có thể sử dụng configurationđối tượng trực tiếp cho cấu hình tùy chỉnh, ngoài ra config.x. Tuy nhiên, nó chỉ có thể được sử dụng cho cấu hình không lồng nhau:

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

Nó sẽ có sẵn như:

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

1
Tôi thích Rails.configuration.coloursnhất (mặc dù tôi ước nó không dài lắm)
Tom Rossi

@TomRossi Tôi đồng ý, ví dụ như configlà tốt như configuration. Chúng tôi có thể hy vọng có được một phím tắt tại một số điểm :)
Halil zgür

Đây có phải vẫn là cách tốt nhất trong rails 6 để xác định các hằng số được chia sẻ trên nhiều bộ điều khiển? cảm ơn câu trả lời
Crashalot

18

Nếu một hằng số là cần thiết trong nhiều hơn một lớp, tôi đặt nó trong config / khởi tạo / contant.rb luôn trong tất cả các mũ (danh sách các trạng thái bên dưới bị cắt ngắn).

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

Chúng có sẵn thông qua ứng dụng ngoại trừ trong mã mô hình như sau:

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

Để sử dụng hằng trong mô hình, hãy sử dụng attr_accessor để cung cấp hằng số.

class Customer < ActiveRecord::Base
    attr_accessor :STATES

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

1
tốt, config/initializers/constants.rbcó lẽ sẽ là một lựa chọn tốt hơn mặc dù
Adit Saxena

tôi cũng sử dụng cái này, nhưng gần đây đã phát hiện ra vấn đề rằng các hằng số này không thể truy cập được trong ứng
dụng.rb

các hằng số của tôi đã hoạt động nhưng đã dừng lại vì một số lý do (vì bằng cách nào đó, tệp của tôi đã chuyển khỏi bộ khởi tạo). Sau khi kiểm tra câu trả lời này, tôi nhìn kỹ và di chuyển chúng trở lại và Hiện đang hoạt động. Cảm ơn
Muhammad Nasir Shamshad 22/03/19

Tôi không nghĩ attr_accessor là cần thiết. Bạn đang nói về bất kỳ phiên bản Rails cụ thể?
Mayuresh Srivastava

16

Đối với các cài đặt trên toàn ứng dụng và cho các hằng số toàn cầu, tôi khuyên bạn nên sử dụng Settingslogic . Cài đặt này được lưu trữ trong tệp YML và có thể được truy cập từ các mô hình, chế độ xem và bộ điều khiển. Hơn nữa, bạn có thể tạo các cài đặt khác nhau cho tất cả các môi trường của mình:

  # 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

Ở đâu đó trong khung nhìn (tôi thích các phương thức trợ giúp cho loại công cụ như vậy) hoặc trong một mô hình mà bạn có thể nhận được, ví dụ, mảng màu Settings.colors.split(/\s/). Nó rất linh hoạt. Và bạn không cần phải phát minh ra một chiếc xe đạp.


7

Sử dụng một phương thức lớp:

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

Sau đó Model.colourssẽ trả về mảng đó. Ngoài ra, tạo một bộ khởi tạo và bọc các hằng số trong một mô-đun để tránh xung đột không gian tên.


4

Cố gắng giữ tất cả không đổi ở một nơi, Trong ứng dụng của tôi, tôi đã tạo thư mục hằng bên trong các trình khởi tạo như sau:

nhập mô tả hình ảnh ở đây

và tôi thường giữ tất cả không đổi trong các tập tin này.

Trong trường hợp của bạn, bạn có thể tạo tệp trong thư mục hằng như colors_constant.rb

màu sắc_constant.rb

nhập mô tả hình ảnh ở đây

Đừng quên khởi động lại máy chủ


1
Đây là câu trả lời tốt nhất mà tôi tìm thấy ở đây. Cảm ơn bạn.
Promise Preston

3

Một tùy chọn khác, nếu bạn muốn xác định các hằng số của mình ở một nơi:

module DSL
  module Constants
    MY_CONSTANT = 1
  end
end

Nhưng vẫn làm cho chúng hiển thị trên toàn cầu mà không cần phải truy cập chúng theo cách đủ điều kiện:

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

3

Một nơi phổ biến để đặt các hằng số toàn cầu ứng dụng là bên trong 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

Tôi thường có một mô hình / bảng 'tra cứu' trong chương trình đường ray của mình và sử dụng nó cho các hằng số. Nó rất hữu ích nếu các hằng số sẽ khác nhau cho các môi trường khác nhau. Ngoài ra, nếu bạn có kế hoạch mở rộng chúng, giả sử bạn muốn thêm 'màu vàng' vào một ngày sau đó, bạn có thể chỉ cần thêm một hàng mới vào bảng tra cứu và được thực hiện với nó.

Nếu bạn cấp quyền cho quản trị viên để sửa đổi bảng này, họ sẽ không đến với bạn để bảo trì. :) KHÔ.

Đây là cách mã di chuyển của tôi trông như thế nào:

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

Tôi sử dụng hạt giống.rb để điền trước nó.

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

1

Biến toàn cục nên được khai báo trong config/initializersthư mục

COLOURS = %w(white blue black red green)

Cảm ơn! Những người khác đã đề cập đến điều này rồi. Đây là dòng cuối cùng của câu trả lời của Holger và Zabba cũng đề cập đến kỹ thuật này, mặc dù Zabba cảnh báo chống lại nó.
AlexC

0

Theo điều kiện của bạn, bạn cũng có thể xác định một số biến môi trường và tìm nạp nó qua ENV['some-var']mã ruby, giải pháp này có thể không phù hợp với bạn, nhưng tôi hy vọng nó có thể giúp người khác.

Ví dụ: bạn có thể tạo tập tin khác nhau .development_env, .production_env, .test_envvà tải nó theo môi trường ứng dụng của bạn, kiểm tra này gen dotenv-ray mà tự động hoá này cho bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.