20分で作るFacebookメッセンジャーボット(hubot)

20分で作るFacebookメッセンジャーボット(hubot)

すごーく簡単にFacebookメッセンジャーボットが作成できました。後のためにその手順を分かりやすくメモしておきます。

別にhubotでなくともボットは作成できますが、今回は使い方に慣れているhubotを使用します。

hubot

hubotの作成

hubot作成手順については以前の記事参考。

uraway.hatenablog.com

アダプターのインストール

hubot作成後、Facebookメッセンジャーアダプタであるhubot-fbをインストールします。

$ npm install -S hubot-fb

次にProcfileを編集します。

web: bin/hubot -a fb

Facebookページ、アプリの作成、設定

メッセンジャーボットを利用するには、FacebookページとFacebookアプリが必要になります。

Facebookページの作成

https://www.facebook.com/pages/create/

上記のページから、ジャンルやその他必要事項を入力し、Facebookページを作成します。

Facebookアプリの作成

https://developers.facebook.com/quickstarts/?platform=web

Facebookディベロッパー登録後、上記のページにアクセスします。

画像のボタンをクリックして、アプリのディスプレイネームやメールアドレス等必要な情報を入力して、新しくアプリを作成します。

トークンの設定

アプリIDの新規作成に成功すると、アプリのダッシュボードに移動します。

サイドバーの"Messenger"をクリックして、ウィザードを起動、Messagerプラットフォームを有効にしましょう。

次に、"Token Generation"から作成したFacebookページを選択し、アクセストークンを生成します。生成されたトークンをコピーしておきましょう。

アプリの登録

先ほど取得したトークンを使い、次のcurlコマンドを実行しましょう。

curl -ik -X POST "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=[FB_PAGE_TOKEN]"

Herokuで動かす

hubotのスクリプトをherokuに上げましょう。

Herokuにプッシュ

まずはgitにコミット。

$ git init
$ git add .
$ git commit -m "first commit"

次にherokuにプッシュ。その際、わかりやすいアプリの名前をつけておきます。

$ heroku apps:create アプリ名
$ git push heroku master

heroku側の設定として、環境変数を使う必要があります。FB_PAGE_TOKENは先ほど生成したもの。FB_VERIFY_TOKENは適当な文字列を使用します。

$ heroku config:set FB_PAGE_TOKEN=****
$ heroku config:set FB_VERIFY_TOKEN=abcd

Webhooks

Webhookを設定することで、ユーザーからのメッセージ等を受け取ることが可能になります。

アプリのダッシュボードからWebhookの設定を行いましょう。

Setup Webhooksをクリックすると、次のようなダイアログが表示されます。

Callback URLにはhubotのURLに/hubot/を追加したものを入力しましょう。

例:

アプリ名.herokuapp.com/hubot/

"Verify Token"の欄には、先ほどherokuに設定したFB_VERIFY_TOKENを入力します。

"Subscription Fields"のすべてのチェックボックスをチェックし、"Verify and Save"をクリックして完了です。

実際にメッセージを送る

適当にスクリプトを追加して、Facebookページを検索して、語りかけてみましょう。

module.exports = (robot) ->
  robot.hear /疲れた/i, (res) ->
    res.send "頑張って!"

人工知能/機械学習: ベイジアンフィルタによるテクスト分類

人工知能/機械学習: ベイジアンフィルタによるテクスト分類

機械学習には多種ありますが、今回は自然言語処理、その中でも特に、ベイジアンフィルタについて扱ってみたいと思います。

このエントリを書くにあたって、下記の書籍がかなり参考になりました。機械学習のみならず、Webデータ全般について、取得法から解析法、利用法までを初心者にも分かりやすく解説しています。

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

ベイジアンフィルタとは、「ナイーブベイズ分類」を応用したもので、対象のデータを解析・学習するためのフィルタです。

フィルタにはキーワード指定のブラックリスト・ホワイトリスト方式から、ベイジアンフィルタを代表とする学習タイプのフィルタまで、幅広く存在します。

テクスト分類とは

テクスト分類とは、与えられた対象のテクストが、同じく与えられたクラスのどれに属するかを判断するタスクを指します。こうしたクラスは、たいてい一般的な主題になります。

この技術は、身近なところでは、

  • Eメールのソート: 受信したメールを「家族から」「SNSから」「広告」といった正しいフォルダ(クラス)に分類する。
  • レビューのクラス分け: 製品や映画のレビューがポジティブであるか、ネガティブであるかを判断し、自動的に分類する。

などに使われています。

テクスト分類には、手作業で行われるものも多々あります。例えば、映画のジャンル分けには、明確な基準はなく、広告代理店の売り方次第で変わり得るものです(多分)。こうした手動の分類は、大規模になると大変です。そこで、機械学習をもとにした自動テクスト分類が必要とされます。

アルゴリズム

ベイジアンフィルタ、つまり、ナイーブベイズ分類のアルゴリズムについて見てみましょう。

文書dが与えられたときに、カテゴリcである確率を、$P(c|d)$と表します。そしてこれを、ベイズの定理で表すと、次のようになります。

$$ P(c|d)=\frac {P(c) P(d|c)}{P(d)} $$

一つ一つ見ていきましょう。$P(c)$とは与えられたある文書がそのカテゴリである事前確率です。つまり、メール1000通中50通がスパムであれば、

P(スパム) = 50 / 1000 = 0.05

となります。

$P(c|d)$とは、ここで求めたい結果、つまり、ある文書dが与えられたとき、カテゴリcである事後確率です。次に、$P(d|c)$とはカテゴリcに文書dが生成される確率で、統計学では尤度(ゆうど)と呼ばれます。$P(d)$はすべての文書中、文書dが生成される確率です。

ここで、文書dは単語の出現場所を考慮しない単語の集合$(t_1, t_2, ... , t_i)$とします。すると次の式が成り立ちます。

$$ P(d|c)=P(t_1|c)\ P(t_2|c)\ ...\ P(t_i|c)=\prod_i P(t_i|c) $$

第三式のギリシア文字は積の記号らしいです。さっき知りました

今度は、$P(t_i|c)$を求めましょう。あるカテゴリにその単語が出現する確率です。これは訓練データのカテゴリcに単語$t_i$が出てきた回数をカテゴリcの全単語数で割ることで計算できます。

テクスト分類においては、ある文書のもっともそれらしいカテゴリを求めることが目標です。ナイーブベイズ分類では、それはカテゴリcである最大事後確率(maximum a posteriori)の、$c_{map}$で表します。

$$ c_{map}=arg\ max_c P(c|d)=arg\ max_c P(c) \prod_i P(t_i|c) $$

もし、訓練データにおいて、WTOという単語がChinaカテゴリの文書にのみ出現するとき、UKカテゴリにWTOが出現する確率$P(WTO|UK)$について、次の式が成り立ちます。

$$ P(WTO|UK)=0 $$

次に、対象の文書Britain is a member of the WTOが与えられたとします。この文書はBritainを含むので、UKカテゴリである確率が高そうです。しかし、訓練データではUKカテゴリに出てこなかったWTOという新単語が含まれているために、この文書をd1とした時、$P(d1|UK)=0$となり、この文書がカテゴリUKから生成された確率はゼロとなってしまいます。

というのも、カテゴリcに文書dが生成される尤度$P(d|c)$は、単語が出現する確率$P(t|c)$の積で求められるためです。

これを解消するために、各単語を出現回数にシンプルに1を足す、add-one, Laplace-smoothing が使用されます。

ベイジアンフィルタのNodeライブラリ

さて、アルゴリズムについて理解したところで、いや、実は理解していなくともいいんですが、実際にベイジアンフィルタを使用してみましょう。

Node.js には、ベイジアンフィルタを実装したbayesライブラリが存在します。以下、このライブラリを使用して、ベイジアンフィルタを実装します。

まずはインストール。

$ npm install bayes mecab-lite

次にjsファイルを編集します。今回はサンプルとして、Wikipediaから「織田信長」、「明智光秀」、「豊臣秀吉」について学習するフィルタを実装することにしましょう。

var bayes = require('bayes');
var Mecab = require('mecab-lite');
var mecab = new Mecab()

var nobunaga = '織田 信長(おだ のぶなが)は、戦国時代から安土桃山時代にかけての武将・戦国大名。三英傑の一人。尾張国(現在の愛知県)の古渡城主・織田信秀の嫡男[注釈 5]。 尾張守護代の織田氏の中でも庶流・弾正忠家の生まれであったが、父の代から主家の清洲織田氏(織田大和守家)や尾張守護の斯波氏をも凌ぐ力をつけ、家督争いの混乱を収めて尾張を統一し、桶狭間の戦いで今川義元を討ち取ると、婚姻による同盟策などを駆使しながら領土を拡大した。足利義昭を奉じて上洛すると、将軍、次いでは天皇の権威を利用して天下に号令。後には義昭を追放して室町幕府を事実上滅ぼし、畿内を中心に強力な中央集権的政権(織田政権)を確立して天下人となった。これによって他の有力な大名を抑えて戦国乱世の終焉に道筋をつけた。しかし天正10年6月2日(1582年6月21日)、重臣・明智光秀に謀反を起こされ、本能寺で自害した。すでに家督を譲っていた嫡男・織田信忠も同日に二条城で没し、信長の政権は、豊臣秀吉による豊臣政権、徳川家康が開いた江戸幕府へと引き継がれていくことになる。';

var mitsuhide = '明智 光秀(あけち みつひで)は、戦国時代から安土桃山時代にかけての武将。戦国大名・織田信長に見出されて重臣に取り立てられるが、本能寺の変を起こして信長を暗殺。直後に中国大返しにより戻った羽柴秀吉に山崎の戦いで敗れた。一説では、落ちていく途中、小栗栖において落ち武者狩りで殺害されたとも[注釈 4][6]致命傷を受けて自害したもとされる[7]。これは光秀が信長を討って天下人になってからわずか13日後のことであり、その短い治世は「三日天下」とも言う。本姓は源氏で、家系は清和源氏の摂津源氏系で、美濃源氏土岐氏支流である明智氏。通称は十兵衛。雅号は咲庵(しょうあん)。のちに朝廷より惟任の姓を賜ったため惟任光秀とも言う。妻は妻木煕子。その間には、細川忠興室・珠(洗礼名:ガラシャ)、嫡男・光慶(十五郎)、津田信澄室がいる。領地では善政を行ったとされ、忌日に祭事を伝える地域(光秀公正辰祭・御霊神社 (福知山市))もある。後世、江戸時代の文楽「絵本太功記」や歌舞伎「時桔梗出世請状」をはじめ、小説・映画・テレビドラマなどでもその人物がとりあげられている。';

var hideyoshi = '豊臣秀吉(とよとみ ひでよし、とよとみ の ひでよし、旧字体: 豐臣秀吉)、または羽柴 秀吉(はしば ひでよし)は、戦国時代から安土桃山時代にかけての武将、大名、天下人、関白、太政大臣、太閤。三英傑の一人[1][2]。初め木下氏を名字とし、羽柴氏に改める。本姓としては、初め平氏を自称するが、近衛家の猶子となり藤原氏に改姓した後、豊臣氏に改めた。尾張国愛知郡中村郷の下層民の家に生まれたとされる。(出自参照)当初、今川家に仕えるも出奔した後に織田信長に仕官し、次第に頭角を現した。信長が本能寺の変で明智光秀に討たれると「中国大返し」により京へと戻り山崎の戦いで光秀を破った後、信長の孫・三法師を擁して織田家内部の勢力争いに勝ち、信長の後継の地位を得た。大坂城を築き、関白・太政大臣に就任し、豊臣姓を賜り、日本全国の大名を臣従させて天下統一を果たした。天下統一後は太閤検地や刀狩令、惣無事令、石高制などの全国に及ぶ多くの政策で国内の統合を進めた。理由は諸説あるが明の征服を決意して朝鮮に出兵した文禄・慶長の役の最中に、嗣子の秀頼を徳川家康ら五大老に託して病没した。秀吉の死後に台頭した徳川家康が関ヶ原の戦いで勝利して天下を掌握し、豊臣家は凋落。慶長19年(1614年)から同20年(1615年)の大坂の陣で豊臣家は江戸幕府に滅ぼされた。墨俣の一夜城、金ヶ崎の退き口、高松城の水攻め、中国大返し、石垣山一夜城などが、機知に富んだ功名立志伝として広く親しまれ、百姓から天下人へと至った生涯は「戦国一の出世頭」と評される。';

var classifier = bayes({
  tokenizer: function(text) {
    return mecab.wakatigakiSync(text);
  }
});

// 学習
classifier.learn(nobunaga, '織田信長');
classifier.learn(mitsuhide, '明智光秀');
classifier.learn(hideyoshi, '豊臣秀吉');

// 与えられたテキストデータのカテゴリを予想
categorize('部下に謀反を起こされる');
categorize('三日天下');
categorize('下層農民の生まれ');

function categorize(text) {
  var r = classifier.categorize(text);
  console.log("[" + r + "] - " + text);
}

以下出力結果です。ちゃんと分類されていますね。

[織田信長] - 部下に謀反を起こされる
[明智光秀] - 三日天下
[豊臣秀吉] - 下層農民の生まれ

英語と違って、日本語の文章は単語間の区切りがコンピューターにとってはわかりにくいので、分かち書きしたものをベイジアンフィルタにトークンとして与えています。

参考文献

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

情報検索の基礎

情報検索の基礎

  • 作者: Christopher D.Manning,Prabhakar Raghavan,Hinrich Schutze,岩野和生,黒川利明,濱田誠司,村上明子
  • 出版社/メーカー: 共立出版
  • 発売日: 2012/06/23
  • メディア: 単行本
  • 購入: 2人 クリック: 69回
  • この商品を含むブログ (5件) を見る

HubotからGithubのissueを扱う

HubotからGithubのissueを扱う

http://shokai.org/blog/archives/9560

こちらコードに少し手を加えて、HubotからGtihub issueを扱えるようにしてみた。

create, list up, close github issues via hubot

使い方

Githubのトークンが必要:

$ curl -i https://api.github.com/authorizations -d '{"scopes":["repo"]}' -u "username"
$ heroku config:set HUBOT_GITHUB_TOKEN=token_value

TODO listとかブログ記事のネタとかのために使う。

  • hubot issue list

  • hubot issue create タイトル body コンテンツ

  • hubot close issueのナンバー(#)

Processing のエディタとして Atom を使う

Processing のコーディングを快適にするために、Atom を使ってみる。

Processing とは

ビジュアルアートを学ぶためのオープンソースのプログラミング言語であり、開発環境。Java でビルドされており、その構文は Java をシンプルにしたもの。

エディタも備わっているが、より快適にコーディングするために、Atom に Processing 用のパッケージを導入してみる。

インストールするパッケージ一覧

これらのパッケージをインストールする。

$ apm install processing processing-autocomplete processing-language

Processing の導入

まずは、こちらから Processing の開発環境をダウンロード。

Processingを立ち上げて、Toolsメニューからprocessing-java をインストールする。

which コマンドでパスを確認し、

$ which processing-java
/usr/local/bin/processing-java

processing の実行パスに設定する

実際に使ってみる

.pde ファイルを作成し、コーディングする。

ctrl-alt-b コマンドでビルドされれば、成功。

Atomのマークダウンプレビュー内で数式を扱う

Atomのマークダウンプレビュー内で数式を扱う

まずは必要な以下のパッケージをインストール

$ apm install markdown-preview-plus mathjax-wrapper

必要なパッケージ

マークダウン

文章を記述するための軽量マークアップ言語

Mathjax

さまざまな数式用のマークアップ言語を表示するJavaScriptエンジン

ドキュメント

マークダウンプレビュー内で数式を扱う

準備が完了したら、適当なマークダウンファイル(.md)を作成し、編集する:

<h3>The Quadratic Formula</h3>

\[ x = {-b \pm \sqrt{b^2-4ac} \over 2a} \]

<h3>The Cauchy-Schwarz Inequality</h3>

\[ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \]

<h3>A Cross Product Formula</h3>

\[\mathbf{V}_1 \times \mathbf{V}_2 =  \begin{vmatrix}
\mathbf{i} & \mathbf{j} & \mathbf{k} \\
\frac{\partial X}{\partial u} &  \frac{\partial Y}{\partial u} & 0 \\
\frac{\partial X}{\partial v} &  \frac{\partial Y}{\partial v} & 0
\end{vmatrix}  \]

<h3>The probability of getting \(k\) heads when flipping \(n\) coins is</h3>

\[P(E)   = {n \choose k} p^k (1-p)^{ n-k} \]

<h3>An Identity of Ramanujan</h3>

\[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} =
1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}
{1+\frac{e^{-8\pi}} {1+\ldots} } } } \]

<h3>Maxwell’s Equations</h3>

\[  \begin{aligned}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\   \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned}
\]

Ctr+Shift+Mでマークダウンプレビューを、Ctr+Shift+Xで数式のレンダリングを起動する:

(そろそろ大きいディスプレイが必要)

終わり。

おまけ

いろいろググっていると、こんなサイトを見つけた。数学の質問を聞くことができるようだ。文系にはありがたい。英語が出来ればの話だが。

http://math.stackexchange.com/

webpack, React Hot Loader + Browsersync でクロスブラウジング+ホットリロード開発

React Hot Loader + Browsersync

React Hot Loader を使えば、エディタを保存した時点で、React コンポーネントの変更が検知され、ブラウザの更新が自動で行われます。さらに、React Hot Loader はHotModuleReplacementPlugin(HMR) を使用しているため、更新時にページの全読み込みが発生せず、state が保持されたまま React コンポーネントの変更した箇所のみが部分更新されるため、いちいち手動でブラウザをリロードする手間が省けることに加え、チェック毎に state を変える必要がなくなります。

通常はデベロップメントサーバーに webpack-dev-server を使うことで、このホットリローディング機能を使うことができるのですが、今回は Browsersync を使って、クロスブラウジングとさらなるホットリローディングの強化を目指します。Browsersync は、ファイル群の変更を監視して、ブラウザを自動で全読み込みしてくれます。また、他のデバイスからもブラウジングができる、クロスブラウジングを可能にしてくれます。これと、React Hot Loader を使えば、快適に React フロントエンドが開発できるようになります。

まとめると、次のようになります。

  • React Hot Loader : React コンポーネントの変更を監視し、変更があれば最小限のページ更新を行う。

  • Browsersync : js ファイル以外のファイルの変更を監視し、変更があればページの全読み込みを行う。クロスブラウジングを可能にする。

導入

まずは必要な物をインストールします。package.json は次のようになります。

"dependencies": {
  "react": "^0.13.3",
  "react-dom": "^0.14.7"
},
"devDependencies": {
  "babel-core": "^5.8.38",
  "babel-loader": "^5.4.0",
  "browser-sync": "^2.11.2",
  "react-hot-loader": "^1.3.0",
  "webpack": "^1.12.14",
  "webpack-dev-middleware": "^1.5.1",
  "webpack-hot-middleware": "^2.10.0"
}

通常の React Hot Loader に必要なライブラリに加えて Browsersyncwebpack-hot-middlewarewebpack-dev-middleware の ふたつのミドルウェアをインストールしておきます。webpack のホットリローディングは webpack-dev-middleware がファイル変更イベントを検知して webpack-dev-server に送信することで可能になりますが、 webpack-hot-middleware を合わせて使うことで、その他のデベロップメントサーバーにイベントを送信して、自動でブラウザをリロードさせることができます。

webpack config

プロジェクトファイルの構成:

|___app/
|     |___main.jsx
|___dist/
|     |___css/
|     |___index.html
|___server.js
|___webpack.config.js

まずは webpack.config.js を編集します。

下記は React Hot Loader での webpack.config.js です。

// React Hot Loader
var webpack = require('webpack');
var path = require('path');

module.exports = {
  debug: true,
  devtool: '#eval-source-map',

  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './app/main.jsx'
  ],

  output: {
    path: path.join(__dirname, 'dist'),
    publicPath: '/',
    filename: 'bundle.js'
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],

  module: {
    loaders: [
      { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot', 'babel'] }
    ]
  }
};

ここに、プラグインを2つ加えます。

plugins: [
    // Webpack 1.0
    new webpack.optimize.OccurenceOrderPlugin(),
    // Webpack 2.0 fixed this mispelling
    // new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
]

また、webpack-hot-middleware/client をエントリーポイントに設定します。

したがって、webpack.config.js ファイルは次のようになります。

var webpack = require('webpack');
var path = require('path');

module.exports = {
  debug: true,
  devtool: '#eval-source-map',

  entry: [
    'webpack/hot/dev-server',
    'webpack-hot-middleware/client',
    './app/main.jsx'
  ],

  output: {
    path: path.join(__dirname, 'dist'),
    publicPath: '/',
    filename: 'bundle.js'
  },

  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],

  module: {
    loaders: [
      { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot', 'babel'] }
    ]
  }
};

Browsersync

サーバーに、ミドルウェアを加えます。Browsersync を実行する server.js ファイルは次のようになります。

/**
 * Require Browsersync along with webpack and middleware for it
 */
var browserSync = require('browser-sync');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');

var webpackConfig = require('./webpack.config');
var bundler = webpack(webpackConfig);

browserSync({
    server: {
      baseDir: 'dist',

      middleware: [
        webpackDevMiddleware(bundler, {
          publicPath: webpackConfig.output.publicPath,
          stats: { colors: true }
          // http://webpack.github.io/docs/webpack-dev-middleware.html
        }),

        webpackHotMiddleware(bundler)
      ]
    },

    files: [
      'dist/css/*.css',
      'dist/*.html'
    ]
});
  • baseDir: サーバーのベースとなるディレクトリ。
  • middleware: 使用するミドルウェア。devMiddlewareではpublicPathの指定が必須となります。
  • files: Browsersync が変更を監視するファイル群。変更がなされると、ページ全体をリロードします。js ファイル群の監視は webpack に任せるので、ここに記述する必要はありません。

クロスブラウジング

次のコマンドで、サーバーが立ち上がります。

node server.js

同時に、次のような URL が表示されます。

[BS] Access URLs:
 ------------------------------------
       Local: http://localhost:3000
    External: http://{PCのIPアドレス}:3000
 ------------------------------------
          UI: http://localhost:3001
 UI External: http://{PCのIPアドレス}:3001
 ------------------------------------

この URL に繋げば、同じネットワークに接続している他のデバイスからも、ブラウジングすることができます。
モバイルデバイスからの UI や UX のチェックに便利です。

ソースコード

npmのバージョン管理まとめ

たまにしか活用しないから忘れるのでメモ

参考: semver | npm Documentation

バージョン管理

npmでは、「セマンティックバージョニング」と呼ばれるバージョン管理法によって、依存パッケージやリリース物のバージョンを定義している。

npmにあるプロジェクトは少し異なるものもあるが、他の人と共有するようなプロジェクトは、普通 1.0.0 から始めるべきだ。

このルールに加えて、プロジェクトに変更を行った時のバージョンは次のように扱われる。

  • バグの修正やその他のマイナーチェンジ: パッチリリースでは最後の数字をインクリメントする (e.g. 1.0.1)
  • 既存の機能を壊さないような新しい機能: マイナーリリースでは、真ん中の数字をインクリメントする (e.g. 1.1.0)
  • 後方互換性のない変更: メジャーリリースでは、最初の数字をインクリメントする (e.g. 2.0.0)

npm では依存パッケージを package.json ファイルで管理する。そこでは、パッケージのバージョンを範囲指定することもできる。

もし、あるパッケージ 1.0.4 を範囲指定する時、次のようになる。

  • パッチリリースの範囲指定: 1.0 / 1.0.x / ~1.0.4
  • マイナーリリースの範囲指定: 1 / 1.x / ^1.0.4
  • メジャーリリースの範囲指定: * / x

詳しくは、このエントリの最後の後半で説明する。

演算子を用いて、更に細かく範囲指定することができる。これを、comparatorと呼ぶ。

  • < 未満
  • <= 以下
  • > より大きい
  • >= 以上
  • = イコール(演算子の指定がなければ適応される)

例えば、>=1.2.71.2.7 / 1.2.8 / 2.5.3 / 1.3.9 に一致するが、1.2.6 / 1.1.0 には一致しない。

また、comparator はホワイトスペースで結合できる(comparator set)。範囲指定は、さらにこの comparator set を || で結合して示されることがある。

例えば、 >=1.2.7 <1.3.01.2.7 / 1.2.8 / 1.2.99 に一致するが、1.2.6 / 1.3.0 / 1.1.0 には一致しない。

1.2.7 || >=1.2.9 <2.0.01.2.7 / 1.2.9 / 1.4.6 には一致するが、 1.2.8 / 2.0.0には一致しない。

プレリリースタグ (Prerelease Tags)

もし、バージョンがプレリリースタグ(e.g. 1.2.3-alpha.3)を持っていれば、同じ [major, minor, patch] で、プレリリースタグを持つ comparator しか、指定できない。

例えば、 >1.2.3-alpha.31.2.3-alpha.7 に一致するが、 3.4.5-alpha.9 には、一致しない。この場合、バージョンの範囲指定は、1.2.3のプレリリースタグにのみ適応される。3.4.5はプレリリースタグを持たず、1.2.3-alpha.3 より大きいので、これは条件を満たす。

こうした振る舞いには2つの理由があり、ひとつはプレリリースバージョンは頻繁に更新され、多くの前衛的変更を含む。従って、デフォルトでは範囲指定から外されている。

また、プレリリースバージョンを使用することを選んだユーザーは、特定の alpha/beta/rc バージョンを使用する意思をはっきりと示すことができる。範囲指定にプレリリースタグを含めることで、ユーザーはそれを使用することのリスクを自覚していることを示している。

更に詳しく

Hyphen Rangees X.Y.Z - A.B.C

以上/以下の範囲指定:

  • 1.2.3 - 2.3.4 := >=1.2.3 <=2.3.4

最初と最後を含めて範囲指定する。欠けているものがあれば、0が代わりに使われる。

  • 1.2 - 2.3.4 := >=1.2.0 <=2.3.4
  • 1.2.3 - 2 := >=1.2.3 <3.0.0

X-Ranges 1.2.x / 1.x / 1.2.* /*

X / x / * はあらゆる数字の代わりになる。

  • * := >=0.0.0 (すべてのバージョン)
  • 1.x := >=1.0.0 <2.0.0 (メジャーバージョンの範囲指定)
  • 1.2.x := >=1.2.0 <1.3.0 (メジャーバージョン、マイナーバージョンの範囲指定)

Tilde Ranges ~1.2.3 / ~1.2 / ~1

Tilde rangesでは、comparator 上でマイナーバージョンを指定していれば、パッチレベルの変更は許容される。

  • ~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 (Same as 1.2.x)
  • ~1 := >=1.0.0 <(1+1).0.0 := >=1.0.0 <2.0.0 (Same as 1.x)
  • ~0.2.3 := >=0.2.3 <0.(2+1).0 := >=0.2.3 <0.3.0
  • ~0.2 := >=0.2.0 <0.(2+1).0 := >=0.2.0 <0.3.0 (Same as 0.2.x)
  • ~0 := >=0.0.0 <(0+1).0.0 := >=0.0.0 <1.0.0 (Same as 0.x)
  • ~1.2.3-beta.2 := >=1.2.3-beta.2 <1.3.0 (プレリリースタグの範囲指定は 1.2.3 上でのみ適応される)

Caret Ranges ^1.2.3 / ^0.2.5 / ^0.0.4

Caret ranges ではパッチとマイナーチェンジは許容される。

Caret ranges は 0.2.43.0.0 の間にブレイキングチェンジ(既存の機能の削除など後方互換性のない変更)があることを想定している。しかし、 0.2.40.2.5 の間にブレイキングチェンジが絶対にないと想定していることに注意する。

  • ^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
  • ^1.2.3-beta.2 := >=1.2.3-beta.2 <2.0.0
  • ^0.0.3-beta := >=0.0.3-beta <0.0.4

欠けている patchminor の値は 0 とみなし、それ以上のバージョンのパッチやマイナーの変更を許容する。

  • ^1.2.x := >=1.2.0 <2.0.0
  • ^0.0.x := >=0.0.0 <0.1.0
  • ^0.0 := >=0.0.0 <0.1.0
  • ^1.x := >=1.0.0 <2.0.0
  • ^0.x := >=0.0.0 <1.0.0