Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เข้าใจ Ruby Gems: ทำงานยังไง และ Troubleshoot อย่างไร

ส่วนใหญ่ gems ใน Ruby Just Works แตมีปัญหาใหญ่กับ Ruby magic: ตอนพัง ยากหาสาเหตุ

ไม่ค่อยเจอปัญหา gems แตตอนเจอ Google ไมช่วยมาก Error messages generic อาจหลายสาเหตุ ถ้าไม่เข้าใจ gems ทำงานกับ Ruby ยังไง จะ debug ยากมาก

Gems อาจ ดู magical แตขุดนิดหน่อย เข้าใจง่าย

gem install ทำอะไร?

Ruby gem แค่ code zipped กับ extra data ดูโค้ดใน gem ด้วย gem unpack:

~/Source/playground jweiss$ gem unpack resque_unit Fetching: resque_unit-0.4.8.gem (100%) Unpacked gem: '/Users/jweiss/Source/playground/resque_unit-0.4.8' ~/Source/playground jweiss$ cd resque_unit-0.4.8 ~/Source/playground/resque_unit-0.4.8 jweiss$ find . . ./lib ./lib/resque_unit ./lib/resque_unit/assertions.rb ./lib/resque_unit/errors.rb ./lib/resque_unit/helpers.rb ./lib/resque_unit/plugin.rb ./lib/resque_unit/resque.rb ./lib/resque_unit/scheduler.rb ./lib/resque_unit/scheduler_assertions.rb ./lib/resque_unit.rb ./lib/resque_unit_scheduler.rb ./README.md ./test ./test/resque_test.rb ./test/resque_unit_scheduler_test.rb ./test/resque_unit_test.rb ./test/sample_jobs.rb ./test/test_helper.rb ~/Source/playground/resque_unit-0.4.8 jweiss$ 

gem install simplest form ทำแบบนี้ ดึง gem ใส่ files ใน special directory บนระบบ 看 gem environment (ดูบรรทัด INSTALLATION DIRECTORY:) จะรู้ติดตั้งที่ไหน:

~ jweiss$ gem environment RubyGems Environment: - RUBYGEMS VERSION: 2.2.2 - RUBY VERSION: 2.1.2 (2014-05-08 patchlevel 95) [x86_64-darwin14.0] - INSTALLATION DIRECTORY: /usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0 ... 
~ jweiss$ ls /usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0 bin bundler doc gems build_info cache extensions specifications 

โค้ด gems ติดตั้งทั้งหมดอยู่ที่นี่ ใต้ gems directory

Paths แตกต่างระหว่างระบบ และขึ้นกับวิธีติดตั้ง Ruby (rvm ต่าง Homebrew ต่าง rbenv) ดังนั้น gem environment ช่วยตอนอยากรู้ gems อยู่ไหน

Gem Code ถูก Require อย่างไร?

ช่วยใช้ code ใน gems RubyGems override require ของ Ruby (ใน core_ext/kernel_require.rb) comment ชัดเจน:

core_ext/kernel_require.rb
 ## # When RubyGems is required, Kernel#require is replaced with our own which # is capable of loading gems on demand. # # When you call <tt>require 'x'</tt>, this is what happens: # * If the file can be loaded from the existing Ruby loadpath, it # is. # * Otherwise, installed gems are searched for a file that matches. # If it's found in gem 'y', that gem is activated (added to the # loadpath). # 

เช่น อยาก load active_support RubyGems พยายาม require ด้วย Ruby require ได้ error:

LoadError: cannot load such file -- active_support from (irb):17:in `require' from (irb):17 from /usr/local/bin/irb:11:in `<main>' 

RubyGems ดู error message ว่าต้องหา active_support.rb ใน gem แทน สแกน gems metadata หา gem ที่มี active_support.rb:

irb(main):001:0> spec = Gem::Specification.find_by_path('active_support') => #<Gem::Specification:0x3fe366874324 activesupport-4.2.0.beta1> 

จากนั้น activate gem เพิ่มโค้ดใน gem เข้า Ruby load path (directories ที่ require files ได้):

irb(main):002:0> $LOAD_PATH => ["/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0/x86_64-darwin14.0"] irb(main):003:0> spec.activate => true irb(main):004:0> $LOAD_PATH => ["/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/i18n-0.7.0.beta1/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/thread_safe-0.3.4/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0.beta1/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0/x86_64-darwin14.0"] 

ตอน active_support อยู่ load path แล้ว require files ใน gem ได้เหมือน Ruby code ปกติ ใช้ original require ตัวที่ถูก overwrite โดย RubyGems ได้:

irb(main):005:0> gem_original_require 'active_support' => true 

Cool!

A Little Knowledge Goes a Long Way

RubyGems อาจดูซับซ้อน แตระดับพื้นฐาน มันแค่จัดการ Ruby load path ให้คุณ ไม่ได้บอกว่าง่ายหมด ผมไมครอบคลุม version conflicts, gem binaries (rails, rake), C extensions ฯลฯ

แตรู้ RubyGems ระดับผิวก็ช่วยเยอะ ด้วย code reading และเล่น irb จะดำเข้า source gems ได้ เข้าใจ gems อยู่ไหน ทำให้ RubyGems รู้จัก และรู้ gems โหลดยังไง จะขุด loading problems แบบ crazy ได้

อยากเรียนรู้ Rails และ Bundler จัดการ gems ยังไง อ่าน: How does Rails handle gems?