Macのrbenvでインストールエラー

久しぶりにrbenvでRuby2.3.0をインストールしようとしたところ、エラーでインストールできなかった。 OpenSSLとcurlのバージョンを上げることでインストールできるようになった。

環境

rbenv installで出たエラー

$ rbenv install 2.3.0
ruby-build: use openssl from homebrew
Downloading ruby-2.3.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.0.tar.bz2
error: failed to download ruby-2.3.0.tar.bz2

BUILD FAILED (OS X 10.12.6 using ruby-build 20180424-3-g16ac342)

対応

  • OpenSSLとcurlのバージョンを上げる。

Googleで検索すると、Linuxでの同様のエラーがすでに報告されているようだが、Macでも同様の対応方法で解決できた。 バージョンアップの前後のOpenSSLとcurlのバージョンは以下の通り。

VerUp前    VerUp後
OpenSSL OpenSSL 0.9.8zh 14 Jan 2016 OpenSSL 1.0.2o 27 Mar 2018
curl curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8 curl 7.60.0 (x86_64-apple-darwin16.7.0) libcurl/7.60.0 SecureTransport zlib/1.2.8

OpenSSLのインストール

以前Homebrewでインストールしていたようなので、再インストールした。

$ brew reinstall openssl
==> Reinstalling openssl 
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2o_1.sierra.bottle.tar.gz
Already downloaded: /Users/razgriz1/Library/Caches/Homebrew/openssl-1.0.2o_1.sierra.bottle.tar.gz
==> Pouring openssl-1.0.2o_1.sierra.bottle.tar.gz
==> Caveats
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

This formula is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/openssl/lib
    CPPFLAGS: -I/usr/local/opt/openssl/include
For pkg-config to find this software you may need to set:
    PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig

==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2o_1: 1,791 files, 12.3MB

HomebrewでインストールしたOpenSSLは、PATHに設定されていないようなので、インストールコマンドを打った後にした後に表示されるコメントのコマンドを実行し、HomebrewでインストールしたOpenSSLをPATHに通す。

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
$ openssl version
OpenSSL 1.0.2o  27 Mar 2018

OpenSSLのバージョンが1.0.2oになった。

curlの新しいバージョンをインストール

OpenSSL同様にHomebrewでインストールする。

$ brew install curl
==> Downloading https://homebrew.bintray.com/bottles/curl-7.60.0.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring curl-7.60.0.sierra.bottle.tar.gz
==> Caveats
This formula is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/curl/bin:$PATH"' >> ~/.bash_profile

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/curl/lib
    CPPFLAGS: -I/usr/local/opt/curl/include
For pkg-config to find this software you may need to set:
    PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig

==> Summary
🍺  /usr/local/Cellar/curl/7.60.0: 423 files, 3MB

こちらもインストール直後はPATHが通っていないようなので、OpenSSL同様に、インストール後の標準出力を参考に、インストールしたcurlをPATHに通してやる。

$ echo 'export PATH="/usr/local/opt/curl/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
$ curl -V
curl 7.60.0 (x86_64-apple-darwin16.7.0) libcurl/7.60.0 SecureTransport zlib/1.2.8
Release-Date: 2018-05-16

rbenvでRubyをインストール

OpenSSL、curlをバージョンアップした後、めでたくrbenvでRuby2.3.0をインストールできた。

$ rbenv install 2.3.0
ruby-build: use openssl from homebrew
Downloading ruby-2.3.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.0.tar.bz2
Installing ruby-2.3.0...
ruby-build: use readline from homebrew
Installed ruby-2.3.0 to /Users/razgriz1/.rbenv/versions/2.3.0

npm install -g はやめる

npm install -g はやめる

WebpackやGulp、Angularなど多くのNode.js製のツールを最近のWeb開発ではよく使うが、 それぞれプロジェクトごとに基本的にはバージョンが違う。

にも関わらず、それらのツールの公式サイトには、まずそのツールを使うには、 以下のように-gを付けてグローバルにインストールしろ、といったインストール方法が多く書かれている。

npm install -g xxxxxx

手っ取り早く使い始める分にはいいかもしれないが、 複数のプロジェクトが存在する開発者のローカル開発環境には、以下の理由から向いていない。

  • グローバルにNode.jsのツールをインストールすると、複数のプロジェクトでバージョンを固定できない。
  • グローバルにインストールしたNode.jsツールのライブラリー間でバージョンの競合が起こりうる。
  • そして、グローバルにインストールしなくてもそのツールは使える。

グローバルにインストールせずにNode.js製のアプリケーションを使う方法

npm scriptを利用することで、グローバルにインストールしなくてもNode.js製のツールを使うことができる。

Webpackを利用する場合の例

(Node.jsがインストールされていて、npmコマンドが使える前提で書く)

もし、プロジェクトのディレクトリにpackage.jsonが存在しない場合、npm init -yで作成する。 デフォルトのNode.jsプロジェクトとして初期化され、package.jsonが生成される。

Webpackをインストールする

npm install --save-dev webpack

プロジェクトにインストールしたNode.js製のツールは、node_modulesディレクトリ配下にインストールされる。 これらを実行する場合、このようにパスを直接指定しても実行できる。

./node_modules/.bin/webpack

npm scriptを追加する

上記のようにパスを直接指定してもツールの実行はできるが、npm scriptを使ったほうが便利に実行できる。 プロジェクトディレクトリのpackage.jsonのscriptセクションにwebpack実行用のbuildスクリプトを追加する。

{
  "name": "webpack-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack" // <- これ
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    // 割愛
  },
  "dependencies": {
    // 割愛
  }
}

npm scriptでは、プロジェクトにインストールしたNode.js製ツールの実行ファイルのディレクトリ(node_modules/.bin)にパスが通った状態でツールを実行するコマンドを書ける。

上記のnpm scriptのbuildスクリプトに指定したwebpackコマンドは、実際は./node_modules/.bin/webpackが実行される。

npm scriptを実行してみる

npm scriptを実行する時はnpm run xxxxの形式で実行する。 上記のbuildコマンドの場合、npm run buildになる。

実際に実行してみると、webpackコマンドを実行できていることがわかる。

$ npm run build

Hash: 40085d111b680e76b184
Version: webpack 3.10.0
Time: 1988ms
        Asset       Size  Chunks                    Chunk Names
app.bundle.js    1.48 MB       0  [emitted]  [big]  app
   index.html  274 bytes          [emitted]         
   [0] ./src/index.js 598 bytes {0} [built]
   [2] (webpack)/buildin/global.js 509 bytes {0} [built]
   [3] (webpack)/buildin/module.js 517 bytes {0} [built]
   [4] ./src/style.css 1.01 kB {0} [built]
   [5] ./node_modules/css-loader!./src/style.css 233 bytes {0} [built]
   [9] ./src/print.js 164 bytes {0} [built]
    + 4 hidden modules
Child html-webpack-plugin for "index.html":
     1 asset
       [0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.html 580 bytes {0} [built]
       [2] (webpack)/buildin/global.js 509 bytes {0} [built]
       [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 1 hidden module

npm scriptの中には、npm runとnpmにrunを続けずに、そのまま実行できるものもある。 npm startnpm testがこれに当たる。 (runを省略できるコマンドの詳細はドキュメント)を参照。)

npm run スクリプトにオプションを指定したい場合

例えば、webpack --watchなど、オプションを指定したい場合もある。 この場合、登録したnpm scriptに--続けてオプションを入力することで実行できる。

npm run build -- --watch

> webpack-sample@1.0.0 build /Users/xxxxxx/yyyyyyyyyyy
> webpack "--watch"


Webpack is watching the files…

よく使うオプションであれば、オプションを指定したnpm scriptを登録するのがいい。

  "scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  },
  // 割愛

オプションを指定する場合でもnpm scriptに登録しておけば楽に実行できる。

npm run watch

まとめ

  • プロジェクトごとに利用するNode.jsのツールは、グローバルにインストールしなくてもnpm scriptを使うことで利用できる。
  • プロジェクトごとにインストールすることで、プロジェクト間のツールのバージョンの競合を避けられる。
  • npm scriptで、ツールのオプションを指定した状態で登録できる。

Node.jsの開発環境の構築 - nvmとnodebrew

nvmnodebrewは どちらも環境にNode.jsのバージョンを指定してインストールし、インストールした複数のNode.jsを切り替えて使うことができる。

機能の違いとしては、nvmはLong Time Supportのバージョンを自動的に選別してくれる機能がある。構成を選ぶ際には参考にできるかも。

nvmのlts機能

$ nvm install 8 --lts # 8のLTS最新版を落としてくる
Downloading and installing node v8.9.4...
$ nvm ls-remote --lts # LTS版のみをリストする
.
.
.
  v6.13.0 (Latest LTS: Boron)
  v8.9.0 (LTS: Carbon)
  v8.9.1 (LTS: Carbon)
  v8.9.2 (LTS: Carbon)
  v8.9.3 (LTS: Carbon)
-> v8.9.4 (Latest LTS: Carbon)

nvm

インストール

$ brew install nvm

Node.jsのバイナリをインストール

$ nvm install 8.3.0
$ nvm use 8.3.0
$ node -v
v8.3.0

ソースからのビルド

$ nvm install -s 8.3.0

nodebrew

インストール

$ brew install nodebrew

Node.jsのバイナリをインストール

nodebrew install するとソースビルドするのでとても時間がかかる。

$ nodebrew install-binary 8.3.0
$ nodebrew use 8.3.0
$ node -v
v8.3.0

ソースからのビルド

$ nodebrew install 8.3.0

Node.js、JavaScriptのオブジェクトをJSON形式の文字列にする

ES6のテンプレートリテラルでオブジェクトをデバッグログに出力したい場合、 JSON.stringfyでJSON文字列化する。 console.logなどの出力メソッドに、オブジェクトをそのまま渡すと以下のようなエラーを吐くものがある。

Cannot convert object to primitive value

expressのGetting Startedをしている最中、リクエストボディをデバッグした際に出た。

console.log(`req.body: ${req.body}`); //TypeError: Cannot convert object to primitive value

JSON形式の文字列にしてあげるといい。

console.log(`req.body: ${JSON.stringify(req.body)}`); // {name: 'Tom'}

余談

実際自分で適当にオブジェクトを作って、テンプレートリテラルに渡しても、[object object]と表示され、Cannot convert object to primitive valueというエラーにはならない。型の違いなのかもしれないが、どちらもtypeofで調べてもobjectとしか表示されないので、いまいち分かりにくい。

const tom = {name: 'Tom'};
console.log(`tom: ${tom}`); // tom: [object Object]