外部リンクを自動で新規タブで開くUserScript

ブラウザ上でリンクをクリックした時に起こることといえば、そのタブでページが遷移するか新しいタブでクリックしたリンクが開かれるかだと思う。自分の中で「これは新規タブで開かれるだろう」と予想したリンクがそのままそのタブで遷移することが結構あって、そういう時はESCとかで読み込みを中止して新規タブで開き直すようにしたりしてたんだけど、これが結構めんどくさい。自分が新規タブで開かれると予想するリンクは大体外部リンクなので、UserScriptを書いて外部ドメインのリンクは基本的に全部新規タブで開くようにした。

// ==UserScript==
// @match *://*/*
// ==/UserScript==

(function (anchors) {
  for (var i = 0, l = anchors.length; i < l; i++) {
    if (location.host !== anchors[i].host) {
      anchors[i].setAttribute("target", "_blank");
    }
  }
})(document.querySelectorAll("a"));

仕組みとしては、querySelectorAllでa要素を拾ってきて、そのhref属性が開いているページとは違うドメインだったらtarget="_blank"をつけるという感じ。

Togetterの「続きを読む」ボタンを自動でクリック(したことに)するUserScript

Togetterの「続きを読む」ボタンはVimiumのhit-a-hintに反応しないので、マウスでクリックしないと続きが読めなくて大変です。
というわけでUserScript化。

// ==UserScript==
// @match http://togetter.com/li/*
// ==/UserScript==

(function (element) {
  if (element !== null) {
    var event = document.createEvent("MouseEvents");
    event.initEvent("click", false, true);
    element.dispatchEvent(event);
  }
})(document.querySelector(".rich_button[onclick^='tgtr.more']"));

仕組みはそんなに複雑なものではなくて、単純にdocument.querySelectorで「続きを読む」ボタンの要素を取ってきてdispatchEventでクリックイベントを発火させているだけ。
initEventとinitMouseEventとどっちを使うべきなのかよく分からなかったんだけど、initMouseEventは引数も多く色々大変そうだったのでinitEventを使った。
最初、「続きを読む」ボタンの要素を取ってくるのにdocument.querySelector(".rich_button")と書いて動かなくて、「拡張とかUserScriptからだとDOMいじれないのかなー」とか思ってたんだけど、前に作った拡張は普通にいじれてたしそんなことはないだろうと色々調べていたら単にclass=".rich_button"がある要素が「続きを読む」ボダンだけじゃなかったというオチだった。

document.createElementNSの使い道

console.dir( document.createElement("path") ); // HTMLElement
console.dir( document.createElementNS("http://www.w3.org/2000/svg", "path") ); // SVGRectElement

ときどきJSコンソールの補完で出てくるからdocument.createElementNSという関数があるのは知っていたんだけど、何に使うのかはさっぱり分かってなかった。
今日SVGJavaScriptからいじっていて、SVGの要素を生成しようと普段やっているようにdocument.createElementを使ってたんだけど、うまく動かなかった。ちょっと調べてみると、どうやらdocument.createElementNSを使えば期待したように動くことが分かった。
上のコード例のように、普通にcreateElementを使うとHTMLElementオブジェクトになってしまうのだけど、createElementNSを使うとちゃんとSVGRectElementオブジェクトが生成される。createElementNSの第1引数にはSVG名前空間のURL、第2引数には生成する要素名を入れる。

古いgemをインストールしようとすると警告を出すgemを作った

昨日メンテナンスされていないgemをインストールしてはまって大変だったので、インストールする際に古いgemだと警告を出すgemを作った。

1年以上メンテナンスされていなかったら警告を出すようにしている。1年という期間に特に理由があるわけではないんだけど、それだけ古かったら危ないだろうなあという直感。

rubygems.orgにも上げたので、以下のコマンドでインストール出来る。

$ gem install check-date

仕組み

RubyGemsにはインストール前/後、アンインストール前/後にフックを仕掛ける機能があり、今回のgemではそれを利用している。RubyGemsrubygems_plugin.rbというファイルを探すので、そのファイルの中にフックや独自コマンドなどを記述するといいらしい。今回はフックを書きたかったので、ぐぐって見つけたRubyGems Plugin Hook Safety · GitHubを参考にした。

# lib/rubygems_plugin.rb
require "rubygems/installer"
require "check-date/hook"

フックの処理は以下のように書いている。インストールしようとしているgemの作成日が1年以上前だと警告を出す。Yを押すとそのままインストールを実行して、nを押すとabortするようにしている。

# lib/check-date/hook.rb
Gem.pre_install do |installer|
  today = Time.now
  year_sec = 365 * 24 * 60 * 60

  if (today - installer.spec.date) / year_sec > 1 then
    if !Gem::ConsoleUI.new.ask_yes_no("This gem was updated more than 1 years ago. (last update: #{installer.spec.date})\nInstall this gem?", true) then
      abort
    end
  end
end

そんなに複雑なことをしているわけではないんだけど、ネット上を探してもGem.pre_installを使っているサンプルがあまりなく、どう書けばいいか調べるのが大変だった。

gem installでインストールするrefeは使わない方がいい

更新が2006年と古いし、多分まともに動かない。自分の環境(Mac OS X 10.5.8 + RVM 1.6.20 + Ruby 1.9.2p180 + Rubygems 1.8.5)ではエラーが出る。

$ gem specification refe
(略)
date: 2006-02-28 00:00:00.000000000 +09:00
(略)

$ refe
/Users/akira/.rvm/gems/ruby-1.9.2-p180/gems/refe-0.8.0.3/lib/refe/mygetopt.rb:14:in `new': undefined method `map' for #<String:0x596c1c> (NoMethodError)
	from /Users/akira/.rvm/gems/ruby-1.9.2-p180/gems/refe-0.8.0.3/bin/refe:25:in `main'
	from /Users/akira/.rvm/gems/ruby-1.9.2-p180/gems/refe-0.8.0.3/bin/refe:130:in `<top (required)>'
	from /Users/akira/.rvm/gems/ruby-1.9.2-p180/bin/refe:19:in `load'
	from /Users/akira/.rvm/gems/ruby-1.9.2-p180/bin/refe:19:in `<main>'

http://redmine.ruby-lang.org/projects/rurema/wikiからリリースされている方を使ったほうがよさそう。http://redmine.ruby-lang.org/projects/rurema/wiki/ReleasedProductsの「ダウンロード」からダウンロードできる。
http://redmine.ruby-lang.org/projects/rurema/wiki/ReleasePackageHowToにはコマンドライン用のツールとしてrefe2が同梱されている、と書かれているけど、コマンドの名前自体がrefe2というわけではないっぽい。2011/06/29版のものではRuby1.9用の実行ファイルはrefe-1_9_2という名前だった。

blockdiagをインストールする

blockdiagという簡単にダイアグラムを生成出来るツールがあるらしいので、ちょっと使ってみようと思いインストールしたら微妙に手間取ったのでメモ代わりに残しておく。

自分の環境

インストール手順

基本的にマニュアル(http://blockdiag.com/blockdiag-ja/build/html/introduction.html)にある通りにやれば問題なくインストール出来ると思う。

$ sudo port install py25-setuptools py25-pil
$ sudo port install python_select
$ sudo python_select python25
$ sudo /opt/local/bin/easy_install-2.5 blockdiag

python_selectは必要あるのかよく分からない。インストール時にこのコマンド打ってね、って出てきたからとりあえず打っておいた。
自分の場合、pythonの勝手が分からなくて色々手間取ったので上記のコマンドを打つ以外にも色々してた。最初にmacにデフォルトでインストールされているeasy_install(/usr/bin/easy_install)を使ってしまい、blockdiag(/usr/local/bin/blockdiag)のシェバンでシステムにデフォルトでインストールされている方のpythonを使うようになっていたからmacports経由でインストールしたもの(/opt/local/bin/python2.5)を使うようにしたりとか。
最初にデフォルトのeasy_install経由でインストールした時は、http://coffeeash.blogspot.com/2011/02/mac-osx106-blockdiag-python.htmlと同じエラーが出たように記憶している。

使ってみる

http://blockdiag.com/blockdiag-ja/build/html/examples.html#id2にあるシンプルなブロック図を作ってみる。ファイルはsample.diagとして保存。

diagram {
  A -> B -> C -> D;
  A -> E -> F -> G;
}

以下のようにコマンドを打つとsample.pngが出力される。

$ blockdiag sample.diag

こんな感じで綺麗に出力してくれる。
blockdiagのサンプル

RVMを導入している時にgem installでrdocやriをインストールしないようにする

RVMを導入する前は~/.gemrcに色々書いてsudo無しでgem install hogehoge出来るようにしたり、rdocやriをインストールしないようにしていた。けど、RVM導入してからは~/.gemrcでは特に何もせずに*1RVMの設定そのままでgem install hogehogeするようにしていた。
で、そうするとrdocやriも自動的にインストールされてしまう。別にいいやと思ってしばらくそのままにしていたんだけど、インストールに結構時間がかかってしまうのでrdocやriをインストールしないようにした。
ただ、RVMを導入している時に、導入していない時と同じやり方でやってもいいのか分からなかった。ドキュメントを漁った結果、http://beginrescueend.com/gemsets/basics/にやり方が書いてあった。~/.gemrcに以下の2行を書くだけでOKっぽい。

install: --no-rdoc --no-ri
update: --no-rdoc --no-ri

*1:.gemrc_oldとファイル名を変えていた。