Ruby on Rails: จะกำหนดค่าคงที่ทั่วโลกได้ที่ไหน


213

ฉันเพิ่งเริ่มต้นกับ webapp Ruby on Rails ครั้งแรกของฉัน ฉันมีโมเดลที่ต่างกันมุมมองคอนโทรลเลอร์และอื่น ๆ

ฉันต้องการที่จะหาสถานที่ที่ดีในการติดคำจำกัดความของค่าคงที่ทั่วโลกอย่างแท้จริงซึ่งใช้กับแอพทั้งหมดของฉัน โดยเฉพาะอย่างยิ่งพวกเขาใช้ทั้งในตรรกะของแบบจำลองของฉันและในการตัดสินใจในมุมมองของฉัน ฉันไม่สามารถหาสถานที่ที่แห้งเพื่อวางคำจำกัดความเหล่านี้ที่พวกเขาสามารถใช้ได้กับทุกรุ่นของฉันและในทุกมุมมองของฉัน

COLOURS = ['white', 'blue', 'black', 'red', 'green']เพื่อที่จะใช้ตัวอย่างที่เฉพาะเจาะจงที่ผมต้องการอย่างต่อเนื่อง สิ่งนี้ใช้ทั่วสถานที่ทั้งในรุ่นและมุมมอง ฉันจะกำหนดได้ที่ไหนในที่เดียวเพื่อให้เข้าถึงได้

สิ่งที่ฉันได้ลอง:

  • ตัวแปรระดับคงที่ในไฟล์ model.rb @@COLOURS = [...]ที่พวกเขากำลังเกี่ยวข้องมากที่สุดกับเช่น แต่ฉันไม่สามารถหาวิธีที่มีสติที่จะกำหนดมันเพื่อที่ฉันสามารถเขียนในมุมมองของฉันCard.COLOURSมากกว่าสิ่ง kludgy 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

คุณสามารถสร้างค่าคงที่ส่วนกลางซึ่งสามารถเข้าถึงได้จากทุกที่ใน initializer เช่นในตัวอย่างต่อไปนี้ นี่อาจเป็นสถานที่ที่ดีที่สุดถ้าสีของคุณเป็นสากลและใช้ในบริบทของแบบจำลองมากกว่าหนึ่ง

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

หมายเหตุ: เมื่อเรากำหนดค่าคงที่ด้านบนบ่อยครั้งที่เราต้องการfreezeอาร์เรย์ ที่ป้องกันไม่ให้รหัสอื่น ๆ ในภายหลัง (โดยไม่ได้ตั้งใจ) แก้ไขอาเรย์โดยเช่นการเพิ่มองค์ประกอบใหม่ เมื่อวัตถุถูกแช่แข็งแล้วจะไม่สามารถเปลี่ยนแปลงได้อีก


1
ขอบคุณมาก. ดูเหมือนว่าฉันไม่มีคลาส Ruby-Fu เพื่อกำหนดวิธีการเรียน แต่จริง ๆ แล้วฉันชอบตัวเลือกการเริ่มต้นในกรณีนี้เนื่องจากมีการใช้สีในหลายรุ่นและหลายมุมมอง ขอบคุณมาก!
AlexC

21
หากไปตามconfig/initializers/my_constants.rbเส้นทางอย่าลืมรีสตาร์ทเซิร์ฟเวอร์:touch tmp/restart.txt
user664833

4
def self.coloursตัวอย่างไม่เหมาะ เวลาที่คุณโทรหากันdef self.colours, ตัวอย่างใหม่ของอาร์เรย์จะถูกส่งกลับ #freezeจะไม่ช่วยในกรณีนี้ วิธีปฏิบัติที่ดีที่สุดคือการประกาศว่าเป็นค่าคงที่ Ruby ซึ่งในกรณีนี้คุณจะได้รับวัตถุเดียวกันกลับมาเสมอ
Zabba

@Zabba หากการจัดสรรอาร์เรย์เดียวสร้างความแตกต่างที่น่าสังเกตสำหรับแอปของคุณคุณอาจไม่ควรใช้ Ruby ในตอนแรก ... ที่กล่าวว่าใช้วิธีการและส่งกลับอาร์เรย์ใหม่ที่สมบูรณ์ในแต่ละครั้งที่มีคู่ ข้อดี: (1) มันเป็นสิ่งที่ใกล้เคียงที่สุดที่คุณสามารถเข้าถึงวัตถุที่ไม่เปลี่ยนรูปในขอบเขตของคลาสใน Ruby และ (2) คุณเก็บอินเตอร์เฟสที่เหมือนกันในชั้นเรียนของคุณด้วยความเป็นไปได้ที่จะปรับค่าส่งคืนในภายหลังตามสถานะโดยธรรมชาติ อ่านสีจากฐานข้อมูล) โดยไม่ต้องเปลี่ยนอินเตอร์เฟซ
Holger เพียง

@ โฮลเกอร์เพียงอย่างน้อยหนึ่งในเป้าหมายของคุณยังคงสามารถทำได้โดยใช้ค่าคงที่: class Card; COLOURS = ['white', 'blue'].freeze; def self.colours; COLOURS; end; endกล่าวว่าการจัดสรรอาร์เรย์ในภาษาใด ๆ อาจเป็นปัญหาได้ สำหรับหนึ่งมันใช้หน่วยความจำด้วยเหตุผลไม่ (ดี) หากโหลดจากฐานข้อมูลและต้องการแคชค่าหนึ่งยังสามารถใช้ตัวแปรอินสแตนซ์ของคลาสซึ่งสามารถโหลดได้โดยใช้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
เคลวิน

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ที่สุด (แม้ว่าฉันหวังว่ามันจะไม่นาน)
Tom Rossi

@TomRossi ผมเห็นด้วยเช่นเป็นดีconfig configurationเราอาจหวังว่าจะได้รับช็อตคัทในบางช่วงเวลา :)
Halil Özgür

นี่ยังเป็นวิธีที่ดีที่สุดใน Rails 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
Zia Ul Rehman Mughal

ค่าคงที่ของฉันกำลังทำงาน แต่หยุดทำงานด้วยเหตุผลบางอย่าง (เช่นไฟล์ของฉันถูกย้ายออกจาก initializers) หลังจากตรวจสอบคำตอบนี้ฉันดูอย่างใกล้ชิดและย้ายพวกเขากลับและตอนนี้ทำงาน ขอบคุณ
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

พยายามเก็บค่าคงที่ทั้งหมดในที่เดียวในแอปพลิเคชันของฉันฉันได้สร้างโฟลเดอร์ค่าคงที่ภายใน initializers ดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

และฉันมักจะคงที่ทั้งหมดในไฟล์เหล่านี้

ในกรณีของคุณคุณสามารถสร้างไฟล์ภายใต้โฟลเดอร์ค่าคงที่เช่น 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

ฉันใช้ 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 กล่าวถึงเทคนิคนี้เช่นกันแม้ว่า Zabba จะเตือนมัน
AlexC

0

ตามเงื่อนไขของคุณคุณยังสามารถกำหนดตัวแปรสภาพแวดล้อมบางอย่างและดึงข้อมูลผ่าน ENV['some-var']ในรหัสทับทิมวิธีนี้อาจไม่เหมาะกับคุณ แต่ฉันหวังว่ามันจะช่วยคนอื่นได้

ตัวอย่าง: คุณสามารถสร้างไฟล์ที่แตกต่างกัน.development_env, .production_env, .test_envและโหลดได้ตามสภาพแวดล้อมที่ใช้ของคุณตรวจสอบนี้ Gen dotenv รางรถไฟซึ่งโดยอัตโนมัตินี้สำหรับคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.