プログラム

readlineで標準入力を受け付ける(対話型シェル)

Node.jsで書いたコマンドラインツールに対話型のシェルを設けたかったので試してみた。
(参照にしたgistほぼそのままなのだけれども)

アロー関数で書き直そうとしたのだけれども詰まったので一旦メモとして残しておく。

var readline = require('readline');
function Cli(handler) {
this.handler = handler;
}
Cli.prototype.run = function() {
var self = this;
var rli = readline.createInterface(process.stdin, process.stdout);
rli.setPrompt('> ');
rli.on('line', function(line) {
var args = line.split(/\s+/), cmd = args.shift();
if (self.handler[cmd]) {
self.handler[cmd].call(rli, args, function(err, res) {
console.log(res);
rli.prompt();
});
} else if (cmd.length > 0) {
console.log('cmd not found.');
rli.prompt();
} else {
rli.prompt();
}
rli.prompt();
}).on('close', function() {
console.log('');
process.stdin.destroy();
});
rli.prompt();
};
function Handler() {}
Handler.prototype.echo = function(args, fn) {
fn(null, args);
};
(new Cli(new Handler())).run();

参照

https://gist.github.com/murayama/5954017

synapticを使ってXORをニューラルネットワークで再現する

ニューラルネットワーク自作入門を読んでいてサンプルはPythonで書かれているのだけれども、Nodeで書けないかなと思って調べてみたらsynapticというnpmがあったので本で読んだことを踏まえつつXOR(排他的論理和)を再現してみることにした。

www.npmjs.com

XORを再現するために入力層2段, 隠れ層3段, 出力層1段でニューラルネットワークを構成してみる。

var synaptic = require('synaptic');
var Layer = synaptic.Layer,
Network = synaptic.Network,
Trainer = synaptic.Trainer;
function Perceptron(input, hidden, output)
{
// create the layers
var inputLayer = new Layer(input);
var hiddenLayer = new Layer(hidden);
var outputLayer = new Layer(output);
// connect the layers
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
// set the layers
this.set({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
}
Perceptron.prototype = new Network();
Perceptron.prototype.constructor = Perceptron;
var myPerceptron = new Perceptron(2,3,1);
var myTrainer = new Trainer(myPerceptron);
var learning = [
{input:[0,0], output:[0]},
{input:[1,0], output:[1]},
{input:[0,1], output:[1]},
{input:[1,1], output:[0]}
];
var parameters = {
iterations: 2000,
shuffle: true,
cost: Trainer.cost.MSE
};
var results = myTrainer.train(learning, parameters);
console.log(`Error rate = ${results.error}`);
// calculate
console.log(`xor(0,0) = ${myPerceptron.activate([0,0])}`);
console.log(`xor(1,0) = ${myPerceptron.activate([1,0])}`);
console.log(`xor(0,1) = ${myPerceptron.activate([0,1])}`);
console.log(`xor(0,0) = ${myPerceptron.activate([1,1])}`);

上を実行すると以下の様にXORを再現できている。(2入力の双方に同値が入れば0に近くなり、異値が入れば1に近くなっている)

$ node xor.js
Error rate = 0.004983333135247337
xor(0,0) = 0.051013797211575035
xor(1,0) = 0.932949883533232
xor(0,1) = 0.9341209212942756
xor(0,0) = 0.08390604881521503

上のプログラム中では学習回数を2000回にしてあるけれども各回数で試行に対しての誤り率をプロットすると以下の様な感じになった。1000回超えると収束していっているのがわかる。

学生時代に簡単にニューラルネットワークについて眺めるくらいはしていたり、今回買った本を読んで原理についてはわかったつもりなのだけれども実際に自分で試してみると学習された様子がわかって面白い。何一つXORのロジックを書いてないのにXOR回路が再現できている。

参考図書

ニューラルネットワーク自作入門

ニューラルネットワーク自作入門

参考サイト

postd.cc

Grenacheを利用してマイクロサービスを管理&呼び出ししてみる

BITFINEXというビットコイントレードのプラットフォームを提供している会社がGrenacheというマイクロサービスを管理するフレームワークを提供しているので少し触ってみた。

例によってサンプルプログラム、ほぼそのままなのだけど多少咀嚼した(つもり)。

Grenache is a DHT based high-performance microservices framework by Bitfinex. Its decentralised and optimized for performance. Because its simple, it is easy to understand and to set up.

(ref: https://github.com/bitfinexcom/grenache )

github.com

雑にまとめたのが下の図。

特徴としては、サービスを一箇所に登録するのではなく離散ハッシュテーブルで管理されるクラスタに登録できるところで、そこにサービスを登録することでそのサービスのIP等の接続情報が管理されていくところにある。

f:id:hideack:20170701181107p:plain

まずはGRAPEと呼ばれるデーモンを立ち上げてクラスタを構成する。npmで雑にインストール。

$ npm i -g grenache-grape

GRAPEを立ち上げる。3クラスタ構成にしてみる。

$ grape --dp 20001 --aph 30001 --bn '127.0.0.1:20002'
$ grape --dp 20002 --aph 30002 --bn '127.0.0.1:20003'
$ grape --dp 20003 --aph 30003 --bn '127.0.0.1:20001'

dpクラスタ間でのDHTを構成する際に通信するためのポート番号、aphAPIのポート番号になる。 bnは立ち上げ時に参照するGRAPEを指定する。

これでクラスタは構成されたので次は実際にWorker(=サービス)を登録していく。サンプルではフィボナッチ数列の和だったけど、ここでは階乗を求めるサービスにしてみる。

GRAPEクラスタへWorkerを登録したり、登録したWorkerを呼び出すClientを作成するためのライブラリもnpmに登録されているのでこれを利用していく。

GitHub - bitfinexcom/grenache-nodejs-ws: Grenache Node.JS WebSocket implementation

まずはWorkerから。

'use strict'
const { Link, PeerRPCServer }  = require('grenache-nodejs-ws')
function fact (n) {
if (n == 0) {
return 1
}
let m = fact(n-1);
return m * n;
}
const link = new Link({
grape: 'http://127.0.0.1:30001'
})
link.start()
const peer = new PeerRPCServer(link, {})
peer.init()
const service = peer.transport('server')
service.listen(1337)
setInterval(() => {
link.announce('fact_worker', service.port, {})
}, 1000)
service.on('request', (rid, key, payload, handler) => {
const result = fact(payload.number)
handler.reply(null, result)
})

なんとなく見て分かる通り fact_worker という名前でサービスをGRAPEクラスタに登録している。リクエストが来たときに fact というfunction呼び出してその結果をリプライしているという至極シンプルな形。

では、上のWorkerを立ち上げたままの状態で次にこれを実際に呼び出すクライアントを書いてみる。

同じく先のgrenache-nodejs-wsを利用して、

'use strict'
const { Link, PeerRPCClient }  = require('grenache-nodejs-ws')
const link = new Link({
grape: 'http://127.0.0.1:30001',
requestTimeout: 10000
})
link.start()
const peer = new PeerRPCClient(link, {})
peer.init()
const payload = { number: 10 }
peer.request('fact_worker', payload, { timeout: 100000 }, (err, result) => {
if (err) throw err
console.log(`${payload.number}! = ${result}`);
});

上を実行すると

が行われて、

$ node client.js
10! = 3628800

といった結果が得られる。

クライアントはWorkerの接続先を直接知ること無く、GRAPEクラスタに登録された階乗の結果を得るサービスの接続先情報をクラスタから得た上でWorkerに接続を行い処理結果を得ることができた。

確かにこの仕組があるとシンプルにクラスタを形成することもでき、更にそこに対してマイクロサービスを登録していくことが実現できそう。BITFINEXではこれを実際にサービスに投入しているとのことなので面白い仕掛けだなと思った。

参照

package.jsonでnpm runさせるときに記載するパスについて

package.json 中に scripts を定義してその下に実行させたいスクリプトを指定するとnpm run で実行できる。

例えば、

{
"name": "foobar",
(snip)
"scripts": {
"lint": "eslint lib/*.js bin/*",
"test": "npm run lint && mocha --require ./test/helper.js"
},
....

といった内容があると仮定すると npm run lint を実行することで記載したeslintを実行できる様になる。

で、このときにeslintなどを呼び出すときにグローバルにインストールしていなくてnode_modules以下にある場合のケースを想定して

    "lint": "./node_modules/.bin/eslint lib/*.js bin/*",

など書いていたりしたことがあったのだけれども、npm run される際にはnode_modules以下も自動的にパスが通された状態になるので明記しなくても大丈夫ですよ。

ということに今日気づいたというメモエントリーでした。

鶏そぼろの作り方, slack-cli-stream – 徒然日記

鶏そぼろ

晩御飯にそぼろご飯作る。鶏そぼろと卵そぼろを作って好きなだけお茶碗によそいだご飯にかけて食べる方式。雑に以下の様なレシピ。

  • 鶏肉ミンチ 200g
  • 醤油 大さじ2
  • 砂糖 大さじ1.5〜2
  • みりん 大さじ1
  • 水 1/4カップ

作り方は意外と簡単で上の材料一式、肉も含めて全てを 火にかけていないフライパン に全て入れてよく混ぜて中火にかける。そしてこれでもかという位、菜箸数本を束ねてとにかく混ぜるとできあがる。

火にかける前の段階でよく混ぜることが均等でいい塩梅のそぼろになるコツ。

まとめて眺める君

作っていた「Slackで自分が参加しているチャンネルをまとめてターミナルで眺めることができる君」、ひとまずgithubリポジトリに投げ込んでnpmにも登録してみた。荒削りだけど動くちゃ動く。

拾いきれてないイベントがまだいくつかあるので盆栽ワークスとしてちょこちょこ作っていこうと思う。

github.com

SlackのReal Time Messaging APIで得られる情報を眺める

自身のSlackアカウントが受領している情報を1箇所に集めてナニカできるかなと思ったとき、まず雑に情報だけ取得してみようと思い試してみた。

github.com

というのがあってnpmでも公開されているのでnpm installする。

$ npm install --save @slack/client

サンプルにもある通りなのだけれども一応さらっておくとここで取得できるトークンを利用して雑に以下の様なコードを実行するとReal Time Messaging APIで取得できる情報が全てダンプされる。

var token = 'xoxp-******-******-*****-*****';
var slack = require('@slack/client');
var RtmClient = slack.RtmClient;
var RTM_EVENTS = slack.RTM_EVENTS;
var CLIENT_EVENTS = slack.CLIENT_EVENTS;
var rtm = new RtmClient(token, {logLevel: 'debug'});
rtm.start();
rtm.on(RTM_EVENTS.MESSAGE, function (message) {
// 例えばメッセージを検出したときにはここでナニカする
});

実行すると以下の様な形で取得された情報のJSONが流れていく。

debug: { '0': 'raw_message', '1': '{"type":"pong","reply_to":2032}' }
debug: sending message via ws: {"type":"ping","id":2033}
debug: {"type":"message","channel":"C03QHFYDB","user":"U02B59NCD","text":"てすと","ts":"1494125387.277986","source_team":"T02B3CR15","team":"T02B3CR15"}

ログレベルを debug にしているので全てのRTM APIで得られたJSONが出力されている。

チャンネル上で発言された内容や別で立ち上げていたクライアントがサーバー側と接続状態を確認するためのping-pongだったりが眺められたりして素朴に面白い。

WordPressのプラグイン中で外部API等へヘッダにAuthorization入れたHTTPリクエストする

WordPressで独自のプラグインを作成しているときに、そのプラグイン中から外部のAPIなどにHTTPリクエストをする際にWordPressでは以下の様なHTTP APIのヘルパー関数が用意されているのでリクエストを発行できる。

例えばGETリクエストであれば、 wp_remote_get() という関数があるのでこちらを利用すればよい。

外部で公開されているAPIなどで認証のためにリクエストヘッダに Authorization を含める必要がある場合もこの関数の第二引数でヘッダを指定することができるのでそこを指定することで解決できる。

<?php
$token = "(API request token)";
$url = "https://api.hogehoge.jp/v1/bar/123.json";
$response = wp_remote_get($url, array('headers' => array('Authorization' => "Bearer ". $token)));

こうすると以下の様なレスポンスが得られる。

array (size=6)
'headers' =>
object(Requests_Utility_CaseInsensitiveDictionary)[610]
protected 'data' =>
array (size=11)
'server' => string 'nginx/1.4.4'
'date' => string 'Sat, 07 Jan 2017 12:47:42 GMT'
'content-type' => string 'application/json; charset=utf-8'
'status' => string '200 OK'
'x-frame-options' => string 'SAMEORIGIN'
'x-xss-protection' => string '1; mode=block'
'x-content-type-options' => string 'nosniff'
'x-request-id' => string '5f1d6608-8e5f-4fee-b584-90ffb230ece9'
'x-runtime' => string '0.056044'
'body' => string '{.....}'... (length=4209)
'response' =>
array (size=2)
'code' => int 200
'message' => string 'OK' (length=2)
'cookies' =>
array (size=0)
empty
'filename' => null
'http_response' =>
object(WP_HTTP_Requests_Response)[606]
(snip)

レスポンスヘッダを始めとして必要な要素は全て得ることができる。

あとは必要に応じてbodyに含まれる文字列要素を操作すればよい。

Google docsのスプレッドシートに特定フィードの更新内容を追記する

業務でふと必要になって時々似た様なGoogle App Script書いているのですが、毎回書き始めるときに「えーっと、どうだったかな」となることが2回続いたのでエントリとして追加。

以下の様な形で書いてみた。そんなに大したことはやっていないのだけれども少し調整したところは、

  • フィード内のアイテムのプロパティで示されるリンク先URLの重複をシートに記録した中に一致したものが無いかの確認をする様にした
    • やり方としてはスプレッドシートの情報をまるっと取得して素朴にループさせてという愚直な方法にした(プレッドシート呼び出しのAPIを繰り返して叩かない)
  • リンク先のURLのバリデーションを入れる
    • RSSフィードが発行されていないページを外部のサービスを使ってRSS化したときに、更新対象以外の要素がフィードに含まれていたとき*1にそれを除外するため

ぐらい。
あとはSlackに通知させる必要があれば、sheet.appendRow([title, url, description]); と書いている辺りで併せて通知させれば良いかと思う。

下のスクリプトを追加して、定期的に呼び出す設定すればRSSが更新される度にシートの最終行に追記されていく様になる。

function parseNewsFeed() {
var sheet = SpreadsheetApp.getActiveSheet();
var sheetData = sheet.getDataRange().getValues();
var feedURL = "(走査対象のフィードのURL)";
var response = UrlFetchApp.fetch(feedURL);
var xml = XmlService.parse(response.getContentText());
var items = xml.getRootElement().getChildren('channel')[0].getChildren('item');
for(var i = 0; i < items.length; i++) {
var title = items[i].getChild("title").getText();
var url = items[i].getChild("link").getText();
var description = items[i].getChild("description").getText().replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'').replace(/(\s+)/g, '');
var urlValidate = /(フィード内のURLのバリデーション用正規表現を書く)/;
if (urlValidate.test(url)) {
if (!isExist(sheetData, 1, url)) {
sheet.appendRow([title, url, description]);
}
}
}
}
function isExist(sheetData, targetCol, value) {
for(var i=0; i<sheetData.length; i++) {
if (sheetData[i][targetCol] == value) {
return true;
}
}
return false;
}

*1:ページサイドにあるカテゴリなどの要素がフィードのアイテムとして更新されてしまうことがあるため

sepiaを利用したテスト中で一時的に機能を無効化する

Node.js向けのwebmockの作成を楽にしてくれるライブラリにsepiaがあります。RubyVCRみたく一度テストで利用する通信内容を記録してモック化され、2回目以降の通信はそのモックを介して行われる様になるというものです。

github.com

sepiaを基本的に使う様にしているのですが、一部分でNockを利用したテストを含んでいた場合に、Nockで書かれたテスト内のみsepiaの動きを止めたいと考えたときにどうすれば良いのかと調べたのでメモとして残しておきます。

得てしてこういうときは世の中に3000人くらい同じことをやろうとする人がいて、3人くらいが実際に手を動かし内1人くらいがどこかに記録を残してくれているに違いないという思想を持っているのでsepiaのリポジトリのissueを眺めているとまさにという内容があったのでそれに倣いました。

github.com

上と同内容のファイルをプロジェクトに置いて、

var foo = require('../../../lib/plugin/foo.js'),
sepia = require('../../util/sepia'),
path = require('path');
describe('......, function(){
it('should be ..... ', function(done){
sepia.fixtureDir(path.join(process.cwd(), 'test', 'sepia-fixtures'));
sepia.enable();
var args = {
token:'xxxxxx'
};
foo.output(args, "123", function(err, output){
err.should.be.false;
sepia.disable();
done();
});
});
});

といった具合にしてみたのだが、これがスマートなやり方なのかいささか不安な点もある。

LINE Notifyで天気を通知させる

LINE Notifyが公開されて少し触ってみたけれどとてもシンプルにLINEで通知を実装することができたので完全に個人用便利ツールとなりつつevacで天気通知させられる様にoutputとして実装してみた。

実装といってもとてもシンプルでAPIにLINE Notify上で取得されたトークンをPOSTするだけなのだけど。

たとえば東京地方の天気の情報をlivedoorRSSフィードから抜いて天気の情報のみをLINE通知させるには

in:
rss:
url: 'http://weather.livedoor.com/forecast/rss/area/130010.xml'
format: '__description__'
filter:
match:
regexp: '[0-9].*の天気は.*'
out:
line:
token: '(取得したLINE Notifyのトークン)'

みたいなのを書いてevacに渡すと

f:id:hideack:20161005095923p:plain

の様に通知させることができる。一日一回どこかで実行させれば週間天気予報を必ず流せるので自分にはとても便利*1

業務だとSlackに通知系を流すことが多いのですが、LINE Notifyだと例えば家庭内で共有したい情報とかを流せたりして便利そうな気がする。

*1:最近、これを連発していて本来は他の人にも便利であるのがベストとは思うのですが、何かしら小さく手を動かしたいと思うときなにより一番に自分が便利なものを作るというのがよいのかなと最近思ふ