Multi-column Layout Module
早見表
プロパティ | 値 | 初期値 |
---|---|---|
column-width | |
auto |
column-count | |
auto |
columns | <'column-width'> || <'column-count'> | 各項目を参照 |
column-gap | |
normal |
column-rule-width | <'border-width'> | medium |
column-rule-style | <'border-style'> | none |
column-rule-color | |
- |
column-rule | |
各項目を参照 |
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.html1 <!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
結果
- BUFSIZE = 2 (recv が一度に受信する最大バイト数) として実行したが、前回と同様の結果が得られた
memcache クライアントを実装してみた
目的
- 主に自分の勉強です
- クライアントはいろんな言語で実装されてるけど、やっぱり自分でやってみることに意義があるよね
実装項目
以下の3つを実装する- set
- get
- stats
ソース
my_memcache.py1 # -*- 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 を直列化した
- 返り値が "STORED\r\n" のときに "True" を返すようにした
- それ以外は "False" としたため、若干処理が甘いかもしれない
get
- 返り値が3行の文字列なので、まず改行文字で分割した
stats
- 以下のフォーマットで返ってくる
- STAT version 1.2.8
- なので、文字列を分割して辞書に登録する
- 文字列 "END" が返ってきたら終了する
テストコード
my_memcache.py55 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 を使ってみた
動機
インストール
$ 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 を押せばなんとかしてくれる
- ディレクトリ名を打つだけで移動できておもしろい
- でも別にいらないかもしれない
- コマンド名と被ったとき面倒かも