gem2arch
実家に帰ってきているんだが,対応を誤って輪番停電により下宿のマシンが起きてこなくなってしまったためやることが無くなり,なんとなく gem を Arch のパッケージマネージャで管理しようとした.
同じような PKGBUILD を手動で書くのはタルいので cabal2arch のように gem から自動生成することを考えて書いたのがこれ.ローカルにある gem のパスを引数として渡すとそれの PKGBUILD を生成する.
自動といっても特にC向けのライブラリに依存するようなライブラリの depends まで知る術は無いので,必要に応じて適切に手書きする必要がある.*1
あと Gem::Specification のマニュアル によると license(s) というフィールドが (important ではないが) ちゃんとあるのに,これを設定している gem が手元だと rake-compiler しかなく,今回いくつか生成した PKGBUILD については全部 license を手動で書く羽目になった.
#!/usr/bin/ruby # coding: utf-8 require 'rubygems' require 'rubygems/format' require 'fileutils' require 'erb' class Gem2Arch def initialize(gem) @gem = gem @spec = Gem::Format.from_file_by_path(@gem).spec end def name @spec.name end def pkgname 'ruby-' + name end def version @spec.version.to_s end def pkgdesc #@spec.description @spec.summary end def arch @spec.extensions.empty? ? ['any'] : ['i686', 'x86_64'] end def url @spec.homepage || "http://rubygems.org/gems/#{@spec.name}" end def license if @spec.licenses @spec.licenses else $stderr.puts "#{pkgname}: License not found!" [] end end def depends ['ruby'] + @spec.runtime_dependencies.map do |dep| s = 'ruby-' + dep.name if dep.requirement and not dep.requirement.none? s + Gem::Requirement.parse(dep.requirement.to_s).join('') else s end end end ERB.new(DATA.read).def_method self, 'to_pkgbuild' end ARGV.each do |gem| arch = Gem2Arch.new gem Dir.mkdir arch.pkgname unless Dir.exist? arch.pkgname Dir.chdir arch.pkgname do if File.exist? 'PKGBUILD' FileUtils.mv 'PKGBUILD', 'PKGBUILD.bak' end open('PKGBUILD', 'w') do |f| f.write arch.to_pkgbuild end end end __END__ pkgname=<%= pkgname %> _realname=<%= name %> pkgver=<%= version %> pkgrel=1 pkgdesc='<%= pkgdesc %>' arch=(<%= arch.map(&:inspect).join(' ') %>) url='<%= url %>' license=(<%= license.map(&:inspect).join(' ') %>) depends=(<%= depends.map(&:inspect).join(' ') %>) makedepends=('rubygems') source=(http://rubygems.org/downloads/$_realname-$pkgver.gem) noextract=($_realname-$pkgver.gem) build() { cd "$srcdir" local _gemdir="$(ruby -rubygems -e 'puts Gem.default_dir')" gem install --ignore-dependencies -i "$pkgdir$_gemdir" $_realname-$pkgver.gem } # vim:set ts=2 sw=2 et:
Arch Linux で PT2
PT2 を手に入れてしまったので Arch Linux で使えるようにした作業メモ.
マシン環境は
% uname -a Linux reinforce 2.6.37-ARCH #1 SMP PREEMPT Fri Feb 25 07:53:43 CET 2011 x86_64 Intel(R) Core(TM) i3 CPU 530 @ 2.93GHz GenuineIntel GNU/Linux
カードリーダは http://www.amazon.co.jp/dp/B00117VJ7O
arib25
まずこれをインストールする.
PKGBUILD 書いておいたので makepkg -si で.
https://github.com/eagletmt/PKGBUILDs/tree/master/arib25
pt1_drv, recpt1
次にドライバと視聴・録画のためのプログラムをインストール.
これも PKGBUILD 書いておいたので makepkg -si で.
https://github.com/eagletmt/PKGBUILDs/tree/master/pt1-hg
うまくいっていれば /dev/pt1video[0-3] ができているはず.
lspci で
08:01.0 Multimedia controller: Xilinx Corporation Device 222a (rev 01)
みたいなのが表示されないときは,そもそも PT2 がハードウェア的にも認識されていないので接続を確かめる.
B-CAS
pcsc-tools, pcsc-perl パッケージをインストール.
それと ccid パッケージが必要だが,最新の 1.4.2 では B-CAS カードを認識してくれないので 1.3.13 をインストールする.
1.3.13 用の PKGBUILD.
https://github.com/eagletmt/PKGBUILDs/tree/master/ccid13
/etc/rc.d/pcscd start で pcsc のデーモンを立ち上げる.
必要に応じて /etc/rc.conf に DAEMONS=(... pcscd) を追加しておく.
うまくいっていれば pcsc_scan を起動して B-CAS カードを抜き差しするとそれっぽい表示がされるはず.
Unresponsive card と表示されるときは B-CAS カードの向きが間違ってる*1.
これで recpt1 は動いたんだけど,recpt1ctl --channel でチャンネルを変えようとすると
Cannot tune to the specified channel Tuner cannot start recording
と出力して recpt1 が死ぬ.
recpt1ctl --extend はちゃんと動作してる模様.
追記 2011-03-29T00:40:32
現時点で Arch の標準的なカーネルである kernel26-2.6.37.* では DVB 版の PT1 ドライバがモジュールとして含まれている.
% zgrep -B3 CONFIG_DVB_PT1 /proc/config.gz # # Supported Earthsoft PT1 Adapters # CONFIG_DVB_PT1=m
なぜか俺の環境ではこのモジュールは使えなかったんだが,念のため pt1_drv と競合するのを避けるために earth_pt1 をブラックリストに入れてロードしないようにした.
Arch でブラックリストに入れるには /etc/rc.conf に
MODULES=(!earth_pt1 ...)
というように書き加えればいい.
*1:これ絶対多くの人が表裏反対に差すと思うんだけど…
EM::DefaultDeferrable の使い道
というわけで
http://d.hatena.ne.jp/eagletmt/20110211/1297413439
は
class HtmlDocument include EM::Deferrable ...
で良いのであった…
では EM::DefaultDeferrable は何のために提供されているのかというと
DefaultDeferrable is an otherwise empty class that includes Deferrable. This is very useful when you just need to return a Deferrable object as a way of communicating deferred status to some other part of a program.
http://eventmachine.rubyforge.org/EventMachine/DefaultDeferrable.html
とあるので,単に完了/失敗を伝えたいときに使うとよさそう.
em-http-request のようにクライアント用途で EventMachine を使うと,いつ EM.stop するかがめんどくさかったりするので,例えばこういう使い方があるのかもしれない.
#!/usr/bin/ruby # coding: utf-8 require 'em-http' require 'nokogiri' class HtmlDocument include EM::Deferrable attr_reader :http, :parser def initialize(http) @http = http end def succeed @parser = Nokogiri::HTML.parse @http.response super end end def get_document(uri) http = EM::HttpRequest.new(uri).get doc = HtmlDocument.new http http.callback { doc.succeed } http.errback { doc.fail } doc end EM.run do multi = EM::MultiRequest.new [ 'http://www.google.co.jp/', 'http://www.hatena.ne.jp/h/o/g/e', 'http://eagletmt.com/', ].each do |uri| defer = EM::DefaultDeferrable.new multi.add defer doc = get_document uri doc.errback do puts "ng: #{doc.http.error}: #{doc.http.uri}" defer.fail end doc.callback do puts "ok: #{doc.http.uri}: #{doc.http.response_header.status}: #{doc.parser.xpath('//title').inner_text}" m = EM::MultiRequest.new doc.parser.xpath('//a[@href]').each do |a| href = a['href'] if href.start_with? 'http://' doc2 = get_document href doc2.errback do puts " ng: #{doc2.http.error}: #{doc2.http.uri}" end doc2.callback do puts " ok: #{doc2.http.uri}: #{doc2.http.response_header.status}: #{doc2.parser.xpath('//title').inner_text}" end m.add doc2 end end if m.requests.empty? defer.succeed else m.callback do defer.succeed end end end end multi.callback do EM.stop end end
include / extend と super
勘違いしてた.include / extend したメソッド(?)を上書きしたときも,上書きされたメソッドを super で参照できるのか.
extend と super を使ったどーでもいい例.
def mod(i) Module.new do define_method :f do super() + [i] end end end module B def f [] end end module M extend B 5.times do |i| extend mod(i) end def self.f super() + [-1] end end p M.f # => [0, 1, 2, 3, 4, -1] o = Object.new o.extend B 5.times do |i| o.extend mod(i) end p o.f # => [0, 1, 2, 3, 4]
/proc/$PID/fd とテンポラリファイル
http://memo.officebrook.net/20110211.html を見て,なるほどそうやってファイルを取り出せるのかと感心した.
UNIX では open(2) で既に開かれているファイルを unlink(2) してもファイルディスクリプタは有効であり続け,実際に消えるのは close(2) されたときになる.
このことを利用して open(2) した直後に unlink(2) することで,tmpfile(3) のようにプロセスが終了したときに勝手に消えるテンポラリファイルを作ることができる.
で,この unlink(2) されたけどファイルディスクリプタは生きているようなファイルはディレクトリからは見えないけど,/proc/$PID/fd でどうやら参照できるために↑のような形で取り出せるようだ.
この様子を確認するためにこんなコードを書いて実行してみた.
#include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(void) { const char path[] = "/tmp/hello.txt"; int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); system("ls -l /proc/self/fd"); system("ls -l /tmp/hello.txt"); unlink(path); system("ls -l /proc/self/fd"); system("ls -l /tmp/hello.txt"); write(fd, "hello\n", 6); system("cat /proc/self/fd/3"); close(fd); system("ls -l /proc/self/fd"); return 0; }
実行結果.0 が /dev/null になってたり 1,2 がファイルになっているのは quickrun で実行したから.
合計 0 lrwx------ 1 eagletmt eagletmt 64 2月 12 01:11 0 -> /dev/null l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 1 -> /tmp/v9d5oqW/5 l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 2 -> /tmp/v9d5oqW/5 l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 3 -> /tmp/hello.txt lr-x------ 1 eagletmt eagletmt 64 2月 12 01:11 4 -> /proc/18885/fd -rw------- 1 eagletmt eagletmt 0 2月 12 01:11 /tmp/hello.txt 合計 0 lrwx------ 1 eagletmt eagletmt 64 2月 12 01:11 0 -> /dev/null l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 1 -> /tmp/v9d5oqW/5 l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 2 -> /tmp/v9d5oqW/5 l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 3 -> /tmp/hello.txt (deleted) lr-x------ 1 eagletmt eagletmt 64 2月 12 01:11 4 -> /proc/18887/fd ls: /tmp/hello.txt にアクセスできません: そのようなファイルやディレクトリはありません hello 合計 0 lrwx------ 1 eagletmt eagletmt 64 2月 12 01:11 0 -> /dev/null l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 1 -> /tmp/v9d5oqW/5 l-wx------ 1 eagletmt eagletmt 64 2月 12 01:11 2 -> /tmp/v9d5oqW/5 lr-x------ 1 eagletmt eagletmt 64 2月 12 01:11 3 -> /proc/18889/fd
既存の EventMachine::Deferrable なクラスを薄くラップする
本格的に Deferrable なクラスを作りたいときは include EM::Deferrable するけど,そうではなく既存の Deferrable の上にちょっと乗せるだけみたいなときは EM::DefaultDeferrable を利用するといいらしい.
em-http-request を利用して非同期にリクエストを投げ,結果を Nokogiri でパースして例えばタイトルを表示する例.こんなかんじでいいんだろうか.
#!/usr/bin/ruby # coding: utf-8 require 'em-http' require 'nokogiri' class HtmlDocument < EM::DefaultDeferrable attr_reader :http, :parser def initialize(http) @http = http end def succeed @parser = Nokogiri::HTML.parse @http.response super end end def get_document(uri) http = EM::HttpRequest.new(uri).get doc = HtmlDocument.new http http.callback { doc.succeed } http.errback { doc.fail } doc end EM.run do multi = EM::MultiRequest.new [ 'http://www.google.co.jp/', 'http://www.hatena.ne.jp/h/o/g/e', 'http://eagletmt.com/', ].each do |uri| doc = get_document uri doc.callback do puts "ok: #{doc.http.response_header.status}: #{doc.parser.xpath('//title').inner_text}" end doc.errback do puts "ng: #{doc.http.error}: #{doc.http.uri}" end multi.add doc end multi.callback do puts "succeeded #{multi.responses[:succeeded].size}/#{multi.requests.size}" EM.stop end end
実行結果(最後の行以外の順番はバラつく可能性あり)
ng: unable to resolve server address: http://eagletmt.com:80/ ok: 200: Google ok: 404: おさがしのページは見つかりませんでした - はてな succeeded 2/3
コマンドラインで proxy の設定をできるようにするプラグイン
コードの最初に怪しい英語での説明が書いてあるけど,ようは Edit -> Preferences -> Advanced -> Settings のとこにある設定を Vimperator のコマンドラインから行えるようにしたもの.
manual に設定したときの -ssl とか -no-proxy とかのオプションの扱いが適当なのでまずいところがあるかも.
自分が manual を使わないのでよくわからない…
http://code.google.com/p/vimperator-labs/source/detail?r=85e98ededcc43bb2df66ca1b004cf2ea7aff1f61
で入ったサブコマンドのサポートを利用しているので,現時点ではリポジトリからとってきてビルドした Vimperator じゃないと動かないです.