クラスの責務を明確にする、単一にするみたいな話があるけど、なんかの資料みたいなものに関しても色んな責務をもたせると、ただでさえ怠い作業がもっと怠くなって全部中途半端になってしまう。この資料ではこれについての説明だけはちゃんと書いて他は書かないみたいな感じにしたほうが良い。

Functional Programming Principles in Scala

https://www.coursera.org/course/progfun

をやってるんだけど、結構面白い。この講義自体は http://shanon-tech.blogspot.jp/2013/04/top5.html で知った。

プログラミングパラダイムの話で

  • imperative programming
  • functional programming
  • logic programming

の3つがあって

  • object-oriented programming

はその3つとは直交する。で、ScalaではFPとOOPを混ぜたみたいなことを言っていた。

JVM上で動くしJavaの人が慣れやすいように〜とかってそういう感じだったのかなーと勝手に思ってたんだけど、そういう意思のもと入れたものだったんだなと知った。このへんでScalaに興味出てきた。ぶっちゃけscaleさせたいようなweb serviceを運用してるわけでもなく、scalability的な文脈ではさほど興味を持ってなかった。

また、SCIP(途中で投げ出してたのを再開中...)の例題とかが出てくるんだけど、課題にはテストがついてたり、動画の途中にQuizが出たり、一人で本読むより飽きにくいと思う。

あとはScala IDEのworksheetっていうのが柔軟なREPLみたいな感じで面白いし、Scalaで開発やりたい感じ出てきた。最近ChromeIRC ClientとTerminal以外立ち上げずそこで完結するように頑張るみたいな原始的なことをしているおかげか、IDEもサクサク動いた。

dotCloud製Linux Container, Docker触ってみた

http://www.docker.io

とりあえず、VM一個立てて、rackupしようと思った。

digitaloceanのvpsで試していたんだけど、なんかおかしいと思ったらkernelのバグの影響だったようで、3.5.0-21以上を使ったほうが良さそう。 https://github.com/dotcloud/docker/issues/25

ちなみにdigitaloceanのvpsでは

$ uname -r
3.5.0-17-generic

なので、Vagrantで適当なUbuntuのVM立てた。

def v10(config)
  config.vm.box = "ubuntu-quantal"
  config.vm.box_url = "http://cloud-images.ubuntu.com/quantal/current/quantal-server-cloudimg-vagrant-amd64-disk1.box"
end

Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
  v10(config)
end

Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
  v10(config)
end

Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
  config.vm.provider :virtualbox do |vb|
    config.vm.box = "ubuntu-quantal"
    config.vm.box_url = "http://cloud-images.ubuntu.com/quantal/current/quantal-server-cloudimg-vagrant-amd64-disk1.box"
  end
end
$ uname -r
3.5.0-26-generic

Install

$ sudo apt-get install lxc wget bsdtar curl
$ sudo apt-get install linux-image-extra-`uname -r`
$ wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz
$ tar -xf docker-master.tgz

基本(READMEに書いてる)

僕は当初Containerとimageを混同していて、$ sudo ./docker pull IMAGE_NAME でVMのようなものを作っているのかと思ってたが、そうではなくimageはコンテナのひな形、インスタンスに対するクラスのようなもの。また、runで新しくコンテナを作ってコマンドを実行しても、コマンドが終了すればコンテナは消える。

# イメージのダウンロード
$ sudo ./docker pull IMAGE_NAME
# コンテナ上でコマンドを実行
$ sudo ./docker run -i -t IMAGE_NAME COMMAND
# shell立ち上げることで、普通にそのコンテナにログインして作業みたいなこともできる
$ sudo ./docker run -i -t IMAGE_NAME /bin/bash

# Container の IDに対して操作出来る
$ (sudo ./docker -d || echo "Docker daemon already running") &
$ CONTAINER=$(docker run -d base /bin/sh -c "while true; do echo Hello world; sleep 1; done")
$ echo $CONTAINER
2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
$ docker logs $CONTAINER
2013/03/29 07:33:54 docker logs 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
$ docker logs $JOB
2013/03/29 07:33:56 docker logs 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
$ docker kill $JOB
2013/03/29 07:34:23 docker kill 2fa7019f6fdf3e85c249fa2c2eef093c294fc8c0d5262377e9754f491a3dc2e1

# runはコンテナを新しく起動してコマンドを実行するもので、
# 永続的なコンテナを作るものではないので、コマンドの実行が終わると
# コンテナは消える。
# コマンドの実行によりコンテナに与えた変更はcommitすることにより
# 新たなimageが作成できる。
$ sudo ./docker run -i -t base bash
root@ea19df583e44:/# adduser toqoz
Adding user `toqoz' ...
Adding new group `toqoz' (1000) ...
Adding new user `toqoz' (1000) with group `toqoz' ...
Creating home directory `/home/toqoz' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for toqoz
Enter the new value, or press ENTER for the default
        Full Name :
        Room Number :
        Work Phone :
        Home Phone :
        Other []:
        Is the information correct? [Y/n]
root@ea19df583e44:/# su toqoz
bash: no job control in this shell
$ exit
exit
root@ea19df583e44:/# exit
exit
$ sudo ./docker run -i -t base bash
2013/03/29 08:08:37 docker run -i -t base bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@b612c0b3d72d:/# su toqoz
Unknown id: toqoz

自分用のbase imageを作成

$ sudo ./docker pull base
$ CONTAINER=$(docker run -d base apt-get install -y curl)
$ docker commit -m "Installed curl" $CONTAINER $USER/sample
$ docker push $USER/sample
$ docker images
2013/03/29 08:16:58 docker images
REPOSITORY          TAG                 ID                                                                 CREATED             PARENT
vagrant/sample        latest              c287f15311bfbb0c2e664f5e29561d5f8e03567b7a9f640140106213bbc6fa9d   45 seconds ago      base:latest
base                latest              b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-12.10        b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-quantal      b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545
base                ubuntu-quantl       b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc   5 days ago          27cf784147099545

# ps でコンテナを探してattachすることもできる
$ sudo ./docker ps
2013/03/29 09:12:28 docker ps
ID                                                                 IMAGE         COMMAND                CREATED          STATUS          COMMENT
307304dc7bd498f7ee3b8e76e3bbc492b8b1a7c698fb7e100c3b4d331e108d99   base:latest   apt-get install -y r   5 minutes ago    Up 5 minutes
61b385d5cd8ce70d3e6df7a256777aacdebf3cebb483285f50f006d6f0c17e00   base:latest   echo hello             21 minutes ago   Up 21 minutes
7ddd667b4edf3c603b89027a2f2f731b64c6b34487e2a03ab96daa4af3504cbe   base:latest   echo hoge              24 minutes ago   Up 24 minutes
$ sudo ./docker attach 307304dc7bd498f7ee3b8e76e3bbc492b8b1a7c698fb7e100c3b4d331e108d99
$ sudo ./docker commit -m 'dist-updated' $CONTAINER $USER/base
# sudo ./docker run some_image apt-get update && apt-get install foo が
# (sudo ./docker run some_image apt-get update) && apt-get install foo みたいな感じに解釈されてしまうので、とりあえず sh -c '' で実行してる
$ CONTAINER=$(sudo ./docker run -d base sh -c '\
  export DEBIAN_FRONTEND=noninteractive && \
  echo "deb http://archive.ubuntu.com/ubuntu quantal-updates main universe multiverse" >> /etc/apt/sources.list && \
  apt-get -y update && \
  apt-get -y upgrade && \
  apt-get -y dist-upgrade \
  apt-get install -y build-essential openssl libreadline6 libreadline6-dev curl git-core' \
)

$ sudo ./docker commit -m 'Installed build-essential openssl libreadline6 libreadline6-dev curl git-core' $CONTAINER $USER/base

自分用のbase imageをベースにruby用のimageを作る

$ CONTAINER=$(sudo ./docker run -d $USER/base sh -c '\
  export DEBIAN_FRONTEND=noninteractive && \
  apt-get install -y libssl-dev zlib1g-dev libyaml-dev && \
  cd /tmp && \
  git clone git://github.com/sstephenson/ruby-build.git && \
  cd ruby-build && PREFIX=/opt/ruby-build ./install.sh && \
  /opt/ruby-build/bin/ruby-build --with-open-ssl-dir=usr 2.0.0-p0 /opt/ruby2.0.0'
)
$ sudo ./docker commit -m 'Installed Ruby' $CONTAINER $USER/rubybase
$ sudo ./docker run $USER/rubybase env PATH=/opt/ruby2.0.0/bin:$PATH ruby -v
2013/04/01 01:19:08 docker run vagrant/rubybase env PATH=/opt/ruby2.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games ruby -v
ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]

ruby用のimageをbaseにrack app用のimageを作る

$ CONTAINER=$(sudo ./docker run -d $USER/rubybase env PATH=/opt/ruby2.0.0/bin:$PATH gem install rack --no-ri --no-rdoc
$ sudo ./docker commit -m 'Installed rack' $CONTAINER $USER/rackbase

rack appを起動するしてアクセスしてみる

$ CONTAINER=$(sudo ./docker run -d -p 9292 $USER/rackbase \
  env PATH=/opt/ruby2.0.0/bin:$PATH \
  sh -c 'rackup -b "run proc { |env| [200, { %{Content-Type} => %{text/html} }, %{Hello\n}.chars] }"' \
)

$ sudo ./docker logs $CONTAINER
2013/04/01 05:42:48 docker logs baa621dd4def
[2013-04-01 05:42:38] INFO  WEBrick 1.3.1
[2013-04-01 05:42:38] INFO  ruby 2.0.0 (2013-02-24) [x86_64-linux]
[2013-04-01 05:42:38] INFO  WEBrick::HTTPServer#start: pid=5 port=9292
$ PORT=$(sudo ./docker port $CONTAINER 9292)
$ HOST=$(ifconfig | grep 'inet ' | head -n1 | sed -e 's/^.*inet addr://' -e 's/ .*//')
$ curl $HOST:$PORT
Hello

実際にprivate paasみたいな感じで使うには

コンテナの存在をチェックして、再起動する仕組みと、ルーターを作れば、とりあえず簡素なの作れる気がした。

環境構築の自動化について

imageをpushすることも出来るので、chefなどを使った自動化というより、dockerのエコシステムの中でうまく自動化していくのがいいんじゃないかなと思ったりした。ただ現状、そのimageがどのように作られたかとかを知る術を僕は知りません。

irssi、色々設定したらかなり便利な感じになってきて、irc client on browserの開発意欲がとんでもなく下がってきた。またブラウザで操作したくなった時にでも考えよう。

tmuxinatorっぽいことがtmuxのコマンドだけで出来る

tmuxの想像以上にコマンドからなんでも出来る感にうれしさを感じた。

  • tmux send-keysでキー入力を送るみたいなことが出来て、tmux send-keys 'echo hoge' Enterecho hoge[Enter] というキーを送れる、つまり任意のコマンドが実行できる。
  • また、tmux command1 \; command2 という形で順番にコマンドを実行できる。

上記2つを組み合わせることでtmuxinatorでやる、windowをいくつ開いて、そこでpaneをいくつ開いて、それぞれでなんかコマンドを実行する。みたいなことが可能になる。ただ複雑になるくらいなら諦めてtmuxinator使ったりしたほうが良いとは思う。

# 今回この方法でやったこと
tmux new-session \; \
  new-window \; send-keys 'atig -d -h 127.0.0.1' Enter \; last-window \; \
  split-window -h -l 30 \; send-keys 'clear && cat ~/.irssi/nicklistfifo' Enter \; last-pane \; \
  send-keys 'clear && irssi' Enter

hsl(Hue, Saturation, Lightness) による指定が便利で、 scssなどで書いていると、コンパイル時RGBに直してくれるので、サポートしてないブラウザでも使えて便利。

例えば

@mixin interactive-button($hue, $saturation, $lightness) {
  background-color: hsl($hue, $saturation, $lightness);
  &:hover {
    background-color: hsl($hue, $saturation, $lightness * 1.15);
  }
  &:active {
    background-color: hsl($hue, $saturation, $lightness * 0.6);
  }
}

みたいなmixin書いてると、Hue, Saturation, Lightnessを一つ指定するだけで、hoverした時にちょっと明るくなって、押すと暗くなるみたいなボタンを作れて便利。

色を変数に入れておくときに

$some_color_hue: なんとか;
$some_color_saturation: なんとか;
$some_color_lightness: なんとか;
$some_color = hsl($some_color_hue, $some_color_saturation, $some_color_lightness);

みたいにしておくとそのまま使う時は $some_color で使えるし、ちょっと暗くして使いたいときは hsl($some_color_hue, $some_color_saturation, $some_color_lightness * 0.8) とかって感じで使えるし良い。

selfとthatについて

selfが実質予約語であるとか、Function#bindとかは無視して、変数名としてだけの話をします。

function() {
  // ここではその通りだと思う
  var self = this;
  // この時点ではthatではなく間違いなくthisというかself的なもんでしょ
  var that = this;

  (function() {
    // ここでself使ったりするの、名前的に既におかしいよね。ここではもうselfじゃないでしょ。
    // ここでthatと呼ぶのはselfよりはニュアンス的に合ってると思う。
  })();
}

ということでどっちもおかしい気がする。普通に具体的な名前付けるのが良いと思う。

selfのほうがーみたいな意見を見たりしましたが、変数の名前としては、どっちもどっちで微妙ですねという話。

そもそも他の慣習言語の慣習からselfとかっていうのかも知れませんが、少なくとも僕はPythonかじった程度ですが、Pythonでのselfはそのインスタンスを指し、他のオブジェクトを指すことはなかった気がしますと思います。

特に言いたいことはないです。

難しいかったことを「何か」によってシンプルにできるようになって、そうすると「何か」の仕組みを把握するのが難しくなって、「何か」の仕組みが簡単なものが出てきて、でもそれじゃあ難しかったことを難しいまましか出来ないよねってなって、好みと個人の置かれている環境により激しくどっちが優れているかの論争が始まり、結局論争している間に人類が滅亡する夢を見た。その前の夢はガオー氏というライオンのマフィアに追いかけられ続ける夢だった。

Smalltalkベストプラクティス・パターンを読んだ

新横浜-姫路間で運良く座れたので読んでいた。良いと思ったところとか。

  • Intention Revealing Selector

    メソッドの名前を考えるときに、異なる実装の同じことをなすメソッドを考えてみて、それ同じ名前に出来るのであれば、十分抽象化されている。というようなことが書いてあった。指標となる例があるの嬉しかった。抽象化されているかどうかとか一人で考えていると闇の世界に入っていってよく分からなくなることがあると思う。

  • Direct Variable Access

    インスタンス変数に直接アクセスするのは分かりやすいから、アクセサが必要ないときは直接アクセスしてもいいんじゃない?(文法的にRubyとかだとアクセサ定義したほうが変数普通に使ってるっぽいから微妙だけど)みたいなことが書いてあった。継承することのない時までいつでもアクセサ定義しなければならないみたいな呪縛から解かれるかもしれない。柔軟性と手軽さどちらかを自分で選ばないといけないが。

  • Role Suggesting Temporary Variable Name

    変数の型と役割が分かることが重要。式をわかりやすく書けば、型を知ることができるし、それに加え名前で役割を十分に表せればよいと思う。

  • Collect

    コレクションの各要素に処理を行いたい場合、collectで中間コレクションを作れば(そのようなメソッドを用意すれば)、コードは明瞭になる。パフォーマンスのことを気にするのは後で、問題になったときと書いてあった。リファクタリングにも書いてあったけど、きちんとわかりやすく書いたほうが、あとからパフォーマンス上の問題も発見しやすく、変更しやすい。最近は途中で思い出したようにパフォーマンスが気になり出しても、ア、今じゃないんだ、と考えるようにしている。

  • Guard Clause

    僕はRubyでreturnを書かないようにしていたんだけど、ここに良い言葉があった。"複数のリターン文で書かれたメソッドはしばしばプログラマの意図を直接的に表すことがあります。" 途中でreturnするほうが意図に合っていれば、複数のreturnを使うのが良いかもしれない。

あと何回か読みたいと思う。

ケント・ベックのSmalltalkベストプラクティス・パターン―シンプル・デザインへの宝石集

ケント・ベックのSmalltalkベストプラクティス・パターン―シンプル・デザインへの宝石集

  • 作者: ケントベック,Kent Beck,梅沢真史,皆川誠,小黒直樹,森島みどり
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2003/03
  • メディア: 単行本
  • 購入: 7人 クリック: 93回
  • この商品を含むブログ (55件) を見る

これは抽象的な概念とか考え方なんだ、というように説明されているものがあるけど、具体的に解決する問題とかなぜ解決できるのか、とかがなくて良いのかどうか判断しかねるみたいな感じになることがなる。OOの話とかでも途中でそういう話題になってわからなくなるみたいなのある。本でも問題とか、なぜ解決できるかとかだけでもバカにも分かるように書いてる本は読みやすくて嬉しい感じがする。

Rack middlewareのテスト

require 'rack/test'

describe Rack::HogeMiddleware do
  include Rack::Test::Methods

  subject(:response) { last_response }

  let(:app) do
    this_plugin = described_class

    Rack::Builder.new do
      use this_plugin
      map '/foo' do
        run lambda { |env| [200, {'Content-Type' => 'text/html'}, '<html></html>'] }
      end
      map '/bar' do
        run lambda { |env| [200, {'Content-Type' => 'text/html'}, '<body></body>'] }
      end
    end.to_app
  end
  
  before do
    get '/'
  end

  it 'なんかテスト書く' do
    # response.body.should # なんとか~
  end
end

こんな感じdummyのアプリを作ってテスト書いていけるので便利です。

Redisのソースコードをちょっと読んでみようという試み(1)

C言語Hello world以上のものを読むのは初めてだった。とりあえず実装の詳細は飛ばして、
サーバー立ち上げる -> クライアントから繋がる -> コマンドが実行される -> 書き込まれる までの関数の呼ばれる流れだけを追ってみた。
大分意図的に端折ってるし、意図せず抜けてたり間違ってたりすると思うから、間にうけないほうが良さそう

Doxygenというので、ドキュメントっぽいのを生成して、それも時々見てた。全然関係ないけどdoxygengui付きのオプション付けてbrewでビルドしようとしたけど、依存しているQtのビルドに時間がかかって、そのうえGUI(doxywizard)のバイナリ出来てなかった感じがして損した感じがした。

しょうがないからDoxyfile色々いじってやった。https://gist.github.com/ToQoz/5057612 途中関数のコールグラフみたいなのが出てきてる感じになったけど、色々いじってたら出てこなくなった気がする。

main
  initServer
    acceptTcpHandler(acceptUnixHandler)
      anetTcpAccept
        anetGenericAccept
          accept
                                   # クライアントから繋がれました~~~
      acceptCommonHandler
        createClient               # redisClient作って返す(この後こいつが色々持つ、コマンドとか)
          readQueryFromClient      # clientからのqueryの長さとかチェックしたりする
            processInputBuffer     # request typeとか決定したり(決まって無い時), そのチェックしたり
              processCommand       # lookupCommandでコマンド探してcallで実行する
                lookupCommand      # 実行するコマンドを探しす
                call               # コマンドを実行してる(redisClient c->cmd->proc())
                                   # procはredisCommandTableで, コマンドごとに登録されていて、
                                   # それが実行される. ↓こんな感じ

  struct redisCommand redisCommandTable[] = {
    {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
    {"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},

例えばsetコマンドだと、procは

# src/t_string.c
void setCommand(redisClient *c) {
  c->argv[2] = tryObjectEncoding(c->argv[2]);
  setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0);
}

という感じで, valueをエンコードして, setGenericCommandに client, nx(謎), key, value, expire, unit(謎) を渡している.

流れはこんな感じ。

setCommand          # 値をエンコードとか
  tryObjectEncoding # エンコードしようとしてる
  setGenericCommand # expireチェックとか
    setKey          # 実際にキーに値をセット(この関数の中より下見てない, dbAdd or dbOverrideで書き込まれてるっぽい)

とにかくredisClientというやつが, コマンドの情報とか色々持っていて、そいつに対して値をセットしたり色々しながら 最終的にそいつが持ってる情報から実行するみたいな感じだった。とりあえずメモを残そうと思って残したけど、次読もうとするのいつになるか分からない。