Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Rails는 보석을 어떻게 처리합니까?

몇 주 전에 RubyGems가 Ruby의 로드 경로를 관리하는 방법에 대해 썼습니다. 하지만 Rails는 RubyGems를 직접 사용하지 않고 Bundler를 사용하여 gem을 관리합니다.

Bundler가 어떻게 작동하는지 모른다면 보석이 Rails로 들어가는 방식이 약간 너무 보일 수 있습니다. 마법 같은. Gemfile에 줄을 추가하는 방법 앱에 코드를 가져오시겠습니까? Bundler, Rails 및 RubyGems는 종속성을 쉽게 처리하기 위해 어떻게 함께 작동합니까?

번들러를 선택해야 하는 이유

나는 Bundler를 엄격한 보석 관리자라고 생각합니다. 즉, Bundler는 필요한 gem의 올바른 버전을 설치하도록 도와주고 앱이 해당 버전을 사용하세요.

이것은 정말 도움이 됩니다. 그 이유를 이해하려면 Bundler 이전의 세계로 돌아가야 합니다.

Bundler 이전에는 일종의 설정 스크립트를 사용하여 올바른 버전의 gem을 설치하는 것이 여전히 매우 쉬웠습니다.

빈/설정
gem install rails -v 4.1.0
gem install rake -v 10.3.2
...

(즉, Rails 4.1의 종속성이 Rake 10.3.2의 종속성과 충돌하지 않는 한!)

하지만 각각 다른 버전의 gem에 의존하는 몇 가지 다른 Rails 앱에서 작업한다면 어떻게 될까요? 정말 조심하지 않으면 끔찍한 보석 활성화 오류가 발생합니다.

gem_error
Gem::Exception: can't activate hpricot (= 0.6.161, runtime), 
already activated hpricot-0.8.3

어. 그 메시지는 여전히 나에게 악몽을 준다. 일반적으로 보석을 설치하고 제거하는 데 하루가 소요된다는 의미이므로 그냥 해당 컴퓨터의 올바른 버전입니다. 그리고 실수로 gem install rake만 있으면 됩니다. 신중한 계획을 완전히 엉망으로 만들었습니다.

rvm gemsets는 잠시 동안 이 문제를 해결했습니다. 그러나 설정하는 데 시간이 필요했으며 실수로 잘못된 gemset에 설치한 경우 동일한 문제가 다시 발생했습니다. Bundler를 사용하면 종속성에 대해 생각할 필요가 거의 없습니다. 앱은 일반적으로 제대로 작동합니다. 그리고 Bundler는 gemsets보다 훨씬 적은 설정을 필요로 합니다.

따라서 Bundler는 두 가지 중요한 작업을 수행합니다. 필요한 모든 보석을 설치하고 RubyGems를 잠그므로 해당 보석이 유일한 Rails 앱 내에서 필요할 수 있습니다.

Rails는 Bundler를 어떻게 사용하나요?

기본적으로 Bundler는 gem을 설치하고 분리합니다. 하지만 그게 전부는 아닙니다. Gemfile에 있는 gem의 코드는 어떻게 되나요? Rails 앱으로 만드시겠습니까?

bin/rails를 보면 :

빈/레일
#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require_relative '../config/boot'
require 'rails/commands'

../config/boot를 요구하여 Rails를 로드하는 것을 볼 수 있습니다. . 해당 파일을 살펴보겠습니다.

구성/부팅
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

require 'bundler/setup' # Set up gems listed in the Gemfile.

안녕하세요, 번들러입니다! (또한 다른 Gemfile BUNDLE_GEMFILE 환경 변수를 설정하여 사용 . 정말 멋지네요.)

bundler/setup 몇 가지 작업을 수행합니다.

  • $LOAD_PATH에서 gem에 대한 모든 경로를 제거합니다. (RubyGems가 수행한 모든 로드 경로 작업을 반대로 합니다).
  • 그런 다음 Gemfile.lock에 있는 gem의 로드 경로를 추가합니다. $LOAD_PATH로 돌아가기 .

이제 require할 수 있는 유일한 보석 의 파일은 Gemfile에 있는 파일입니다. .

따라서 필요한 모든 보석은 로드 경로에 있습니다. 하지만 RubyGems를 단독으로 사용하는 경우에도 여전히 require 필요한 파일. Bundler와 함께 Rails를 사용할 때 보석이 필요하지 않은 이유는 무엇인가요?

config/application.rb를 간단히 살펴보세요. , Rails 부팅 후 실행:

config/application.rb
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

다시 번들러입니다! Bundler.require 전달하는 모든 그룹의 모든 보석이 필요합니다. ("그룹"은 Gemfile에서 지정한 그룹을 의미합니다.)

Rails.groups에 있는 그룹 , 하지만?

railties/lib/rails.rb
   # Returns all rails groups for loading based on:
    #
    # * The Rails environment;
    # * The environment variable RAILS_GROUPS;
    # * The optional envs given as argument and the hash with group dependencies;
    #
    #   groups assets: [:development, :test]
    #
    #   # Returns
    #   # => [:default, :development, :assets] for Rails.env == "development"
    #   # => [:default, :production]           for Rails.env == "production"
    def groups(*groups)
      hash = groups.extract_options!
      env = Rails.env
      groups.unshift(:default, env)
      groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
      groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
      groups.compact!
      groups.uniq!
      groups
    end

글쎄, 그것은 그것을 설명합니다. Rails.groups [:default, :development]가 됩니다. 개발 모드에서 Rails를 실행할 때 [:default, :production] 프로덕션 모드에서 등등.

따라서 Bundler는 Gemfile에서 각 그룹에 속하는 gem에 대해 require를 호출합니다. 발견한 각각의 보석에 보석이 있다면 nokogiri , require "nokogiri"를 호출합니다. 당신을 위해. 그렇기 때문에 gem은 일반적으로 추가 코드 없이 Rails에서 작동합니다.

도구 알아보기

당신이 잘 사용하는 도구를 이해한다면, 그것들을 사용하는 것이 더 쉬울 것입니다. 따라서 항상 무언가를 사용하고 있다면 잠시 시간을 내어 좀 더 자세히 알아볼 가치가 있습니다.

Ruby와 Rails에서 작업하고 있다면 매일 gem을 사용하게 될 것입니다. 시간을 내어 잘 배워보세요!