すぎゃーんメモ

2013-03-14

FluentdとGrowthForecastを使って自分の行動をロギング・可視化する

の記事のように、FluentdGrowthForecastを使ったロギングって面白そうだなーと思って自分でもやってみた。


アプリケーション毎のアクティブな時間を取る

まず考えたのはこれ。キーイベント発火回数ほど細かくなくても、「アクティブにしている時間の割合」が取れたらそれはそれで良いかな、と。

1秒ごとにアクティブアプリを調べてロギングしていく。Mac OS Xにおいてアクティブアプリケーションを調べるならAppleScriptが簡単。

name of (info for (path to frontmost application))

これだけでアクティブにしているアプリケーション名が取れる。

もしくは

tell application "System Events"
    name the first process whose frontmost is true
end tell

といった形でプロセス名として取ることもできるらしい。以下のように変形することもできる。

tell application "System Events" to name of first process whose frontmost is true

なんにせよAppleScript一行で取れるので、ワンライナーコマンドで書ける。fluentdconfig

<source>
  type exec
  command osascript -e 'name of (info for (path to frontmost application))'
  keys application
  tag application
  run_interval 1s
</source>

のように書けば1秒ごとにAppleScript経由でアクティブアプリケーション名がemitされる。これをdatacounterで1分ごとに集計。

<match application>
  type datacounter
  count_key application
  count_interval 1m
  aggregate all
  tag counted.apps
  pattern1 iTerm iTerm.app
  pattern2 Emacs Emacs.app
  pattern3 Chrome Google Chrome.app
  pattern4 Twitter Twitter.app
</match>

これで主要なアプリがどれくらいアクティブになっているかがだいたい取れる。ので、そのままgrowthforecastに投げてしまう。

<match counted.apps>
  type growthforecast
  gfapi_url http://gf.*****.my.server/api/
  service sugyan
  section apps
  name_keys iTerm_percentage,Emacs_percentage,Chrome_percentage,Twitter_percentage,unmatched_percentage
</match>

f:id:sugyan:20130314011501p:image

こんなかんじでそれぞれのアプリの単位時間あたりのアクティブ率の変化がグラフで取れて、複合グラフにすると

f:id:sugyan:20130314011502p:image

というかんじで「あぁ、このへんはずっとEmacsで何か書いてて、このへんはずっとTwitter見てるな」とかが分かる。


Emacsの文字入力数を取る

Emacsで色々作業する場合に、どんな言語をどれくらい書いているか…じゃなくても、major-modeごとに集計できたら良いかな、と。

post-self-insert-hookで文字入力した際にhookして非同期プロセスからfluentdに送信する。

(defun my-insert-hook ()
  (start-process "post-fluent" nil "curl" "-X" "POST" "-d" (concat "json={\"mode\":\"" (symbol-name major-mode) "\"}") "localhost:9880/emacs"))
(add-hook 'post-self-insert-hook 'my-insert-hook)

上記のようなelispEmacsの設定に書いておけば、文字が入力されるごとにそのバッファmajor-mode名がcurlを使ってHTTP経由でemitされる。

start-process関数は非同期にコマンド実行されるので連打していても動作が重くなったりしない。と思う。

fluentdconfig

<source>
  type http
</source>

<match emacs>
  type datacounter
  count_key mode
  count_interval 1m
  aggregate all
  tag counted.emacs
  pattern1 perl cperl-mode
  pattern2 ruby ruby-mode
  pattern3 javascript js2-mode
  pattern4 minibuffer minibuffer-inactive-mode
</match> 

<match counted.emacs>
  type growthforecast
  gfapi_url http://gf.*****.my.server/api/
  service sugyan
  section emacs
  name_keys perl_count,ruby_count,javascript_count,minibuffer_count,unmatched_count
</match>

といった感じで、1分間あたりに文字入力が起こった回数をmajor-mode毎に集計してグラフ化できる。

f:id:sugyan:20130314012919p:image


zshの実行コマンドを取る

zshでもシェルコマンド実行前後に処理を挟むことができるので、実行したコマンドで集計したりできる。

function preexec_fluent() {
    curl -X POST -d 'json={"command":"'${1%% *}'"}' localhost:9880/zsh
}
preexec_functions=(preexec_fluent)

のようなものを~/.zshrcに書いておけば、コマンド実行するたびにそのコマンド名がemitされる。(ダブルクォートのエスケープとかあんまり考えていないけど)

<source>
  type http
</source>

<match zsh>
  type datacounter
  count_key command
  count_interval 1m
  aggregate all
  tag counted.zsh
  pattern1 ls ls
  pattern2 cd cd
</match>

<match counted.zsh>
  type growthforecast
  gfapi_url http://gf.*****.my.server/api/
  service sugyan
  section zsh
  name_keys ls_count,cd_count,unmatched_count
</match>

コマンドごとに集計する意味はあまり無いかもしれないけど、特定のコマンドをよく使う、という場合は割合を知ることができて良いかも?

合計値を取れば1分あたりどれくらいzshコマンド叩いているか、の推移は分かると思う。


その他

ほかに何か面白いデータ取れるかなぁ

2013-03-01

Slateを入れてみた

Macで起動中のアプリを切り替えるのに、⌘+Tabだけだとたくさん起動しているときに選択するために連打するのが面倒だし頻繁に使うものは一発で切り替わるようにしたい、というのがあって、

今までずっとそれをQuicksilverのHotKey機能を利用して

  • ⌘+Ctrl+Q でiTerm2
  • ⌘+Ctrl+W でEmacs
  • ⌘+Ctrl+E でChrome

みたいに左手の操作だけで切り替えられるようにしていた。

ところで最近「Slateというのが面白い」という話を某所で聞いたので試しに入れてみた。

jigish/slate ? GitHub

DivvySizeUpといったウィンドウ操作系ツールとして使えるもののようだけど(両方つかったことないから知らない)、

~/.slateに設定ファイルを書くことで様々な操作を規定してキーバインド設定できたりして良い。さら~/.slate.jsJavaScriptコードで設定を書くこともできる、というのが面白い。slateもしくはSUnderscore.jsがglobal objectとして使うことができて、それらを使って色々できる。

JavaScript Configs ? jigish/slate Wiki ? GitHub

S.bind('q:ctrl,cmd', function (win) {
    win.doOperation(S.operation('focus', { app: 'iTerm' }));
});

と書くことで⌘+Ctrl+QでiTermにfocusさせる、といったことが出来る。

これくらいならJSで書く必要もないのだけど、これは起動していないときに何も起こらないので、もし指定したappが立ち上がっていない場合はshellコマンド経由で/usr/bin/openを使って起動させた上でfocusさせるようにする。条件分岐はプログラミング言語で書きやすいので嬉しい。

S.bind('q:ctrl,cmd', function (win) {
    var apps = [];
    S.eachApp(function (app) { apps.push(app.name()); });
    if (! _.find(apps, function (name) { return name === 'iTerm'; })) {
        win.doOperation(
            S.operation('shell', {
                command: '/usr/bin/open -a iTerm',
                waithFoeExit: true
            })
        );
    }
    win.doOperation(S.operation('focus', { app: 'iTerm' }));
});

S.eachApp()で起動中のappを列挙できるので、その中にfocusしたいappの名前があるか否かを調べて見つからなかったらopen。

あとは共通で同じようなことができるように関数を定義してやって、

var launch_and_focus = function (target) {
    return function (win) {
        var apps = [];
        S.eachApp(function (app) { apps.push(app.name()); });
        if (! _.find(apps, function (name) { return name === target; })) {
            win.doOperation(
                S.operation('shell', {
                    command: "/usr/bin/open -a " + target,
                    waithFoeExit: true
                })
            );
        }
        win.doOperation(S.operation('focus', { app: target }));
    };
};
S.bind('q:ctrl,cmd', launch_and_focus('iTerm'));
S.bind('w:ctrl,cmd', launch_and_focus('Emacs'));
S.bind('e:ctrl,cmd', launch_and_focus('Google Chrome'));
S.bind('r:ctrl,cmd', launch_and_focus('Safari'));
S.bind('t:ctrl,cmd', launch_and_focus('Twitter'));

という感じでたくさんHotKeyでの切り替えを定義していける。

…というようなことが下記記事に書いてありまして大変参考になりました。

Slateでアプリ切り替えをする設定 - ミントフレーバー緑茶

でもこれ、"Google Chrome"のときにopenコマンドが動かない。無条件でスペースで区切ってコマンド引数として渡されてしまうらしく、クォートしてもダメ。のようだったので一応pull req送ってみた。。


なんかたまに変なエラーが出ておかしくなるけど、ほぼ問題なく快適に使えている。のでQuicksilver卒業してlauncherとしてはAlfredを使うようにしてみた。

Slate自体はウィンドウの移動とかサイズ変更とか柔軟に色々できるはずなのでもう少し色々設定して試してみたい。オヌヌメ設定とかあれば教えてください…

2013-01-12

Heroku上からService Account認証でGoogle APIにアクセスする

Googleの様々なAPIを叩く際、認証にOAuth 2.0を用いる。

Using OAuth 2.0 to Access Google APIs - Google Accounts Authentication and Authorization — Google Developers

使用する場面やパターンによって以下のような6つのシナリオが想定されている。

  • Login
  • Web Server Applications
  • Client-side Applications
  • Installed Applications
  • Devices
  • Service Accounts

多くの場合は「ユーザごとに認証させて個別のtokenを発行しリクエストに利用する」という流れなのだけど、中にはたとえばURL短縮APIとか、必ずしもユーザ個別にtokenを発行させる必要がないこともある。

そういう場合には「サービス固有のtoken」だけあれば良い。ということで使えるのが「Service Accounts」という方式。

Using OAuth 2.0 for Server to Server Applications - Google Accounts Authentication and Authorization — Google Developers

サービスアカウントに発行されたprivate keyを使って署名したJWTリクエストを生成してAPIを叩く、という仕組みのようだ。


rubyでgoogle-api-clientを使う例

GoogleのAPIを叩くためのライブラリとして、Ruby Gemsではgoogle-api-clientというのがある。

google-api-client | RubyGems.org | your community gem host

JWTを使ったリクエストや認証なども対応してくれている。

これを使って実際にやってみる。

準備

Google Accounts

にて、Application typeを"Service account"と選択してClient IDを生成する。

f:id:sugyan:20130112213142p:image

すると、"Email address"が発行され、private keyがダウンロードできるようになるので、これを使う。

書く

Gemfileに

source :rubygems
gem 'google-api-client', '0.6.0'

と書いてbundle installして、以下のようなスクリプトを書く。

require 'google/api_client'

client = Google::APIClient.new(:application_name => '')
key = Google::APIClient::PKCS12.load_key('/Users/sugyan/Downloads/...-privatekey.p12', 'notasecret')
client.authorization = Signet::OAuth2::Client.new(
  :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
  :audience             => 'https://accounts.google.com/o/oauth2/token',
  :scope                => 'https://www.googleapis.com/auth/urlshortener',
  :issuer               => <発行されたemail address>
  :signing_key          => key,
)
client.authorization.fetch_access_token!

shortener = client.discovered_api('urlshortener')
result = client.execute(
  :api_method  => shortener.url.insert,
  :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' },
)
puts result.data.id

ダウンロードしたprivate keyのファイルを読みこんでkeyを生成し、email addressやscopeを指定し、認証。

これで、GoogleのURL短縮APIを叩いて短縮URLを得ることができる。

$ bundle exec ruby shorten.rb
http://goo.gl/zLVjD

Herokuに上げるために

で、こんなものをHeroku上で動かそうとすると、API Consoleからダウンロードしたprivate keyのファイルをgit repositoryに含める必要が出てしまう。それはイヤだ。

幸いにも、Google::APIClient::PKCS12でloadしたkeyは文字列として得られる。

$ bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")'
-----BEGIN RSA PRIVATE KEY-----
MIICXQ...

...

-----END RSA PRIVATE KEY-----

なので、これを丸ごとheroku configで渡してしまえばいい。ついでに発行されたemail addressも。

$ heroku config:set GOOGLE_API_KEY="$(bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")')"
$ heroku config:set GOOGLE_API_EMAILADDRESS=...@developer.gserviceaccount.com

で、これを使用してKeyを生成するようにコードを変更する。

require 'google/api_client'

client = Google::APIClient.new(:application_name => '')
key = OpenSSL::PKey::RSA.new(ENV['GOOGLE_API_KEY'])
client.authorization = Signet::OAuth2::Client.new(
  :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
  :audience             => 'https://accounts.google.com/o/oauth2/token',
  :scope                => 'https://www.googleapis.com/auth/urlshortener',
  :issuer               => ENV['GOOGLE_API_EMAILADDRESS'],
  :signing_key          => key,
)
client.authorization.fetch_access_token!

shortener = client.discovered_api('urlshortener')
result = client.execute(
  :api_method  => shortener.url.insert,
  :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' },
)
puts result.data.id

これで、このへんをcommitしてherokuにpushすると、設定したconfigの文字列を使用してheroku上からService Accounts認証を使ってAPIを叩けるようになる。

$ git push heroku master
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 1.10 KiB, done.
Total 6 (delta 0), reused 0 (delta 0)
-----> Ruby app detected
-----> Installing dependencies using Bundler version 1.3.0.pre.5
       Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin --deployment
       Fetching gem metadata from http://rubygems.org/.........
       Fetching gem metadata from http://rubygems.org/..
       Installing addressable (2.3.2)
       Installing extlib (0.9.16)
       Installing multi_json (1.5.0)
       Installing autoparse (0.3.2)
       Installing multipart-post (1.1.5)
       Installing faraday (0.8.4)
       Installing jwt (0.1.5)
       Installing launchy (2.1.2)
       Installing signet (0.4.4)
       Installing uuidtools (2.1.3)
       Installing google-api-client (0.6.0)
       Using bundler (1.3.0.pre.5)
       Your bundle is complete! It was installed into ./vendor/bundle
       Cleaning up the bundler cache.

...

 * [new branch]      master -> master
$ heroku run bundle exec ruby shorten.rb
http://goo.gl/WnqP5

まとめ

…と、そういうようなことが、以下の記事に書いてあった。

How To Store Private Key Files In Heroku - arzumy md

2012-10-11

Express 3.xのjadeでHTMLをpretty出力する

結論

先に書くと、ここに答えがある。

node.js - ExpressJS: how to output pretty html - Stack Overflow


問題

現時点での最新express@3.0.0rc5+jade@0.27.6でjadeをレンダリングすると、改行やインデントされずにHTMLが吐き出される。

$ express hoge

   create : hoge
   create : hoge/package.json
   create : hoge/app.js
   create : hoge/public
   create : hoge/public/javascripts
   create : hoge/public/images
   create : hoge/public/stylesheets
   create : hoge/public/stylesheets/style.css
   create : hoge/routes
   create : hoge/routes/index.js
   create : hoge/routes/user.js
   create : hoge/views
   create : hoge/views/layout.jade
   create : hoge/views/index.jade

   install dependencies:
     $ cd hoge && npm install

   run the app:
     $ node app

$ cd hoge && npm install
$ node app &
Express server listening on port 3000
$ curl localhost:3000
GET / 200 24ms - 170
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>

これはこれで問題ないのだけど、開発中とかちゃんと想定通りのHTMLが出力されているのか確かめづらいし、改行やインデントを含んだ出力になってくれると嬉しい。


解決策

jadeはcompile時にprettyというオプションを渡してやることで出力を整形してくれるようになるらしい。

jade/Readme.md at 0.27.6 ? visionmedia/jade ? GitHub

expressview engineとしてjadeを使っている場合は、例えばrenderメソッドの第2引数に渡すことでそういったオプションを指定することができるようだ。

--- a/routes/index.js
+++ b/routes/index.js
@@ -4,5 +4,5 @@
  */

 exports.index = function(req, res){
-  res.render('index', { title: 'Express' });
+  res.render('index', { title: 'Express', pretty: true });
 };

これで下記のように見やすくインデントされた形式でHTMLがレンダリングされる。

<!DOCTYPE html>
<html>
  <head>
    <title>Express</title>
    <link rel="stylesheet" href="/stylesheets/style.css">
  </head>
  <body>
    <h1>Express</h1>
    <p>Welcome to Express</p>
  </body>
</html>

とはいえ毎回のrenderメソッドにこれを指定するのは面倒。アプリケーション共通の変数を保持するために、Express 3.xではapp.localsというものが使えるらしい。

Express - api reference

これを使ってセットしておくことで、いちいちrenderメソッドの第2引数にオプションを渡す必要がなくなる。

--- a/app.js
+++ b/app.js
@@ -15,6 +15,7 @@ app.configure(function(){
   app.set('port', process.env.PORT || 3000);
   app.set('views', __dirname + '/views');
   app.set('view engine', 'jade');
+  app.locals.pretty = true;
   app.use(express.favicon());
   app.use(express.logger('dev'));
   app.use(express.bodyParser());

Express 2.xではview optionsという設定項目があったようだけど、3.xではapp.localsを使いましょう、ということのようだ。

Migrating from 2.x to 3.x ? visionmedia/express Wiki ? GitHub

2012-09-27

以前作ったSinatraアプリをPadrinoで書き直した

ももクロの人気上昇ぶりをグラフで可視化する - すぎゃーんメモ

という記事で作ったアプリをちょっと作り直した。

というかんじでシンプルなSinatraアプリでpgから直接SQLを叩いて云々してhamlでレンダリング、というかたちだったものを、

を使った構成に。

あとはTwitter bootstrapHighstockのアップデートなど。


Padrinoメモ

gemでPadrinoをインストールしてpadrino gen project hogeみたいなかんじでスケルトン作成。ORMやtemplate engineは色々選べるのでオプションで指定。Herokuで動かすの想定しているなら-a postgresとか。

padrino startアプリを起動できる。

モデルやコントローラを追加するとき

$ padrino gen model fuga
$ padrino gen controller piyo

とかで作ると自動でテストやヘルパーやmigration用スクリプトなども追加してくれるっぽい。便利。


Padrino app on Heroku

Herokuで動かすために諸々設定。

Procfileには

web: bundle exec padrino start -a thin -p $PORT

とか記述しておく(Gemfileにthin追加しておく)。


データベースはORMがSequelの場合config/database.rb

Sequel::Model.db = case Padrino.env
  when :production  then Sequel.connect(ENV['DATABASE_URL'], :loggers => [logger])
end

とか書いておけば繋がるはず。migrationは

$ padrino rake sq:migrate:*

とかでゴニョゴニョできるっぽい。deploy後に本番の方を更新する場合

$ heroku run padrino rake sq:migrate:auto

とかでいいのかな。


Before After

Heroku Scheduler

コメント数の情報を更新するのに、以前は

https://github.com/sugyan/momoclo-visualizer/blob/ff93c6459933433a441c4349a78c80701acb6877/bin/scrape.rb

というスクリプトを用意して実行させていたけど、今はrake taskとして

https://github.com/sugyan/momoclo-visualizer/blob/143698ccba5d2128da686bf42940cb1cbe91ae18/tasks/scrape.rake

というのを定義して、$ padrino rake scrapeで実行するようにした。

DBの接続とか意識せずに書けるしmodelに突っ込むだけなので簡潔になった。


JSON API

Highchartsで使うコメント数の情報は、SQLで引いた結果をJSON APIで返してAJAXで使う、というかんじなのだけど、

https://github.com/sugyan/momoclo-visualizer/blob/ff93c6459933433a441c4349a78c80701acb6877/app.rb#L28

と、アレげだったものが

Visualizer.controllers :api do
  get :blog_comments, :provides => :json, :cache => true do
    expires_in 60
    Entry.for_highstock.to_json
  end
end

と、非常に簡潔に書けるようになった。モデルの方で

class Entry < Sequel::Model
  unrestrict_primary_key
  dataset_module do
    def for_highstock
      # ...
    end
  end
end

と、どんな情報を返すかだけ定義してるだけ。コントローラの方では「JSONで返す」「結果は60秒キャッシュする」といったことを簡単に指定できるので便利。


use Memcache

キャッシュにはDalliを使って、app/app.rb

  register Padrino::Cache
  enable :caching
  set :cache, Padrino::Cache::Store::Memcache.new(
    ::Dalli::Client.new(
      ENV['MEMCACHE_SERVERS'] || '127.0.0.1:11211',
      :exception_retry_limit => 1,
      :username => ENV['MEMCACHE_USERNAME'],
      :password => ENV['MEMCACHE_PASSWORD'],
  ))

と書いておけばだいたいどこでも使えるはず。


from Haml to Slim

まぁ書き方のクセはあるものの、慣れればどちらも大して変わらないかな?ライトに使う分には。

パフォーマンス的な部分は比較していないし分からない。


まとめ

まぁ、単純にPadrinoに興味があって使ってみたかったので使ってみた、というわけですが。

今回はほぼ1ページだけの簡単なアプリだったのでそれほど恩恵を受けなかったけど、ページ数が多くなったり複雑なことが増えてくるとSinatraだとキツくなってくる部分があると思っていて、Padrinoならそのへんうまくキレイに書けるようになるのでは?と思いました。

あとテストとかも書きやすそうな気がする。