hubot-scriptを公開する方法(npm編)

hubot-scripts Advent Calendar 2013の7日目の記事になります。
6日目は hubotで突然の死に備える でした。

hubot-scriptの公開

hubot-scriptを書いていて、これを一般にも公開してみたいなと思う瞬間はありませんか?
先日作った>突然の死<ですが、現場でも好評(?)をいただいており練習も兼ねて公開してみることにしました。

ただしこの日本のネット文化丸出しのスクリプトが本家のスクリプト群に取り込んでもらえるとは到底思えません。
そこで、単独でnpmへ公開する方法をとることにしたので、インストール方法と合わせて紹介したいと思います。

今回実際に公開したものは以下になります。

hubot-suddendeath

https://npmjs.org/package/hubot-suddendeath

公開するための準備

公開を行うにあたりディレクト構成は以下のようにしておきます。

hubot-suddendeath/
|-- README.md
|-- index.coffee
|-- package.json
`-- scripts
    `-- suddendeath.coffee

まずpackage.jsonを用意しましょう。
npmへの公開には必須となります。
手で書くこともできますが、package.jsonを対話的に作成するnpm initというコマンドもあります。

$ cd hubot-suddendeath
$ npm init

一度npm initで作ってからエディタで微調整を行うのがいいかもしれません。
詳しい項目の解説はこちら(英語)を参照してください。
今回の例でいうと、mainとdependenciesが正しく設定されていれば他は割とどうでもいいかと思います。

package.jsonができあがったらエントリポイント(packeage.jsonでいうmain)となるindex.coffeeにスクリプトをロードさせるためのモジュールを書いておきます。

Fs   = require 'fs'
Path = require 'path'

module.exports = (robot) ->
  path = Path.resolve __dirname, 'scripts'
  Fs.exists path, (exists) ->
    if exists
      robot.loadFile path, file for file in Fs.readdirSync(path)

robot.loadFileにパスを渡すとhubotがスクリプトを読み込んでくれます。
ひとまずこれで公開できる形にはなりました。

公開する

npmに公開するにあたって、まずnpm専用のアカウントを取得する必要があります。
アカウントの登録は公式サイト上で行うことができますが、コマンドからでも登録は可能なのでこちらの方法を使います。
以下のコマンドを叩くとユーザ名やパスワード、メールアドレスを求められるので適宜入力してください。

$ npm adduser

アカウントの登録が完了したらpackage.jsonの置いてあるディレクトリ上で

$ npm publish

を行うだけで公開が完了します。
お手軽ですね!

$ npm search hubot-suddendeath

で実際に登録されたかどうか確認できます。

またバグ修正などでスクリプトに変更を加えた場合は、再度npm publishを行うことで更新内容が反映されます。
ただしpackage.jsonのversionを上げておく必要があるのでご注意ください。

公開したスクリプトをインストール

本家が公開しているhubot-scriptsは通常、npm install後にhubot-scripts.jsonに使用するスクリプト名を指定して読み込みを行います。

$ npm install hubot-scripts
$ echo '["redis-brain.coffee", "tweet.coffee", "shipit.coffee"]' > hubot-scripts.json

では外部モジュールの場合はどうするかというと、external-scripts.jsonという仕組みが用意されていて、そこにモジュール名を記述することでスクリプトが利用できるようになっています。

$ npm install hubot-suddendeath
$ echo '["hubot-suddendeath"]' > external-scripts.json

あとはhubotを再起動して好きなだけコマンドを打ってみましょう。

まとめ

npmへの公開はとても簡単に行えます。
本家のhubot-scriptsにプルリクを投げるのはなんか怖いという方もまずはこの方法から始めてみては如何でしょうか?

また自分以外にもたくさんの人がこの方法でhubot-scriptを公開していますので面白い/役に立つスクリプトを是非探してみてください。

hubotで突然の死に備える

hubot-scripts Advent Calendar 2013の6日目の記事になります。
1日空きましたが、4日目はdaxanya2さんによる Hubot-scriptsでサイコロを投げる でした。

_人人人人人人_
> 突然の死 <
 ̄Y^Y^Y^Y^Y ̄

突然の死とは (トツゼンノシとは) [単語記事] - ニコニコ大百科

botでは比較的よく見るベタなネタではありますが、自分の調べた範囲ではhubot用のものがまだ無さそうだったので作ってみました。

対象文字列の長さによって吹き出しの幅を変える必要がありますが、
上の「人」は全角文字数(半角文字は2個で1)+2個分、下は「Y」1つと「^Y」を全角文字数分で表示するようにしました。

全角半角が混ざった場合の幅の計算はなんかメンドそうなのでライブラリに任せてます。
https://npmjs.org/package/eastasianwidth

事前に npm install しておきましょう。

$ npm install eastasianwidth

実際のコードは以下のようになります。

# Description:
#   >Prepare for sudden death.<
#
# Dependencies:
#   "eastasianwidth": "~0.1.0"
#
# Configuration:
#   None
#
# Commands:
#   hubot >< <message> - Ascii art generator for sudden death.
#
# Notes:
#   None
#
# Author:
#   saihoooooooo

eastasianwidth = require 'eastasianwidth'

strpad = (str, count) ->
  new Array(count + 1).join str

module.exports = (robot) ->
  robot.respond />< (.*)$/i, (msg) ->
    message = msg.match[1].replace /^\s+|\s+$/g, ''
    return until message.length

    length = Math.floor eastasianwidth.length(message) / 2

    suddendeath = [
      "_#{strpad '人', length + 2}_"
      "> #{message} <"
      " ̄Y#{strpad '^Y', length} ̄"
    ]
    msg.send suddendeath.join "\n"

簡単ですね!

使い方は

hubot >< 突然の死

とするだけです。
これで例え急にサーバが落ちてしまっても快適なhubot生活を送れること間違いないですね!

次回はnpm化でもしてみようと思います。 → しました。
それでは。

vimにluajitを対応させてみた作業ログ

先日、vacにneocompleteを入れてみた作業ログという記事を書き、それをlingrで報告すると以下のような発言をもらった。

f:id:saihoooooooo:20131202111744p:plain

luajit知りませんでした。
現状でneocompleteの速度には困ってないし、if_luaを駆使してプラギン作る予定も無いので正直必要ないっちゃないけど、せっかく教えてもらったので入れてみることにする。

前回のluaはyumでインストールしたけど、luajitはyumにないので自前でビルドする。
本家サイト(http://luajit.org/)を参考にcloneしてmake。

$ cd /usr/local/src
$ git clone http://luajit.org/git/luajit-2.0.git luajit
$ cd luajit
$ sudo make
$ sudo make install

特に問題なし。
続いてvimもluajit対応させます。

$ cd /usr/local/src/vim
$ vim vuild.sh
$ cat vuild.sh
#!/bin/sh
./configure \
  --prefix=/usr/local \
  --with-features=huge \
  --enable-multibyte \
  --enable-luainterp \
  --with-luajit \
  --enable-fail-if-missing && make && make install
$ ./vuild.sh

configureは前回と比べると、--with-luajit の部分だけ追加しました。
ビルドで特にエラーが発生せず、:version で +lua もついてるので多分いけてるはず。

むしろこれを機にluaを勉強してみようかな。

neocompleteを入れてみた作業ログ

Vim Advent Calendar 2012の364日目の記事になります。
363日目は@haya14busaさんによる Vim-Easymotionを拡張してカーソルを縦横無尽に楽々移動する でした。
あと1日でvacが365日達成してしまうことに驚きを隠せません。
advent calendarとは一体何だったのか。

f:id:saihoooooooo:20131129152502p:plain

では本題に入ります。
neocomplcacheが進化してneocompleteとなったことは知ってましたがif_luaを使ってるためインストールは敬遠していました。
そんな僕もShougoさんのスライドを見てneocompleteに移行することを決意しました。
neocomplcacheはもう開発がされないそうですしね。

という訳でやったことをメモしておきます。

luaのインストール

まず手元のlinuxマシンにluaが入っていなかったのでインストールから。

$ sudo yum install -y lua lua-devel

vimのビルド

その後、if_luaを有効にするためvimをビルドします。

$ cd /usr/local/src
$ hg clone https://vim.googlecode.com/hg/ vim
$ cd vim
$ vim vuild.sh
$ cat vuild.sh
#!/bin/sh
./configure \
  --prefix=/usr/local \
  --with-features=huge \
  --enable-multibyte \
  --enable-luainterp \
  --enable-fail-if-missing && make && make install
$ chmod +x vuild.sh
$ ./vuild.sh
$ alias vim="/usr/local/bin/vim"

正直configureはそんなにわかってない。
vimを起動して、:version で確認してみる。

f:id:saihoooooooo:20131129175246p:plain

問題なさそうです!
では最後にneocompleteをvimにインストールしていきます。

neocompleteのインストール

.vimrcに以下を記述します(neobundleを使用)。

" if_luaが有効ならneocompleteを使う
NeoBundle has('lua') ? 'Shougo/neocomplete' : 'Shougo/neocomplcache'

if neobundle#is_installed('neocomplete')
    " neocomplete用設定
    let g:neocomplete#enable_at_startup = 1
    let g:neocomplete#enable_ignore_case = 1
    let g:neocomplete#enable_smart_case = 1
    if !exists('g:neocomplete#keyword_patterns')
        let g:neocomplete#keyword_patterns = {}
    endif
    let g:neocomplete#keyword_patterns._ = '\h\w*'
elseif neobundle#is_installed('neocomplcache')
    " neocomplcache用設定
    let g:neocomplcache_enable_at_startup = 1
    let g:neocomplcache_enable_ignore_case = 1
    let g:neocomplcache_enable_smart_case = 1
    if !exists('g:neocomplcache_keyword_patterns')
        let g:neocomplcache_keyword_patterns = {}
    endif
    let g:neocomplcache_keyword_patterns._ = '\h\w*'
    let g:neocomplcache_enable_camel_case_completion = 1
    let g:neocomplcache_enable_underbar_completion = 1
endif
inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>"
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<S-TAB>"

念のため、if_luaが効いてない環境でも補完が動くよう、neocomplcacheの設定は残しました。

感想

速度の向上はまだ実感できてませんが、あいまい補完の動作はちょっとした感動です。
またtypoした文字列をいつまでもキャッシュされないというのもポイント高いです。

とりあえずこれで様子を見ながら他のオプションも設定したいですね。

vac2012について

明日はついにvim advent calendar 2012のファイナルです!
マジですごい!!
この偉業に少しでも貢献できたことは幸せだと思います。
それでは!
Have a nice vim!