Rubyが辛くなってきた俺はHaskellに浮気する
はじめに
社内で、Web API呼び出しを束ねるWeb API作成ブームだから、Haskellでブームに乗ってみようかなと。Ruby好きなんだけれど、人数が増えてくるとコードのフォーマットがバラバラになるのが辛くて、型チェックが羨ましくなったのが選択理由。
関数プログラミング実践入門
関数プログラミング実践入門 ──簡潔で、正しいコードを書くために (WEB+DB PRESS plus)
- 作者: 大川徳之
- 出版社/メーカー: 技術評論社
- 発売日: 2014/11/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
確実にこの本に感化されてるw こちらは、いざHaskell使って何か作ろうとしたときに、つまずくであろう箇所を丁寧に解説してくれている。Haskellをサンプルに、オブジェクト試行の言語と比較しながら解説されているので、自分にはよく刺さった。
すごいHaskellたのしく学ぼう!
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (67件) を見る
次に購入した本、Haskellという言語自体を学ぶのに適している。 堅い言語とは逆に、軽快な口調で解説しているので、とっつきやすい。ファンクターの説明辺りで理解が追いつかなくなったので、精進せねば。
環境構築
Haskell Platform
何はともあれ、https://www.haskell.org/platform/
for Vimmer
fast-tag
ctagsはHaskell未対応なので、fast-tagsにtagsファイル作成してもらう
ghc-mod
依存解消でエラーになったので、下記ページを参考に
Build failed · Issue #425 · kazu-yamamoto/ghc-mod · GitHub
$ ghc-pkg unregister --force monad-control $ cabal install --constraint "monad-control < 1" ghc-mod
Framework
https://www.haskell.org/haskellwiki/Web/Frameworks https://www.techempower.com/benchmarks/
Yesod
Yesodが人気なようなので、Yesodでベースに作成しようと思う。
Rubyの子クラスで定数を再定義したのに親の定数が参照されちゃう
ちゃう
class Parent CONST = 'parent' def initialize p CONST end end class Child1 < Parent CONST = 'child' end Child1.new # "parent"
"child"を期待してたら、"parent"が返ってきた!?
定数の場合、今のスコープになかったら、外のスコープから探索するので initializeメソッドの外に出たら、Parent::CONSTがいるので、その値を返しているみたい。
じゃあ、子クラス毎にinitializeメソッドを定義すれば
class Parent CONST = 'parent' end class Child1 < Parent CONST = 'child' def initialize p CONST end end Child1.new # "child"
やりました、期待通り"child"が返ってきました!
ないな
要は 子クラス::CONST を参照してくれれば良いのでしょ
class Parent CONST = 'parent' def initialize p self.class::CONST end end class Child1 < Parent CONST = 'child' end Child1.new # "child"
いけそう
class Parent CONST = 'parent' def initialize p self.class::CONST end end class Child2 < Parent; end Child2.new # "parent"
さらに、継承しても
class Parent CONST = 'parent' def initialize p self.class::CONST end end class Child2 < Parent; end class Grandchild < Child2 CONST = 'grandchild' end Grandchild.new # "grandchild"
期待通り!
git用のpre-commit gemが便利すぎる
まずは
$ gem install pre-commit # In your git repo $ pre-commit install
これだけで、うっかり残念なコミットをする事故が防げます。
Git フック使ってますか?
コミットする前にバリデーションかけて、通らなかったらコミット中止する。コミットメッセージにブランチ名を自動挿入する。など、ルーティンワークをフックに仕込んでおけば、誰も読まないガイドラインページを書く手間が省けますし、強制する事ができて幸せです。
じゃあ、何をフックに設定するかと言うと
コードレビューしていると、気になるけどいちいち指摘するのも面倒くさい事が良いんじゃないかと。
- 行末の空白文字くらい消せよ!
- タブインデント止めて!
- 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構築編
本日の流れ
- veeweeのtemplateから作成したいVMのtemplate選ぶ→definition作成
- definitionを微修正
- definitionを基にubuntuをインストール
- Vagrant用boxファイル作成
- 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使って開発環境構築 準備編
動機
備えあれば憂いなしでございます。
準備
- Install Ruby http://rubyinstaller.org/
- Install pik https://github.com/vertiginous/
- Install Development Kit http://rubyinstaller.org/downloads/
- Install veewee https://github.com/jedi4ever/veewee
- Install VirtualBox https://www.virtualbox.org
- Install Vagrant http://www.vagrantup.com
- Install Git http://git-scm.com/downloads
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 Git
バージョン管理さまさま
RubyでDSL作ってみる
てことで
- DSL用のメソッドAを用意
- DSLで書かれたファイルを読み込む
- メソッドAと同じインスタンスで、ファイルの内容をinstance_eval
- -_-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"]