カニ本読書メモ 1章:テストでコードを駆動する
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (47件) を見る
でかいテストを書いてからとかやるより、小さいステップからイテレーティブにやってくとハッピーだよ
- sanity(正気) check
SAN値チェック!?- あからさまな不整合がないか手早く確認していくこと
- サニティー・チェックの直訳は「正気確認」?──IT業界のカタカナ用語はくせ者ばかり:ITpro
コードの正しさは書いたテストの正しさと同じ程度にしかならない
テスト中にプライベートメソッドをsendで頻繁に呼び出す必要があったらリファクタリングのサイン
- モジュールにするとか
- 内部でしか使わないメソッドは隠蔽すべきだし、それの細かい挙動もテストしたい場合、send()は必要だと思うのだけどなぁ…
- プライベートメソッド、テスト駆動開発と優れたデザイン
と、言うことらしい
TDDの最終成果物→仕様通りにメソッドが動作することを検証可能な、自動化されたセーフティーネット
- 問題発見、リファクタリング、イテレーティブな設計といったプロセスがTDDの真価
- 書かれたテストは副作用に過ぎず、TDDの本当の力は最初にテストを書くことで得られる洞察
1個のテストケースに複数のテストを書くより分けた方がよいよー
- どんな問題が起きたかすぐ分かる
- 個々のテストをクリーンな環境で実行できる
例外のテストを書いたら、例外じゃない場合のテストも書こう
XMLとか複雑な出力をテストする時はパーサーライブラリ使った方が、テストがはっきりするよ
- ライブラリがないなら、簡単なフォーマットだったらパーサー書いた方がよいよ
- パーサー書くのに時間かかりすぎるなら、期待する出力をファイルにしといてdiffした結果を出すと良いよ
まとめ
node.js を軽くいじったメモ
hello world
$ node > console.log("hello world"); hello world undefined
サーバ起動
- createServer にレスポンスを返すfunctionを渡す
- res.writeHead でレスポンスヘッダを渡す
- res.write でレスポンスボディを渡す
- res.end で終了
var http = require('http'); var server = http.createServer( function(req, res){ res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('Hello World\n'); res.end(); } ); server.listen(3000); > node server.js
http://localhost:3000 にアクセスすると、
hello world が表示される
Socket.IO を使ってみる
Socket.IOって何?
いろんなブラウザのwebsokectの実装の違いを吸収してくれるライブラリ
ローカルディレクトリにインストール
npm install socket.io
サンプルコード書いてみる
クライアント
<html> <head> <meta charset="utf-8"> <!-- クライアント側のsocket.io.js が違う所にあった --> <script src="./node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js"></script> <script> var socket = io.connect('http://localhost:3000'); // socket.on が送信 この場合、newsイベントを受信している socket.on('news', function (data) { console.log(data); // socket.emitが送信 この場合、my other event に、オブジェクトを送信している socket.emit('my other event', { my: 'data' }); }); </script> </head> </html>
サーバ
var io = require('socket.io').listen(3000); io.sockets.on('connection', function (socket) { socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); });
起動
$ node socket.js
クライアントを開くと console に object が届いた
Object { hello="world"}
その後サーバ側でデータを受け取っている
debug - websocket writing 5:::{"name":"news","args":[{"hello":"world"}]} { my: 'data' }
これだけじゃつまらないので、1個のブラウザが投げたイベントを他のクライアントが出すのを作る
soket.broadcast.emit を使う
soket.broadcast.emit は、onイベントの中で使うと、
イベントを投げたクライアント以外の全クライアントにemitするメソッド
クライアント
<script> $(function(){ var socket = io.connect('http://localhost:3000'); socket.on('message', function(data){ console.log(data); message(data); }); socket.emit('login', window.navigator.userAgent); function message(msg){ $("#message").html($("#message").html() + '<br>' + msg); } }); </script>
サーバ
var io = require('socket.io').listen(3000); io.sockets.on('connection', function (socket) { socket.on('login', function (data) { console.log(data); // イベントを送ったクライアントに返信 socket.emit("message", "welcome " + data); // イベントを送った以外の全クライアントに送信 socket.broadcast.emit("message", data +" が来ました"); }); });
2個ブラウザを立ち上げると…
まずはそんなところで
カニ本読書メモ 2章:美しいAPIを実装する
コードの共通化する時のコツの学べる本はないか? と、相談した時にお勧めされたので読んでみました
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (47件) を見る
2章 美しいAPIを設計する
- メソッドにおける順序付き引数の数は最小限にする
- メソッドにデフォルト値を持つパラメータが複数あるなら、optionsハッシュによる擬似キーワード引数の利用を検討する
- 同時に複数の引数処理を扱う必要がある場合、splat(*) 演算子を使う
- 必須のパラメータがあるなら、それはoptionsハッシュに入れない
2.3 コードブロック
ブロックの使い道
- each メソッドを実装して、Enumerable を include すれば、組み込みコレクションで使える機能が使えるようになる
- 後処理/前処理の抽象化
- 動的コールバック
- &block を配列とかに保存しておいてコールバックが必要な時に実行したりする
- instance_evalでインターフェースをシンプルにする
- おんなじインスタンスを何度も呼ぶ場合に
Server.run do handle(….) handle(….) … end class Server def self.run(&block) server = Server.new server.instance_eval(&block) end end
yeild と、block.call の戻り値は、与えられたブロックの戻り値とおんなじにしよう!!
2.4 驚かせないこと
- atter_reader, atter_writer, atter_accesser を使う
- get_something, set_something より、something, something= を使う
- メソッドに疑問符をつけた場合、ブール値を返す
- 感嘆符は破壊的であるというより、注意すべきメソッドという意味で使う
- カスタム演算子
- append, add は、<<, + にする
WebSocketってなあに?
ざっくりと
ブラウザの対応状況はどんな感じなの?
実は何回かプロトコルが細々と変わってて、色々ややこしい事になってるので一覧表を見てくださいな
node.js ならば、socket.ioっていうライブラリを使えば、その辺の問題をなんとかしてくれる
cometって昔あったよね?
- あれは、擬似的に双方向通信を可能にしてた
- リクエストを受けてもすぐに情報を返さないで、本当に情報を返したい時に返す
- 接続方法は、ストリーミングと、通信リソースの軽減を目指した、ロングポーリングがメジャー
ストリーミング
- 接続後、データの送信を完了させないで、必要に応じてデータを送信する方法
ロングポーリング
- 接続後、イベントが発生してデータが来ると接続を遮断して、クライアントはpushが欲しい時に再度接続を行なって待つ方法
- ストリーミングよりもコネクションを節約できる
- どちらにせよ無理やり従来のブラウザで双方向通信を可能にしたので、ブラウザ側やサーバ側で色々問題があったらしい
- HTTPコネクションを維持し続けるので、通信リソースを食う
- 同時接続数やタイムアウトの時間がブラウザ毎にまちまちなので、その辺を意識したクライアント、サーバ側の実装が大変
その辺の問題点を解決するため、
っていうのが、websocket
websocket = node.jsって印象だけど、サーバ側はnode.jsじゃないとダメなの?
当然ながらプロトコルが実装されたモジュールがあるなら、どんなのつかっても構わない
- em-websocketっていうgemもあるよ!
ざっくりとそんな感じで
参考
- HTML 5 Web Sockets vs. Comet and Ajax
- WebSocket - Wikipedia
- WebSocket サーバの実装とプロトコル解説 - Block Rockin’ Codes
- 技術から業務の利用シーンを考えてみる part2 | Developers.IO
- ここの一覧表が、Ajax/Comet/Websocket の差異がわかりやすく解説されてる
WebSocketと、SPDYってどう違うのよ?
どっちもクライアント、サーバ間でコネクションを確立して、双方向通信が可能なので、
SPDYって、WebSocketの上位互換なの? みたいな印象が強かったけど違うらしい
目的がそもそも違う
- WebSocket → サーバ、クライアント間での双方向通信を可能にしたい
- SPDY → 従来のHTTP通信って遅いから早くしたい
できることは似てる
- WebSocket → サーバ、クライアント間でコネクションを1本確立して、その中でメッセージの送受信を行う
- SPDY → サーバ、クライアント間でコネクションを1本確立して、その中でリソースの受け渡しを行う
どう違うの?
WebSocket
- 1本のコネクションで、インタラクティブなweb体験の為の双方向通信をしたい
SPDY
- 1本のコネクションで、
- リソース(画像とか、cssとか)を優先度をつけて受け渡すことで、従来より高速な通信がしたい
- クライアントがhtml読んでから、必要なリソースを要求するんじゃなくて、サーバ側がpush通信で先に送りつけることで高速な通信がしたい
なので、
- 通信の高速化をしたい → SPDYを使う
- 双方向通信をしたい → WebSocketを使う
という事になるっぽい
やれることは似てるけど、目的が違うと
実装方法の違い
- WebSocket → 何を送受信するか、クライアント、サーバ側それぞれで実装する必要がある
- SPDY → 単にリソースを単一コネクションで渡すだけなら、ブラウザ側の実装は不要。サーバ側もSPDYモジュールを追加するだけ
目的がそもそも違うから実装方法も違う
参考
- SPDY と WebSocket の基礎と SPDY の Push - Block Rockin’ Codes
- ネタ元。大体おんなじことをもっとくわしく書いてる
- こてさきAjax:node-spdy 試してみた - livedoor Blog(ブログ)
- SPDYの詳しい解説とか、WebSocketでおんなじ機能を実装して比較とかしてる
- SPDY対WebSockets?
- WebSocketのSPDYと比較しての脆弱性や問題点。速度を追求することが目的ならSPDYに分があるよねとかいう話
Lionにnode.jsの環境を作ってみる
前に1回つくったけど、なんか色々有耶無耶だったので、もっかい色々理解しながら作ってみる
目標
nvmを使って、node.js や npmの管理をできるようにする
nvmのインストール
nvmって何?
node.jsのバージョンマネージャ。ruby における rvm 的なもの
nvm - Node Version Manager - Simple bash script to manage multiple active node.js versions.
インストール
オフィシャルに書いてあるやり方でインストール
$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1202 100 1202 0 0 418 0 0:00:02 0:00:02 --:--:-- 571 Cloning into '/Users/kasei_san/.nvm'... remote: Counting objects: 557, done. remote: Compressing objects: 100% (328/328), done. remote: Total 557 (delta 290), reused 470 (delta 218) Receiving objects: 100% (557/557), 76.38 KiB | 77 KiB/s, done. Resolving deltas: 100% (290/290), done. => Appending source string to /Users/kasei_san/.bash_profile => Close and reopen your terminal to start using NVM
.bash_profileに書いたよ! とか言われたので、設定を.zshrcに移す
# NVM # 複数マシンでzshrcを共有しているので、特定マシンでだけ動作するようにした if sw_vers | grep 10.7 ;then [[ -s /Users/kasei_san/.nvm/nvm.sh ]] && . /Users/kasei_san/.nvm/nvm.sh # This loads NVM fi
シェルを1個起動してみる
$ nvm Node Version Manager Usage: nvm help Show this message nvm install [-s] <version> Download and install a <version> nvm uninstall <version> Uninstall a version nvm use <version> Modify PATH to use <version> nvm run <version> [<args>] Run <version> with <args> as arguments nvm ls List installed versions nvm ls <version> List versions matching a given description nvm ls-remote List remote versions available for install nvm deactivate Undo effects of NVM on current shell nvm alias [<pattern>] Show all aliases beginning with <pattern> nvm alias <name> <version> Set an alias named <name> pointing to <version> nvm unalias <name> Deletes the alias named <name> nvm copy-packages <version> Install global NPM packages contained in <version> to current version Example: nvm install v0.4.12 Install a specific version number nvm use 0.2 Use the latest available 0.2.x release nvm run 0.4.12 myApp.js Run myApp.js using node v0.4.12 nvm alias default 0.4 Auto use the latest installed v0.4.x version
インストールできた
とりあえずnodeの最新版を入れたい
いっぱいある
$ nvm ls-remote
v0.1.14 v0.1.27 v0.1.96 v0.2.4 v0.4.1 v0.5.1 v0.6.3 v0.6.16 v0.7.7 v0.8.7 v0.8.20
v0.1.15 v0.1.28 v0.1.97 v0.2.5 v0.4.2 v0.5.2 v0.6.4 v0.6.17 v0.7.8 v0.8.8 v0.9.0
v0.1.16 v0.1.29 v0.1.98 v0.2.6 v0.4.3 v0.5.3 v0.6.5 v0.6.18 v0.7.9 v0.8.9 v0.9.1
v0.1.17 v0.1.30 v0.1.99 v0.3.0 v0.4.4 v0.5.4 v0.6.6 v0.6.19 v0.7.10 v0.8.10 v0.9.2
v0.1.18 v0.1.31 v0.1.100 v0.3.1 v0.4.5 v0.5.5 v0.6.7 v0.6.20 v0.7.11 v0.8.11 v0.9.3
v0.1.19 v0.1.32 v0.1.101 v0.3.2 v0.4.6 v0.5.6 v0.6.8 v0.6.21 v0.7.12 v0.8.12 v0.9.4
v0.1.20 v0.1.33 v0.1.102 v0.3.3 v0.4.7 v0.5.7 v0.6.9 v0.7.0 v0.8.0 v0.8.13 v0.9.5
v0.1.21 v0.1.90 v0.1.103 v0.3.4 v0.4.8 v0.5.8 v0.6.10 v0.7.1 v0.8.1 v0.8.14 v0.9.6
v0.1.22 v0.1.91 v0.1.104 v0.3.5 v0.4.9 v0.5.9 v0.6.11 v0.7.2 v0.8.2 v0.8.15 v0.9.7
v0.1.23 v0.1.92 v0.2.0 v0.3.6 v0.4.10 v0.5.10 v0.6.12 v0.7.3 v0.8.3 v0.8.16 v0.9.8
v0.1.24 v0.1.93 v0.2.1 v0.3.7 v0.4.11 v0.6.0 v0.6.13 v0.7.4 v0.8.4 v0.8.17 v0.9.9
v0.1.25 v0.1.94 v0.2.2 v0.3.8 v0.4.12 v0.6.1 v0.6.14 v0.7.5 v0.8.5 v0.8.18
v0.1.26 v0.1.95 v0.2.3 v0.4.0 v0.5.0 v0.6.2 v0.6.15 v0.7.6 v0.8.6 v0.8.19
とりあえず、0.8.19入れてみる
nvm install v0.8.19
######################################################################## 100.0%
Now using node v0.8.19
インストール済一覧をチェック
$ nvm ls current: v0.8.19 $ node -v v0.8.19 $ which node /Users/kasei_san/.nvm/v0.8.19/bin/node
よさそう
デフォルトのnodeを決めたい
オフィシャルに、以下の設定をすれば良いと書いてある
To set a default Node version to be used in any new shell, use the alias 'default': nvm alias default 0.6
やってみる
$ nvm alias default 0.8.19 default -> 0.8.19 (-> v0.8.19/)
別シェル開いて、バージョン確認
$ node -v
v0.8.19
よさそう
npmについて
npmのインストール先
npmにはグローバルインストールとローカルインストールがある
グローバルインストールは環境全体に影響して
ローカルインストールはカレントディレクトリにのみ影響するらしい
インストール先の確認
$ npm root -g
/Users/kasei_san/.nvm/v0.8.19/lib/node_modules
$ npm root
/Users/kasei_san/work/node_modules
なるほど
基本的にはローカルインストールですすめることにする
知らなかったコマンド色々
忘れるのでメモ
特定のユーザでコマンドを実行
$ sudo /sbin/runuser -l {ユーザ名} -c "コマンド"
日時を指定してtouch
$ touch -t201301090000 01.txt $ touch -t201301080000 02.txt $ touch -t201301070000 03.txt $ touch -t198001010000 04.txt $ ll total 0 -rw-r--r-- 1 kobayashi wheel 0 1 9 00:00 01.txt -rw-r--r-- 1 kobayashi wheel 0 1 8 00:00 02.txt -rw-r--r-- 1 kobayashi wheel 0 1 7 00:00 03.txt -rw-r--r-- 1 kobayashi wheel 0 1 1 1980 04.txt
タイムスタンプを指定して検索(今日が2013/02/08として)
$ find . -name "*.txt" -mtime +30 # 過去31日以上 ./02.txt ./03.txt ./04.txt $ find . -name "*.txt" -mtime 31 # 31日前のみ ./02.txt $ find . -name "*.txt" -mtime -32 # 過去31日以降 ./01.txt ./02.txt
xargsでmvする
$ find . -name "*.txt" -mtime +30 | xargs mv --target-directory=./test/
Macだと、
$ find . -name "*.txt" -mtime +30 | xargs -J % mv % ./test/ $ ll * -rw-r--r-- 1 kobayashi wheel 0 1 9 00:00 01.txt test: total 0 -rw-r--r-- 1 kobayashi wheel 0 1 8 00:00 02.txt -rw-r--r-- 1 kobayashi wheel 0 1 7 00:00 03.txt -rw-r--r-- 1 kobayashi wheel 0 1 1 1980 04.txt