カワイイはつくれる

http://d.hatena.ne.jp/m4i/ から移行

2012-03-09

Slim3Scala を作った(1年半前に)

Slim3 の Scala 版を作りかけて、途中で時間がとれなくなり、いつかリリースしようと思いつつも、Google App Engine への興味を失い放置されていたコード片をアップしました。

https://github.com/m4i/slim3scala

結構な時間をかけて Scalaコンパイラとか、SBT のすごい読みにくいソースを読んだりしてたのに、公開しないのももったいないので。meta クラスの生成みたいなのがんばった記憶がある。

2012-02-22

Rails + Pow + pry/ruby-debug(Ruby 1.9.3)

pry

Gemfile

group :development, :test do
  gem 'plymouth', require: false
  gem 'pry-exception_explorer'
  gem 'pry-nav'
  gem 'pry-remote'
  gem 'pry-stack_explorer'
end

breakpoint

binding.pry_remote

connect

$ bundle exec pry-remote

ruby-debug

Gemfile

group :development, :test do
  gem 'ruby-debug19'
  gem 'ruby-debug-base19x', '>= 0.11.30.pre10'
  gem 'linecache19', git: 'https://github.com/mark-moseley/linecache.git', ref: '869c6a65155068415925067e480741bd0a71527e'
end

initializer

require 'ruby-debug'
Debugger.settings[:autoeval] = true
Debugger.settings[:autolist] = 1
Debugger.settings[:reload_source_on_change] = true
Debugger.start_remote

breakpoint

debugger

connect

$ bundle exec rdebug --client

メモ

  • ruby-debug-pry は rdebug --client からは利用できなかった
  • plymouth は使いたい時だけ rspec --no-drb -r plymouth spec

まとめ

pry ヤバイ

2012-02-13

omniauth-runkeeper を公開しました

もう2ヶ月近く前ですが、omniauth-runkeeper を公開しました。

https://rubygems.org/gems/omniauth-runkeeper
https://github.com/m4i/omniauth-runkeeper

OmniAuth の作者(社)が作った omniauth-github と同じ OAuth 2.0 なのでそれをベースにしました。

Nike+ から RunKeeper へのインポートアプリを作ろうと思って作ったのに、そっちは全く手付かず。

2012-02-13

tmux での1行コピー または man tmux に載っていないコマンド

screen から tmux に乗り換えて2ヶ月ほど立ちますが、不便なことが4つありました。

  1. copy した文字列を直接ファイルやプログラムに渡せない
  2. 2ストロークの key bind が定義できない
  3. cdd が動かない
  4. copy mode で1行コピーがない

(1), (2) は未だに未解決ですが、
(3) は 「cdd を tmux, bash, multi session +α に対応した」で解決、
今回は先日解決した (4) について書こうと思います。


先日 .tmux.conf を整理しようと man tmux を眺めていたんですが、どこからかコピペしてきた begin-selectioncopy-selection が man に載ってないことに気づきました。どうやら key-table を指定した際に使用可能なコマンドは基本的に載っていないようです。

そこでソースを検索してみたところ、mode-key.c に見たことのないコマンドが大量あり、copy-line といういかにもそれっぽいコマンドを見つけました。

例えば、

bind-key -t vi-copy Y copy-line

vim や screen と同じように Y で1行コピーが出来るようになります。(tmux 1.5 以上)


最後に tmux 1.6 の mode-key.c からコマンド一覧を以下に抜粋しておきます。
それぞれの使い方についてはまとまった説明は見つけられなかったので、名前から推測するか、検索するか、ソースを読むのが良いと思います。

key-bind -t vi-edit/emacs-edit で使えるコマンド

backspace cancel complete cursor-left
cursor-right delete delete-line delete-end-of-line
delete-word end-of-line enter history-down
history-up next-space next-space-end next-word
next-word-end paste previous-space previous-word
start-of-line switch-mode switch-mode-append transpose-chars

key-bind -t vi-choice/emacs-choice で使えるコマンド

cancel choose down page-down
page-up scroll-down scroll-up up

key-bind -t vi-copy/emacs-copy で使えるコマンド

back-to-indentation bottom-line cancel clear-selection
copy-line copy-end-of-line copy-selection cursor-down
end-of-line goto-line history-bottom history-top
jump-forward jump-again jump-reverse jump-backward
jump-to-forward jump-to-backward cursor-left rectangle-toggle
middle-line page-down next-space next-space-end
next-word next-word-end page-up previous-space
previous-word cursor-right scroll-down scroll-up
search-again search-forward search-reverse search-backward
select-line start-number-prefix start-of-line begin-selection
top-line cursor-up

2012-02-13

はてなブログサイドバーにカテゴリ一覧を自動表示する

コピペ用

以下のコードをサイドバーとして追加

<script>
(function($) {
  var $module_body = $('.hatena-module-body:last');

  $module_body.parent()
    .removeClass('hatena-module-html')
    .addClass('hatena-module-categories')
    .prepend($('<div>').addClass('hatena-module-title').text('カテゴリー'));

  $.get('/categories').done(function(html) {
    $module_body.html(html.replace(
      /[\s\S]*<div class="entry-content">\s*|\s*<\/div>[\s\S]*/g, ''));
  });
})(jQuery);
</script>

https://github.com/m4i/hatenablog-snippets/blob/master/category.js


解説

他にもいくつかカテゴリ一覧を表示するコードが紹介されていますが、このコードは以下の点で優れています。

  1. 利用しているカテゴリが自動でリストアップされる
  2. HTML 構造が正規のモジュールに近い

1. 利用しているカテゴリが自動でリストアップされる

自分でメンテナンスしなくても使用しているカテゴリが自動でリストアップされます。
カテゴリ一覧は /categoies から取得しています。

2. HTML 構造が標準モジュールに近い

プロフィール、検索、リンクなどと同じように、以下のような HTML 構造になっています。

<div class="hatena-module hatena-module-categories">
  <div class="hatena-module-title">カテゴリー</div>
  <div class="hatena-module-body">
    <ul>
      <li>カテゴリー1</li>
      <li>カテゴリー2</li>
    </ul>
  </div>
</div>

なので、既存のテーマと相性が良かったり、将来カテゴリモジュールが実装された時に CSS の修正が少なくて済むと思います。



利用例はこのブログの最下部で見れます。お好みで適当に CSS を書いてください。

2012-02-05

はてなブログで「続きを読む」記法

はてなブログでははてな記法の実装がはてなダイアリーと違うようで、動かない記法がいくつかあります。*1

続きを読む記法 もその内の一つです。
はてなダイアリーで書いていたときは使ったこともなかったんですが、最近立て続けに長めの記事を書いたら、トップページが長くなりすぎて見難くなってしまったので、無理やり使えるようにしてみました。

動作確認のためにこの下に続きを読む記法を書いてみますので、トップページなどから確認してみてください。

(この上)

どうやって実現するかですが、==== を記述した部分には以下の HTML が挿入されるようです。

<div class="seemore">

ですので、これ以降の要素をすべて削除し、「続きを読む」のリンクを設置してあげれば OK です。

そこで以下のコードを書きました。これをサイドバーにコピペすれば使えるようになります。

<script>
(function($) {
  if (!(location.pathname === '/' ||
        location.pathname.indexOf('/category/') === 0)) return;

  function pathname(url) {
    return url.replace(/^https?:\/\/[^\/]*|[#?].*$/g, '');
  }

  $('article.entry').each(function() {
    var $seemore = $(this).find('div.entry-content div.seemore');
    if ($seemore.length === 0) return;

    var url = $(this)
      .find('header.entry-header a.entry-title-link').attr('href');

    var $a = $seemore.find('a');
    if ($a.length === 1 && pathname($a.attr('href')) === pathname(url)) return;

    var $tail = $('<a>')
      .addClass('seemore')
      .attr('href', url)
      .text('続きを読む')
      .prependTo($seemore);

    while (!$tail.hasClass('entry-content')) {
      $tail.nextAll().detach();
      $tail = $tail.parent();
    }
  });
})(jQuery);
</script>

https://github.com/m4i/hatenablog-snippets/blob/master/seemore.js


2012-02-13: カテゴリページに対応

*1:この脚注記法も脚注が表示されず、tooltip を読むしかありません。逆に gist 記法のような新しいものもあります。

2012-02-03

Gyazo の URL はどこまで短くできるか

序論

Gyazo というサービスがあります。
スクリーンショットを共有するためのウェブサービスで、撮ったあとにその URL を共有したい人に送るだけで画像を簡単に共有することができます。

その際の画像の URL は次のようになっています。

http://gyazo.com/587011ed4b960eff36ef351efe393e37

Gyazo というアプリケーションの性質上、このURLが推測可能なものであってはいけません。
Gyazo は画像を特定する部分の文字列が32文字で、この文字列は長ければ長いほど推測が難しくなります。

これを8文字にしてはいけないのでしょうか?8文字であったら口頭で伝えることも可能で、新たな利用方法が生まれるかもしれません。
逆に32文字で本当に推測不可能と言えるのでしょうか?本当は1024文字必要なところをコピペの利便性を考えて32文字にしてしまっているのではないでしょうか。

今回は、この文字列をどの程度まで短くしても推測不可能と言えるのか考えてみたいと思います。

ちなみに数学に関しては高校の知識も危ういので、間違っている可能性があります。そのときはぜひ指摘していただけたらと思います。

この先を読み進める前に、自分がこのサービスを作るとしたらどの程度の文字数にするか考えてみると面白いかもしれません。8文字は明らかにダメだろうと思う方は12文字ではどうでしょうか?16文字は?それとも32文字では不安だから64文字くらいにしますか?


見積もり

推測不可能な文字数を決めるためには以下の3つの値をそれぞれ自分で見積もる必要があります。

  • アップロードされる画像の数は最大でどの程度になるのか
    • この画像数を m とおきます
  • URL を見つけるために総当たり攻撃を仕掛けられたときに何回までのアクセスを許すのか
    • この回数を n とおきます
  • n回の総当たり攻撃を仕掛けられたときに URL が発見される確率をどの程度に抑えたいのか
    • この確率を p とおきます

ここでは

  • m = 2^{30} \simeq 1,000,000,000
  • n = 2^{20} \simeq 1,000,000
  • p = 1/2^{14} \simeq 0.006%

と仮定して考えてみたいと思います。


8文字の場合

仮に8文字だとして考えてみましょう。

Gyazo の場合どうやら16進数のようですので 16 ^ 8 = 4,294,967,296 通りの URL が存在しうることになります。
画像の数は 2 ^ {30} = 1,073,741,824 ですので全体の4分の1が利用されることになります。

これでは直感的にもすぐに URL を探し当てられてしまいそうですね。
この簡単な例でどうやって確率を求められるのか式を考えてみましょう。

1回適当な URL を入力するだけで 25% の確率で見つかってしまいます。式は

\frac{2 ^ {30}}{16 ^ 8} = 25%

ですね。
2回試した場合はどうでしょうか?

1 - (1 - \frac{2 ^ {30}}{16 ^ 8}) ^ 2 = \frac{7}{16} \simeq 44%

なんか一気に難しくなった気がしないでもないですが、答えはそれっぽいですね。

いや、本当は

1 - (1 - \frac{2 ^ {30}}{16 ^ 8})(1 - \frac{2 ^ {30}}{16 ^ 8 - 1})

なのかもしれませんが、最大でも n = 2 ^ {20} 程度の試行回数なので、今回はわかりやすくするため上の式で近似…でも問題ないでしょうか?

10回試して正しい URL が発見される確率は

1 - (1 - \frac{2 ^ {30}}{16 ^ 8}) ^ {10} \simeq 94%

です。
つまり8文字では n \simeq 1,000,000 回の試行を p \simeq 0.006% 以下に抑えるなんて到底難しそうです。


必要な文字数を計算する

ここから9文字の場合はどうか、10文字の場合はどうかとやっていっても時間がかかるので、この条件をみたす文字数を x とおきましょう。
それを先ほどの計算式に当てはめてみます。

1 - (1 - \frac{m}{16 ^ x}) ^ n < p

これを x を求める式に変形してみましょう。

x > \log_{16} \frac{m}{1 - \sqrt[n]{1 - p}}

これくらいは簡単ですよね?
もちろん私はグーグル先生に聞きました。

m, n, p にそれぞれ値を代入して計算してみましょう。Ruby だと

m = 2 ** 30
n = 2 ** 20
p = 1.0 / 2 ** 14
Math.log(m / (1 - (1 - p) ** (1.0 / n)), 16)
  => 15.999988993278293

となりました。ということは

x > 15.999988993278293

ですので、文字数が16文字あれば見積もった条件を満たすことになります。


条件を緩めてみる

このサービスは少人数で短期間使うだけだし、せいぜい人力で URL 入れてみるくらいの人しかいないと考えて以下の条件にしてみます。

  • m = 1,000
  • n = 100
  • p = 1%

この場合

x > 5.8115813621245

となり、6文字で済むことになります。


文字種を増やしてみる

そもそも URL が16進数でなくてはいけない理由はないし、もっと短くしたいと思えば、文字の種類を増やせば短くできるかもしれません。

  • 16進数: 16
  • 数字 + 英小文字: 36
  • 数字 + 英字: 62

62なんて16の約4倍なんだから、16文字必要だったところが4文字で済んでもいいのではないかと期待が膨らみます。
では計算してみましょう。

数字 + 英小文字: 36
x > \log_{36} \frac{m}{1 - \sqrt[n]{1 - p}} \simeq 12.38

数字 + 英字: 62
x > \log_{62} \frac{m}{1 - \sqrt[n]{1 - p}} \simeq 10.75

13文字と11文字で期待したほどではないかもしれませんが、短くすることが出来ました。


結論

ということで Gyazo の16進数32文字は十分な文字数だということがわかりました。
そもそもこの文字数は MD5 と同じ 128bit ですので、たかがネットワーク越しの総当たり攻撃で見つかってしまっては困ります。

一方で8文字ではやはり少なすぎました。

計算では16文字となりましたが、設定した見積もりで十分なのか定かではないですし、この計算があっているかどうかの保証が全くありません。
どうしてもという理由がなければ長めにしたほうが安全だと思います。