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

Rbenv, RubyGems 및 Bundler가 함께 작동하는 방식 이해

Ruby에서 종속성을 관리하려면 일반적으로 프로젝트가 의존하는 Ruby 및 gem 버전을 지정해야 합니다. Ruby로 작업한 경험에 따르면 종속성을 디버깅하는 것이 가장 큰 문제 중 하나였습니다. 많은 것들이 "그냥 작동"하기 때문에 실패는 일반적이지 않습니다. 그러나 일이 잘못되면 일반적으로 디버그하고 수정하기가 불필요하게 어렵습니다. 이 기사에서는 Ruby에서 종속성 관리와 관련된 부분을 배치합니다. 이렇게 하면 이러한 이상한 문제가 발생할 때 디버깅하는 데 도움이 됩니다.

Rbenv, RubyGems 및 Bundler가 함께 작동하는 방식 이해

루비 코드 로딩

기본적으로 Ruby 언어는 다른 곳에 정의된 코드를 로드하기 위한 두 가지 주요 방법을 제공합니다. load &require .

load 'json.rb'
require 'json.rb'
require_relative 'json.rb'

두 로드 방법 모두 절대 및 상대 경로를 인수로 허용합니다. 그러나 두 가지 차별화 요소가 있습니다.

  1. load에 대한 다중 호출 require를 여러 번 호출하면 파일이 다시 실행됩니다. 파일을 다시 실행하지 않습니다. 대신 false를 반환합니다. .
  2. load 호출 절대 및 상대 경로로만 확인됩니다. require 호출 $LOAD_PATH 확인 경로가 절대 경로로 확인되지 않는 경우.

세 번째 변형은 require_relative입니다. , 상대 경로를 사용하여 Ruby 프로세스의 작업 디렉토리가 아닌 현재 파일의 위치에 상대적인 코드를 요구합니다.

Rbenv

버전 관리자는 인터프리터(이 경우 Ruby) 버전을 관리하고 쉽게 전환하고 프로젝트의 각 gem을 찾을 위치를 지정하는 데 사용되는 도구입니다. 버전 관리자는 주로 언어에 구애받지 않는 도구이며 다양한 언어에는 Node.js의 경우 Nvm, n, Python의 경우 pyenv, Ruby의 경우 Rbenv, rvm 및 chruby와 같이 각각의 구현이 있습니다. 이제 rbenv를 살펴보겠습니다. 한 번 돌려볼까요?

루비 버전 설치

rbenv install 명령을 사용합니다. 모든 버전의 Ruby를 설치하려면:

# Install ruby 2.6.1
$ rbenv install 2.6.1
Downloading openssl-1.1.1i.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242
Installing openssl-1.1.1i...
Installed openssl-1.1.1i to /home/directory/.rbenv/versions/2.6.1

Downloading ruby-2.6.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.bz2
Installing ruby-2.6.1...
ruby-build: using readline from homebrew
Installed ruby-2.6.1 to /home/directory/.rbenv/versions/2.6.1

# Check Installation
$ rbenv versions # Shows all versions installed.
  system
  2.6.1

# Lookup versions available for installation
$ rbenv install -L
1.8.5-p52
1.8.5-p113
1.8.5-p114
...
2.7.0-rc1
2.7.0-rc2
2.7.0
...
truffleruby+graalvm-20.1.0
truffleruby+graalvm-20.2.0
truffleruby+graalvm-20.3.0

# The full list above amounts to about 500 versions, scrolling through the entire list is a lot.
# The command below is an easy shortcut to find your specific version with fzf.
$ rbenv install `rbenv install -L | fzf`

버전 간 전환

Ruby 버전 간에 전환하는 방법을 식별하는 방법에는 여러 가지가 있습니다. 항상 rbenv 다음을 수행합니다.

  • RBENV_VERSION 확인 .
  • .ruby-version 검색 루트 디렉토리에 도달할 때까지 스크립트의 디렉토리와 그 상위 파일에 있습니다.
  • .ruby-version 검색 $PWD의 파일 루트 디렉토리에 도달할 때까지 상위 디렉토리에 있습니다.
  • 전역 파일 사용 ~/.rbenv/version .

우선 순위는 위에서 아래로 이동합니다. ~/.rbenv/version 최종 대체이며 전역 버전으로 처리됩니다. 아래 참조:

# Inside First Project Root

# Select ruby version for project
$ touch .ruby-version && echo "2.7.1" >> .ruby-version

# Verify selected version
$ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin20] # Result

$ rbenv version
2.7.1 (set by /path/to/current/directory/.ruby-version) # Result

# Change selected version
$ : >> .ruby-version && echo "2.6.1" >> .ruby-version

# Verify selection change
$ ruby --version
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin20] # Result

$ rbenv version
2.6.1 (set by /path/to/current/directory/.ruby-version)

# Change selection with RBENV_VERSION while .ruby-version is present
$ export RBENV_VERSION=2.5.1

# Verify selection change
# .ruby-version is ignored.
$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin20] # Result

$ rbenv version
2.5.1 (set by RBENV_VERSION environment variable) # Result

# Change to a version that is not installed & remove RBENV_VERSION
$ unset RBENV_VERSION & : >> .ruby-version && echo "2.4.1" >> .ruby-version

# Verify selection change
$ ruby --version
rbenv: version `2.4.1' is not installed (set by full/path/to/current/directory/.ruby-version) # Result

심 및 재해싱

rbenv를 디버그할 수 있으려면 이 두 가지 개념을 제대로 이해해야 합니다. 효과적으로.

Shim은 PATH에 존재하는 가벼운 bash 스크립트입니다. 명령을 가로채 실행을 위해 적절한 버전으로 라우팅합니다. 높은 수준에서 모든 명령(예:rspec )는 rbenv exec rspec으로 변환됩니다. . 자세한 내용은 아래를 참조하세요.

먼저 rbenv 모든 명령에 대한 shim을 만듭니다(rspec , bundle 등) 설치된 모든 Ruby 버전에서 버전에 관계없이 CLI 호출을 가로챕니다. 이 shim은 ~/.rbenv/shims에서 찾을 수 있습니다. . 모든 shim에는 아래와 같이 동일한 bash 스크립트가 포함되어 있습니다.

#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"
if [ "$program" = "ruby" ]; then
   for arg; do
     case "$arg" in
     -e* | -- ) break ;;
     */* )
        if [ -f "$arg" ]; then
         export RBENV_DIR="${arg%/*}"
         break
       fi
       ;;
     esac
   done
 fi

 export RBENV_ROOT="/home/directory/.rbenv"
 exec "/usr/local/Cellar/rbenv/1.1.2/libexec/rbenv" exec "$program" "$@"

다음으로 위의 스크립트는 대략 다음과 같이 번역됩니다.

  • 프로그램 이름이 ruby인 경우 인수 -e 사용 ,
    • rbenv exec ruby <args>로 번역
  • 프로그램 이름이 ruby인 경우 스크립트 경로,
    • RBENV_DIR 설정 스크립트의 디렉토리로 이동합니다. 이렇게 하면 rbenv가 활성화됩니다. .ruby-version 검색 $PWD 앞의 스크립트 디렉토리 . .ruby-version인 경우 rbenv 두 위치에 모두 지정됩니다. 스크립트의 디렉토리를 선택합니다.
  • 프로그램 이름이 Ruby가 아닌 경우
    • rbenv exec <program-name> <args>로 번역 .

마지막으로 rbenv exec <command-name> <args> RBENV_VERSION을 확인하여 명령을 전달할 올바른 버전을 식별합니다. 환경 변수. RBENV_VERSION을(를) 기억하십시오. 위에서 정의한 알고리즘에 의해 설정됩니다.

PATH의 심 앞에 붙여야 합니다. 이렇게 하면 Ruby 실행 파일에 대한 첫 번째 접촉 지점이 되고 적절하게 가로챌 수 있습니다. PATH를 이해하는 가장 좋은 방법 shim이 제대로 가로채는지 여부를 설정하고 확인하는 방법은 다음과 같습니다.

$ which -a bundle

/path/to/home/.rbenv/shims/bundle
/usr/bin/bundle

which -a bundle :이것은 PATH를 순진하게 살펴봅니다. bundle가 있는 위치를 찾은 순서대로 인쇄합니다. 찾을수있다. ~/.rbenv/shims에서 무엇인가 앞에 인쇄된 경우 , 심이 제대로 설정되지 않았음을 의미합니다. rbenv which bundle 명령이 rbenv 컨텍스트에서 작동하기 때문에 이를 공개하지 않습니다. PATH를 검색하지 않음 .

Rehashing은 shim을 만드는 과정입니다. rspec과 같이 실행 파일을 제공하는 Ruby gem을 새로 설치할 때 , rbenv rehash를 실행해야 합니다. shim을 생성하여 rspec에 대한 후속 호출이 rbenv가 가로챌 수 있습니다. 적절한 Ruby 버전으로 전달됩니다.

루비젬

다음은 RubyGems입니다. Ruby 공식 사이트에서 다운로드할 수 있습니다. RubyGems는 라이브러리 생성, 공유 및 설치를 용이하게 하도록 설계된 Ruby 패키징 시스템입니다. 어떤 면에서는 apt-get과 비슷하지만 Ruby 소프트웨어를 대상으로 하는 배포 패키징 시스템입니다. RubyGems는 보석을 공유하는 사실상의 방법입니다. 일반적으로 ~/.rbenv/versions/{version-number}/lib/ruby/gems/{minor-version}/에 설치됩니다. 사용되는 버전 관리자에 따라 또는 그 변형입니다. Ruby의 기본 필수 메소드 Kernel.require Gems 설치 디렉토리에서 gem을 로드하는 메커니즘을 제공하지 않습니다. RubyGems 원숭이 패치 Kernel.require

  • 먼저 $LOAD_PATH에서 gem을 검색합니다. .
  • 찾을 수 없으면 GEMS INSTALLATION DIRECTORY에서 보석을 검색하십시오. .
    • 찾으면 $LOAD_PATH에 경로 추가 .

이것은 Ruby가 버전 1.9부터 기본적으로 RubyGems와 함께 제공되기 때문에 "기본적으로" 작동합니다. 이전 Ruby 버전에서는 RubyGems를 수동으로 설치해야 했습니다. 이것은 기본적으로 작동하지만 디버깅할 때 이 차이점을 아는 것도 중요합니다.

보석은 특정 문제를 해결하는 데 사용되는 관련 코드 묶음입니다. 다음과 같이 gem을 설치하고 gem 환경에 대한 정보를 얻으세요.

$ gem install gemname
$ gem env

RubyGems Environment: 
    - RUBYGEMS VERSION: 3.1.2 
    - RUBY VERSION: 2.7.1 (2020-03-31 patchlevel 83) [x86_64-darwin20] 
    - INSTALLATION DIRECTORY: /path/to/home/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0 
    - USER INSTALLATION DIRECTORY: /path/to/home/.gem/ruby/2.7.0 
    - RUBY EXECUTABLE: /path/to/home/.rbenv/versions/2.7.1/bin/ruby 
    - GIT EXECUTABLE: /usr/bin/git 
    - EXECUTABLE DIRECTORY: /path/to/home/.rbenv/versions/2.7.1/bin 
    - SPEC CACHE DIRECTORY: /path/to/home/.gem/specs 
    - SYSTEM CONFIGURATION DIRECTORY: /path/to/home/.rbenv/versions/2.7.1/etc 
    - RUBYGEMS PLATFORMS:   
        - ruby   
        - x86_64-darwin-20 
    - GEM PATHS:    
        - /path/to/home/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0    
        - /path/to/home/.gem/ruby/2.7.0 
    - GEM CONFIGURATION:    
        ...
    - REMOTE SOURCES:    
        - https://rubygems.org/ 
    - SHELL PATH:    
        - /path/to/home/.rbenv/versions/2.7.1/bin

RubyGems는 이 문제를 어떻게 해결합니까? 원숭이는 Kernel을 패치합니다. 자체 require가 있는 require 시스템 방법. 이 제자리를 사용하면 require honeybadger할 때 호출되면 gem 폴더에서 honeybadger.rb를 검색합니다. 발견하면 보석을 활성화합니다.

예를 들어, require 'honeybadger' 다음과 유사한 결과를 생성합니다.

  • spec = Gem::Specification.find_by_path('honeybadger')
  • spec.activate

gem을 활성화한다는 것은 단순히 $LOAD_PATH에 넣는 것을 의미합니다. . RubyGems는 또한 gem 자체를 다운로드하기 전에 gem의 모든 종속성을 다운로드하는 데 도움이 됩니다.

또한 Rubygems에는 gem open <gem-name>으로 연결된 gem의 디렉토리를 열 수 있는 멋진 기능이 있습니다.; 예를 들어,

Rbenv, RubyGems 및 Bundler가 함께 작동하는 방식 이해

이를 통해 앱이 참조하는 gem의 특정 버전을 쉽게 찾고 추적할 수 있습니다.

번들러

이 계층에서 Bundler는 모든 프로젝트 종속성을 쉽게 지정하고 선택적으로 각각에 대한 버전을 지정하는 데 도움이 됩니다. 그런 다음 gem을 해결하고 gem과 해당 종속성을 설치합니다. 실제 애플리케이션 사전 번들러를 구축하는 데는 다음과 같은 수많은 과제가 수반되었습니다.

  • 저희 애플리케이션은 수많은 종속성을 가지고 있으며 이러한 종속성은 다양한 기타 종속성과 각각의 버전을 가지고 있습니다. 하나의 gem의 잘못된 버전을 설치하면 앱이 쉽게 중단되며 이 문제를 수정하는 데 많은 눈물이 필요했습니다.
  • 또한 종속성 중 2개는 동일한 3단계 종속성을 참조할 수 있습니다. 호환성을 찾는 것이 문제였고 문제가 있었다면 문제였습니다.
  • 같은 시스템에 여러 애플리케이션이 있고 다양한 종속성이 있는 경우 애플리케이션은 시스템에 설치된 모든 gem에 액세스할 수 있습니다. 이는 최소 권한 원칙에 위배되고 애플리케이션이 시스템에 설치된 모든 gem에 노출됩니다. 악성인지 여부.

Bundler는 세 가지 문제를 모두 해결하고 다음을 수행하여 앱 종속성을 관리하는 올바른 방법을 제공합니다.

번들러는 종속성을 해결하고 잠금 파일을 생성합니다.

# Gemfile
gem 'httparty'

bundle을 실행하면 또는 bundle install , 잠금 파일을 생성합니다:

GEM
  specs:
    httparty (0.18.1)
      mime-types (~> 3.0)
      multi_xml (>= 0.5.2)
    mime-types (3.3.1)
      mime-types-data (~> 3.2015)
    mime-types-data (3.2020.1104)
    multi_xml (0.6.0)

PLATFORMS
  ruby

DEPENDENCIES
  httparty

BUNDLED WITH
   2.1.4

위에서 번들러는 httparty 버전을 생성합니다. Gemfile.lock.의 자체 종속성뿐만 아니라 설치해야 합니다. 이 파일은 앱 종속성의 청사진이며 버전 제어에 체크인해야 합니다. 이를 통해 프로젝트 종속성이 환경(개발, 스테이징 또는 프로덕션) 전반에 걸쳐 일관되게 유지됩니다.

번들러는 종속성 간의 호환성을 해결합니다.

httparty에 대한 종속성을 해결합니다. 종속성에 적합한 버전을 찾고 지정합니다. Bundler는 또한 gem 간의 종속성을 해결하려고 시도합니다. 예를 들어,

# Gemfile
gem 'httparty' # That relies on gem 'mime-types', '>= 3.0.1, < 4.0.1'
gem 'rest-client' # That relies on gem 'mime-types', '>= 2.0.1, < 3.0'

위의 예는 임의적이며 다음과 같은 오류가 발생합니다.

Bundler could not find compatible versions for gem "mime-types":
In Gemfile:
    httparty was resolved to 0.18.1, which depends on
        mime-types ('>= 3.0.1, < 4.0.1')

    rest-client was resolved to 2.0.4, which depends on
        mime-types ('>= 2.0.1, < 3.0')

이는 두 gem에 호환되지 않고 자동으로 해결할 수 없는 종속성이 있기 때문입니다.

번들러는 설치되었지만 Gemfile에 지정되지 않은 gem에 대한 액세스를 제한합니다.

다음과 같은 샘플 gemfile에서

# Gemfile
gem 'httparty'

# irb
require 'rest-client'

# raises
LoadError (cannot load such file -- rest-client)

Gemfile에 지정된 종속성만 확인합니다. 우리 프로젝트에 필요할 수 있습니다.

번들 실행

rspec을 실행할 때 프로젝트 디렉토리에서 Gemfile에 지정된 것과 다른 버전을 실행할 가능성이 있습니다. . 이는 Gemfile에 지정된 버전과 비교하여 가장 최신 버전이 실행되도록 선택되기 때문입니다. . bundle exec rspec rspec 보장 해당 프로젝트의 컨텍스트에서 실행됩니다(즉, Gemfile에 지정된 gem).

binstub 번들

종종 ./bin/rails와 같은 명령을 실행하는 기사를 읽습니다.; 이 명령은 bundle exec rails와 유사합니다. . Binstub은 bundle exec 사용을 쉽게 해주는 Ruby 실행 파일을 둘러싼 래퍼입니다. .

binstub 실행을 생성하려면 bundle binstubs gem-name을 사용하세요. . 이것은 ./bin에 binstub를 생성합니다. 폴더이지만 --path로 구성할 수 있습니다. 설정된 경우 디렉토리.

참조

자세히 알아보려면 다음 참조를 확인하십시오.

  • 보석은 어떻게 작동합니까?
  • 르벤브
  • RubyGems
  • 번들러