Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rbenv、RubyGems、Bundlerがどのように連携するかを理解する

Rubyでの依存関係の管理には、通常、プロジェクトが依存するRubyとgemのバージョンを指定することが含まれます。 Rubyでの作業経験では、依存関係のデバッグは私の最大の課題の1つです。多くのものが「うまくいく」ので、失敗は一般的ではありません。ただし、問題が発生した場合、通常、デバッグと修正は不必要に困難になります。この記事では、Rubyの依存関係管理に関連する部分について説明します。これは、これらの奇妙な問題が発生したときにデバッグするのに役立ちます。

Rbenv、RubyGems、Bundlerがどのように連携するかを理解する

ルビーコードの読み込み

デフォルトでは、Ruby言語は、他の場所で定義されたコードをロードするための2つの主要なメソッドを提供します。loadrequire

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

どちらのロードメソッドも、引数として絶対パスと相対パスの両方を受け入れます。ただし、2つの差別化要因があります。

  1. loadへの複数の呼び出し requireを複数回呼び出すと、ファイルが再実行されます。 ファイルを再実行しません。代わりに、falseを返します 。
  2. loadの呼び出し 絶対パスと相対パスにのみ解決されます。 requireへの呼び出し $LOAD_PATHをチェックします パスが絶対パスに解決されない場合。

3番目のバリアントはrequire_relativeです 、相対パスを使用して、Rubyプロセスの作業ディレクトリではなく、現在のファイルの場所に相対的なコードを要求します。

Rbenv

バージョンマネージャーは、インタープリター(この場合はRuby)のバージョンを管理して簡単に切り替え、プロジェクトのそれぞれのgemを見つける場所を指定するために使用されるツールです。バージョンマネージャーは主に言語に依存しないツールであり、Nvm、Node.jsの場合はn、Pythonの場合はpyenv、Rubyの場合はRbenv、rvm、chrubyなど、さまざまな言語にそれぞれの実装があります。それでは、rbenvを見てみましょう。 スピンのために、私たちはしませんか?

Rubyバージョンのインストール

コマンド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をデバッグするには、これら2つの概念を正しく理解する必要があります。 効果的に。

シムは、PATHに存在する軽量のbashスクリプトです。 コマンドをインターセプトし、実行のために適切なバージョンにルーティングします。大まかに言うと、すべてのコマンド(rspecなど )はrbenv exec rspecに変換されます 。以下の詳細を参照してください。

まず、rbenv すべてのコマンドのシムを作成します(rspecbundle 、など)インストールされているすべてのRubyバージョンにわたって、バージョンに関係なくCLIへの呼び出しをインターセプトします。これらのシムは~/.rbenv/shimsにあります。 。以下に示すように、すべてのシムには同じ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を理解するために私が見つけた最良の方法 シムが適切にインターセプトされているかどうかを設定して確認するには、次のようにします。

$ which -a bundle

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

which -a bundle :これはあなたのPATHを素朴に調べます 見つかった順序で、bundleの場所を印刷します 見つけることができます。 ~/.rbenv/shimsで何かが何かの前に印刷されている場合 、それはあなたのシムが適切に設定されていないことを意味します。 rbenv which bundle コマンドはrbenvのコンテキストで機能するため、これは明らかになりません。 PATHを検索していません 。

再ハッシュは、シムを作成するプロセスです。 rspecなどの実行可能ファイルを提供するRubygemを新しくインストールする場合 、rbenv rehashを実行する必要があります その後のrspecの呼び出しで、シムを作成します。 rbenvによって傍受される可能性があります そして適切なRubyバージョンに渡されます。

RubyGems

次はRubyGemsです。 Rubyの公式サイトから入手できます。 RubyGemsは、ライブラリの作成、共有、インストールを容易にするために設計されたRubyパッケージングシステムです。ある意味では、これはapt-getなどに似た配布パッケージシステムですが、Rubyソフトウェアを対象としています。 RubyGemsは、gemを共有するための事実上の方法です。これらは通常、~/.rbenv/versions/{version-number}/lib/ruby/gems/{minor-version}/にインストールされます。 、またはそのバリアント。使用するバージョンマネージャーによって異なります。 Rubyのデフォルトの必須メソッドKernel.require Gemsインストールディレクトリからgemをロードするメカニズムは提供していません。 RubyGemsモンキーパッチKernel.require

  • まず、$LOAD_PATHで宝石を検索します 。
  • 見つからない場合は、GEMS INSTALLATION DIRECTORYでgemを検索してください 。
    • 見つかったら、パスを$LOAD_PATHに追加します 。

バージョン1.9以降、RubyにはデフォルトでRubyGemsが付属しているため、これは「ネイティブに」機能します。以前のRubyバージョンでは、RubyGemsを手動でインストールする必要がありました。これはネイティブで機能しますが、デバッグ時にこの違いを知ることも重要です。

gemは、特定の問題を解決するために使用される一連の関連コードです。次のように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 が呼び出され、gemsフォルダーで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がどのように連携するかを理解する

これにより、アプリが参照している宝石の特定のバージョンを簡単に見つけて追跡することができます。

バンドラー

このレイヤーでは、Bundlerを使用すると、プロジェクトのすべての依存関係を簡単に指定し、オプションでそれぞれのバージョンを指定できます。次に、gemを解決し、gemとその依存関係をインストールします。プレバンドラーで実際のアプリケーションを構築するには、次のような無数の課題がありました。

  • 私たちのアプリケーションには多数の依存関係があり、これらの依存関係には他にもさまざまな依存関係とそれぞれのバージョンがあります。 1つのgemの間違ったバージョンをインストールすると、アプリが簡単に壊れてしまいます。この問題を修正するには、多くの涙が必要でした。
  • また、2つの依存関係が同じ第3レベルの依存関係を参照できます。互換性を見つけることが問題でした。もしあれば、それは問題でした。
  • 同じマシン上に複数のアプリケーションがあり、さまざまな依存関係がある場合、アプリケーションはマシンにインストールされているすべてのgemにアクセスできます。これは、最小特権の原則に反し、マシンにインストールされているすべてのgemにアプリケーションを公開します。悪意があるかどうか。

Bundlerは、3つの問題すべてを解決し、次のようにしてアプリの依存関係を管理するための適切な方法を提供します。

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.内の独自の依存関係 このファイルはアプリの依存関係の青写真であり、バージョン管理にチェックインする必要があります。これにより、プロジェクトの依存関係が環境(開発、ステージング、または本番)全体で一貫していることが保証されます。

Bundlerは依存関係間の互換性を解決します

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')

これは、2つのgemに互換性がなく、自動的に解決できない依存関係があるためです。

Bundlerは、インストールされているがGemfileで指定されていないgemへのアクセスを制限します

次のようなサンプルgemfileでは、

# Gemfile
gem 'httparty'

# irb
require 'rest-client'

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

Gemfileで指定された依存関係のみが保証されます 私たちのプロジェクトで要求される可能性があります。

バンドルexec

rspecを実行する場合 プロジェクトディレクトリで、Gemfileで指定されたもの以外の異なるバージョンを実行する可能性があります 。これは、Gemfileで指定されたバージョンではなく、最新バージョンが実行対象として選択されるためです。 。 bundle exec rspec rspecを保証します そのプロジェクトのコンテキストで実行されます(つまり、Gemfileで指定されたgem)。

バンドルbinstubs

多くの場合、./bin/railsなどのコマンドを実行する記事を読みます。;このコマンドは、bundle exec railsに似ています。 。ビンスタブは、bundle execの使用を容易にするRuby実行可能ファイルのラッパーです。 。

binstub実行を生成するには、bundle binstubs gem-nameを使用します 。これにより、./binにbinstubが作成されます フォルダですが、--pathで設定できます 設定されている場合はディレクトリ。

参照

詳細については、次のリファレンスをご覧ください:

  • 宝石はどのように機能しますか?
  • Rbenv
  • RubyGems
  • バンドラー

  1. Ruby 2.6のMJITとは何ですか?どのように機能しますか?

    Rubyのパフォーマンスはバージョンごとに大幅に向上しており、Ruby開発チームはRubyをさらに高速化するためにあらゆる努力をしています! これらの取り組みの1つは、3×3プロジェクトです。 目標は? Ruby3.0はRuby2.0の3倍高速になります 。 このプロジェクトの一部は、この記事のトピックである新しいMJITコンパイラです。 MJITの説明 MJITは「メソッドベースのジャストインタイムコンパイラ」の略です。 それはどういう意味ですか? RubyはコードをYARV命令にコンパイルします 、これらの命令はRuby仮想マシンによって実行されます。 JITはこれに別のレ

  2. Cortana と Alexa を一緒に使用する方法

    Microsoft は、1 月の Build 開発者会議で発表した後、ついに Windows 10 PC に Alexa を、Echo スピーカーに Cortana をもたらします。現在、プレビューは米国でのみ利用可能ですが、このパートナーシップには長い道のりがあるようです. 私たちは多面的な世界に住んでおり、デバイスにアシスタントを搭載することが理にかなっており、有益であることが証明されています.個々のオペレーティング システムではなく、アプリなどの 3 つのアシスタントにアクセスできることほど優れたものはありません。 現在、統合は基本的なものですが、間もなく Cortana と Ale