読者です 読者をやめる 読者になる 読者になる

スプーキーズのちょっとTech。

SPOOKIES社内のより技工的な、専門的なブログページです。

Seleniumというブラウザ操作を自動でやってくれるツールの紹介

どうも、一人暮らし歴10ヶ月ようやく電子レンジを買いました、いまもんです。
興奮のあまり牛乳を600Wで30秒チンして新品の電子レンジ内を牛乳まみれにしてしまいました。
次はゆで卵でリベンジしてやろうと思います。

さて、面白いツール見つけたのでご紹介します。

Seleniumとは

f:id:spoo-imamoto:20161124042020p:plain

Seleniumはクロスブラウザ、クロスプラットフォームのUIテストツールです。 ブラウザに表示される要素を操作し、取得して想定されうる状態になっているかをテストできます。

macで環境構築しよう

今回はrubyを使うのでgemでinstall かつchromeで動かしたいのでドライバをDLしておきます。

seleniumをinstallする gem install selenium-webdriver

chromedriverをDL https://sites.google.com/a/chromium.org/chromedriver/downloads

DLしてきたchromedriverを/usr/local/bin以下に配置

さあこれで準備が整いました、seleniumを動かしてみましょう。

なにができるのか

Selenium WebDriver — Selenium Documentation

(若干読みづらい)ドキュメントを見てみましょう。 主に要素を検索して中身を書き換えたりクリックさせたりという感じですね、javascriptも実行できるのでn番目の子要素を取得なども可能です!

サンプル

require "selenium-webdriver"

# chrome用のドライバを使う
driver = Selenium::WebDriver.for :chrome

sleep 5 #目視確認用に5秒待つ

# Googleにアクセス
driver.navigate.to "http://google.com"

# `q`というnameを持つ要素を取得
element = driver.find_element(:name, 'q')

# `spookies`という文字を、上記で取得したinput要素に入力
element.send_keys "spookies"

# submitを実行する(つまり検索する)
element.submit

# 表示されたページのタイトルをコンソールに出力
puts driver.title

sleep 5 #目視確認用に5秒待つ

# テストを終了する(ブラウザを終了させる)
driver.quit

参考にした記事 qiita.com

大体こんな感じです。 わかりやすくていいですね。

ちなみにspookiesと検索していますが、titleはgoogleのままなのでgoogleと出力されます。 実行したら裏でChromeが立ち上がるので切り替えて目視確認してみてください。 いい使い道探してます!アイデアあったらどんどん記事にしてください!

twilioとAWS Lambdaで自分に電話をかけてみた

こんにちは。

最近体重の増加が止まりません。秋ですねー。 逆に電話の着信が減った気がします。 前者を治す方法は皆目検討がつきませんが、後者なら解決できそうです。

そこで、 「slackでコマンド命令を与えると、自分の電話が鳴ってカラオケに誘われる。」 ってのをやってみたいと思います。

twilio

twilioとは「クラウド通信企業」であり、プログラムを使って簡単に電話がかけられるAPIを提供している会社です。 本社はアメリカにあり、日本ではKDDIウェブコミュニケーションズさんが代理店としてサービスを提供しています。

f:id:spookies-nishimura:20161123041537p:plain

お試し期間ならクレジットカード登録もいらないので、気軽に試せますね。

API利用するにあたって必要なもの

  1. twilioアカウント登録
  2. 050電話番号の取得
    • この番号からかかってくるようになります。(お試しでも取得できました)
  3. 認証用AccessTokenの確認
    • コンソールダッシュボードに表示されているのでコピっときます。

サンプルプログラムを試す

ドキュメントもしっかり整備されてます。

今回は、AWSのLambdaにプログラムを置きたかったので、Nodeで書くことにします。 https://jp.twilio.com/docs/quickstart/node/programmable-voice
↑に記載された通りに書き換えてローカルPC上で動かすだけで、電話がかかってきました。

書き換えたのは以下だけ

  • accountSid
  • authToken
  • to
  • from

実に簡単。

音声レスポンス命令の作成とアップロード

電話がかかってきた際に、特定の文言を喋らせたり、音楽データを流したり、通話を録音したりすることができます。 それを制御するのが、TWIMLと呼ばれるXMLです。

<Say>: 発信者にテキストを読み上げます。
<Play>: 発信者に音声ファイルを再生します。
<Record>: 通話や通話の一部を録音します。
<Gather>: 発信者がダイヤルした数字を取得します。
<Dial>: 相手の電話番号またはカンファレンスに電話をかけ、発信者を接続します。
<Sms>: 通話中に SMS メッセージを送信します。

自分で設定した音声レスポンスを使う場合はアップグレードが必要みたいです。 管理画面から、クレジットを登録して、2000円だけ入れてみました。

f:id:spookies-nishimura:20161123041637p:plain

今回は カラオケに誘われる 為に、以下の内容のxmlファイルをサーバ(amazon S3)にアップロードし、公開設定を行いました。

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice">let's go karaoke</Say>
</Response>

プログラム書き換え

xmlのアップロードが完了したので先程のプログラム内の url 箇所を S3にアップしたxmlの公開urlに書き換えます。

client.calls.create({
    url: 'https://xxxxxx.xxxxxx/xxxxxx.xml',

Debugger

プログラムを動かしたら電話はかかってきたのですが、 「アプリケーションエラーが発生しました」とアナウンスされました。

おやおや?と悩んでいたら、twilioの管理画面にDebugger機能があったので見てみました。

f:id:spookies-nishimura:20161123041748p:plain

f:id:spookies-nishimura:20161123041801p:plain

むむ?POSTでアクセスに行っているではないか・・。 デフォルトではこのような挙動になるようです。

プログラムを修正して、methodにGETを指定します。

client.calls.create({
    url: 'https://xxxxxx.xxxxxx/xxxxxx.xml',
    method: 'GET',
    ...

これで無事に読み上げてくれました。

AWS Lambdaにプログラムを配置

Lambdaは最近よく聞くサーバレスアーキテクチャ。 リクエスト待受するために常にサーバを立てておくのではなく、必要な時に呼び出されて実行プログラムが起動する仕組み。イベントドリブンな感じ。

今回は、

  • API gateway
  • Lambda

を組み合わせ、作成したURLが叩かれた時に先程のプログラムが動くようにしました。

Lambda関数

Lambda関数として動かす為に、先程のプログラムに handler の関数名を指定します。

exports.handler = function(event, context) {
    // プログラム
    ...
}

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html

NodeプロジェクトとしてZip圧縮

Lambdaでは、node_modulesも含めてアップする必要があるため、zipに固めます。

$ zip -r index.zip index.js node_modules

Lambdaにアップロード

  1. Select blueprint templateのようなものですかね。今回はAPI Gatewayと連携するので「api-gateway-authorizer-nodejs」を選びました

  2. Configure triggers API Gatewayを選択

  3. Configure function

Name: callme
Runtime: Node.js 4.3
Code Entry Type: Upload a .ZIP file
    先程固めたzipをアップロード
Hander: index.hander // 先程のプログラム(index.js)をhandlerの名でexportしたので。ファイル名やhandler名が異なってるとエラーになるので注意。
Role: create new role from template
Role Name: test
残りはデフォルト

API Gateway

  1. APIを作成して、GETをLambdaで作成したcallmeファンクションと紐付け
  2. APIのデプロイで、公開(stageは新規作成) するとURLが手に入ります!!

slackのコマンドでプログラムを呼び出そう

さて、呼び出しURLが決まったので、これを直接呼び出してもいいんですが、 どうせならSlackから特定のコマンドを入力した時に、呼び出すようにしてみましょう。

slackのAppDirectoryで Slash Commands を選択。

f:id:spookies-nishimura:20161123042025p:plain

叩いてみたら 電話かかってきましたー

まとめ

少し今更感もありますが、twilioもLambdaも興味あったけど使ったことがなかったので遊んでみました。

サービスでアカウント登録後に電話がかかってきて、音声で流れてきたコードを入力して完了という流れがたまにありますが、twilio使ってるんですかね?

コールセンターや受付サービスなどたくさんの事例があるみたいです。
サーバ監視で障害発生したら電話で叩き起こすってのもよくありですが、いいですね。
遅刻メンバーを叩き起こす・・。

さて、寒くなってきましたが、皆さん風邪にはお気をつけください。
では一人カラオケへ行こうかな。いやジムですかね。
それでは、失礼します。

Three.js を用いてインタラクティブなコンテンツを作ってみよう

 こんにちは、モリタです。

最近、Web上で3Dグラフィックを描画できるjsライブラリであるThree.jsに魅了されています。

f:id:moritanian:20161014213058p:plain

three.js では、以下のようにシーンオブジェクトにライトオブジェクト、ボックスオブジェクトなどを追加することで3D空間をつくりだすことができます。

$(function(){
    // create scene
    var scene = new THREE.Scene();
    // create camera
    var width  = 600;
    var height = 400;
    var fov    = 60;
    var aspect = width / height;
    var near   = 1;
    var far    = 1000;
    var camera = new THREE.PerspectiveCamera( fov, aspect, near, far );

    // マウスでうごかせるようにする
    var controls = new THREE.OrbitControls(camera);
    
    // set renderrer
    camera.position.set( 0, 0, 50 );
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    $("body").append( renderer.domElement );
    
    // add light
    var directionalLight = new THREE.DirectionalLight( 0xffffff );
    directionalLight.position.set( 0, 0.7, 0.7 );
    scene.add( directionalLight );

    // create box
    var geometry = new THREE.CubeGeometry( size_x, size_y, size_x);
    var material = new THREE.MeshPhongMaterial( { color : Math.random() * 0xffffff } );
    var mesh = new THREE.Mesh( geometry, material );
    mesh.position.set(pos_x,pos_y,0);
    scene.add( mesh );
        
    // rendering
    controls.update(); 
    renderer.render( scene, camera );

    // オブジェクトを回転させる
    ( function renderLoop () {
    requestAnimationFrame( renderLoop );
     mesh.rotation.set(
          0,
          mesh.rotation.y + .008,
          mesh.rotation.z + .008
        );
    renderer.render( scene, camera );
  } )();
});

three.jsの公式サイト には素晴らしいデモが沢山紹介されています。 早速いくつか紹介してみましょう

Ellie Goulding – Lights

f:id:moritanian:20161014173126p:plain

イギリスのシンガーソングライター、エリー・ゴールディングの曲に合わせて、光の球が輝きます。 光はユーザのクリックに反応し、インタラクティブなミュージックライブ体験ができます。

WorldPopulation

f:id:moritanian:20161014173423p:plain

年別に世界の人口密度が視覚的にわかるデモです。地球をスクロールすることで回転することができます。 学術的なコンテンツにもこういった技術が多用されていく予感がします。

The Boat

f:id:moritanian:20161014174810p:plain

スクロールすると背景のアニメージョンとともに、イラストと文章が表示されます。インタラクティブな「動くマンガ」といった感じです。新しいwebマンガのスタイルになりそうな予感がします。

The Fallen of World War Ⅱ

f:id:moritanian:20161014180618p:plain

通常の動画の中にインタラクティブタイムというものが設けられており、この間はユーザがグラフを選択して、詳細を見ることができます。 内部でどう処理されているのか気になります!

3D図書館

自分でもインタラクティブな3Dコンテンツを作ってみよう、ということで、3D図書館を作ってみました!

f:id:moritanian:20161014185339p:plain

外部のwebAPI から今月のマンガの新刊を取得して、表示するようにしています。 本にカーソルを合わせると、本が飛び出し詳細情報が表示され、クリックするとアマゾンの商品ページに飛ぶようにしました。 たまに商品画像が取得できないものがあり、この場合、デフォルトの画像を表示するようにしたかったのですが、うまい方法が見つかっていません。 画像サイズが0かどうか調べればよさそうですが、画像を取得するのにタイムラグがあるため、画像を張り付けた平面オブジェクトを作成し、画像のロードが完了したタイミングで画像サイズを調べるコールバックを設定する、なんてまわりくどいやり方しか思いつきません。どなたかいい方法ご存じないですかね?

さらに最近のVRブームのもと、webVRなんてのも登場しています。webにVRを持ち出す必要性があるのかな、という気もしますが、箱スコなどスマホ向けのVRコンテンツに利用価値があるみたいです。URLだけでコンテンツを共有できれば手軽です。Three.js のサンプルにはwebVRに対応したサンプルもいくつかあるので、今後試したいなと考えています。

以上、Three.js の紹介でした!

【LINE BOT API】食べるなら動け、動かぬなら食べるな【カロリーと運動とお店】

こんにちは。登山と美味しいものとお酒が好きです、高野です。

f:id:spookies-takano:20160606235632j:plain

(2015.9.2 槍ヶ岳山頂にて。一緒に上ってくれる方を大募集です!)

海か山かと聞かれれば山派でして普段は波には乗らないのですが、Botの波がやってきたということで少し乗ってみることにしました。

Microsoft Bot Framework、Facebook Messenger Platform、LINE BOT API と、Bot関連の大きな波が来てから早2ヶ月以上が過ぎましたが、まだ乗れますよね?

【LINE】「LINE BOT API Trial Account」の追加募集を開始

ということで、追加募集に参加してみました。

各所にすばらしい関連記事はまとまっていますが、備忘録も兼ねて作成までの流れを書きます。

f:id:spookies-takano:20160606235805p:plain

LINE BOT API では何ができる?

APIベースのメッセージ送受信

あなたのアカウントに対して友だちになってくれたユーザーにメッセージを送受信することができます。 メッセージのやり取りはすべてAPIベースとなっています。

f:id:spookies-takano:20160606235830p:plain

リッチメッセージ

サイトのサムネイルとリンクを含んだメッセージを配信することができます。 サイトへの誘導効果が大きく見込める機能です。

f:id:spookies-takano:20160606235848p:plain

利用条件

料金 友だち数上限
無料 〜50人まで ※トライアルのため友だち数を制限しています。

Botを作ろう

ところで、社内ではジムブームというものがあったようですが、いまいちピンときていません。(最初に会員登録した。2ヶ月行っていない

日頃の運動ももちろん大事ですが、食事から生活を整えていくことも大事ではないでしょうか。現代人は、常に食べ過ぎ飲み過ぎだとも言われています。外食が多い生活だと尚更ですよね。

そんな日常を背景に、スプーくんという救世主(Bot)は現れました。 私たちの食べたいものを伝えると、

  • 目安カロリー
  • カロリー消費に必要な運動量
  • 関連するおすすめのお店

を教えてくれます。

食べた量に対して自分がどれだけ運動しなければいけないのかを把握できれば、自分にあった運動量から食べる量も調節できるというわけです。

決して運動したくないわけではありませんよ。

アカウント作成

基本的なユーザー情報と、Botの名前・画像を登録します。

Botは、Spookiesのマスコットキャラである「スプーくん」をイメージに、お腹すいた感じの名前にしておきます。(特に腹ペコキャラではない)

以下は、登録後の画面イメージ。

f:id:spookies-takano:20160606235931p:plain

  • Channel ID
  • Channel Secret
  • MID

上の3つをAPIでも使います。

サーバー用意

今回は、Heroku上にアプリを作成することにします。

固定IPが必要になるので、 HerokuのアドオンであるFixieをインストールしておきます。

(無料ですが、事前にHerokuへのクレジットカード登録が必要です。)

コマンドからインストールする場合は以下。アドオンの追加とともに、Outbound IPsが出力されます。

$ heroku addons:create fixie:tricycle

Outbound IPsは、

「HerokuのDashboad」>「作成したアプリの詳細ページ」>「Add-ons」>「Fixie」

と辿っていっても確認可能です。

Server IP Whitelist登録

Outbound IPsを確認できたら、「Server IP Whitelist」にIPを追加します。以下では、Fixieで確認した2つのIPを登録しています。

f:id:spookies-takano:20160606235959p:plain

Callback URL登録

また、「Basic Infomation」にCallback URLも登録しておきます。

「HerokuのDashboad」>「作成したアプリの詳細ページ」>「Settings」 > 「Domains」 > 「Heroku Domain」

でURLが確認できます。

その際、SSL通信必須でポート番号の指定も必要なことに注意します。

https://[Heroku Domain]:443/linebot/callback.php

f:id:spookies-takano:20160607000029p:plain

(今回は、PHPで実装しました。)

Channel ID等をHerokuへ登録

アカウント作成時に確認した以下の項目は、コード内部に埋め込まずHerokuに環境変数として外部化しておく。

  • Channel ID
  • Channel Secret
  • MID

「HerokuのDashboad」>「作成したアプリの詳細ページ」>「Settings」 > 「Config Variables」>「Reveal Config Vars」より設定

f:id:spookies-takano:20160607000114p:plain

さて、準備は整ったのであとはAPIを実装していきましょう。

API作成

基本的に、以下の項目は固定となります。

||| |API URL | "https://trialbot-api.line.me/v1/events" (POST)| | toChannel |"1383378250"(固定値) | | eventType | "138311608800106203" (固定値)|

(詳細:Getting started with BOT API Trial)

あとは、CallbackURLで指定した箇所にAPIを実装したファイルを配置するだけになります。

しかし。。。。

callbackが返ってこない

f:id:spookies-takano:20160607000137j:plain

(怒涛のKS(既読スルー)。無意味に投げ過ぎ。)

期待した応答がBotから無い場合、主な原因は以下の通りです。

  • Callback URLの反映が遅れている
  • Server IP Whitelistの反映が遅れている

これらは、設定から反映までに数十分から1日程度かかることがあるようです。ゆっくり待つのも一手。

まずはレスポンスを確認。

$ curl -H "X-Line-ChannelID: [CHANNEL ID]" -H "X-Line-ChannelSecret: [CHANNEL SECRET]" -H "X-Line-Trusted-User-With-ACL: [MID]" "https://trialbot-api.line.me/v1/profiles?mids=[MID]"
{"statusCode":"427","statusMessage":"Your ip address [XXX.XXX.XX.XX] is not allowed to access this API. Please add your IP to the IP whitelist in the developer center."}

この場合は、Heroku・FixieのproxyURLが設定できていませんでした。

  • CURLOPT_HTTPPROXYTUNNEL
  • CURLOPT_PROXY
  • CURLOPT_PROXYPORT

上記を踏まえて実装を見直すと、

{"contacts":[{"displayName":"はらぺこスプーくん","mid":"[MID]","pictureUrl":"http://dl.profile.line-cdn.net/xxxxxxxxxxx","statusMessage":""}],"count":1,"display":1,"pagingRequest":{"start":1,"display":1,"sortBy":"MID"},"start":1,"total":1}

問題なさそうですね。

その他では、ログを埋め込んだり、herokuのログを確認しつつ進めると分かりやすいです。

 $ heroku logs -t

f:id:spookies-takano:20160607000204j:plain

(オウム返しで試してみた)

「きたー(嬉)」

完成

f:id:spookies-takano:20160607000218j:plain

  • 食べ物をつぶやくと以下の情報が返ります。
    • 類似の食べ物のカロリー値
    • カロリー消費に必要な運動量
    • つぶやいた食べ物のありそうな店一覧(スプーキーズ京都本社より500m圏内のみ)

これで、健康的な身体まっしぐらですね!

腕立ての回数は、最低限やってくださいね!(筋トレはカロリー消費が少ない。。)

f:id:spookies-takano:20160607000238p:plain

上記のQRコードからスプーくんとともだちになれます。

よろしければ一緒に昼食へ連れて行ってあげてください。

制限

現行では、以下の制限があるのでご注意ください。

  • Line Bot API Trialでは友達登録50人まで。
  • Fixieのtricycleプランではリクエストは500/月まで。

API

その他APIとしては、以下の2つを使用させていただいています。

また、カロリー消費に必要な運動量は、カラダカラ消費カロリーの参考例にて[男、20~29歳、65kg]の値を基に計算させていただいています。

どうなるBotの波

最近でも、カスタマーサポートをBotで置き換えて人件費が削減されたという話をちらほら聞くようになりました。 1企業に1Botが当たり前になれば、Botを通してのブランディングも当然起こりうるかと思います。 ゆるキャラブームから企業でもいろんな個性を持ったマスコットキャラを推す流れができましたが、Botの普及でより個性的なBotが登場してくることもあるでしょう。 これからBotを取り巻く環境はますます成熟していくでしょうが、多様なBotが世に出ていくのが楽しみですね!

社内でも以前よりSlackでHubotを運用していますが、外と繋がるBotでも面白い試みをしていきたいところです。

みなさま、健康にはお気をつけ下さいませ。

参考

関連

Microsoft Bot Framework

Your bots — wherever your users are talking.

f:id:spookies-takano:20160607000317p:plain

Facebook Messenger Platform

900 million people are on Messenger Now you can interact with them where they already are

f:id:spookies-takano:20160607000343p:plain

LINE BOT API

BOT API Trial AccountではあなたのサービスとLINEユーザーの 双方向コミュニケーションを可能にするAPI開発をお試しいただけます。

f:id:spookies-takano:20160607000500p:plain

Jupyter notebookの布教

実験のグラフ書いたりするツールとしてJupyterがとっても便利だよ、コードが書けるならExcelなんて使うのやめようよ、という話をします。

あんまり業務には関係ないです。 Jupyter notebookを布教するためのステマ記事です。

Excelでグラフを描くのはつらい

Excelは、汎用性が高くて、とりあえず誰でも使える便利なツールであるが為に、多くの大学生はグラフを書くのにExcelを使っているようです。 しかし、Excelは科学技術分野で使うためのアプリケーションではないので、Excelのグラフ描画機能は微妙だと思います。

データを単に線形グラフに起こせばいい、とかだとまだマシなんですが、「一つの実験データに対して、線形グラフと片対数グラフを書く。あ、実験データ増えたから二つのグラフの範囲を変更して…」とかなると、だんだんつらい感じがしてきます。 対数グラフを書くのとか、控えめに言って大変すぎでは、と思ってしまいます。

また、学部生の実験だとあんまり無いですが、測定機器から吐き出される大量のtsvファイルから、似たようなグラフをめっちゃたくさん書く、ということもあります。この作業は単純作業であり、同じ処理をするだけなので自動化できますが、これをExcelでやるのは大変です。VBAで書くの…?

そこでJupyter notebookの出番、というわけです。

Jupyter notebookとは

この記事を読めば、Jupyter notebookのなんたるか、どう使うのか、というのはだいたい分かると思いますが、結局のところ、「インライン画像表示機能を備えた、超強力なREPL」ということになります。

次のリンクと画像は、私が実験中に書いたグラフ(とそのコード)になります:こんな感じ

f:id:sancha-san:20160523185928p:plain

見た感じでだいたい分かると思いますが、灰色の枠の中にコードを書いて実行すると、次の行から、出力が表示されていきます。

もちろん、コードを修正して、もう一度実行することもできます。

Jupyter notebookの便利なところは、標準出力だけではなく、グラフも同じ画面に出力できる、ということです。 これによって、グラフを描画→データを追加/修正→グラフを再描画の流れが容易になります。Excelとは比べ物にならないぐらい快適です。

また、Pythonで書けるので、繰り返しや分岐や関数などを駆使して、大量のデータを少ない行数で処理したり、mapやreduceを使って、カッコよくデータを加工したりできます。

外部のtsvファイルを読み込んで処理する、といった処理も簡単です。 また、PythonにはNumpyやSciPyといった科学技術計算用の優れたライブラリがあるので、重い計算でも比較的高速に行うことができます。

さらに、このJupyterのファイルは、githubやgistにupするだけで、公開することができます。 実際に、上記のリンクは、私のPCに保存していた.ipynbファイルをgistにアップロードしたものです(https://gist.github.com/chart-linux/51c64775d07658a4f63a2722eb82d630)。班員と実験結果を共有したりするのに便利です。

また、Excelしか読み書きできない班員がいたとしても、Pythonのライブラリpandasを使っていれば、to_excelメソッドを用いることで、実験データをExcelに出力することもできます。もう班員に合わせて無理にExcelを使う必要もないわけです。

まとめ

Excelでグラフを描くのはやめよう。Jupyter notebookを使おう。

Node.jsについて(紹介・導入編)

f:id:spookies_kuriyama:20160415094819p:plain

前置き(Node.jsの前に)

プログラミング言語について

エンジニアの皆さんは、どのプログラミング言語をお使いでしょうか?

よく挙げられるのがNativeコードを吐き出すCPP、Objective-C、バイナリコンパイル型のC#、Java、 LLのPython、PHP、Ruby、Perlあたりだと思います。 最近だと、SwiftやGo lang等も候補にあがりそうです。

それでは、JavaScriptはどうでしょうか?
がちがちのサーバサイドアプリケーションエンジニアや、 インフラ・ネットワーク・データベース・セキュリティ専業エンジニア以外のアプリケーションエンジニアの大多数の方は、この言語を使って、コードを書いた事があると思います。

「JavaScriptはプログラミング言語じゃないよ」という方もいらっしゃるかもしれませんが、 私は、JavaScriptは立派なプログラミング言語であり、非常に汎用的、かつ将来性のある言語だと思いますし、 一番好きなプログラミング言語でもあります。

最近では、JavaScriptを中心に組むWeb Front-end Engineerという職種もあり、 もりもりJavaScriptを駆使して、Webのフロント側を作っている専業の方もいる程です。

JavaScriptについて

元々のJavaScript

そもそもJavaScriptは、かなり昔にNetscape社がクライアントのWebブラウザ内で、 決められた動的な制御を実行するために、策定された言語です。

当初はクライアントマシンも非力で、実行エンジンも良いものではなく、 APIも少なく、使いにくかったので、かなり限定的な使い方しか出来ませんでした。

しかし、ブラウザ内で動くというのはかなり画期的で、Netscapeの動きを見て、 ライバルのMicrosoftがInternet Explorer(IE)に慌てて同様の機能を盛り込んでリリースしました。
※ 余談ですが、IEに盛り込んだJavaScriptは、正式名称はJavaScriptという名称ではなくJScriptという、JavaScriptの方言の1つです

しかし、IEのJScriptが独自拡張や、互換性のない記述をしていたり、 言語仕様が一致しない等、利用する上で、かなりの苦労がありました。

Mozilla主導による言語仕様の統一化の動き

そのようなカオスなJavaSciprtの世界に統一化の動きを見せたのが、Firefoxの開発元Mozillaです。 Mozillaは仕様化されていなかったJavaScriptの統一化を目指し、 JavaScriptの統一仕様として、GoogleやAdobe等とともにECMAScriptを主導していきました。

今日提供されているブラウザでは、ECMAScriptに準拠して作られており、 JavaScriptの記述方法やAPIにもかなりの互換性があります。
※採用しているECMAScriptのVersionの違いはあります

また、クライアントマシンのマシンパワー向上、JavaScript実行エンジンの最適化、 AJAXによる、非同期通信を用いた動的なページの更新も、JavaSciprtの地位を押し上げました。

Node.jsへの流れ

このようにJavaScriptは使いやすくなり、非常に汎用的、将来性のある言語となりました。

汎用的や将来性につきましては、別に機会がありましたら記載しようと思いますが、 今回は、クライアントサイドで動くJavaScriptではなく サーバサイドあるいは、スタンドアローンアプリとしても動くNode.jsについて、ご紹介しようと思います。

Node.js

Node.jsとは何か

https://nodejs.org/en/

Node.jsを一言で言うと、JavaSciprt言語を駆使してコードを動作させるスクリプティングエンジン環境です。要は、LLと非常に似ています。というよりも、ほぼLLです。
Node.jsならではのメリットを挙げますと、

  • 記述言語が、JavaScriptなので、Web Front-end EngineerやDesignerの人も修得が用意
  • 既存のJavaScriptが流用可能
    • 但し、条件ありで、元々の記述次第で、流用度がかなり変化します
    • デフォルトのAPIがかなり豊富
      http://nodejs.jp/nodejs.org_ja/api/
      ※ 暗号化(Crypto)、ネットワーク(Net)、圧縮伸長(Zlib)等、他のLLではオプションや拡張扱いのものもデフォルトで利用可能
      ※ Web APIの待ち受けも、Apache・IIS・nginx等のWebサーバなしで、簡単に作成可能です
  • サーバサイドでも動かせるが、クライアントサイドで、スタンドアローンアプリやスクリプトツールとしても動かせる
  • 公式純正のパッケージマネージャーがデフォルトで提供(Node Package Manager[npm])されていて、かなり高機能かつ使いやすい
    • 他のLLには公式純正かつデフォルトで付いてくるパッケージマネージャーは存在しない
      (デファクトスタンダードならある。Python pip、PHP Composer、Ruby Rubygems、Perl CPAN)
  • 豊富なライブラリやフレームワークがある
    • Electron(旧ATOM Shell)、nw.js(旧Node-Webkit)、Express、Meteor、moment.js(これはNode.js専用という訳ではない)、node-fs-extra(スタンドアローンアプリ作成に非常に使える)等、他多数

ライブラリやフレームワークについては、機会があれば、またご紹介したいと思います。

Node.jsダウンロード

以下の公式サイトにアクセスして下さい。

Download | Node.js

Download | Node.js

Windows・Macなら公式バイナリやインストールファイルを落として、実行するだけで簡単に動作可能です。
Linuxのバイナリは、最近のディストリビューションなら、大抵展開して、パスを通せば実行可能です。
ソースコードも提供されていますので、Mac以外のUnix環境、FreeBSDやNetBSD等でも、ビルドできます。
※ 公式からのダウンロード以外にも、Homebrew、yum、aptget、Nuget経由でのインストールする方法もあります

Node.jsインストール

前提

ここからは、Mac OS X環境前提で説明していきます。

インストール

上記でダウンロードしたMac用のインストールファイルをクリックして、インストール

Download | Node.js

Mac OS X Installer (.pkg) 64-bit
node-v5.10.1.pkg

長期運用に使う場合は、LTS版がおすすめですが、大抵は、最新のStable版で良いと思います。今回も最新のStable版 v5.10.1で試します。

後は、以下の順に画面を進めていけば導入可能です。

f:id:spookies_kuriyama:20160413095710p:plainf:id:spookies_kuriyama:20160413095715p:plainf:id:spookies_kuriyama:20160413095720p:plainf:id:spookies_kuriyama:20160413095724p:plainf:id:spookies_kuriyama:20160413095728p:plain

最近のバージョンは、インストール時に以下の場所にPATHを通してくれるので、そのままターミナルで実行できます。

Node.js was installed at

   /usr/local/bin/node

npm was installed at

   /usr/local/bin/npm

Make sure that /usr/local/bin is in your $PATH.

ターミナルで、以下コマンドを入力すると、バージョン情報が表示されると正しく導入されています。  

MacBook-Pro:~ spookies$ node -v
v5.10.1
MacBook-Pro:~ spookies$ npm -v
3.8.3

次に、http簡易サーバを作ってみます。

MacBook-Pro:~ spookies$ mkdir -p src/nodejs/
MacBook-Pro:~ spookies$ cd src/nodejs
MacBook-Pro:~ spookies$ vim http.js
MacBook-Pro:~ spookies$ cat http.js

const http = require('http’);

const hostname = '127.0.0.1’;
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hoge Nyanko\n’);
  console.log('Client request!');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

MacBook-Pro:~ spookies$ node http.js 
Server running at http://127.0.0.1:3000/

最後のコマンドで、作成したソースをNode.jsで実行すると、
「Server running at http://127.0.0.1:3000/」と表示されます。
これは、127.0.0.1のホスト名と、ポート3000番でサーバを待ち受けており(待機状態で)、
クライアント側(ブラウザ)からアクセスすると、server側の処理が実行されます。

f:id:spookies_kuriyama:20160413095732p:plain

クライアント側(ブラウザ側)には、 200番ステータス(リクエスト成功)+テキストのコンテンツ属性+「Hoge Nyanko」 という文字列が返されます。

また、今回は、ターミナルから、直に実行しましたが、Service化やDaemon化、 nginx等のWebサーバと連携し、 ポートやServer側での処理を駆使すると、同時に幾つものリクエストを捌く事も出来、
Node.jsのサーバから、Node.jsのサーバも初期で提供されているAPIでリクエスト・レスポンスが可能ですので、 まさしく名前の通り、ノードとして振る舞わせる事も出来ます。

サンプルソース2(スタンドアローンスクリプト)

上記では、サーバサイドの例を記載しましたが、次はターミナルでコマンドを実行すると、 クライアント内で、特定の処理を実行するコードを例としてあげてみます。

以下は、コマンドライン引数で、バックアップ元のディレクトリ・バックアップ先のディレクトリ・ファイル名の3つを指定して、zip圧縮する例です。

サードパーティ製のモジュールとして、zip圧縮するモジュールもありますが、 ここでは、簡略化の為、MacのOSコマンドzipで代用します。

ソースコードは以下となります。

$ cat backup.js

const fs = require('fs');
const path = require('path');

const process = require('process')
const child_process = require('child_process');

function isDirectory(pathName) {
    // ファイル存在チェック
    var isExist = fs.existsSync(pathName);
    if(isExist) {
        // ディレクトリかどうか
        var isDir = fs.statSync(pathName).isDirectory();
        if(isDir) {
            return true;
        } else {
            console.log(pathName + ' is not directory');
            return false;
        }
    } else {
        console.log(pathName +' is not directory or file.');
        return false;
    }
}

// from -> to/zipにzip圧縮する
function backup(from, to, zip){
    var fromDir = path.dirname(from);
    var fromBase= path.basename(from);
    console.log('cd ' + fromDir);
    process.chdir(fromDir);
    // MacでのOSコマンドzipのコマンド文を生成(多分、最近のLinuxでも使える)
    var exeCom = 'zip -r ' + to + '/' + zip + ' ' + fromBase;
    console.log('[Exec] ' + exeCom);
    var result = require('child_process').exec(exeCom, function(err, stdout, stderr) {
        if (!err) {
            console.log(stdout);
        } else {
            console.log(err);
        }
    });
    return true;
}

// 引数チェック
if (process.argv.length != 5) {
    console.log('Usage: ' + process.argv[0] + ' ' + process.argv[1] + ' from_directory_name to_directory_name zip_file_name');
    console.log('For example: node ' + process.argv[1] + ' ~/dat/from =/dat/to backup.zip');
    return;
}

// 引数の内容を受け取る
var from = process.argv[2];
var to = process.argv[3];
var zipName = process.argv[4];

if(isDirectory(from) && isDirectory(to)){
    backup(from, to, zipName);
} else {
    console.log('Error!');
}

実行は、以下のコマンドで実行して下さい。

MacBook-Pro:~ spookies$ node backup.js from_directory_name to_directory_name zip_file_name

実行すると、「from_directory_name」ディレクトリをzip圧縮し、 「to_directory_name」ディレクトリに「zip_file_name」の名称でzip圧縮します。

まとめ

このようにNode.jsは、今後も将来性があり、サーバサイド・クライアントサイド問わずプログラムが作れますので、 皆様も是非活用してみてください。
Let's enjoy Node.js!

Preztoでzshをいい感じに使う

Preztoとは

Github: Prezto — Instantly Awesome Zsh

Preztoはインストールするだけでzshがいい感じに使えるようになるフレームワークです。 prezto/modulesを見ると分かるのですが、様々なモジュールがありそれぞれ関連したコマンドがいい感じに使えるようになります。

例えばrailsのモジュールをロードした場合以下のようなエイリアスコマンドが利用できるようになります。

alias ror='bundle exec rails'
alias rorc='bundle exec rails console'
alias rordc='bundle exec rails dbconsole'
alias rordm='bundle exec rake db:migrate'
...

類似のフレームワークとしてoh-my-zshがありますが、Preztoの方が後発でより軽量でシンプルな仕様になっています。 oh-my-zshが重いと感じてPreztoに乗り換える人が散見されますが、Githubを見るとoh-my-zshの方が活発みたいですね。

f:id:ashida_spookies:20160408165726p:plain

インストール方法はprezto#installationにある通りです。

使い方

インストールが済んだら基本的なコマンドは既に使えるようになっています。 例えば、よく使うものでいうと

# ls系
alias ll='ls -lh'
alias la='ll -A'

# オプション無しでサブディレクトリも作成、さらに作成したディレクトリに移動する
function mkdcd {
  [[ -n "$1" ]] && mkdir -p "$1" && builtin cd "$1"
}

などなど... (詳しくはprezto/modules/utility)

カスタマイズ

モジュール

  1. prezto/modules を眺めて自分の必要なモジュールを確かめます。 (それぞれにREADME有)
  2. ~/.zpreztorc を編集してそのモジュールをロードします。

例: ruby, rails, homebrewモジュールをロード

zstyle ':prezto:load' pmodule \
  'environment' \
  'terminal' \
  'editor' \
  'history' \
  'directory' \
  'spectrum' \
  'utility' \
  'completion' \
  'prompt' \
  'ruby' \
  'rails' \
  'homebrew'

これでターミナルで新しいタブを開けばそれぞれのコマンドが使えるようになっています。 簡単!

テーマ

  1. prompt -l でテーマの一覧表示。
  2. prompt -p themename でサンプルを表示。
  3. 気に入ったものがあれば ~/.zpreztorc を編集
zstyle ':prezto:module:prompt' theme 'cloud'