2013年6月17日月曜日

JSONと仲良くする

最近大量だったり巨大だったりなJSONファイルから必要な部分を探す必要に迫られたときにjqが便利だったので紹介。



http://stedolan.github.io/jq/
表示する範囲を絞り込めるので、繰り返し同じ場所を表示したいときとかに便利。
抽出結果がJSONになるので一部分を抽出したJSONを作成したりできる。
クエリがちょっと独特な気がする。

sample.json
{"1":{"name": "data1","point":1001,"optional":1},"2":{"name":"data2","point":1002},"3":{"name":"data3","point":1003,"optional":3}}

使い方例
・整形して表示する
jq '.' sample.json
・nameだけを抜き出す
jq '.[].name' sample.json
・nameとoptionalを抜き出す、どっちかがないやつはnullになる
jq '.[]|{name, optional}' sample.json
・トップレベルの"1"のオブジェクトだけを抽出する
jq '.["1"]' sample.json
・findとxargsと組み合わせたときにファイル名も付ける、特定の要素がnullになってる項目を含むファイルを探すときに便利
find -name '*.json' | xargs -I{} jq -c '.[]|{name,optional,filename: $filename}' --arg filename {} {}
・各要素の要素数を取得
jq '.[]|length' sample.json
・トップレベルの要素数を取得
jq 'keys|length' sample.json



jq以外の方法
JSONPath
プログラムからJson形式の検索や操作を行うためのライブラリというか仕様。
XPathをJSON用に変更したものらしい?
いろいろな言語用にライブラリがある、メジャーな言語にはほとんどありそう。
プログラムの中でJSONの一部を使うときとかは便利そうなんだけど、
ちょっと試して動かなかったのでわからない。

上のsample.jsonの1のnameを検索する場合はこんな感じだと思う。
"$..1.name"



行指向に変換してみる

jqは便利だけど独自クエリが思い出せなかったり、
コマンドラインツールとの相性がいまいち。


ということで、さっきのsample.jsonをこんな風に変換するスクリプトを作ってみた。
/1/name                                  => "data1"
/1/point                                 => 1001
/1/optional                              => 1
/2/name                                  => "data2"
/2/point                                 => 1002
/3/name                                  => "data3"
/3/point                                 => 1003
/3/optional                              => 3

こうなったら、grepとかで好きなように検索できる。


#!/usr/bin/php
<?php
function jr($data, $path) {
    if (is_array($data)) {
        foreach ($data as $key => $value) {
            jr($value, $path . '[' . $key . ']');
        }
    } elseif (is_object($data)) {
        foreach($data as $key => $value) {
            jr($value, $path . '/' . $key);
        }
    } else {
        $output = is_string($data) ? '"' . $data . '"' : $data;
        printf("%-40s => %s\n", $path, $output);
    }
}

$input = (count($argv) > 1) ? $argv[1] : 'php://stdin';
$all_data = json_decode(file_get_contents($input), false);
jr($all_data, '');



作ってはみたけど、jqとjr(上のスクリプトの仮称)で必要なコマンドを比較してみたら
jqの使いやすさを再確認するだけになった。
正規表現だとややこしくなる箇所もあるし。
でも、独自クエリがなかなか覚えられない人にはjr+grepも便利。

比較
トップレベルのキー一覧を取得する
$jq 'keys' ~/work/sample.json
$jr.php ~/work/sample.json | sed -e 's|^/\([^/]*\)/.*|\1|' | uniq

第二階層のnameの一覧を取得する
$jq '.[].name' ~/work/sample.json
$jr.php ~/work/sample.json | grep '/\w*/name\s' | sed -e 's/.*=> //'

どっかにあるnameがdata2のデータを探す
$jq '.[].name == "data2"' ~/work/sample.json
false
true
false

$jr.php ~/work/sample.json | grep "data2"
/2/name                                  => "data2"




その他
最近の言語はだいたいJSONにオブジェクトとか配列を保存できる機能があるので、
JSONをうまく扱えるとそういうのを読むのにも便利。

S式とかXMLとかyamlとかテキストの構造化データ形式が色々あるので、
自分が読み書きしやすいやつと相互変換させたりするのもいいかも。


clojureなら

(clojure.data.json/json-str
  {1 {:name "data1", :point 1001, "optional" 1}
   2 {:name "data2", :point 1002}
   3 {:name "data3", :point 1003, "optional" 3}})
で上のsample.jsonと同じのができる。
emacsだとS式編集用に便利な機能がいろいろあったり、pareditで括弧間違えなくて便利。


あとはJaqlとかJSONiqとかJSONをSQL風なクエリで検索したりするのもある。

2013年6月2日日曜日

nreplxmp作った

lispxmpをclojureで使うために移植してみた。



elispもclojureも初心者なので、
エラー処理とかnsの扱いとか実装も結構酷いけど一応動いた。
簡単なことでしか試してないので、ダメなとこ見つけたら直す予定。



https://github.com/wakala/nreplxmpから
nreplxmp.elをどっかにコピーして
(require 'nreplxmp)
  

cljファイルを1ファイルをまるごとevalして実行できるようにして、
nrepl-jack-inしたあとにM-x nreplxmpとかで動く。


lispxmpについてはここを参照
http://d.hatena.ne.jp/rubikitch/20090313/lispxmp

nreplについてはここを参照
https://github.com/kingtim/nrepl.el

2013年5月26日日曜日

LittleProxyでフィルターする。

http://wakalambda.blogspot.jp/2013/05/clojurejettyproxyservlet.html

をやったあとに、ホスト名とコンテントタイプが指定したものだったときだけ
コンテンツを表示するようにしようとしたらどこを拡張していいかわからなかった。

あきらめてLittleProxyでやってみたら簡単にできた。
(defproject littleproxy-filter-example "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.littleshoot/littleproxy "0.5.1"]]
  )
(ns littleproxy-filter-example.core)

(import '(org.littleshoot.proxy DefaultHttpProxyServer
                                HttpResponseFilters
                                HttpRequestFilter
                                HttpFilter))

(use 'clojure.java.io)

(def ^:dynamic *log-file* "/tmp/proxy-log.txt")

(defn write-log [request response]
  (let [host (.getHeader request "Host")
        uri (.getUri request)
        content-type (.getHeader response "Content-Type")]
    (if (.startsWith (str content-type) "text/html")
      (try
        (spit *log-file*
              (str
               "new response  -----------------------------------------------------------\n"
               uri "\n"
               content-type "\n"
               (.toString (.getContent response) "UTF-8") "\n"
               )
              :append true)
        (catch Exception e
          (spit *log-file* (str e) :append true))
        ))))

(defn get-filter [host-and-port]
  (proxy [HttpFilter] []
    (filterResponses [request]
      true
      ;(.endsWith (.getHeader request "Host") "clojure.org")
      )
    (filterResponse [request response]
      (write-log request response)
      response)
    (getMaxResponseSize []
      Integer/MAX_VALUE)))

(defn create-response-filters []
  (proxy [HttpResponseFilters] []
    (getFilter [host-and-port]
      (get-filter host-and-port))))

(defn create-request-filter []
  (proxy [HttpRequestFilter] []
    (filter [http-request]
      ;; some ops
      )))

(def ^:dynamic *server* (DefaultHttpProxyServer. 8888 (create-request-filter) (create-response-filters)))
(.start *server*)
;; (.stop *server*)

2013年5月20日月曜日

swank-jsを動かしてみた

https://github.com/swank-js/swank-jsのREADMEを参考に。

nodejsはyum install nodejs

npmはgit clone https://github.com/isaacs/npm.git
でmake install

npm install -g swank-js
swank-jsで起動

js2-modeが上手く動かなかったので、横道にそれてemacs24にした。
color-themeみたいのとpackageが標準で入ってて便利になってた。

packageからslime, slime-js, js2-modeをいれた。

ウェブブラウザで
http://IP:8009/swank-js/test.htmlを開く


javascriptファイルを開いてjs2-modeとslime-js-minor-modeを実行。
slime-connect
, select-remoteでブラウザ選択

C-M-xで関数を送る。
slime-eval-regionでリージョンを送る。


$('head').append('<link rel="stylesheet" href="http://IP/a.css" type="text/css" />');
とかするとheadタグの中にcssを追加させられる。
 
cssファイルを変更してから、slime-js-refresh-cssで変更が反映される。

時間がないので、設定のメモだけ、使いこなせたらよさそうなのだけど。
swank-jsのREADMEに書いてあるキーバインドが使えないことがあったりして、
うまく設定できてない気もする。

http://emacsrocks.com/e11.html
みたいなことできたらかっこいい。

2013年5月4日土曜日

emacsをdaemon化してみた。

LinuxをVirtualBoxで動かしてるけど、接続しっぱなしにしたままホストOSのスリープをするとゲストOSがフリーズする可能性が高かったので、スリープ前に閉じても復帰しやすくするために。

こんな感じのスクリプトで、daemonがいなければ作って、ログイン元のIPアドレスのXサーバに表示させる。
ログイン元のIPアドレスの抽出が適当なので駄目なケースもあるのかも。同じユーザで別の場所から入ってるとか。


#!/bin/sh
ret=`pgrep -f 'emacs --daemon'`
if [ -z $ret ]; then
        emacs --daemon
fi

name=`whoami`
addr=`w -h -s | grep "^$name" | awk '{print $3}' | grep '^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$' | head -n 1`

emacsclient -c -n -d $addr:0.0

ClojureでJettyのProxyServletを使う

細かく挙動を変更させられるプロキシサーバが欲しかったので、Clojureでできないか調べた。

 http://grepcode.com/file/repo1.maven.org/maven2/org.eclipse.jetty/example-jetty-embedded/8.1.1.v20120215/org/eclipse/jetty/embedded/ProxyServer.java

基本的にはこれをClojureに書き直しただけ。

ProxyServletまではJettyサーバにインスタンスが保存されてしまうのでREPLで変更しても反映されない。
REPLで動作を変更させるためにはProxyServletから他の関数を呼ぶようにしてそこを変更するようにすれば良い。
下のコードではprocess-customize-exchangeとprocess-customize-continuationへの変更は動的に反映される。

core.clj
(ns jetty-proxy.core)

(import '(org.eclipse.jetty.server Server)
        '(org.eclipse.jetty.server.nio SelectChannelConnector)
        '(org.eclipse.jetty.servlet ServletContextHandler ServletHolder ServletHandler)
        '(org.eclipse.jetty.servlets ProxyServlet)
        '(javax.servlet.http HttpServletRequest))

(defn process-customize-exchange [exchange ^HttpServletRequest request]
  (println "customize-exchange")
  (println (str "requestURL: " (. request getRequestURL)))
  )

(defn process-customize-continuation [continuation]
  (println "customize-continuation")
  )

(defn create-proxy-servlet []
  (proxy [ProxyServlet] []
    (customizeExchange [exchange request]
      (process-customize-exchange exchange request)
      (proxy-super customizeExchange exchange request))
    (customizeContinuation [continuation]
      (process-customize-continuation continuation)
      (proxy-super customizeContinuation continuation))
    ))

(defn create-server []
  (let [server (Server.)
        handler (ServletHandler.)
        connector (SelectChannelConnector.)]
    (. connector setPort 8888)
    ;;(. handler addServletWithMapping ProxyServlet "/*")
    (. handler addServletWithMapping (ServletHolder. (create-proxy-servlet)) "/*")
    (. server addConnector connector)
    (. server setHandler handler)
    server))

(def *server* (create-server))
(. *server* start)
;; (. *server* stop)


project.clj
(defproject jetty-proxy "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.eclipse.jetty/jetty-server "8.1.9.v20130131"]
                 [org.eclipse.jetty/jetty-servlet "8.1.9.v20130131"]
                 [org.eclipse.jetty/jetty-servlets "8.1.9.v20130131"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 ])


これくらいだとRubyでWEBrick使う方が簡単だし手軽に見えるけど、 clojureで書くことが目的の半分なので。

参考:http://jp.rubyist.net/magazine/?0002-WEBrickProxy

2013年4月29日月曜日

nreplのインストール

(require 'package)
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
(setq package-user-dir (concat user-emacs-directory "vendor/elpa"))
(package-initialize)

をやってからpackage-list-packagesでnreplをインストール。

.cljのファイルを開いてclojure-jack-inを実行する。

*nrepl*バッファは作成されてclojureのプロセスも起動するのだが、
*nrepl*のバッファが空っぽになる。
何も表示されないのでかなり途方に暮れる。

nreplと連動するためのnrepl-interaction-modeを実行すると
completion-at-point-functionsが見つからないらしいエラーが表示される。

というわけでinit.elにこれを追加して解決。
(setq completion-at-point-functions '(auto-complete))


という設定をしたのを以前やったのに、
まったく思い出せずに2回目を体験したので忘れないようにメモ。

Save CSSの設定

開発環境がWindowsで
Linux上のファイルを操作したいときの設定。
Linuxファイルをsambaで更新できるように設定されている必要がある。


http://tomicloud.com/2012/04/save-css-chrome-ext

からリンクされてるChrome storeでSave CSSアドオンインストール。



http://www.artonx.org/data/asr/
からrubyをダウンロードしてインストール。
Ruby-2.0.0-p0 Microsoft Installer Package(2013-02-24)で試した。

ruby.exeにパスを通す。


https://github.com/tomimick/chrome-ext-save-css/tree/master/servers
からserver.rbをダウンロード、pythonの方が好きな人はserver.pyを。

コマンドラインでダウンロードしたファイルのディレクトリに移動して
ruby server.rb
でサーバ起動。


編集したいページをGoogle Chromeで開いて
右クリック→要素を検証→Saveタブ
MappingsにURLとそれに対応しているローカルパスを設定する。


http://192.168.11.14/html/で表示できるパスが、
Sambaで\\192.168.11.14\path\to\htmlで参照できるなら
URL prefixにhttp://192.168.11.14/html/、Local folderに\\192.168.11.14\path\to\html
とすれば良い。

2013年3月10日日曜日

windowsとvmwareとlinuxとemacs

現在使っている開発環境は

ホストOSがWindowsでVMWareで
ゲストOSのCentOSでemacsを使っているのだけど、
ホストOSから利用する方法に色々なやり方があったのでメモ。

VMWareのウインドウをそのまま利用するタイプ
・Linuxデスクトップを表示して、その中でemacsを表示する。(1a)
・emacsだけをUnityでwindowsアプリっぽく表示する。(1b)
ホストOSでCygwinのX Window Serverを起動しておいて、emacsの-dオプションでそれを指定する。(2)
端末エミュレータで接続して、テキストモードで利用する。(3)
ゲストOSでVNCサーバを起動する。(4)

1bはアプリケーションのウインドウだけがホストOSに表示できるのが便利
3は使用できないキー操作があったりして不便
4はなんとなく遅い気がする
ということでしばらく1bと2で試していたのだけど、
ホストOSをスリープさせたときの復旧でどちらも
不安定だったので結局1aで使っている。
ウインドウのタイトルバーが邪魔に感じてきたという理由もある。

1aを使うときはVMWareをフルスクリーン表示にして、
emacsもフルスクリーンモードに設定すると画面上に余計なものがまったく表示されなくていい感じ。2の方法でもフルスクリーンは使えるけど。
 (set-frame-parameter (selected-frame) 'fullscreen 'fullboth)

1aはalt-tabがゲストOSに捕捉される
ホストOSのアプリがなぜかVMWareの下に隠れる
とかの細かい問題があるけど。

vmwareの上にツールバーが出るのが邪魔なら
vmwareのpreferences.iniに
pref.vmplayer.fullscreen.nobar = "TRUE"
で消せる。

2013年3月9日土曜日

PHP開発用emacs設定

php-mode, flymake


geben

ステップ実行に頼らずに生活できるようになりたいけど。
/etc/php.d/xdebug.ini
zend_extension=/usr/lib/php/modules/xdebug.so
xdebug.remote_enable=yes
xdebug.remote_autostart=no
xdebug.remote_host=localhost
xdebug.remote_port=9000

phpファイルにxdebug_break()
リクエストにXDEBUG_SESSION_STARTをつける


ctags

M-.
(defun run-refresh-tag()
(interactive)
(shell-command "cd /path/to/source; ctags -e -R --php-kinds=cfd &"))

yasnippet-php-mode

標準ライブラリの関数の引数の順序がいつもわからなくなるので助かる。

その他



;; 開いてるファイルをphpunitする
(defun run-php-unit()
(interactive)
(shell-command (concat "phpunit " (buffer-file-name) " &")))

;; 選択したリージョンをevalする

(defun php-eval-region ()
(interactive)
(shell-command-on-region (region-beginning) (region-end) "php ~/.emacs.d/php-eval-region/eval.php"))


~/.emacs.d/php-eval-region/eval.php
<?php
$region = file_get_contents('php://stdin');
if (strlen($region) > 0) {
  ob_start();
  eval('$_tmp_ = ' . $region . ';');
  $output = ob_get_contents();
  ob_end_clean();
  if (strlen($output) > 0) {
   print($output . "\n");
  }
  print_r($_tmp_);
}


まだ、これくらいしか設定してない。

2013年3月8日金曜日

MySQLでinsert, updateを大量に実行したいとき

PHP+MySQLで同じテーブルに大量のinsertやupdateを行う場合に、
1回ずつ実行すると遅いのでまとめてinsertすると早くなります。

INSERT INTO table (id, name) VALUES (1, 'name1');
INSERT INTO table (id, name) VALUES (2, 'name2');
....

のような場合、

INSERT INTO table (id, name) VALUES (1, 'name1'), (2, 'name2'), ...;
のようにvaluesを複数にしてまとめて実行するようにします。


 updateの場合にはON DUPLICATE KEY UPDATEをつけたinsertを実行すれば同様の効果が得られます。

UPDATE table set name = 'name1' where id = 1;
UPDATE table set name = 'name2' where id = 2;

を以下のようにします。

INSERT INTO table (id, name) VALUES (1,'name1'), (2, 'name2')
ON DUPLICATE KEY UPDATE name = VALUES(`name`)';

UPDATE文をたくさんつなげた長いSQLを作って実行すると、
接続回数が減るためか一応早くなりますが大きな効果は得られません。


初投稿なので練習も兼ねて適当な感じで終わりです。