Redisのソースコードをちょっと読んでみようという試み(1)
C言語でHello world以上のものを読むのは初めてだった。とりあえず実装の詳細は飛ばして、
サーバー立ち上げる -> クライアントから繋がる -> コマンドが実行される -> 書き込まれる までの関数の呼ばれる流れだけを追ってみた。
大分意図的に端折ってるし、意図せず抜けてたり間違ってたりすると思うから、間にうけないほうが良さそう
Doxygenというので、ドキュメントっぽいのを生成して、それも時々見てた。全然関係ないけどdoxygenをgui付きのオプション付けて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というやつが, コマンドの情報とか色々持っていて、そいつに対して値をセットしたり色々しながら 最終的にそいつが持ってる情報から実行するみたいな感じだった。とりあえずメモを残そうと思って残したけど、次読もうとするのいつになるか分からない。
■
会話のなかでモヒカンという言葉を使うのには特になにも感じないけど、オッサンが「おれモヒカンッじゃねっしー」とか「xxさんモヒカンッスよー」とか「モッヒー、やべモヒ」とか言い合ってるの見るの苦手でとてもつらい気持ちになる。特に言いたいことなし。
もう一個面白かったやつ
irb(main):007:0> require 'benchmark' => true irb(main):008:0> require 'bigdecimal' => true irb(main):009:0> n = 1000 => 1000 irb(main):011:0> Benchmark.realtime { n.times { BigDecimal('3') == true } } => 0.023832 irb(main):010:0> Benchmark.realtime { n.times { BigDecimal('3').is_a?(TrueClass) } } => 0.001259
https://github.com/rails/rails/commit/1fd78305b5812c186d9eed9475677f90946eba5f
シンボル
フト見たRailsのコミットログ https://github.com/rails/rails/commit/5d58948fe72e3b0422790b8adc0fab7bbf9e6573
to_symからto_s使うように変えてるけど、なぜだか一瞬分からなかった。シンボルがGCに回収されないから、入力値によっていくらでもシンボル増えるようなのはダメだ。ってことだろうということで落ち着いた。他の理由は特に思いつかなかった。
熱
肩こり本当にひどいなーと思ったら熱出ることがよくあるんだけど、この1年くらい体調崩してないから大丈夫かなと思ってたらやっぱり熱出た。
WebSocketからIRC繋げるやつ作った
今日Web Audioのハッカソンというかまあそんな感じのをしてて、音のなるIRC Client(on Browser)を作りたいと思って、そのクライアントを作るのを簡単にするために、WebSocketからIRC見たり、ポストしたり出来るようにしようと思って作った。Web Audioの会なのに、Audio関係するところまでいかずに終わってしまった。
https://github.com/ToQoz/ircmad
IRC <-> WebSoket Server(ここ作った) <-> WebSocket Client
こんな感じで立ち上げる
# proxy.rb require 'ircmad' Ircmad.new do set :host, '127.0.0.1' set :port, 6667 set :channel_list, [ '#channel', '#channel2' ] set :username, 'username' set :password, 'password' # 必要であれば set :websocket_port, 3333 end.run!
$ ruby proxy.rb
普通にonmessageにコールバック登録しておいたら、JSONが渡ってくるし、sendでJSON送ったら投稿できる。
var socket = new WebSocket('ws://localhost:3333') // Send socket.send(JSON.stringify({ channel: '#channel1', body: 'yeah' })) // Get socket.onmessage = function(msg) { console.log(msg.data) }; // => '{"username":"ToQoz","channel":"#channel1","body":"hello world"}'
ブラウザでIRCクライアントを作るとなるとちょっと面倒そうだけど、 こんな感じでWebSocketから扱えれば、簡単に作り放題だし、好きなの作れるかなと思った。
追記
サーバからの:PINGに対する PONG ってどーしてるんすか?
とのことですが、使用しているzirconというgemがやってくれています。 https://github.com/r7kamura/zircon/blob/master/lib/zircon.rb#L69
というかそもそもIRCとのやりとりは今のところ完全に任せっきりですね。
まあそれはともかくとして、IRC Server側が落ちた時とか長時間スリープした時(?)とかに微妙な感じなの、直したいと思っております。。
追記 v0.0.3
やりとりするjsonのフィールド名変えました。
at v0.0.3
var socket = new WebSocket('ws://localhost:3333') // Send socket.send(JSON.stringify({ to: '#channel1', body: 'yeah', type: 'privmsg' })) socket.send(JSON.stringify({ type: 'join', to: '#ruby'})) // Get socket.onmessage = function(msg) { console.log(msg.data) }; // => '{"from":"ToQoz","to":"#channel1","body":"hello world","type":"privmsg"}' // => {"from":"ToQoz","to":"#ruby","body":null,"type":"join"} // => {"from":"hybrid7.debian.local","to":"ToQoz","body":"@","type":"353"} // => {"from":"hybrid7.debian.local","to":"ToQoz","body":"#ruby","type":"366"}
あとreconnect的なのとかちょこちょこ入れた。
Devise and https
devise使ってて, ログインとか登録とかcontroller で force_sslとかして, https使うようにしてた場合、 大体普通に動くけどログアウトのリンクをプロトコル付きで生成するようにしとかないと、httpなページからログアウトしようとすると、DELETE http://example.com/users/sign_out 送って、そんなページないと怒られる。
- <%= link_to destroy_user_session_path, :method => 'delete' do %> + <%= link_to destroy_user_session_url(:protocol => "https"), :method => 'delete' do %>
こんな感じで良さそう
Qiita Hackathonで作ったやつ
https://github.com/ToQoz/anything-hub
$ anything-hub search:rails $ anything-hub starred:ToQoz
みたいな感じでリポジトリ検索の結果やユーザーのスター一覧をanything風のインターフェースで絞り込んで選択肢ブラウザで表示するってやつ。
キャッシュするためだけのコマンドがあるので、
$ crontab -e 05 12 * * * /Users/toqoz/.rbenv/shims/anything-hub cache:search:rails >> /tmp/anything-hub.cron.log 2>> /tmp/anything-hub.cron.error.log 05 1 * * * /Users/toqoz/.rbenv/shims/anything-hub cache:starred:ToQoz >> /tmp/anything-hub.cron.log 2>> /tmp/anything-hub.cron.error.log
みたいな感じで定期的に実行するようにしておけば、実際使うときには比較的新しいキャッシュから高速に検索できる。
普通に使う分にはユーザー名だけ~/.anything-hubrc
に書いておけば良くて(書き方はREADME.mdに書いてる)、一回目パスワード聞いて、そのユーザー名とパスワードからoauth tokenとって来てキャッシュしているが、キャッシュ消しちゃったときとかまたパスワード聞くプロンプト出たりするので、cronとか非対話環境で実行する場合は~/.anything-hubrc
にtokenも書いておいたほうが良いと思う。tokenを取得するには$ anything-hub token
ってコマンドで取れるので、それを書いたら良い。
検索結果をanything風に絞り込んで選択はオマケ程度で、ユーザーのスターを検索できるのが便利だと思う。公式ではstarsはリポジトリ名ユーザー名からしか検索できないが、descriptionからも絞り込めるので、昔star押したと思う、よく覚えてないファイルアップロードのgemとかも、uploadとかで絞り込める。
OSX使ってないひと、というかOSXのopenコマンドみたいなのが違う名前の人は-s オプションにそのコマンド名渡せば良い。
■
ドメイン駆動とかそういうの、意図してないとしても言葉の節々から、「自分たちは顧客の業務への理解を高める必要があるが、顧客はシステムを分からないのはしょうがない」のような、虐げられる側が必死で頑張る姿勢みたいなもの感じて、とても辛く悲しくなってきて、結局頭に入らない。
Generate password for .htpasswd in Ruby
Nginx用に作るときにApache入れてないとかでダルいので
■
昨日の夢に出てきたgem、「ズンドコドーン、承認ッ、ズイズイ」っていって人々の承認欲求を満たしてくれるやつだった。
pryの履歴からanythingっぽい感じで絞り込む
必要. 適当にgem installすれば良いと思う
~/.pryrc に追記
require 'ruby-anything' def anything_hist(tail = 10) _anything_ command_result("hist --tail #{tail}").lines.map { |h| h.gsub(/^ *\d+: */, '').chomp } end
$ pry pry> anything_hist(50) | -> v { puts v; command_result(v) } >>>> 履歴からanything インターフェースで選択して実行 >>>>