tomykaira makes love with codes

http://d.hatena.ne.jp/ToMmY/

2012-05-08

Reading code of rack 1

これは codereading グループの活動の一貫の記事なので、broken English で書かれています。
おもな活動場所は Issues · codereading/rack なので、興味があったらチェックしてみてください。

This is part of codereading group activity, so written in broken English.
We are in Issues · codereading/rack.
Thank you for any discussion, notation, and pointing out errors.

In this series of article (if I can continue), I start from /lib/rack/lobster.rb, the easiest rack application, to see through all of the rack codes.

First, see the lobster

At first, clone the rack repository and run the following command. It runs WEBrick.

$ ruby lib/rack/lobster.rb
[2012-05-08 08:15:56] INFO  WEBrick 1.3.1
[2012-05-08 08:15:56] INFO  ruby 1.9.2 (2011-02-18) [i686-linux]
[2012-05-08 08:15:56] WARN  TCPServer Error: Address already in use - bind(2)
[2012-05-08 08:15:56] INFO  WEBrick::HTTPServer#start: pid=3408 port=9292

By accessing to http://localhost:9292/, you see cute lobster like this.

                             ,.---._
                   ,,,,     /       `,
                    \\\\   /    '\_  ;
                     |||| /\/``-.__\;'
                     ::::/\/_
     {{`-.__.-'(`(^^(^^^(^ 9 `.========='
    {{{{{{ { ( ( (  (   (-----:=
     {{.-'~~'-.(,(,,(,,,(__6_.'=========.
                     ::::\/\
                     |||| \/\  ,-'/,
                    ////   \ `` _/ ;
                   ''''     \  `  .'
                             `---'

In order to quit, because this application blocks SIGINT, you should send SIGKILL from kill command.

$ ps -ef | grep "lobster"
tomita    3408  3137  0 08:15 pts/4    00:00:03 ruby lib/rack/lobster.rb
tomita    3669  2789  0 08:30 pts/3    00:00:00 grep --color=auto lobster
$ kill -9 3408

Code of lobster.rb

In this file, we define Rack::Lobster class as an application class, and run it if $0 == __FILE__, that is, this file is executed directly.
If you require this file from irb, WEBrick never runs.

LobsterString is ascii art data of the lobster. Paste this code into irb to check it.

LambdaLobster is the lambda-version of basically the same application, but this is used only for testing in spec_lobster.rb. This proves that rack can take lambda, not an instance of some class.

In order to run LambdaLobster, rewrite the last code like this, then re-run.

if $0 == __FILE__
  require 'rack'
  require 'rack/showexceptions'
  Rack::Handler::WEBrick.run \
    Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster::LambdaLobster)),
    :Port => 9292
end

Inside the call method is the logic of this application. Here uses Request and Response class, and not in Lambda version. Check these applications as samples.

At last, we runs this application with middlewares. About middleware, check #151 Rack Middleware - RailsCasts or something.

Middleware usually takes an app as the first constructor argument, and respond to call. In normal condition, a middleware calls the given app's call method.

Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new))

Rack::Lint.new(Rack::Lobster.new) returns a callable object, which calls Rack::Lobster.new.call in it.

Then, check these two middlewares.

Rack::ShowExceptions

This class is in /lib/rack/showexceptions.rb.

ShowExceptions rescues all errors raised in the given app, and report them for debugging. Try clicking the "crash" button of lobster.rb without this and with this.

From now on, I just point out something interesting.

env["rack.errors"]

This is specified in SPEC as "The Error Stream". Usually this is connected to $stderr by Rack::Handler('s subclass).
It should respond to write, puts, etc. likes a writable IO object.

In contrast, env["rack.input"] represents raw HTTP POST data. This is also specified as "The Input Stream". This should behave like a readable IO object.

Rack::Lint

Rack::Lint is for checking that your app is satisfying rack SPEC. At first, assert method is defined.

module Assertion
  def assert(message, &block)
    unless block.call
      raise LintError, message
    end
  end
end

This method is used not only in this top class, but in InputWrapper and ErrorWrapper. Therefore it is defined in a module, and included.

Lint checks the following points.

  • env, contents of env
  • env['rack.input'] (using wrapper)
  • env['rack.error'] (using wrapper)
  • status
  • header
  • body (in each)
    def call(env=nil)
      dup._call(env)
    end

is maybe for protecting the internal state of a Lint instance.

There is an idiom in _call.

    def _call(env)

      # by return self as the body, self.each will be called on presentation
      # Lint#each validates @app.body and a caller of self.each
      [status, headers, self]
    end

Other parts describe rack SPEC in ruby. Check them to get detail view of the specification.

The point I noticed is hash key of headers should be string, symbol is not allowed.

        assert("header key must be a string, was #{key.class}") {
          key.kind_of? String
        }

That's all for the first article.
I will see through WEBrick.run tomorrow.

2012-05-06

milkode 0.7.0 の git 機能に対応しました

Milkode0.7リリース - githubに公開されているソースコードを簡単に追加&検索 - おんがえしの日記 で対応された git 形式の URL を add できる機能は、gitomb が勝手に実装していたものです。
本家のほうにとりこんでいただいたようなので、これを利用する形に更新しました。

これで gitomb はさらに薄い wrapper になってしまいましたが、よいことだと思います。

さらに、`milk update` も git 対応したので、すべてのパッケージを update する機能をとりあえず用意してみました。
すべてをアップデートしたい事は稀で、個別のほうがよさそうですが、そもそも使用するかわからないので。

tomykaira/gitomb · GitHub

2012-05-03

emacs の ruby-mode で eigen class が heredoc に認識されるのを直す

class << self
end

と書けばいいのですが、たまに

class <<self
end

と書く人とか

class <<HogeClass
end

とか書くひとがいて、こうすると HEREDOC だと認識されてファイルの終わりまでピンクになる。

ruby-mode.el を修正した。

Index: ruby-mode.el
===================================================================
--- ruby-mode.el        (リビジョン 34176)
+++ ruby-mode.el        (作業コピー)
@@ -1308,11 +1308,10 @@
           (point)))))
 
   (defun ruby-here-doc-beg-syntax ()
-    (save-excursion
-      (goto-char (match-beginning 0))
-      (unless (or (ruby-in-ppss-context-p 'non-heredoc)
-                  (ruby-in-here-doc-p))
-        (string-to-syntax "|"))))
+    (cond
+     ((ruby-in-ppss-context-p 'non-heredoc) nil)
+     ((ruby-in-here-doc-p) (string-to-syntax "|"))
+     (t nil)))

意味がちがっちゃってるような気もしますが、やりたかったことはできたし、不具合もないので。

emacs の構文処理は poor ですね。lex とか直接扱っちゃえばいいのに。

2012-05-03

tweet-button gem を rails で読み込んだ瞬間に有効にする

intridea/tweet-button
intridea のバージョンだと、

module ApplicationHelper
  include TweetButton
end

みたいに書かないと動作しなくて面倒。

Rails Railtie
Railitie を使用する。起動時に Rails とたわむれるための方法。

To extend Rails using Railtie, create a Railtie class which inherits from Rails::Railtie within your extension’s namespace.

テンプレ:

# lib/my_gem/railtie.rb
module MyGem
  class Railtie < Rails::Railtie
  end
end

# lib/my_gem.rb
require 'my_gem/railtie' if defined?(Rails)

will_paginate ではこんなかんじ(will_paginate/lib/will_paginate/railtie.rb at master · mislav/will_paginate · GitHub)

initializer "will_paginate" do |app|
  ActiveSupport.on_load :active_record do
    require 'will_paginate/active_record'
  end

  ActiveSupport.on_load :action_controller do
    WillPaginate::Railtie.setup_actioncontroller
  end

  ActiveSupport.on_load :action_view do
    require 'will_paginate/view_helpers/action_view'
  end

  self.class.add_locale_path config

  # early access to ViewHelpers.pagination_options
  require 'will_paginate/view_helpers'
end

tweet-button は action_view で使用するので、次のように書いた。

module TweetButton
  class Railtie < Rails::Railtie
    initializer "tweet-button" do |app|
      ActiveSupport.on_load :action_view do
        ::ActionView::Base.send :include, TweetButton
      end
    end
  end
end

これを tweet-button.rb から呼び出す。
Rails のバージョン問題を回避するために、Rails::Railitie が定義されていることを条件とした。

require File.join(File.dirname(__FILE__), 'tweet-button', 'railitie') if defined?(Rails::Railtie)

最終的に: tomykaira/tweet-button at auto-load

2012-05-01

WM_CLASS 名を書き換えて firefox をビルドする

awesome window manager は WM_CLASS を見てふりわけをするので、たとえば二つの Firefox をことなる tab で動作させたい場合はべつの WM_CLASS を設定しておかないと不便。

以前(Firefox 6.0 stable) とやりかたが違っていたので、まとめなおす。

本来は "Firefox" という WM_CLASS がわりあてられるので、これを "FxDev" に書き換える。

対象バージョン

Firefox 15.0 nightly (hg tip. 992588c2eab6)

環境

OS と desktop manager には依存しているので、これらが異なる場合は書き換えるファイルのパスを適宜変更する。

  • Ubuntu 10.04
  • awesome v3.4.3
  • gtk 2

やりかた

hg cl http://hg.mozilla.org/mozilla-central/ mozilla-central
nsWindow::SetWindowClass(const nsAString &xulWinType)
{
  if (!mShell)
    return NS_ERROR_FAILURE;

  const char *res_class = "FxDev"; // gdk_get_program_class();
  if (!res_class)
    return NS_ERROR_FAILURE;
...
  • build config を書く。ac_options で名前指定しているのは、意味あるのか不明。
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_MAKE_FLAGS="-j4"
ac_add_options --enable-optimize
ac_add_options --disable-tests
ac_add_options --with-app-name="FxDev" --with-app-basename="FxDev"
  • build する。
make -f client.mk build
  • 起動してみる
obj-i686-pc-linux-gnu/dist/bin/firefox -no-remote -P
  • xprop で window 情報を確認する

中間ファイルを削除する的なものがほしいんだけどないのかな。

2012-04-29

ファイル名で cd する

~/.bashrc に次の設定を書く。

function mycd(){
    \cd $1 || \cd $(dirname $1)
}
alias cd=mycd

第一引数に与えられたファイル / ディレクトリに cd を試みて、だめだったらそのファイルのあるディレクトリに cd する。
ブラウザやエディタからパスをコピーして、そこに cd する時に手間が減る。

~/programs/ > cd ~/my/note.org
bash: cd: /home/tomykaira/my/note.org: Not a directory
note.org  portfolio.md
~/my (master)> 

私は Emacs の C-x x で現在編集中のバッファのファイル名をコピーするようにしていて、それを terminal にさっと貼り付けて移動することができる。

2012-04-28

tmux をカスタマイズして bash のかわりに -- と表示する

f:id:ToMmY:20120428085309p:plain
最近 screen から tmux にのりかえました。
tmux はコマンド名を自動で判定してくれるので、これを生かしつつ、bash のような trivial なコマンドではコマンド名を表示したくない。
うまいやりかたがわからなかったので source を直接編集。
names.c のコマンドを切り出す部分に patch

char *
parse_window_name(const char *in)
{

...

	// PATCH
	if (strcmp(name, "bash") == 0) {
		strcpy(name, "--");
	}

	name = xstrdup(name);
	xfree(copy);
	return (name);
}

ubuntu なので、apt を使用する。

$ sudo apt-get install quilt
$ apt-get source tmux
$ cd tmux-*
// names.c を編集
$ dpkg-buildpackage -rfakeroot -uc -b
$ cd ..
# dpkg -i tmux*.deb