วิธีที่ดีที่สุดในการโหลดโมดูล / คลาสจากโฟลเดอร์ lib ใน Rails 3?


273

เนื่องจาก Rails 3 รีลีสล่าสุดไม่ใช่โมดูลการโหลดอัตโนมัติและคลาสจาก lib อีกต่อไปอะไรจะเป็นวิธีที่ดีที่สุดในการโหลดมัน?

จาก GitHub:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);

คำตอบ:


251

ในฐานะของ Rails 2.3.9มีการตั้งค่าconfig/application.rbที่คุณสามารถระบุไดเรกทอรีที่มีไฟล์ที่คุณต้องการโหลดอัตโนมัติ

จาก application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)

7
หมายเหตุของ @ ขอบคุณคำตอบถ้าคุณกำลังมองหาเพื่อ autoload app/libทรีย่อยทั้งหมดของ
ทอมแฮร์ริสัน

199
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

แหล่งที่มา: Rails 3 Quicktip: ไดเร็กทอรี lib รวมถึงไดเรกทอรีย่อยทั้งหมดหลีกเลี่ยงการโหลดที่ขี้เกียจ

โปรดทราบว่าไฟล์ที่อยู่ในโฟลเดอร์ lib จะถูกโหลดเมื่อเซิร์ฟเวอร์เริ่มทำงานเท่านั้น หากคุณต้องการความสะดวกสบายในการ autoreload ไฟล์เหล่านั้นอ่าน: Rails 3 QuickTip: อัตโนมัติโหลด lib โฟลเดอร์ในโหมดการพัฒนา โปรดทราบว่านี่ไม่ได้มีไว้สำหรับสภาพแวดล้อมการผลิตเนื่องจากการโหลดซ้ำอย่างช้าๆทำให้เครื่องช้าลง


ลิงก์เหล่านี้ตาย
Besi

84

ความมหัศจรรย์ของการโหลดอัตโนมัติ

ฉันคิดว่าตัวเลือกในการควบคุมโฟลเดอร์ที่สิ่งที่ถูกโหลดอัตโนมัติได้รับการครอบคลุมอย่างเพียงพอในคำตอบอื่น ๆ อย่างไรก็ตามในกรณีที่คนอื่นกำลังมีปัญหาในการโหลดแม้ว่าพวกเขาจะมีการแก้ไขเส้นทางอัตโนมัติตามที่ต้องการก็ตามคำตอบนี้จะพยายามอธิบายว่าอะไรคือความมหัศจรรย์ที่อยู่เบื้องหลังสิ่งที่โหลดอัตโนมัตินี้

ดังนั้นเมื่อพูดถึงการโหลดสิ่งต่าง ๆ จากไดเรกทอรีย่อยจะมี gotcha หรือแบบแผนที่คุณควรระวัง บางครั้งเวทมนต์ Ruby / Rails (คราวนี้ส่วนใหญ่เป็น Rails) อาจทำให้ยากที่จะเข้าใจว่าทำไมบางสิ่งเกิดขึ้น โมดูลใด ๆ ที่ประกาศในเส้นทาง autoload จะถูกโหลดก็ต่อเมื่อชื่อโมดูลสอดคล้องกับชื่อไดเรกทอรีหลัก ดังนั้นในกรณีที่คุณพยายามใส่lib/my_stuff/bar.rbสิ่งที่ชอบ:

module Foo
  class Bar
  end
end

มันจะไม่ถูกโหลดโดยอัตโนมัติ จากนั้นอีกครั้งถ้าคุณเปลี่ยนชื่อ dir แม่เพื่อทำให้โฮสติ้งโมดูลของคุณในเส้นทาง:foo lib/foo/bar.rbมันจะอยู่ที่นั่นเพื่อคุณ ตัวเลือกอื่นคือตั้งชื่อไฟล์ที่คุณต้องการโหลดอัตโนมัติด้วยชื่อโมดูล เห็นได้ชัดว่ามีเพียงไฟล์เดียวเท่านั้นที่ใช้ชื่อนั้น ในกรณีที่คุณต้องการแบ่งเนื้อหาของคุณเป็นไฟล์หลาย ๆ ไฟล์คุณสามารถใช้ไฟล์หนึ่งไฟล์เพื่อต้องการไฟล์อื่น ๆ ได้ แต่ฉันไม่แนะนำเพราะเมื่ออยู่ในโหมดการพัฒนา โหลดซ้ำให้คุณ แต่ถ้าคุณต้องการจริง ๆ คุณสามารถมีหนึ่งไฟล์โดยชื่อโมดูลที่ระบุไฟล์จริงที่ต้องการใช้โมดูล ดังนั้นคุณอาจมีไฟล์สองไฟล์: lib/my_stuff/bar.rbและไฟล์lib/my_stuff/foo.rbเก่าเป็นไฟล์เดียวกับด้านบนและไฟล์หลังมีบรรทัดเดียว:require "bar"และนั่นก็ใช้ได้เหมือนกัน

ป.ล. ฉันรู้สึกว่าถูกบังคับให้เพิ่มสิ่งสำคัญอีกอย่าง เมื่อเร็ว ๆ นี้เมื่อใดก็ตามที่ฉันต้องการมีบางสิ่งบางอย่างในไดเรกทอรี lib ที่ต้องมีการโหลดอัตโนมัติฉันมักจะเริ่มคิดว่าถ้านี่เป็นสิ่งที่ฉันพัฒนาขึ้นมาโดยเฉพาะสำหรับโครงการนี้ (ซึ่งมักจะเป็นบางวัน) เปลี่ยนเป็นส่วนย่อย "คงที่" ของรหัสที่ใช้ในหลายโครงการหรือ submodule คอมไพล์ ฯลฯ ซึ่งในกรณีนี้มันควรจะอยู่ในโฟลเดอร์ lib) แน่นอนแล้วมันอาจจะไม่ได้อยู่ในโฟลเดอร์ lib เลย บางทีมันควรจะอยู่ในโฟลเดอร์ย่อยภายใต้โฟลเดอร์แอพ·ฉันรู้สึกว่านี่เป็นวิธีใหม่ในการทำสิ่งต่างๆ เห็นได้ชัดว่าเวทมนตร์เดียวกันนั้นใช้ได้ทุกที่ในเส้นทางที่คุณใส่สิ่งของของคุณดังนั้นมันจึงเป็นสิ่งที่ดีสำหรับสิ่งเหล่านี้ อย่างไรก็ตามนี่เป็นเพียงความคิดของฉันในเรื่อง คุณมีอิสระที่จะไม่เห็นด้วย :)


UPDATE: เกี่ยวกับประเภทของเวทย์มนตร์ ..

ในขณะที่ Severin ชี้ให้เห็นในความคิดเห็นของเขาหลัก "autoload a module mechanism" แน่ใจว่าเป็นส่วนหนึ่งของ Ruby แต่สิ่งที่เส้นทาง autoload ไม่ได้ คุณไม่จำเป็นต้องใช้ Rails ทำautoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"). และเมื่อคุณพยายามอ้างอิงโมดูล Foo เป็นครั้งแรกมันจะโหลดให้คุณ อย่างไรก็ตามสิ่งที่ Rails ทำคือช่วยให้เราสามารถลองและโหลดเนื้อหาโดยอัตโนมัติจากโฟลเดอร์ที่ลงทะเบียนและสิ่งนี้ได้ถูกนำไปใช้ในลักษณะที่ต้องการสมมติบางสิ่งเกี่ยวกับอนุสัญญาการตั้งชื่อ ถ้ามันไม่ได้ถูกนำมาใช้เช่นนั้นทุกครั้งที่คุณอ้างอิงบางสิ่งที่ไม่ได้โหลดในปัจจุบันมันจะต้องผ่านไฟล์ทั้งหมดในโฟลเดอร์ autoload ทั้งหมดและตรวจสอบว่ามีสิ่งใดที่คุณพยายามอ้างอิง สิ่งนี้จะเอาชนะความคิดของการโหลดอัตโนมัติและการโหลดอัตโนมัติ อย่างไรก็ตามด้วยข้อตกลงเหล่านี้ในสถานที่ก็สามารถหักออกจากโมดูล / ชั้นเรียนของคุณพยายามที่จะโหลดในที่ที่อาจมีการกำหนดและเพียงแค่โหลดที่


1
ทำไมทับทิมเวทมนต์ถึงเป็นเช่นนี้? Ruby ให้ฟังก์ชั่น Module # autoload ซึ่งคุณสามารถใช้เพื่อสั่งไฟล์ที่โหลดเมื่อเข้าถึงค่าคงที่ (ไม่ได้กำหนด) (ดูruby-doc.org/core-1.9.3/Module.html#method-i-autoload ) การจับคู่ชื่อโมดูล / คลาสไปยังไดเรกทอรี / ไฟล์อยู่ในความคิดของฉันใน Rails / ActiveSupport (เช่นที่นี่: github.com/rails/rails/blob/ ...... ) ฉันผิดหรือเปล่า?
Severin

ใช่ฉันเชื่อว่าคุณถูกต้อง ฉันรีบร้อนเกินไปที่จะ "แก้ไข" คำตอบดั้งเดิมของฉันเมื่อ Zabba ชี้ให้เห็น "ข้อบกพร่อง" ของมัน ให้ฉันอัปเดตคำตอบของฉันอีกเล็กน้อยเพื่อชี้แจงปัญหานี้
Timo

1
ฉันใช้เวลาครึ่งชั่วโมงหรือประมาณนั้น ฉันต้องการ (ต้องการ) เพื่อ autoload Sprockets :: JSRender :: Processor เส้นทางสำหรับสิ่งนั้นสามารถพบได้โดยการเข้าไปในคอนโซลของ rails และทำการ "Sprockets :: JSRender :: Processor" .underscore และ disvoering ว่าเป็น "sprockets / js_render / processor" (เพิ่มด้วย. rb) HTH บางคน
pedz

คุณเพิ่งช่วยสติของฉัน ~ ถอนหายใจด้วยความโล่งอก ~ ขอบคุณมากสำหรับการแบ่งปัน :)
เบรนเดน

ขอบคุณสำหรับความคิดเห็นที่มีประโยชน์ที่สุดนี้ ฉันไม่เข้าใจว่าทำไมบางโมดูลทำงานเหมือนที่พวกเขาทำจนกระทั่งฉันอ่านความคิดเห็นของคุณ พรกับคุณ!
mjnissim

41

คำเตือน: หากคุณต้องการโหลด 'Monkey patch' หรือ 'open class' จากโฟลเดอร์ 'lib' ของคุณอย่าใช้วิธี'autoload' !!!

  • วิธี " config.autoload_paths ": ใช้งานได้เฉพาะเมื่อคุณโหลดคลาสที่กำหนดไว้ในที่เดียวเท่านั้น หากบางคลาสมีการกำหนดไว้แล้วที่อื่นคุณจะไม่สามารถโหลดได้อีกด้วยวิธีการนี้

  • วิธีการ " config / initializer / load_rb_file.rb ": ใช้งานได้! ไม่ว่าคลาสเป้าหมายจะเป็นคลาสใหม่หรือ "open class" หรือ "monkey patch" สำหรับคลาสที่มีอยู่ก็ใช้งานได้!

สำหรับรายละเอียดเพิ่มเติมโปรดดู: https://stackoverflow.com/a/6797707/445908


6
นี่คือความแตกต่างที่สำคัญที่จะเข้าใจ ขอบคุณสำหรับสิ่งนี้.
Tyler Collier

28

คล้ายกันมาก แต่ฉันคิดว่านี่สวยกว่านี้เล็กน้อย:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]

18

ในกรณีของฉันฉันพยายามโหลดไฟล์โดยตรงภายใต้ lib dir

ภายในแอปพลิเคชัน ...

require '/lib/this_file.rb' 

ไม่ทำงานแม้แต่ในคอนโซลแล้วเมื่อฉันลอง

require './lib/this_file.rb' 

และรางโหลดไฟล์อย่างสมบูรณ์แบบ

ฉันยังสวยอยู่และฉันไม่แน่ใจว่าทำไมมันถึงใช้ได้ดี หากใครบางคนต้องการที่จะอธิบายให้ฉันฉันขอขอบคุณมัน: DI หวังว่านี้จะช่วยให้คนอย่างใดอย่างหนึ่ง


2
นั่นเป็นเพราะ. /lib/this_file.rb ดูในไดเรกทอรีปัจจุบัน (ในคอนโซล Rails นั่นจะเป็นรูท Rails ของคุณ) และ /lib/this_file.rb จะมองหาว่าเป็นพา ธ สัมบูรณ์ ตัวอย่าง: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
Jason

7

ผมมีปัญหาเหมือนกัน. นี่คือวิธีที่ฉันจะแก้ไขมัน วิธีการแก้ปัญหาโหลดไดเรกทอรี lib และไดเรกทอรีย่อยทั้งหมด (ไม่เพียงโดยตรง) แน่นอนคุณสามารถใช้สิ่งนี้กับไดเรกทอรีทั้งหมด

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

5
สิ่งนี้มีผลข้างเคียงที่น่ารังเกียจจากการจัดประชุมการตั้งชื่อของ Rails โดยสิ้นเชิง หาก lib / bar / foo.rb กำหนด Bar :: Foo ปรากฏขึ้นก่อน lib / foo.rb กำหนด Foo ในการค้นหา autoload คุณจะได้รับข้อผิดพลาดที่ทำให้สับสนเช่นExpected lib/bar/foo.rb to define constant Fooถ้าคุณพยายามโหลด lib / foo.rb โดยอ้างถึง Foo คงที่
Jacob

5

config.autoload_paths ไม่ทำงานสำหรับฉัน ฉันแก้มันด้วยวิธีอื่น

Ruby on Rails 3 อย่าโหลดรหัส (autoload) อัตโนมัติจากโฟลเดอร์ / lib ฉันแก้มันโดยใส่เข้าไปข้างในApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 

4

หากไฟล์บางไฟล์เท่านั้นที่ต้องการเข้าถึงโมดูลใน lib เพียงเพิ่มคำสั่ง require ลงในไฟล์ที่ต้องการ ตัวอย่างเช่นหากหนึ่งโมเดลต้องการเข้าถึงหนึ่งโมดูลให้เพิ่ม:

require 'mymodule'

ที่ด้านบนของไฟล์ model.rb


50
คุณไม่ควรใช้requireแอพทางรถไฟเพราะมันจะป้องกันไม่ให้ActiveSupport::Dependencies[un] โหลดรหัสนั้นถูกต้อง แต่คุณควรใช้config.autoload_pathsเช่นคำตอบข้างต้นแล้วรวม / ขยายตามที่ต้องการ
ben_h

13
ขอบคุณ @ ไมค์ฉันจะทำสิ่งที่คุณทำมันเป็นการดีที่ได้เห็นคำอธิบายว่าทำไมมันถึงไม่ดีขอบคุณที่ไม่ลบคำตอบ
pupeno

สิ่งที่เกี่ยวกับ 'mymodule' รวมถึงถ้าคุณต้องการโหลดหนึ่งโมดูล?
Mike

1
@ben_h คุณไม่ควรrequireอยู่ในแอพ Rails หรือไม่? ในงานที่เสาะหาฉันกำลังrequireไอเอ็นจีและไอเอ็นจีโมดูลที่อาศัยอยู่ในinclude lib/ฉันไม่ควรทำอย่างนั้นหรือ
เดนนิส

@ben_h ค้นหาของฉันแสดงให้เห็นว่ามันเป็นเรื่องธรรมดาที่จะrequireของคุณlib/รหัส (เช่นบล็อกโพสต์นี้ , คำตอบดังนั้นนี้ ) ฉันยังไม่แน่ใจเกี่ยวกับเรื่องทั้งหมด คุณสามารถให้หลักฐานเพิ่มเติมหลังการอ้างสิทธิ์ว่าไม่ได้ใช้requireหรือไม่?
เดนนิส

2

สะกดชื่อไฟล์อย่างถูกต้อง

อย่างจริงจัง. ฉันต่อสู้กับคลาสเป็นเวลาหนึ่งชั่วโมงเนื่องจากคลาสเป็นภิบาล :: ArchitectureBoard และไฟล์อยู่ใน lib / ภิบาล / architecture_baord.rb (transposed O และ A ใน "board")

ดูเหมือนจะชัดเจนในการหวนกลับ แต่มันเป็นปีศาจที่ติดตามลง หากคลาสนั้นไม่ได้กำหนดไว้ในไฟล์ที่ Rails คาดว่าจะเป็นไปตามชื่อคลาสนั้นจะไม่สามารถค้นหาได้


2

ขณะที่Rails 5ก็จะแนะนำให้ใส่โฟลเดอร์ lib ภายใต้ไดเรกทอรีแอปหรือแทนที่จะสร้างพื้นที่ชื่ออื่น ๆ ที่มีความหมายสำหรับโฟลเดอร์เป็นservices, presenters, featuresฯลฯ และวางไว้ใต้ไดเรกทอรี app สำหรับการโหลดอัตโนมัติโดยทางรถไฟ

โปรดตรวจสอบลิงค์การสนทนา GitHubนี้เช่นกัน


1

มีสาเหตุหลายประการที่คุณอาจมีปัญหาในการโหลดจาก lib - ดูที่นี่สำหรับรายละเอียด - http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/

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