Memento memo.

Today I Learned.

npm installで依存関係エラーが出る場合の対処

eslint周りで色々installしていたら依存関係でエラーを吐いてどうにもならなくなりました。

結論としては、npmのversionを最新化したら直りました。

npm installが失敗した時に試したい3つのコマンド - Qiita にもありましたが、以下のコマンドで最新化できます。大抵の場合これでいける気がします。

$ npm update -g npm

ついでですが、以下のようにversion指定してあげることもできます。

$ npm install -g npm@3.10.3

Node.js周りは、nvm, node, npmそれぞれにversionがあってややこしいですね。

パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)

Gemfileとpackage.jsonでみるバージョン指定の読み方

Semantic Versioning 2.0.0

セマンティック・バージョニング(SemVer)によると

バージョンナンバーは、メジャー.マイナー.パッチとし、バージョンを上げるには、

  • APIの変更に互換性のない場合はメジャーバージョンを、
  • 後方互換性があり機能性を追加した場合はマイナーバージョンを、
  • 後方互換性を伴うバグ修正をした場合はパッチバージョンを上げます。
  • プレリリースやビルドナンバーなどのラベルに関しては、メジャー.マイナー.パッチの形式を拡張する形で利用することができます。

SemVerはバージョニングのルールみたいなものですね。

Gemfile(Ruby)やpackage.json(Node.js)等で依存関係を記述する時、 チルダ(~)やキャレット(^)を使って指定するかと思いますが、自分の中での解釈が曖昧だったのでまとめます。

Gemfile

Rubyの場合は ~> の記法が使われます。

これはpessimistic operator(悲観的バージョン演算子)というそうです。

解釈は以下のようになります。

  • 指定バージョンの一番右の数字を取り除く
  • 次に、一番右の数字をインクリメントしたものを上限とする
# e.g.
gem 'hoge', '~> 5.1.1'
# 次と等価
gem 'hoge', '>= 5.1.1', '< 5.2.0'

参考: Ruby's Pessimistic Operator

package.json

package.jsonではチルダ(~)とキャレット(^)を使います。

参考: semver | npm Documentation

Tilde Ranges

Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not.

とのことで

  • 基本的にはマイナーバージョンまで固定
  • マイナーバージョンが指定されてない場合はメジャーバージョン固定

です。

e.g.
~1.2.3 := >=1.2.3 <1.(2+1).0 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.(2+1).0 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <(1+1).0.0 := >=1.0.0 <2.0.0

Caret Ranges

Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple. In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for versions 0.X >=0.1.0, and no updates for versions 0.0.X.

  • 基本的にはメジャーバージョン固定(メジャーバージョンが0でない場合)
  • メジャーバージョンが0の場合マイナーバージョン固定
  • メジャー・パッチバージョンが0の場合はアップデートしない
^1.2.3 := >=1.2.3 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4

大抵の場合はメジャーバージョン固定のCaret(^)指定で問題ないかと思います。

プログラマのためのDocker教科書を読み始めた

Dockerを使いこなせないと会社で人権がないので プログラマのためのDocker教科書を読み始めました。

プログラマのための、というタイトルから、 "Dockerの使い方だけ紹介" みたいな雰囲気があるんですが、 想定以上に諸々しっかり書いてありました。

序盤にインフラ・仮想化・コンテナ技術の歴史と概要についてしっかり書かれていて、 非常に参考になりました。定期的に読み直したい内容です。

以下、適当にDocker周りの備忘録を適当にまとめていきます。

Docker の Install(Mac

Docker for Macがリリースされているのでこちらを使います。 (数ヶ月前にちょっと触った時はまだプライベートベータでした)

$ brew update && brew upgrade
$ brew cask install docker

あとは適当にDocker.appでsetupします。

いろいろ試す

docker pull

超軽量Linux Distributionの"Alpine Linux"を使います。

とりあえず docker search してみます。

$ docker search alpine
NAME                           DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
alpine                         A minimal Docker image based on Alpine Lin...   1475      [OK]
anapsix/alpine-java            Oracle Java 8 (and 7) with GLIBC 2.23 over...   151                  [OK]
...

docker pull でイメージを落とします

$ docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
c0cb142e4345: Pull complete
Digest: sha256:ca7b185775966003d38ccbd9bba822fb570766e4bb69292ac23490f36f8a742e
Status: Downloaded newer image for alpine:latest

Imageの確認

docker images で一覧確認します。

$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
rails                 latest              9df3ff98cd15        9 days ago          840.6 MB
nginx                 latest              ba6bed934df2        2 weeks ago         181.4 MB
alpine                latest              ee4603260daa        2 weeks ago         4.803 MB
msaraiva/elixir-dev   latest              6287638223f3        3 months ago        52.54 MB

いろいろ試しに入れてたので余計なものも出てきてますが、alpineのdocker imageだけ圧倒的に軽いですね。

個別に中身を見たい場合は inspect を使います

$ docker inspect alpine
...
     "Architecture": "amd64",
        "Os": "linux",
        "Size": 4802964,
        "VirtualSize": 4802964,
...

hello world

基本型は

docker run <dockerイメージ名> <コマンド>

です。

$ docker run alpine /bin/echo 'hello world'
hello world

dockerイメージがローカルに見つからなければ勝手にpullしてくれます。

tag

イメージにtagを付けて管理することができます。

$ docker tag alpine:latest shotat/alpine:foo
$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
alpine                latest              ee4603260daa        2 weeks ago         4.803 MB
shotat/alpine         foo                 ee4603260daa        2 weeks ago         4.803 MB

コンテナのshellを起動する

単体でshellを起動したい場合は以下のようなコマンドを使います。

$ docker run -it --rm alpine /bin/sh
オプションについて

-it --rm のオプションを見ていきます。

$ docker run --help | grep -E '(?:--tty|--interactive|--rm)'
  -i, --interactive                 Keep STDIN open even if not attached
      --rm                          Automatically remove the container when it exits
  -t, --tty                         Allocate a pseudo-TTY
  • -i : 標準入力を開く
  • -t : 疑似ttyを割り当てる
  • --rm : exit後にコンテナを破棄する

-it を付けないと対話shellをうまく扱えないので必ずつけましょう。 /bin/echo 'hoge' みたいなことしかしない場合は不要です。

マスコットキャラクターの名前

Dockerのクジラのキャラクターの本名は "Moby Dock" というそうです。

白鯨 - Moby-Dick; or The White Whale - を文字ってるみたいですね。( 白鯨 - Wikipedia

blog.docker.com

npm scriptsを並列実行する

npm-run-all を使う

github.com

使い方

npm i --save-dev npm-run-all でinstallします。 直列・並列実行コマンドの run-s, run-p が使えるようになります。

package.jsonのscripts部を以下のように設定してみます。

  "scripts": {
    "dev": "run-p stub watch",
    "stub": "stubcell",
    "build": "webpack",
    "watch": "run-p watch:*",
    "watch:js": "watch 'npm run build' ./src/scripts"
  },

この状態で npm run dev とすると、stubサーバとjavascriptのwatch & buildが同時に走るようになります。 楽ちんですね。

Node.jsのversionをプロジェクト毎に設定する

Rubyの場合は .ruby-version にバージョン指定すればプロジェクト毎にversion指定できるのですが、 Node.jsの場合どうすればいいのか分からなかったので備忘録。

大きく分けて2パターンありました。

avn を使う

.node-version でversion指定すればディレクトリ移動したときに自動でversionが切り替わるみたいです。 nodeのversion指定するために別コマンドいれるの微妙な気がしたので今回は使ってません。

github.com

nvm useを使う

.nvmrc にversionを記述して、nvm use を実行します。 rbenvみたいに自動で切り替わらないのかな?と思ったのですが、以下のissueを見るに切り替わらないっぽいです。

github.com

zshのhook使って自動的にnvm use実行する、みたいなのも見ました。個人的にはavn使うよりこっちの方がスマートな気がします。

現場で通用する力を身につける Node.jsの教科書

現場で通用する力を身につける Node.jsの教科書

Ruby Style Guide読んだ && 一部抜粋

github.com

普段はRubocopのおかげで概ねStyle準拠できているはず、と思ってたのですが、 改めてStyle Guideを読むと知見がたくさんありました。

以下、個人的に把握できてなかった箇所を抜粋しつつ、今後意識していきます。

andとorの使用は禁止

使うべき理由がないです。 常に、代わりに&&と||を使いましょう。

メソッドチェーンでのdo...endは避ける

# 悪い例
names.select do |name|
  name.start_with?('S')
end.map { |name| name.upcase }

# 良い例
names.select { |name| name.start_with?('S') }.map(&:upcase)

do...end.map やりたくなる場面がたまにあるんですが、我慢します。

代入部分を括弧で囲まずに、=の返り値を条件式に用いてはいけない

# 悪い例 (+ 警告が出ます)
if v = array.grep(/foo/)
  do_something(v)
  ...
end

# 良い例 (MRIはこれでも文句を言いますが、RuboCopでは問題ありません)
if (v = array.grep(/foo/))
  do_something(v)
  ...
end

# 良い例
v = array.grep(/foo/)
if v
  do_something(v)
  ...
end

変数がまだ初期化されていないときにだけ初期化したいのであれば、||=を使う

# 悪い例
name = name ? name : 'Bozhidar'

# 悪い例
name = 'Bozhidar' unless name

# 良い例 - nameがnilかfalseの場合のみ、Bozhidarで初期化します
name ||= 'Bozhidar'

値が入っているかわからない変数の前処理のは&&=を用いる

# 悪い例
if something
  something = something.downcase
end

# 悪い例
something = something ? something.downcase : nil

# ok
something = something.downcase if something

# 良い例
something = something && something.downcase

# より良い例
something &&= something.downcase

Proc.newよりprocを使う

# 悪い例
p = Proc.new { |n| puts n }

# 良い例
p = proc { |n| puts n }

STDOUT/STDERR/STDINの代わりに$stdout/$stderr/$stdinを用いる

STDOUT/STDERR/STDINは定数であり、 Rubyでの定数は、実際は再代入できます(つまりリダイレクトに使えます)が、 もし実行するとインタープリタからの警告が出ます。

ロジックを使って複雑な比較を行うよりも、 可能な限りRangeやComparable#between?を用いる

# 悪い例
do_something if x >= 1000 && x <= 2000

# 良い例
do_something if (1000..2000).include?(x)

# 良い例
do_something if x.between?(1000, 2000)

collectよりmap、detectよりfind、find_allよりselect injectよりreduce、lengthよりsizeを使う

これは絶対のルールではないです。 別名のほうが可読性に優れているなら、 そちらを使っていただいて構いません。 韻を踏んでいるほうのメソッド名はSmalltalkから引き継いできたもので、 他のプログラミング言語でそこまで一般的ではないです。 find_allよりもselectが推奨されるのは、 rejectとの相性がよいことと、 メソッド名から挙動を推察することも容易だからです。

シンボル、メソッド、変数にはsnake_caseを用いましょう。

シンボルはcamelCaseでも怒られない気がしたんですが、こちらもsnake_caseが良いみたいです。

危険 な可能性のあるメソッド (引数やselfを変更するようなメソッドや、 exit!(exitと違ってファイナライザが走らない)のようなもの) は、その安全なバージョンがある場合には、 危険 であることを明示する意味で感嘆符で終わる

# 悪い例 - 対応する「安全」なメソッドが存在しません
class Person
  def update!
  end
end

# 良い例
class Person
  def update
  end
end

# 良い例
class Person
  def update!
  end

  def update
  end
end

対応する安全なメソッドが存在しない場合は ! 付けない方が正しいみたいですね。副作用が大きいメソッドは ! つけるようにしてたんですが、Style Guilde的には微妙みたいです。

コメント

  • パフォーマンスに問題を及ぼすかもしれない遅い、または非効率なコードの注釈にはOPTIMIZEを使いましょう。
  • 疑問の残るコードの書き方でコードの臭いを感じた箇所の注釈にはHACKを使いましょう。
  • 意図したとおりに動くか確認する必要がある箇所の注釈にはREVIEWを使いましょう。

ハッシュから連続して複数の値が必要になる時は、Hash#values_atを用いる

# 悪い例
email = data['email']
username = data['nickname']

# 良い例
email, username = data.values_at('email', 'nickname')

利用するケースにより特化した速い代替手段がある場合、String#gsubは使わないようにする

url = 'http://example.com'
str = 'lisp-case-rules'

# 悪い例
url.gsub('http://', 'https://')
str.gsub('-', '_')

# 良い例
url.sub('http://', 'https://')
str.tr('-', '_')

文字列の添字に直接正規表現を渡すことで、文字列の構築をシンプルにできる

match = string[/regexp/]             # マッチした内容が得られる
first_group = string[/text(grp)/, 1] # キャプチャグループの内容が得られる
string[/text (grp)/, 1] = 'replace'  # string => 'text replace'

sub/gsubでの複雑な置換は、ブロックやハッシュを用いることで実現できる

words = 'foo bar'
words.sub(/f/, 'f' => 'F') # => 'Foo bar'
words.gsub(/\w+/) { |word| word.capitalize } # => 'Foo Bar'

Effective Ruby 第六章まとめ テスティング

Effective Ruby 第五章 メタプログラミング まとめ その2 - Memento memo. の続き。

Effective Rubyのテスティング章をまとめていきます。

MiniTestはRuby標準のテスティングライブラリで、 主要なコンポーネントは以下の3つです。

require('minitest/autorun') でライブラリ全体をロードすると、上記のコンポーネントも含まれます。

テストのファイル名は tests/xxx_test.rb といった名前で格納するとrailsの作法に乗れて良いみたいです。

ユニットテストとスペックテスト(ビヘイビアスペック)はどっちでもいいらしいですが、 RubyだとRSpecデファクト感あるのでスペックテストの方が馴染みがありそうです。

Effective Ruby

Effective Ruby

MiniTestユニットテストに慣れる

  • テストクラスを定義し、スーパークラスMiniTest::Unit::TestCase にする
  • 個々のテストケースはインスタンスメソッドとして記述し、"test_"プレフィクスをつける
  • ユニットテスト時はアサーションを使う
  • テストメソッドは短くする
  • アサーションメソッドは assert メソッドだけでなく、 assert_equal 等の適切なものを使う
  • assert_xxx に対応する refute_xxx で逆の動作を扱える
  • テストをまとめて実行するRake taskを使う(または作る)
class HogeTest < MiniTest::Unit::TestCase
  def test_hoge
    hoge = Hoge.new
    assert_equal(0, hoge.xxx)
  end
end

MiniTestスペックテストに慣れる

  • 基本的にはユニットテストをラップしてるだけ
  • describeメソッド呼び出しでクラスが自動的に定義されるため、自前でクラス定義を書く必要がない
  • アサート系メソッドの代わりにオブジェクトに注入されたエクスペクテーションメソッド( must_equal, wont_equal )を使う。
describe(Hoge) do
  describe('xxx') do
    before do
      @hoge = Hoge.new
    end
    it('returns initial value') do
      @hoge.xxx.must_equal(0)
    end
  end
end

モックオブジェクトで決定論をシミュレートする

  • 非決定的な処理(HTTPリクエスト等)からテストを切り離したいときはモックを使う
  • モックで交換するメソッドは外部ライブラリが提供している部分にすべき
  • テストメソッドを終える前に必ず verify を呼んで、モックメソッドが実行されていることを確認する。
  • MiniTest::Mockでモックを作成できるが、Mocha等の別ライブラリ使った方が高機能なのでおすすめ

インタフェースをテストする、という原則があるのですが、実装詳細に立ち入ってモックを使わざるを得ないケースもあると思います。その場合 verify でモックメソッドが確実に実行されていることをテストする必要があります。 すると、実装詳細がしれっと変わった場合でもテストで検知できるようになります。

効果的なテストを追求する

  • ハッピーパスと例外パスの両方を試すためにファズテスト、プロパティテストツールを使う
  • コードカバレッジを見て安心しない
  • 機能とテストは同時に書く(テストを後回しにしない)
  • テストは自動化する

用語等

ハッピーパステスト

テストしているコードのすべての前提条件を丁寧に準備して有効な入力しか与えないテスト。バグ発見の効果が薄い。

例外パステスト

さまざまな入力を送り込んでコードの全ての分岐先を確実に実行するテスト。一般に複雑になりすぎるが、ファズテストとプロパティテストで対処できる。

ファズテスト

プログラムや特定のメソッドにランダムデータを大量に送り込むことで、 クラッシュさせたり予想外の例外を発生させることができるかをチェックするテスト。

FuzzBert gem等で実行可能。基本的にほぼ無限にテストを行うため時間がかかる。

プロパティテスト

ファズテスト同様にランダムなデータを大量に送り込むが、コードが満たすべきプロパティ(性質)を満足するかをチェックするテスト。

MrProper gem等で実行可能。

参考: ソフトウェアの品質を学びまくる:Property-based Testing、そしてExample-based testing、とは


Rubyとは別でテスティングの理論的なところをあまり深く理解できていないので、もうちょっと勉強していきたいです。 あとは実践的なところでRSpecを使いこなせるようになりたいです。(適当にしか使えてないので。。。)

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)

Elixir & Phoenix のLT資料

先日、Elixir & Phoenix布教をすべく、社内でLTをしてきました。社内LT会自体は毎週やっているので、個人的に最低月1回は発表するよう心がけています。

内容は超薄いのですが、とりあえず公開することが大事だと思うので資料upしました。SpeakerDeckデビュー。

プログラミングElixir

プログラミングElixir

GitHubのリポジトリを直接指定してgemをinstallする

自作gemとかOSSのgemのリポジトリとかブランチとかを指定して使いたい場合の解決策です。

リポジトリ.gemspec ファイル等は設定済み前提で。

Gemfile使う方法とGemfile使わない方法の2通りあります。

Gemfileを使う場合

普通に指定できます。以下のようにGemfileに記述

gem 'hogehoge', git: 'git@github.com:foo/bar.git', branch: 'develop'

Gemfileを使わない場合

specific_install gemを使います。

specific_installのInstall

$ gem install specific_install

リポジトリからgem install

$ gem specific_install git@github.com:foo/bar.git develop

内部的にはcloneしてbuild & installしてローカルリポジトリ破棄してるっぽいです。

Effective Ruby 第五章 メタプログラミング まとめ その2

Effective Ruby 第五章 メタプログラミング まとめ その1 - Memento memo. の続きです。

モンキーパッチの代わりとなるものを検討する

モンキーパッチとは

  • モンキーパッチとは、実行時にコアクラスを拡張したり挙動を書き換える(パッチを当てる)こと。Active Supportが有名。
  • 複数パッチが衝突すると大事故になるのでできるだけモンキーパッチは避けたい

方法1 module関数を使う

愚直にmoduleを定義&オブジェクトをextendする方法です。

module OnlySpace
  ONLY_SPACE_UNICODE_RE = %r/\A[[:space]]*\z/
  def self.only_space? (str)
    if str.ascii_only?
      !str.bytes.any? do |b|
        b != 32 && !b.between?(9, 13)
      end
    else
      ONLY_SPACE_UNICODE_RE === str
    end
  end

  def only_space?
    OnlySpace.only_space?(self)
  end
end

str1 = "   \r\n"
puts OnlySpace.only_space?(str1)
# OOPっぽくなくなる

str2 = "hello"
str2.extend(OnlySpace)
puts str2.only_space?
# extendしないと使えない

方法2 新しい別のクラスを作成する

Stringクラスの代わりにStringExtraクラスを作成します。 継承ではなく委譲を使ってStringライクなStringExtraを実装します。(実装省略)

方法3 Refinements機能を使う

Ruby 2.1から入ったRefinements機能を使います。 Refinementsでは、パッチの適用範囲がレキシカルスコープの中に限定されます。以下の例で String#loudFoo の外で呼ぶことはできません。

使い方はこんな感じになります。

module Loud
  refine String do
    def loud
      "#{self}!!!"
    end
  end
end

class Foo
  using(Loud)
  def initialize(str)
    puts str.loud
  end
end

Foo.new("wei")
#=> wei!!!

参考

エイリアスチェイニングで書き換えたメソッドを呼び出す

  • 既存のメソッドに新しい名前を与え、元のメソッド名でメソッドを再定義して最終的に元のメソッドを呼び出す。
  • エイリアス作成時にメソッド名がユニークになるよう注意
  • エイリアスチェイニングを取り消すメソッドも作成する

alias_methodについて

alias_method(new_name, original_name) でmethodにエイリアスを貼ることができます。

参考: ref.xaio.jp

sample

module LogMethod
  def log_method(method)
    orig = "#{method}_without_logging".to_sym
    
    if instance_methods.include?(orig)
      raise(NameError, "#{orig} isn't a unique name")
    end

    alias_method(orig, method)

    define_method(method) do |*args, &block|
      $stdout.puts("calling method '#{method}'")
      result = send(orig, *args, &block)
      $stdout.puts("'#{method}' returned #{result.inspect}")
      result
    end
  end

  #エイリアスチェイニングを取り消すメソッドも作成する
  def unlog_method(method)
    orig = "#{method}_without_logging".to_sym
    
    if !instance_methods.include?(orig)
      raise(NameError, "was #{orig} already removed?")
    end

    remove_method(method)
    alias_method(method, orig)
    remove_method(orig)
  end
end

Array.extend(LogMethod)
#=> Array
Array.log_method(:first)
#=> :first
[1,2,3].first
# calling method 'first'
'# first' returned 1
#=> 1
Array.unlog_method(:first)
#=> Array
irb(main):008:0> [1,2,3].first
#=> 1

Procの引数の個数の違いに対応できるようにする

  • Procオブジェクト生成には"強いProc"と"弱いProc"がある。 lambda? メソッドで識別可能。
  • 弱い(Weak)Proc: 引数の扱いが緩く、間違った個数の引数を与えてもエラーにならない。e.g. block
  • 強い(Strong)Proc: 通常メソッド呼び出しと同じで、引数の個数が違うとArgumentError例外が発生する。e.g. lambda
  • Proc#arityメソッドを使うと、Procオブジェクトが期待する引数の数がわかる。
  • Proc#arityメソッドで引数の個数の違いをうまく吸収させる。

モジュールのprependを使うときは慎重に考える

  • 継承階層は ancestors メソッドで確認可能
  • includeは継承階層においてレシーバの後にモジュールを挿入する。
  • prependは継承階層においてレシーバのにモジュールを挿入する。
  • prependよりもalias_methodを使った方が柔軟になる

メタプログラミング面白いですね。Refinementsで局所的にクラス拡張するのは結構気に入りました。

まだ メタプログラミングRuby 第2版 も読めていないのですが、健全なメタプロパワーを高めていきたいです。

Effective Ruby

Effective Ruby