HTML::Selector::XPath @ 0.17 で normalize-space() するようになった


HTML::Selector::XPath@0.16 は CSS における class セレクタと等価な XPath を出力しない - Sexual Knowing

Normalize space characters of the class attribute by aereal · Pull Request #5 · Corion/HTML-Selector-XPath · GitHub がマージされた 0.17 がリリースされた。

0.17 以降に更新すると @normalize-space()@ を使うようになっているので、class 属性値の中に改行などの空白 (U+0020) 以外の空白文字が含まれていても正しくマッチするようになった。

めでたい。

HTML::Selector::XPath@0.16 は CSS における class セレクタと等価な XPath を出力しない

再現コード


class_with_lf.t

class 属性の値における「空白文字」

class 属性の値は:

The attribute, if specified, must have a value that is a set of space-separated tokens representing the various classes that the element belongs to.

HTML Standard

とあるように、a set of space-separated tokens と定義されている。

では a set of space-separated tokens の定義を参照すると:

A set of space-separated tokens is a string containing zero or more words (known as tokens) separated by one or more space characters, where words consist of any string of one or more characters, none of which are space characters.

HTML Standard

とある。

space characters について合意を持たないといけなさそう。

The space characters, for the purposes of this specification, are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).

HTML Standard

とある。つまり改行 (line feed, carriage return) は「空白文字」として定義されている。

HTML::Selector::XPath のバグ

class 属性の値の前後に空白 (U+0020 SPACE) を追加して contains() 関数でマッチするか試しているが、前述の通り class 属性の値は U+0020 以外の文字も空白文字として許容する。

たとえば改行 (U+000A LINE FEED) があった場合には contains() 関数は偽を返すので、CSS における class セレクタと等価ではない。

対策と修正

XPath には normalize-space() 関数がある。この関数は、連続する空白文字をひとつの空白 (U+0020) に置き換える。

この Pull Request では normalize-space() を使うよう変更している。

YAPC::Asia Tokyo 2014,プログラマブル Mac OS X

プログラマブル Mac OS X - YAPC::Asia Tokyo 2014

プログラマブル Mac OS X」というタイトルで YAPC::Asia Tokyo 2014 のトークに応募した。

ひとつの仕事をうまくやるプログラムの組み合わせでより大きな仕事をこなす、という UNIX 的な考え方は Mac OS X にも継承されており、設定を取得・変更したり普段 GUI から操作する機能と同等の仕事をこなすコマンドが種々用意されている。

pkgutil, defaults, launchctl といったツールおよびそれらの背後にある技術 (e.g. Property List, launchd, Installer Package) についての簡単な紹介や実際にそれらのツールを組み合わせた簡単なスクリプトの紹介をする予定。

Mac OS X ユーザがより効率的に「大きな仕事」をこなす助けとなればよいと思う。

npm でモジュールのバージョンを固定する

A. npm shrinkwrap

npm shrinkwrap を実行すると package.json に列挙されている依存を読みとり、実際にインストールされるべきモジュールのバージョンが列挙された npm-shrinkwrap.json というファイルが作られる。

npm install はこのファイルが存在すればこちらを優先して読むようになっている (--no-shrinkwrap オプションを与えることで無視することもできる)。

いくつか気をつける点があって:

  • devDependencies も固定するには npm shrinkwrap を実行する際に --dev オプションを与える必要がある
  • npm-shrinkwrap.json が存在しない場合、package.json の依存関係を読み取る。つまり、Bundler などのように固定された依存関係が保存されたファイルが存在しない場合にエラーないし警告を出す仕組みは (現在のところ) ない

特に後者は気を利かせてフォールバックしてほしくない時に npm だけではどうしようもないという問題がある。

aws-cli とファイルのパス

aws route53 change-resource-record-sets--change-batch 引数の値にファイルパスを渡すには file スキームの URI を渡す必要がある。

aws route53 change-resource-record-sets --hosted-zone-id /hostedzone/XXXX --change-batch file://${PWD}/batch.json

ドキュメント読んでも file スキームが書いてあるのを無意識の内に無視してずっと Invalid JSON と言われ続けていた。

✘╹◡╹✘ < aws --version
aws-cli/1.3.9 Python/2.7.6 Darwin/13.2.0

work tree の外から git pull する

バッチスクリプトなどで work tree の外から git pull を行いたい、ということはままあるシチュエーションであると思います。

git コマンドは --git-dir オプションでコマンドで操作する .git ディレクトリを指定できるので、これを指定すれば済むと思うかもしれませんが、これではうまくいきません:

cd $HOME
git --git-dir=$HOME/repos/@aereal/dotfiles/.git pull
# Cannot pull with rebase: You have unstaged changes.
# Please commit or stash them.

work tree も指定する必要があります:

git --git-dir=$HOME/repos/@aereal/dotfiles/.git --work-tree=$HOME/repos/@aereal/dotfiles pull

あるいは Git 1.8.5 以降であれば -C オプションが使えます:

git -C $HOME/repos/@aereal/dotfiles pull

git-sh-setup.shrequire_clean_work_tree() 関数が git diff-files --quiet --ignore-submodules を実行して終了ステータスが非ゼロだと上記のようなエラーメッセージを出力して終了するようです。

Kyoto.なんかで“Introduce browserify”という発表をした

Kyoto.なんかという Kyoto.js の潮流を汲んだような汲んでいないような、勉強会のようななんなのかよくわからないイベントで発表した。

browserify という Common JS Modules/1.0 などに互換のあるモジュールを、Common JS Modules/1.0 などモジュール機構に対応していないブラウザで動かせるようにするツールについて簡単な紹介をした。

browserify-shim を使って jQuery plugin も browserify で扱えるようにするのがとても便利。

実際には browserify-shim が require() を解決し展開するやりかたはけっこう凝っている (出力されたコードとちょっと実装を読んだくらいで、僕も詳しい実装はあまり追っていない)。

作者の substack さんは browserify という比較的大きなツールを作る際に、細かく部品化してそれぞれ独立した npm モジュールとして公開されていたりする。

unix philosopher.

substack in cyberspace

というポートフォリオの一文が実にしっくりくる人だと思います。

anyenv-exec を作った

anyenvプラグイン anyenv-exec を作った。

使い方

たとえばインストールされている *env のバージョンを出力する例:

anyenv exec --version
# plenv 2.1.1-9-g26dbef7
# pyenv 0.4.0-20140404-10-g601ac4b
# rbenv 0.4.0-97-gfe0b243

rehash する例:

anyenv exec rehash

*env 系は大抵のサブコマンドが共通していることから、出力を比較したいとか、いちいち for 文を書くのはめんどうであるとか、そういった思いから作ってみた。

実装は大したものではないけれどけっこう便利。

インストール

mkdir -p $(anyenv root)/plugins
git clone git://github.com/aereal/anyenv-exec.git $(anyenv root)/plugins/anyenv-exec
aereal/anyenv-exec · GitHub

chrome.contextMenus.create() に parentId とコンテキストを指定するプロパティは同時に指定できない?


ドキュメントには特に何も書いていない。

子メニュー (= parentId を指定されている) は親メニューより狭いコンテキストを指定することはできない?

Vim で現在カーソルがあるテストメソッドを実行する

Perl には Test::Class という JUnit 風にテストを書けるモジュールがある。すなわち:

ことができる。

Test::Class はテスト実行時に環境変数 TEST_METHOD に与えられた値で実行するテストメソッドをフィルタリングすることができる。

Sometimes you just want to run a single test. Commenting out other tests or writing code to skip them can be a hassle, so you can specify the TEST_METHOD environment variable. The value is expected to be a valid regular expression and, if present, only runs test methods whose names match the regular expression.

Test::Class - search.cpan.org

テストコードが存在するソフトウェアに修正を加える場合、大抵、いつかのタイミングでテストコードを修正することになる。そうなるとまず修正したテストのみを実行し、最小限度のフィードバックを得たい。

つまり、TEST_METHOD を指定してテストを走らせたいのだが、いちいちテストメソッド名を入力するのも面倒である。

そこでテストの実行には vim-quickrun を、現在カーソルのあるメソッド名を取得するために current-func-info.vim を使う。

let g:quickrun_config['prove/carton'] = {
      \ 'exec'    : 'carton exec -- %c %o %s',
      \ 'command' : 'prove',
      \ }
let g:quickrun_config['prove/carton/contextual'] = extend(g:quickrun_config['prove/carton'], {
      \ 'exec' : 'TEST_METHOD=%a ' . g:quickrun_config['prove/carton'].exec,
      \ })
command! ProveThis call s:prove_this()
function! s:prove_this()
  let func_name = cfi#format('%s', '')
  if func_name == ''
    QuickRun prove/carton
  else
    execute 'QuickRun prove/carton/contextual -args ' . func_name
  endif
endfunction

メソッド名を得ることができればその値を引数として渡し prove/carton/contextual の設定を使って QuickRun を実行する。得られなければ prove/carton を実行する。

rbenv など *env でどの設定 (global, local, shell) を参照しているか知る

rbenv *1rbenv version-origin というサブコマンドがあり、それは「rbenv がバージョンを決定するのに使われた設定」を出力する。

出力は次の3つのうちのいずれかとなる。すなわち:

  • global の設定
    • $HOME/.rbenv/version
  • ディレクトリごとの設定 (local)
    • $HOME/myproj/.rbenv-version
  • シェルのプロセス固有 *2 の設定 (shell)
    • RBENV_VERSION environment variable

つまり rbenv version-origin の出力が:

  • global の設定ファイルに一致するならば global
  • global の設定ファイルに一致せず、ファイルとして読取可能ならば local
  • それ以外は shell

という風になる。

それでこのような zshスクリプトを書いた。たぶん bash でも動く。

dotfiles/.zsh.d/functions/llenv_version_origin at 78327cd0349b53686cacada45b1252a2cd38a95f · aereal/dotfiles · GitHub

*1:以下、plenv や pyenv など anyenv で管理できるものについてはそれぞれに読み替える

*2:厳密には環境変数 RBENV_VERSION が export されているあいだ

ログインシェルに fish を指定しているときに Vim の system 関数を実行すると E484 が発生する

解決

set shell=/bin/sh する。あるいは POSIX 互換のシェルを shell オプションに設定する。

原因

Vimsystem 関数はシェルに文字列を渡してコマンドを実行しようとするが、実行するシェルが POSIX 互換であることを期待している。

Homebrew で入れた Pow が起動しなくなる問題の解決

brew install pow したあと brew info pow に書かれている以下の手順でインストールした Pow が突然起動しなくなった。

sudo pow --install-system
pow --install-local

原因

~/Library/LaunchAgents/cx.pow.powd.plist にインストールされるプロパティ・リストの起動時の引数として Homebrew でインストールした noderealpath が書かれているが、この realpath にはバージョン番号が含まれているため、Pow をインストールした時点から node のバージョンが変わることによって存在しないパスを参照して起動に失敗した。

symlink を参照しても特に支障はないと考えたので起動時の引数を変更した。

diff --git a/cx.pow.powd.plist b/cx.pow.powd.plist
index e6e4e27..ded9906 100644
--- a/cx.pow.powd.plist
+++ b/cx.pow.powd.plist
@@ -6,8 +6,7 @@
         <string>cx.pow.powd</string>
         <key>ProgramArguments</key>
         <array>
-                <string>/opt/homebrew/Cellar/node/0.10.24/bin/node</string>
-                <string>/opt/homebrew/Cellar/pow/0.4.1/libexec/bin/pow</string>
+                <string>/opt/homebrew/bin/pow</string>
         </array>
         <key>KeepAlive</key>
         <true/>

結論

  • launchd に登録するプロパティ・リストには標準出力・標準エラー出力のリダイレクト先も指定しておく
    • StandardOutPath
    • StandardErrorPath

Go-lang で書かれた Fast GitHub command line client: gh を試している

jingweno/gh · GitHub

Go で書かれた hub の port のひとつ、と表現するのが簡潔でわかりやすい。

インストールの方法はいろいろある

拡張・追加しているサブコマンドは hub のそれとあまり変わらない。

hub のおよそ2倍ほど速いので、alias git=gh してもそれほどストレスを感じない。

コードもそれほど規模も大きくなくあまり凝ったことをしていないので Go-lang のコード・リーディングにもまあまあよさそう。