Multi-column Layout Module

早見表

プロパティ 初期値
column-width | auto auto
column-count | auto auto
columns <'column-width'> || <'column-count'> 各項目を参照
column-gap | normal normal
column-rule-width <'border-width'> medium
column-rule-style <'border-style'> none
column-rule-color -
column-rule || || [ | transparent ] 各項目を参照
break-before auto | always | avoid | left | right | page | column | avoid-page | avoid-column auto
break-after auto | always | avoid | left | right | page | column | avoid-page | avoid-column auto
break-inside auto | avoid | avoid-page | avoid-column auto
column-span 1 | all 1
column-fill auto | balance balance

参考

FORM の action の URI にクエリをつけたまま GET で送信できなかったのでなんでか調べてみた

あれ?タイトルわかりづらい?

要はこういうことです

  1 <html>
  2 
  3 <body>
  4   <form method="GET" action="./form.html?key1=value1">
  5     <input type="text" name="key2" value="value2" />
  6     <input type="submit" value="test" />
  7   </form>
  8 </body>
  9 
 10 </html>

これでボタン押したときにクエリとして

  • key1 : value1
  • key2 : value2

が送られてきてほしいのです
しかし、結果として key2 しか送られてきません
...おい、key1 どこいった?
というわけでアクセスログを見てみると

::1 - - [25/Jul/2010:23:06:04 +0900] "GET /~xxx/form.html?key2=value2 HTTP/1.1" 200 191

となっています。はい、 key1 どっかに消えました

そこで以下のように GET, POST の両方を作って実験してみました

  1 <html>
  2 
  3 <body>
  4   <form method="GET" action="./form.php?key1=value1">
  5     method : GET <br />
  6     <input type="text" name="key2" value="value2" />
  7     <input type="submit" value="test" />
  8   </form>
  9 
 10   <form method="POST" action="./form.php?key1=value1">
 11     method : POST <br />
 12     <input type="text" name="key2" value="value2" />
 13     <input type="submit" value="test" />
 14   </form>
 15 
 16   <pre>
 17     $_GET : <?php var_dump($_GET); ?>
 18   </pre>
 19 
 20   <pre>
 21     $_POST : <?php var_dump($_POST); ?>
 22   </pre>
 23 
 24 
 25 </body>
 26 
 27 </html>

GET の結果

    $_GET : array(1) {
  ["key2"]=>
  string(6) "value2"
}
  
    $_POST : array(0) {
}

POST の結果

    $_GET : array(1) {
  ["key1"]=>
  string(6) "value1"
}
  
    $_POST : array(1) {
  ["key2"]=>
  string(6) "value2"
}

ここからわかる状況

  • GET : action で指定した URL の ? 以下を無視している
  • POST : action で指定した URL の通りに動作している


なんでこのようなことになっているのかは調べきれなかったので、また今度調べます
なんとなくブラウザの仕様なんじゃないかなーと思ったりもしました
(あれ?ブラウザの仕様なら HTML の定義のどっかに書いてあるのかな。。。でも見つけられんかった。。。

参考
http://labs.uechoco.com/blog/2007/11/htmlformactionget.html
http://chaichan.web.infoseek.co.jp/perlnote/perlnote2010-05-31.htm

websocket の echo server を作ってみた

目的

  • html5 の websocket を使って簡単な通信をしてみる

ソース

websocket.html

  1 <!DOCTYPE html>
  2 
  3 <html>
  4 
  5 <head>
  6   <meta charset="utf-8">
  7 
  8   <script type="text/javascript">
  9     var ws = new WebSocket("ws://localhost:8080/");
 10 
 11     ws.onopen = function(event) {
 12       alert("websocket " + event + " is opened");
 13     };
 14 
 15     ws.onmessage = function(event) {
 16       alert(event.data);
 17     };
 18 
 19     function send() {
 20       var msg = document.getElementById("msg").value;
 21       ws.send(msg);
 22     }
 23 
 24   </script>
 25 </head>
 26 
 27 <body>
 28   <input type="text" id="msg" />
 29   <button onclick="send();">send</button>
 30 </body>
 31 
 32 </html>

echo_server.py

  1 import socket
  2 
  3 BUFSIZE = 4096
  4 
  5 host = socket.gethostbyname('localhost')
  6 port = 8080
  7 
  8 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  9 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 10 sock.bind((host, port))
 11 sock.listen(1)
 12 
 13 print 'waiting for connection...'
 14 
 15 (client_sock, client_addr) = sock.accept()
 16 msg = client_sock.recv(BUFSIZE)
 17 
 18 [head, body] = msg.split("\r\n", 1)
 19 header = {}
 20 
 21 for line in body.splitlines():
 22   if line == "":
 23     break
 24   else:
 25     [key, value] = line.split(": ", 1)
 26     header[key] = value.strip()
 27 
 28 # websocket handshake
 29 handshake  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
 30 handshake += "Upgrade: %s\r\n" % header['Upgrade']
 31 handshake += "Connection: %s\r\n" % header['Connection']
 32 handshake += "WebSocket-Origin:%s\r\n" % header['Origin']
 33 handshake += "WebSocket-Location: ws://localhost:8080/\r\n"
 34 handshake += "\r\n"
 35 
 36 client_sock.send(handshake)
 37 
 38 print 'connection start'
 39 print '---------- ---------- ----------'
 40 
 41 while True:
 42   msg = client_sock.recv(BUFSIZE)
 43 
 44   if msg == "":
 45     break
 46   else:
 47     print "client: %s" % msg[0:len(msg)-1]
 48     client_sock.send(msg)
 49 
 50 client_sock.close()
 51 
 52 sock.close()
 53   
 54 print '---------- ---------- ----------'
 55 print 'connection end'
 56     

実行

  • chrome から文字列を送ってみる
connection start
---------- ---------- ----------
client: this
client: is
client: a
client: test
---------- ---------- ----------
connection end

考察

  • そもそも最初は通信自体がうまくいなかった
    • handshake を行わないと通信を確立してくれない
  • 47行目で受け取った文字列の後ろ2バイトを削っている
    • 終端文字として何か入ってるみたいだが文字化けするので削った


参考

memcache クライアントを実装してみた その2

目的

  • 前回 methaneさんにコメントで指摘された部分の修正


ソース

  • 修正した関数は以下の2つなので今回はその部分のソースのみ載せます
    • set
    • get
 21   def set(self, key, value, exptime=0, flags=0):
 22     # translate value to binary
 23     pickled_value = pickle.dumps(value, 2)
 24     length = len(pickled_value)
 25     totalsent = 0
 26 
 27     # request
 28     sendmsg  = "set %s %d %s %d" % (key, flags, exptime, length)
 29     sendmsg += "\r\n"
 30     sendmsg += pickled_value
 31     sendmsg += "\r\n"
 32 
 33     while totalsent < length:
 34       sent = self.__sock.send(sendmsg[totalsent:])
 35       totalsent += sent
 36 
 37     # response
 38     recvmsg = ''
 39 
 40     while True:
 41       chunk = self.__sock.recv(BUFSIZE)
 42       recvmsg += chunk
 43 
 44       if recvmsg.find("\r\n") != -1:
 45         break
 46 
 47     # return
 48     if recvmsg == "STORED\r\n":
 49       return True
 50     else:
 51       return False
 52 
 53 
 54   def get(self, key):
 55     # request
 56     sendmsg = "get %s\r\n" % key
 57     length = len(sendmsg)
 58     totalsent = 0
 59 
 60     while totalsent < length:
 61       sent = self.__sock.send(sendmsg[totalsent:])
 62       totalsent += sent
 63 
 64     # response
 65     recvmsg = ''
 66 
 67     ## header
 68     header = ''
 69 
 70     while True:
 71       chunk = self.__sock.recv(BUFSIZE)
 72       recvmsg += chunk
 73 
 74       if header == '' and recvmsg.find("\r\n") != -1:
 75         [header, dummy] = recvmsg.split("\r\n", 1)
 76         break
 77 
 78     [dummy1, dummy2, dummy3, length] = header.split()
 79 
 80     ## body
 81     body = ''
 82     length = len(header) + len("\r\n") + int(length) + len("\r\n") + len("END\r\n")
 83 
 84     totalrecv = len(recvmsg)
 85 
 86     while totalrecv < length:
 87       chunk = self.__sock.recv(BUFSIZE)
 88       recvmsg += chunk
 89       totalrecv += len(chunk)
 90 
 91     [header, body] = recvmsg.split("\r\n", 1)
 92     [pickled_value, dummy1, dummy2] = body.rsplit("\r\n", 2)
 93 
 94     # return
 95     return pickle.loads(pickled_value)


修正点

  • set
    • value をバイナリに変換
    • メッセージを確実に全て送信
    • メッセージを少なくとも必要な分は全て受信
  • get
    • メッセージを確実に全て送信
    • メッセージの一つ目の改行文字"\r\n"で分割して header とする
      • header から set した value の長さを取得
    • メッセージの全ての長さを計算
      • header + "\r\n" + value + "\r\n" + "END\r\n"
    • メッセージを少なくとも必要な分は全て受信
    • バイナリから value へ変換

結果

  • BUFSIZE = 2 (recv が一度に受信する最大バイト数) として実行したが、前回と同様の結果が得られた

memcache クライアントを実装してみた

目的

  • 主に自分の勉強です
  • クライアントはいろんな言語で実装されてるけど、やっぱり自分でやってみることに意義があるよね


実装項目

以下の3つを実装する

  • set
  • get
  • stats


ソース

my_memcache.py

  1 # -*- coding:utf-8 -*-
  2 import socket
  3 import pickle
  4 import base64
  5 
  6 class MyMemcache:
  7 
  8   def __init__(self):
  9     self.__sock = socket.socket()
 10 
 11   def __del__(self):
 12     self.__sock.close()
 13 
 14   def connect(self, host, port):
 15     self.__sock.connect((host, port))
 16 
 17   def close(self):
 18     self.__sock.close()
 19 
 20   def set(self, key, value, exptime=0, flags=0):
 21     serialized_value = base64.b64encode(pickle.dumps(value))
 22     self.__sock.send("set %s %d %s %d\r\n" % (key, flags, exptime, len(serialized_value)) )
 23     self.__sock.send("%s\r\n" % serialized_value)
 24 
 25     result = self.__sock.recv(64)
 26 
 27     if result == "STORED\r\n":
 28       return True
 29     else:
 30       return False
 31 
 32   def get(self, key):
 33     self.__sock.send("get %s\r\n" % key)
 34     lines = self.__sock.recv(1024).splitlines()
 35     serialized_value = lines[1]
 36     value = pickle.loads(base64.b64decode(serialized_value))
 37     return value
 38 
 39   def stats(self):
 40     self.__sock.send("stats\r\n")
 41     lines = self.__sock.recv(1024).splitlines()
 42 
 43     values = {}
 44 
 45     for line in lines:
 46       if line == 'END':
 47         break
 48 
 49       else:
 50         [dummy, key, value] = line.split()
 51         values[key] = value
 52 
 53     return values
 54 

解説

set

  • 引数は以下の4つ
    • key : 必須
    • value : 必須
    • exptime : 任意(デフォルトは0)
    • flags : 任意(デフォルトは0)
  • 整数や文字列以外に、リストや辞書などもくることを考慮し、pickle を使って value を直列化した
    • 直列化した際、改行が含まれていたため、base64 エンコードすることで、1行の文字列として格納した
  • 返り値が "STORED\r\n" のときに "True" を返すようにした
    • それ以外は "False" としたため、若干処理が甘いかもしれない

get

  • 返り値が3行の文字列なので、まず改行文字で分割した
    • そのうち2行目が set で登録した値である
      • set したときに value を直列化してエンコードしたので、デコードして非直列化して value を取り出す

stats

  • 以下のフォーマットで返ってくる
    • STAT version 1.2.8
  • なので、文字列を分割して辞書に登録する
    • 文字列 "END" が返ってきたら終了する


テストコード

my_memcache.py

 55 if __name__ == '__main__':
 56   memcache = MyMemcache()
 57   memcache.connect("localhost", 11211)
 58 
 59   print "########## stats ##########"
 60   print memcache.stats()
 61   print "\n"
 62 
 63   print "########## string ##########"
 64   key = "string_key"
 65   value = "string_value"
 66   print "key :" , key
 67   print "value :" , value
 68   print "---------- set ----------"
 69   print memcache.set(key, value)
 70   print "---------- get ----------"
 71   print memcache.get(key)
 72   print type(memcache.get(key))
 73   print "\n"
 74 
 75   print "########## list ##########"
 76   key = "list_key"
 77   value = ['a', 'b', 'c']
 78   print "key :" , key
 79   print "value :" , value
 80   print "---------- set ----------"
 81   print memcache.set(key, value)
 82   print "---------- get ----------"
 83   print memcache.get(key)
 84   print type(memcache.get(key))
 85   print "\n"
 86 
 87   print "########## tuple ##########"
 88   key = "tuple_key"
 89   value = (1, 2, 3)
 90   print "key :" , key
 91   print "value :" , value
 92   print "---------- set ----------"
 93   print memcache.set(key, value)
 94   print "---------- get ----------"
 95   print memcache.get(key)
 96   print type(memcache.get(key))
 97   print "\n"
 98 
 99   print "########## dict ##########"
100   key = "dict_key"
101   value = {'a' : 1, 'b' : 2, 'c' : 3}
102   print "key :" , key
103   print "value :" , value
104   print "---------- set ----------"
105   print memcache.set(key, value)
106   print "---------- get ----------"
107   print memcache.get(key)
108   print type(memcache.get(key))
109   print "\n"
110 


実行

$ python my_memcache.py
########## stats ##########
{'pid': '11509', 'total_items': '64', 'uptime': '47141', 'listen_disabled_num': '0', 'version': '1.2.8', 'limit_maxbytes': '134217728', 'rusage_user': '0.138425', 'bytes_read': '4793', 'accepting_conns': '1', 'rusage_system': '0.238628', 'cmd_get': '86', 'curr_connections': '6', 'threads': '2', 'total_connections': '35', 'cmd_set': '65', 'curr_items': '6', 'get_misses': '0', 'cmd_flush': '0', 'evictions': '0', 'bytes': '618', 'connection_structures': '7', 'bytes_written': '8006', 'time': '1272860539', 'pointer_size': '64', 'get_hits': '86'}


########## string ##########
key : string_key
value : string_value
---------- set ----------
True
---------- get ----------
string_value
<type 'str'>


########## list ##########
key : list_key
value : ['a', 'b', 'c']
---------- set ----------
True
---------- get ----------
['a', 'b', 'c']
<type 'list'>


########## tuple ##########
key : tuple_key
value : (1, 2, 3)
---------- set ----------
True
---------- get ----------
(1, 2, 3)
<type 'tuple'>


########## dict ##########
key : dict_key
value : {'a': 1, 'c': 3, 'b': 2}
---------- set ----------
True
---------- get ----------
{'a': 1, 'c': 3, 'b': 2}
<type 'dict'>
  • 格納した値が、ちゃんと返ってきてるよ、やったね
    • リストとか辞書は、型も正しいよ、やったね


参考

telnet から memcached をつないでみた

memcached とは

  • 簡単に言うと、メモリ上にデータを格納できる
  • 運用例
    • DB への問い合わせ結果を一時的に保存することでアクセス回数を減らし、高速化やスケーラビリティを向上させる

インストール

$ sudo port install memcached
--->  Computing dependencies for memcached
--->  Fetching libevent
--->  Attempting to fetch libevent-1.4.13-stable.tar.gz from http://distfiles.macports.org/libevent
--->  Verifying checksum(s) for libevent
--->  Extracting libevent
--->  Applying patches to libevent
--->  Configuring libevent
--->  Building libevent
--->  Staging libevent into destroot
--->  Installing libevent @1.4.13_0
--->  Activating libevent @1.4.13_0
--->  Cleaning libevent
--->  Fetching memcached
--->  Attempting to fetch memcached-1.4.4.tar.gz from http://memcached.googlecode.com/files/
--->  Verifying checksum(s) for memcached
--->  Extracting memcached
--->  Configuring memcached
--->  Building memcached
--->  Staging memcached into destroot
--->  Creating launchd control script
###########################################################
# A startup item has been generated that will aid in
# starting memcached with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo launchctl load -w /Library/LaunchDaemons/org.macports.memcached.plist
###########################################################
--->  Installing memcached @1.4.4_0
--->  Activating memcached @1.4.4_0
--->  Cleaning memcached

memcached のオプションを確認

$ memcached -h
memcached 1.2.8
-p <num>      TCP port number to listen on (default: 11211)
-U <num>      UDP port number to listen on (default: 11211, 0 is off)
-s <file>     unix socket path to listen on (disables network support)
-a <mask>     access mask for unix socket, in octal (default 0700)
-l <ip_addr>  interface to listen on, default is INDRR_ANY
-d            run as a daemon
-r            maximize core file limit
-u <username> assume identity of <username> (only when run as root)
-m <num>      max memory to use for items in megabytes, default is 64 MB
-M            return error on memory exhausted (rather than removing items)
-c <num>      max simultaneous connections, default is 1024
-k            lock down all paged memory.  Note that there is a
              limit on how much memory you may lock.  Trying to
              allocate more than that would fail, so be sure you
              set the limit correctly for the user you started
              the daemon with (not for -u <username> user;
              under sh this is done with 'ulimit -S -l NUM_KB').
-v            verbose (print errors/warnings while in event loop)
-vv           very verbose (also print client commands/reponses)
-h            print this help and exit
-i            print memcached and libevent license
-P <file>     save PID in <file>, only used with -d option
-f <factor>   chunk size growth factor, default 1.25
-n <bytes>    minimum space allocated for key+value+flags, default 48
-R            Maximum number of requests per event
              limits the number of requests process for a given con nection
              to prevent starvation.  default 20
-b            Set the backlog queue limit (default 1024)


起動

# 最大メモリ 64M, 127.0.0.1:11211 で起動
$ memcached -d -m 64  -l 127.0.0.1 -p 11211 

telnet で接続

$ telnet 127.0.0.1 11211
set key1 0 3600 6
value1
get key1
VALUE key1 0 6
value1
END
  • コマンドは以下のように指定する
    • [noreply]\r\n
  • set で値を保存する
    • 今回は キーを"key1", フラグを"0", 期限を"3600"(秒), 値を"value1"として保存
  • get で値を取得する

まとめ

  • とりあえずどうやって値の保存、取得をしているのかは理解できた
    • まぁ set, get をしてみただけなんですけどね〜

参考

zsh を使ってみた

動機

  • なにやらシェルにもいろいろあるらしい
    • そういえば zsh っていうのを耳にしたことがある
      • 調べていると、どうやら zsh は最強のシェルらしい
        • テンションあがってきたよー!

インストール

$ sudo port install zsh
Password:
--->  Computing dependencies for zsh
--->  Fetching zsh
--->  Attempting to fetch zsh-4.2.7.tar.bz2 from ftp://ftp.iij.ad.jp/pub/misc/zsh/
--->  Attempting to fetch zsh-4.2.7.tar.bz2 from http://nchc.dl.sourceforge.net/zsh
--->  Verifying checksum(s) for zsh
--->  Extracting zsh
--->  Configuring zsh
--->  Building zsh
--->  Staging zsh into destroot
--->  Installing zsh @4.2.7_0
--->  Activating zsh @4.2.7_0
--->  Cleaning zsh

設定ファイル記述

これを書かないと始まらないらしいのでいくつか書いてみる

# プロンプト
## 色の反映
autoload -U colors
colors

PROMPT="
%{${fg[magenta]}%}[%n@%m] [%~] %{${reset_color}%}
$ "
RPROMPT="[%D{%Y/%m/%d} %T]"

# 補完機能
autoload -U compinit
compinit

# 文字コード
export LANG=ja_JP.UTF-8

# オプション
setopt auto_cd      # ディレクトリ名だけで移動できる
setopt auto_pushd   # cd -<tab> でこれまでに移動したディレクトリが表示される
setopt correct      # コマンドのミスを教えてくれる
setopt list_packed  # 補完候補が詰めて表示される

# エイリアス
alias ll="ls -laFhG"

# コマンド履歴機能
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt hist_ignore_dups # 重複したコマンドを保存しない
setopt share_history    # 履歴ファイルを共有する

感想

  • とりあえず自動補完がおもしろい
    • tab を押せばなんとかしてくれる
  • ディレクトリ名を打つだけで移動できておもしろい
    • でも別にいらないかもしれない
    • コマンド名と被ったとき面倒かも

参考