git用のpre-commit gemが便利すぎる

まずは

$ gem install pre-commit
# In your git repo
$ pre-commit install

これだけで、うっかり残念なコミットをする事故が防げます。

Git フック使ってますか?

http://git-scm.com/book/ja/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF

コミットする前にバリデーションかけて、通らなかったらコミット中止する。コミットメッセージにブランチ名を自動挿入する。など、ルーティンワークをフックに仕込んでおけば、誰も読まないガイドラインページを書く手間が省けますし、強制する事ができて幸せです。

じゃあ、何をフックに設定するかと言うと

コードレビューしていると、気になるけどいちいち指摘するのも面倒くさい事が良いんじゃないかと。

  • 行末の空白文字くらい消せよ!
  • タブインデント止めて!
  • binding.pry消し忘れてんぞ! などなど

そこで、pre-commit gemですよ

https://github.com/jish/pre-commit

デフォルトで便利なバリデーションをたくさん持っているので、いちいち自分で作成する必要が無いところがありがたい。 現在のチェック項目を確認したい場合は、pre-commit listで確認できます。

$ pre-commit list
Available providers: default(0) git(10) git_old(11) yaml(20) env(30)
Available checks   : before_all ci closure coffeelint common console_log csslint debugger gemfile_path go jshint jslint json local merge_conflict migration nb_space php pry rails rspec_focus rubocop ruby ruby_symbol_hashrockets scss_lint tabs whitespace yaml
Default   checks   : common rails
Enabled   checks   : common rails
Evaluated checks   : tabs nb_space whitespace merge_conflict debugger pry local jshint console_log migration
Default   warnings : 
Enabled   warnings : 
Evaluated warnings : 

common と rails がデフォルトで有効になっているcheck項目で、実際には"Evaluated checks"のが実行されます。

  • tabs: インデントにタブが使われていないか
  • nb_space: ノーブレークスペースが使われていないか
  • whitespace: git diff --check --cachedを実行する(Git自体に便利コマンドがある!)
  • merge_conflict: コンフリクトが解消されていないファイルを上げようをしていないか
  • debugger: debuggerを消し忘れていないか
  • pry: binding.pryを消し忘れていないか
  • local: ユーザが定義したバリデーションが通るか
  • jshint: JSファイルがjshintを通るか
  • console_log: console.logを消し忘れていないか
  • migration: Railsのdb, schemaファイルの整合性は取れているか(片方だけコミットしようとしていないか)

これだけでも、十分有用です

check追加したい

git configに追加する方法と、YAMLファイルで管理する方法があります。YAML形式で書き出しておいて、このファイルもプロジェクトに含めてしまえば、他のチームメンバーはpre-commit installをするだけで済むので、YAMLがいいんじゃないかと。

checks, warningsの違いはチェックに引っかかった時に、コミットを許容するか否かです。 cheksだと、そのままコミットさせない。warningsだと、警告を出すだけにとどめる。

$ pre-commit <enable|disable> <git|yaml> <checks|warnings> check1 [check2...]
# rubocopのチェックを追加するが、警告を出すだけ
$ pre-commit enable yaml warnings rubocop

独自バリデーションも適応したい

config/pre-commit.rb というファイルを置いておけば、引数としてコミット対象のファイル名を渡してくれます。exitで0よりも大きな数字を返せば、バリデーションに通らなかったことを通知できます。

# config/pre-commit.rb
valid = ARGV.all? do |file|
  # validate
end

exit valid ? 0 : 1

Guidelines as a Code

人力チェックリストを作るくらいなら、機械にテストさせた方がよっぽど正確だよね。って話でした。

新人への洗礼

新人の子が離席時にPCロックを忘れている!

チャンスですね

ls

ど定番

alias ls=sl

echo

牛さんのAAに癒されます

alias echo=cowsay
 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

xcowsayは、最近知りました。 より癒されます。

alias echo=xcowsay

cat

猫がマウスカーソルを追いかけてきます。 無駄な引数があると、エラーになるので引数は捨て去りますw

function cat() {
  oneko
}

EDITOR

nanoで十分です

export EDITOR=nano
alias vim=nano
alias gvim=nano
alias emacs=nano
alias xemacs=nano
alias sbul=nano
alias atom=nano

32bit Win7上でVagrant使って開発環境構築 box構築編

今日こそは

boxの構築をするぞ!ってことで
最終目標は、Ubuntu-13.04 desktop上に開発環境整える

2013/08/16 18:40 apt.shを修正

本日の流れ

  1. veeweeのtemplateから作成したいVMのtemplate選ぶ→definition作成
  2. definitionを微修正
  3. definitionを基にubuntuをインストール
  4. Vagrant用boxファイル作成
  5. vagrant up

VMのtemplate選ぶ

veewee落としてきたディレクトリへ移動する
ここは、適宜読み替えて

> cd work\veewee

template一覧表示

> bundle exec veewee vbox templates

ubuntu-13.04-desktop-i386が無いorz
しょうがないので、server版をインストールして、後でGUI対応する。

> bundle exec veewee vbox define 'ubuntu-13.04-desktop-i386' 'ubuntu-13.04-server-amd64'

これで、definitions\ubuntu-13.04-desktop-i386以下にtemplateがコピーされるわけだが
改行コードがWin仕様だと、Ubuntuが正しく解釈できないので
全てのファイルの改行コードを\nに変換しておく。

忘れると、invalid operationと言われ泣きを見るはめになる。。。

definitionを微修正

definitions\ubuntu-13.04-desktop-i386\definition.rbを編集
32bitな民は、amd64ではなくi386のイメージを指定する。

8,11c8,11
<   :os_type_id => 'Ubuntu_64',
<   :iso_file => "ubuntu-12.10-server-amd64.iso",
<   :iso_src => "http://releases.ubuntu.com/12.10/ubuntu-12.10-server-amd64.iso",
<   :iso_md5 => '4bd3270bde86d7e4e017e3847a4af485',
---
>   :os_type_id => 'Ubuntu',
>   :iso_file => "ubuntu-13.04-server-i386.iso",
>   :iso_src => "http://releases.ubuntu.com/raring/ubuntu-13.04-server-i386.iso",
>   :iso_md5 => '73d595b804149fca9547ed94db8ff44f',

apt.shに追記

1行目にsourceのURLをミラーサーバから取得するよう変更するsed追記
変えなくても取得は可能だが、転送スピードが1.5~2倍近く早くなるので、推奨。

> sed -i -e 's#us.archive.ubuntu.com#mirror.math.ucdavis.edu#g' /etc/apt/sources.list

末尾に、ubuntu-desktopをインストールする行を追記。
このままだと、CLIなのでGUI用のパッケージ入れる。

> apt-get -y install ubuntu-desktop

definitionを基にubuntuをインストール

> bundle exec veewee vbox build 'ubuntu-13.04-desktop-i386' --workdir=C:/Users/takeshi.takizawa/work/veewee
Downloading vbox guest additions iso v 4.2.16 - http://download.virtualbox.org/virtualbox/4.2.16/VBoxGuestAdditions_4.2.16.iso
Checking if isofile VBoxGuestAdditions_4.2.16.iso already exists.
Full path: C:/Users/takeshi.takizawa/work/veewee/iso/VBoxGuestAdditions_4.2.16.iso

The isofile VBoxGuestAdditions_4.2.16.iso already exists.
Building Box ubuntu-13.04-desktop-i386 with Definition ubuntu-13.04-desktop-i386:
- debug : false
- cwd : C:/Users/takeshi.takizawa/work/veewee
- force : false
- nogui : false
- auto : false
- checksum : false
- redirectconsole : false
- postinstall_include : []
- postinstall_exclude : []

We did not find an isofile here : C:/Users/takeshi.takizawa/work/veewee/iso/ubuntu-13.04-server-i386.iso.

The definition provided the following download information:
- Download url: http://releases.ubuntu.com/raring/ubuntu-13.04-server-i386.iso
- Md5 Checksum: 73d595b804149fca9547ed94db8ff44f


Download? (Yes/No)

忘れずに、Yesとタイプする。
時間がかかるので、まったりコーヒーでも飲みながら待つ。

The box ubuntu-13.04-desktop-i386 was built successfully!
You can now login to the box with:
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 7222 -l vagrant 127.0.0.1

成功!

Win7 32bit Core i5-2520M 2.5GHz 4.00GB
で、3時間程度かかった。

Vagrant用boxファイル作成

> bundle exec veewee vbox export 'ubuntu-13.04-desktop-i386'

vagrantに登録

> vagrant box add ubuntu-13.04-desktop-i386 'C:/Users/takeshi.takizawa/work/veewee/ubuntu-13.04-desktop-i386.box'

vagrant up

ここからは、boxファイルに対して色々操作していく。
作業用ディレクトリに移動して

> cd working\directory

Vagrantfile作成

> vagrant init 'ubuntu-13.04-desktop-i386'

いざ、起動!

> vagrant up

sshで入れるかな?

> vagrant ssh
Welcome to Ubuntu 13.04 (GNU/Linux 3.8.0-19-generic i686)

 * Documentation:  https://help.ubuntu.com/
Last login: Thu Aug 15 07:15:17 2013 from 10.0.2.2
vagrant@ubuntu-13:~$

すばらっ

あとは、rails等のCookbookを
http://community.opscode.com/
から、ありがたく頂戴すれば良いはず。

32bit Win7上でVagrant使って開発環境構築 準備編

動機

備えあれば憂いなしでございます。

準備

  1. Install Ruby http://rubyinstaller.org/
  2. Install pik https://github.com/vertiginous/
  3. Install Development Kit http://rubyinstaller.org/downloads/
  4. Install veewee https://github.com/jedi4ever/veewee
  5. Install VirtualBox https://www.virtualbox.org
  6. Install Vagrant http://www.vagrantup.com
  7. Install Git http://git-scm.com/downloads

Install Ruby

Vagrantのpluginは、Rubyで書かれているので
gem installでサクッと入れられるように、Ruby入れます。

Install pik

2013/08/17 14:00
rubyのバージョンを1.9.2 -> 1.9.3に変更

Rubyのバージョン管理してくれるgem
Windows版のrmvもしくはrbenv

同じバージョンだけれど、gemだけ分けたいってのは無理なのかな?こういうの

$ rvm use 1.9.2@veewee --create

この件は、いったん保留して

サクッとgem install

> gem install pik

まだ、終わりじゃない。
PATH通っているところに、pikコマンドをインストールする必要がある。
Cドライブ直下に、binディレクトリ作ってパス通しておく。

> mkdir C:\bin

コントロールパネル -> システム -> システムの詳細設定 -> 詳細設定 -> 環境変数 -> システム環境変数
無ければPathを新規作成して、あればpathを編集
末尾に追記

;C:\bin

pikコマンドをインストールする

> pik_install C:\bin

ようやく完了。
ruby-1.9.3入れとく。

> pik install ruby 1.9.3
> pik use 1.9.3

もし、7zipが無いと怒られたら
エラーの通り、7zipを入れる。
親切なエラーですね、エラーってこうあるべき。

> pik package 7zip install

Install Development Kit

C/C++の拡張をbuildするのに必要。

最初のRubyと一緒に入れても良かったけれど
pikで取ってきたRubyにもインストールするので
ここで、一緒に入れる。

インストール手順
https://github.com/oneclick/rubyinstaller/wiki/Development-Kit

Cドライブ直下に、DevKitフォルダ作る
https://github.com/oneclick/rubyinstaller/downloads/
から、DevKit-4.5.2 self extracting archive ダウンロードする
展開先を、C:\DevKitにする

> cd C:\DevKit
> ruby dk.rb init

気をつけるのは、config.ymlにpikで入れたRubyのpathを追加することくらい
最後の行に、pikで入れたRubyのpathを追記。

# This configuration file contains the absolute path locations of all
# installed Rubies to be enhanced to work with the DevKit. This config
# file is generated by the 'ruby dk.rb init' step and may be modified
# before running the 'ruby dk.rb install' step. To include any installed
# Rubies that were not automagically discovered, simply add a line below
# the triple hyphens with the absolute path to the Ruby root directory.
#
# Example:
#
# ---
# - C:/ruby19trunk
# - C:/ruby192dev
#
--- 
- C:/Ruby192
- C:/Users/takeshi.takizawa/.pik/rubies/Ruby-193-p429

インストール
pikで新しくruby追加したら、そのバージョンにも忘れずにDevKit入れる。

> ruby dk.rb install

defaultのrubyを変更したい場合は、環境変数のPathのrubyの向き先を変える必要がある。
今回の場合、Pathには
C:/Ruby192
が書かれているので
C:/Users/takeshi.takizawa/.pik/rubies/Ruby-193-p429
に変更する。

いちいち面倒だな。。。

Install veewee

Vagrantのbox作成をサポートしてくれるgem

既存のboxで十分な場合は、不要。
既存のVagrant box一覧は、こちら
http://www.vagrantbox.es/

veeweeを使う場合、githubから頂戴してくる

> cd <path_to_workspace>
> git clone https://github.com/jedi4ever/veewee.git
> cd veewee

bundlerでinstall

> gem install bundler
> bundle install

veeweeコマンド叩いて、コマンド一覧が見れれば
インストール成功

> bundle exec veewee

Install VirtualBox

仮想マシンを実行する仮想化ソフトウェア・パッケージ

Install Vagrant

仮想マシンの雛形(box)から、お手軽に仮想マシンのセットアップができるツール。
仮想マシンごと作成、破棄できるので、気に入らなければ気軽に1から作り直せる。

Install Git

バージョン管理さまさま

RubyでDSL作ってみる

肝となる処理

instance_eval が便利。

ブロック渡し以外にも、文字列を渡してインスタンス内のコンテキストで実行可能。

てことで

  1. DSL用のメソッドAを用意
  2. DSLで書かれたファイルを読み込む
  3. メソッドAと同じインスタンスで、ファイルの内容をinstance_eval
  4. -_-b

Example

DSL用のメソッドを持ったクラス

class DSL
  attr_reader :names

  def self.execute
    contents = File.open('DSLFile', 'rb'){ |f| f.read }
    dsl = new
    dsl.instance_eval(contents)

    p dsl.names
  end

  def initialize
    @names = []
  end

  def hello(name)
    @names << name
  end
end

DSLファイル

hello 'world'
hello 'again'
hello 'kitty'

実行してみる

DSL.execute

["world", "again", "kitty"]

imagemagickをインストール

公式のFAQや、似たような記事は多数あるのですが、それでも嵌ったのでメモ残しときます。

事前準備

Install XQuartz

結果を出力するときに、ウィンドウ(X11)に出す場合は、先にXQuartzをインストールしておく必要があります。

display: delegate library support not built-in `' (X11) @ error/display.c/DisplayImageCommand/1909.
Unsatisfied dependency: XQuartz·
Homebrew does not package XQuartz. Installers may be found at:
  https://xquartz.macosforge.org

こんなエラーが出る場合は、XQuartzが入ってないです。
エラーが教えてくれている通りに、 https://xquartz.macosforge.org からインストールします。

Install libtool

なかったら入れておきます。

sudo brew install libtool

動かしたときに、以下のエラーが出る場合はリンク張りましょう

Library not loaded: /usr/local/lib/libltdl.7.dylib (LoadError)
sudo brew link --force libtool

set display

表示先を指定しておきます

export DISPLAY=:0.0

Install imagemagick

デフォルトだと、X11(XQuartz)利用しないので、オプションに指定してインストールします。

sudo brew install imagemagick --with-x11

お疲れ様です。インストール完了です。

動かしたときに、以下のエラーが出る場合は、X11が起動してません。

`display': unable to open X server `' @ error/display.c/DisplayImages/1666 (Magick::ImageMagickError)

/Applications/Utilities/XQuartz をダブルクリックして起動してやりましょう。


やれやれ、インストールで力つきちゃったよ。。。

バイナリ形式にするだけでメモリ節約プログラミング

比較条件に関係しない値なら、バイナリにする価値あり

小さい数値を多く扱う場合は、バイナリ(可変長表現)にすると結構小さくできます。

データ量が小さくなれば、DBからの取得速度が向上し、アプリのメモリ上にもより多くのデータを保持できるようになります。

ただ、バイナリ形式のままだと大小比較などの計算ができないので、必要に応じて数値型に戻してあげる必要があります。

コード

今回比較に利用したコードは、githubに上げてあります。

https://github.com/TakiTake/seiseki

数値とバイナリの速度比較

学校の生徒100人毎日テストを5科目受けたときの成績管理用DBを想定

バイナリ形式のDBは、科目カラムに5科目分のデータを保持させます。

テストデータなんで、土日祝日お構いなしにテスト受けてますw

ノーマルなDB

カラム名
日付 text
学籍番号 text
科目1 integer
科目2 integer
科目3 integer
科目4 integer
科目5 integer

バイナリ形式のDB

カラム名
日付 text
学籍番号 text
科目 blob

結果

1年分のデータ

size

normal  1.9MB
binary  1.7MB

insert

user     system      total        real
normal  4.740000  11.920000  16.660000 ( 94.378284)
binary  4.660000  11.920000  16.580000 ( 93.093927)

select

user     system      total        real
normal  0.210000   0.010000   0.220000 (  0.221661)
binary  0.190000   0.010000   0.200000 (  0.201906)
5年分のデータ

size

normal  9.6MB
binary  8.8MB

insert

user     system      total        real
normal 23.860000  58.860000  82.720000 (476.608330)
binary 23.530000  58.400000  81.930000 (507.330652)

select

user     system      total        real
normal  1.190000   0.070000   1.260000 (  1.300539)
binary  0.980000   0.070000   1.050000 (  1.078891)
10年分のデータ

size

normal  19MB
binary  18MB

insert

user     system      total        real
normal 47.350000 118.030000 165.380000 (920.600014)
binary 46.610000 118.380000 164.990000 (935.340207)

select

user     system      total        real
normal  2.140000   0.100000   2.240000 (  2.236974)
binary  1.790000   0.100000   1.890000 (  1.891564)

なぜ、サイズが小さくなるのか?

小さい数値を表すのに、8 byte もいらないよね

環境差分はありますが、自分の環境ではRubyのFixnumは、8 byte でした。

0~255までの数値なら 1byte で表すことができるので、固定長ではなく可変長で数値を扱えば、 7byte 削減できというわけです。

どうやって変換すればいいの?

数列 -> バイナリ文字列

Array#pack のテンプレートに w(BER-compressed integer) を指定するだけです。

http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-pack

バイナリ文字列 -> 数列

String#unpack テンプレートに w(BER-compressed integer) を指定するだけです。

http://www.ruby-doc.org/core-1.9.3/String.html#method-i-unpack

応用編

ただの文字列なので、文字列結合もできます

  [33, 49, 300, 99, 1000].pack('w5') // "!1\x82,c\x87h"
  'abc' + [33, 49, 300, 99, 1000].pack('w5') // "abc!1\x82,c\x87h"

また、こういった文字列処理には、StringScannerが便利です。

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/strscan/rdoc/StringScanner.html

先頭から、3 byte 取得

  str = 'abc' + [33, 49, 300, 99, 1000].pack('w5')
  ss = StringScanner.new(str)
  ss.peek(3) // "abc"

3 byte 分ポインタを進める

  ss.pos += 3

ここから後ろは、可変長なので何byte分取得すればいいかわかりません。

そんなときは、文字列中にbyte数を持たせてあげれば、そこから判断できます。

サイズを含む文字列

  packed = [33, 49, 300, 99, 1000].pack('w5')
  
  # packした文字列のサイズをさらに、packする
  # C(8-bit unsigned)にすることで、この1byteを読めば続く文字列のサイズが分かる
  # 文字列のサイズが、8bitで表しきれない場合は、テンプレートにS, L, Qを適宜使う
  packed_size = [packed.size].pack('C')
  
  str = 'abc' + packed_size + packed
  ss = StringScanner.new(str)
  
  # "abc"の3byte分進める
  ss.pos += 3
  
  # 1byte取得して、unpackすることで保存しておいたサイズを取得
  # unpackは、配列で返ってくるので、最初の要素を取得している
  ss.peek(1).unpack('C').first // 7
  
  # サイズを格納している、1byte分進める
  ss.pos += 1
  
  # 最後に、先ほど判明した7byte分取得して、unpackすると
  # めでたく最初の数列が取得できる
  ss.peek(7).unpack('w5') // [33, 49, 300, 99, 1000]