โหลดไฟล์ lib อัตโนมัติใน Rails 4


229

ฉันใช้บรรทัดต่อไปนี้ใน initializer เพื่อสร้างรหัสอัตโนมัติใน/libไดเรกทอรีของฉันในระหว่างการพัฒนา:

config / initializers / custom.rb:

RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?

(จากRails 3 Quicktip: โหลดโฟลเดอร์ lib ซ้ำอัตโนมัติในโหมดการพัฒนา )

มันใช้งานได้ดี แต่ไม่มีประสิทธิภาพเกินกว่าที่จะใช้ในการผลิต - แทนที่จะโหลด libs ในแต่ละคำขอฉันแค่ต้องการโหลดมันเมื่อเริ่มต้น บล็อกเดียวกันมีบทความอื่นที่อธิบายวิธีการนี้:

config / application.rb:

# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

อย่างไรก็ตามเมื่อฉันสลับไปที่แม้ในการพัฒนาฉันได้รับ NoMethodErrors เมื่อพยายามใช้ฟังก์ชั่น lib

ตัวอย่างหนึ่งในไฟล์ lib ของฉัน:

lib / extensions.rb:

Time.class_eval do
  def self.milli_stamp
    Time.now.strftime('%Y%m%d%H%M%S%L').to_i
  end
end

การโทรTime.milli_stampจะโยน NoMethodError

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


โฟลเดอร์ config / initializers ถูกโหลดโดยอัตโนมัติเมื่อแอพ Rails เริ่มทำงานหรือไม่
Jwan622

คำตอบ:


548

ฉันคิดว่านี่อาจช่วยแก้ปัญหาของคุณ:

  1. ในconfig / application.rb :

    config.autoload_paths << Rails.root.join('lib')

    และให้ตั้งชื่อที่ถูกต้องในlib

    ในlib / foo.rb :

    class Foo
    end

    ในlib / foo / bar.rb :

    class Foo::Bar
    end
  2. หากคุณต้องการทำแพทช์ลิงในไฟล์เช่นlib / Extensions.rbคุณอาจต้องใช้มันด้วยตนเอง:

    ในconfig / initializers / require.rb :

    require "#{Rails.root}/lib/extensions" 

PS


1
@ ifyouseewendy- คุณพูดถูก - เพราะส่วนขยาย. rb ไม่ได้ทำตามแบบแผนการตั้งชื่อของ Rails Rails จะไม่รวมอยู่ในกระบวนการโหลด ฉันได้รับมันทำงานด้วยตนเองต้องการมัน
Yarin

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

@Matrix "รวมไฟล์ก่อนที่จะโหลดรุ่น" คุณอาจต้องใช้ไฟล์ของคุณเองโดยไม่ต้องใช้คุณสมบัติ autoload
ifyouseewendy

@ ปรับปรุงให้เหมาะสมถ้าฉันต้องการมันใน initializer แต่ไฟล์อยู่ใน autoload_path มันจะถูกโหลดใหม่ (โหลด 2 ครั้ง) หรือไม่? theire เป็น "require_once" เช่นเดียวกับใน php?
เมทริกซ์

5
ดูเหมือนจะไม่ทำงานใน Rails 5 API ในการผลิต (แต่กำลังพัฒนาอยู่) config.eager_load_paths << Rails.root.join('lib')ผมเชื่อว่าคุณจำเป็นต้องใช้ อย่างไรก็ตามนั่นมีข้อเสียที่สำคัญในการeager_load_pathsโหลดทุกอย่างในงานเช่นกัน ฉันคิดว่าทางออกของ lulalala ดีกว่า นี่คือโพสต์บล็อกที่มีรายละเอียดเพิ่มเติม: blog.arkency.com/2014/11/…
hirowatari

33

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

เพื่อหลีกเลี่ยงการทั้งหมดautoload_pathsหรือeager_load_pathsยุ่งยากสร้าง "lib" หรือ "อื่น ๆ" ไดเรกทอรีภายใต้ "แอป" ไดเรกทอรี ใส่รหัสสถานที่ตามปกติแล้ว Rails จะโหลดไฟล์เหมือนกับที่โหลด (และโหลดซ้ำ) ไฟล์โมเดล


3
ฉันอยู่ใน Rails 4.2 และมันไม่โหลดไฟล์อัตโนมัติใต้appฉันต้องทำมันเอง ...... หรือต้องใส่มันในเส้นทางการโหลด ..
Arup Rakshit

6
คุณผิด Arup ไดเรกทอรีย่อยของไดเรกทอรีแอพจะอยู่ในอาร์เรย์ autoload_paths ใน Rails 4.2.See edgeguides.rubyonrails.org/ โดยอัตโนมัติ
Dr.Strangelove

ยกเว้นapp/viewsไดเรกทอรีที่ไม่ได้รับการเพิ่ม; หรือค่อนข้างถูกลบออกอย่างชัดเจน
James B. Byrne

1
คำตอบที่ดี สิ่งเดียวที่ทำงานกับฉันบนราง 5 / api
jstafford

6
เพียงจำไว้ว่าlibมีไว้สำหรับรหัสที่สามารถนำไปใช้กับหลายโครงการและอาจถูกดึงไปยังอัญมณี หากไม่ได้สร้างโฟลเดอร์ที่เหมาะสมยิ่งขึ้นภายใต้การค้นหาแอปในฐานะservices/หรือpresenters/แม้กระทั่งแยกย่อยโฟลเดอร์เหล่านี้
PhilT

6

นี่อาจช่วยคนอย่างฉันที่ค้นหาคำตอบนี้เมื่อค้นหาวิธีแก้ปัญหาที่ Rails จัดการกับการโหลดคลาส ... ฉันพบว่าฉันต้องกำหนด moduleชื่อที่ตรงกับชื่อไฟล์ของฉันอย่างเหมาะสมแทนที่จะกำหนดคลาส:

ในไฟล์lib / development_mail_interceptor.rb (ใช่ฉันใช้รหัสจาก Railscast :))

module DevelopmentMailInterceptor
  class DevelopmentMailInterceptor
    def self.delivering_email(message)
      message.subject = "intercepted for: #{message.to} #{message.subject}"
      message.to = "myemail@mydomain.org"
    end
  end
end

ใช้งานได้ แต่ไม่โหลดถ้าฉันไม่ได้ใส่คลาสไว้ในโมดูล


1
ใน ruby ​​"การจับคู่อย่างเหมาะสม" หมายความว่าไฟล์นั้นอยู่ในระบบไฟล์ที่LOAD_PATH/module/class.rb(ขีดล่าง) ซึ่งLOAD_PATHอยู่ในโหลดพา ธ ที่ใช้โดยแอพ Ruby (autoload_paths ในกรณีของ Rails) libมีความผันผวนจากการโหลดอัตโนมัติโดย Rails เพื่อไม่ให้โหลดอัตโนมัติและในรุ่นล่าสุด (> = Rails 3.x) ไม่ได้โหลดอัตโนมัติ ไม่แนะนำให้ใช้เวทย์มนตร์นี้สำหรับคุณ บางทีมันอาจเป็น Railscast แบบเก่า ๆ ?
Peter H. Boling

0

ใช้ config.to_prepare เพื่อโหลดตัวติดตั้ง / ส่วนขยายลิงสำหรับทุกคำขอในโหมดการพัฒนา

config.to_prepare do |action_dispatcher|
 # More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
 Rails.logger.info "\n--- Loading extensions for #{self.class} "
 Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
   Rails.logger.info "Loading extension(s): #{entry}"
   require_dependency "#{entry}"
 end
 Rails.logger.info "--- Loaded extensions for #{self.class}\n"

ปลาย

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