Home > Tags > hack

hack

Ticket2RTM

A Trac plugin that synchronizes Trac tickets and Remember the Milk tasks.

  • when a ticket is created, a RTM task also will be added.
  • when you close a ticket, the associated RTM task will be completed.

Screenshot

Screencast

Download

ticket2rtm-0.1.zip

Benefits

  • unified Task List
  • check tickets from mobile

Install

build

  1. edit Makefile and set PLUGINS_DIR to your Trac plugin dir
  2. make

or:

  1. python setup.py –bdist_egg
  2. copy dist/TracTicket2RTMPlugin*.egg into your Trac plugin dir

RTM API key, token

This plugin requires RTM API key, shared secret, and write-permitted token. You can obtain your API key and shared secret from here.

After acquired key and shared secret, you can get your token by using bundled script. Run as follow:

% python get_token.py <api key> <shared secret>

Configuration

  1. copy/paste contents of trac_ini_sample.txt to your trac.ini
  2. set rtm_api_key, rtm_api_secret, rtm_api_token to ones acquired above
  3. enjoy !

Ticket2RTM 0.1

TracのチケットからRemember the Milkのタスクを自動生成するTracプラグインをつくりました。 詳しくは trac:Ticket2RTMに。

スクリーンショット

チケットがつくられたら、自動的にRTMのタスクがつくられます。

スクリーンキャスト

ダウンロード

ticket2rtm-0.1.zip

動機

やることリストを1つにしたいと思ったのでした。

  • RTMで日々のやることを管理しつつ、何かつくるときはTracでチケットを切ってる → タスクがあちこちに分散
  • 複数のプロジェクトを同時にTracですすめる → チケットがあちこちにありすぎ

あちこちにタスクが散らばっていては、やらなきゃいけないことを見落としてしまったり、やらないことを決めるためのコストが増えてしまったりで、ストレスフリーな状態になかなかなれません。

ぜんぶまとめて1つのインターフェイスで見られたら幸せだろうな、RTMのインターフェイスはいいよな、と考えてプロトタイプをつくってみたのでした。

あと、RTMはモバイル版もあるので、チケットをケータイからも確認できるという副作用的メリットもあります。

学んだこと

Python

Pythonのコードをまともに書くのは始めてでした。selfうっとうしいとか、正規表現めんどいとか、インタラクティブ環境でのreloadを最初しらなくてえらい非効率だったりとか、switchないの? とか驚きの連続でしたが、なんとかなるもんですね。 プラグインをつくるためにTracのコードを読んでたのですが、インデント強制な言語はコードリーディングにいいですね。 ものの見方を増やせたのはよかったかなと。

RTM連携させるために、RTM APIのPythonラッパの一部を書くことになりました。 これには1年ほど前にrtmilk.rbというRuby版をつくっていた経験が活きました。多少言語のsyntaxが変わろうが、やりたいことがはっきりしてれば考え方は同じ。

Trac Plugin

TracでPluginを書くのは思っていたよりも簡単でした。 これをとっかかりにして、いろいろなアイデアを形にしていこうかなと。


今年のアウトプット第一弾はTrac+RTMハックでした。 1/14から始めて、夜なべでつくり、1/18に0.1リリース。思いつき駆動だなー。 しかしこれで明日のShibuya.trac ミーティング1.0、明後日のRemember The Milk 交流会 in 東京で話せるネタができたということで。

はてなハイクに投稿するシェルスクリプト

はてなハイクで遊び始めました。

コード

#!/bin/sh
RKM='REPLACE WITH YOUR RKM'
KEYWORD=$1
BODY=$2
 
curl http://h.hatena.ne.jp/entry \
  -d rkm=$RKM \
  -d "word=$KEYWORD" \
  -d "body=$BODY" \
  -b cookies.txt

実行例

% ./haiku.sh Test "test post from haiku.sh !"

メモ

  • ASCIIなキーワード、本文だけ
  • cookies.txtはFirefoxのprofileから、はてなでログイン済みのものをコピーしてきました

RKMの値は、はてなハイクのソースをブラウザから見て

input type=”hidden” name=”rkm” value=”xxxxxx”

のxxxxxxの部分にある文字列をコピペるとOKでした。


http://h.hatena.ne.jp/keyword/はいくハックでいろいろやってます。

WP CalendarCloud plugin

よくあるタグクラウドのカレンダー版をつくりました。 ただずらっと年月のリストがあるよりも、ちょっとばかりは見た目が楽しくなるんではないでしょうか。

WordPressのCustom Template Tag として実装してあります。 CodeReposに置いておきました

実際に動いているものが、このサイトのサイドバー、アーカイブのところにあります。 詳しくは、こちらをご覧ください。


明日以降に、WordPressのplugin レポジトリにも登録できないか調べてみます。今日はもう寝なきゃなので…

cmd2twitter

コマンドラインの入力をすべてTwitterにpostするコマンドを、思いつきで作りました。 chatty を改造しています。

コード

に置いてあります。

使い方

make して、

./cmd2twitter <twitterのユーザ名> <パスワード>

として、あとは、ふつうにlsとかcdとか打ち込んでると、自分のTimelineに同じものがpostされてます。 終わるときは、Ctrl-Dとかで。

必要なもの

  • cURL : 最近のUnixなシステムには付いていると思います。

ToDo

  • TwitterにpostしたあとのJSON出力がうっとうしいので消す (> /dev/nullすればよいかな)
  • curlでpostするときに待たされる遅延がうっとうしいので非同期化する
  • 自分のTimelineに出てくるのはうっとうしいので、Twitter botをたててDirectMessageする

詳しくは CodeRepos::Share::cmd2twitterにて。

TwitterTimeTrack 0.1.5

Twitterにダイレクトメッセージで書き込んだメッセージをiCalendar形式で提供する、TwitterTimeTrack というWebサービスをつくりました。 時間家計簿をつけるのにどうぞ。

使いかた

  1. 2cという専用アカウントをfollowします。(followするとすぐに、2cはあなたをfollowします。これでダイレクトメッセージを2cに送れるようになります。)
  2. 2c にダイレクトメッセージを投げます (例 : d 2c なんとかかんとか)。 2件以上のダイレクトメッセージがないと、iCalendarは生成されません。
  3. あなた用のiCalendar URLを、subscribeします。URLは、http://deadbeaf.org/twitter2ical/ical/あなたのスクリーン名 になります。

スクリーンショット

TwitterTimeTrack via Google Calendar TwitterTimeTrack via iCal

制限

データがすべてオンメモリにあるため、そんなに大量に保持することができません。 具体的には、ユーザは100人まで、各ユーザ毎に蓄えているメッセージは最近の128件まで、としています。 ユーザが100人を越えたら、いちばん昔に更新してた人から順に消されてゆきますのでご了承ください。

プロジェクト

redMineで管理してます。

ソース

一式がダウンロードできます。 たいへん見苦しいコードですがとりあえず。


眠いので解説とかは明日以降に書きます。

TwitterのTimelineからiCalendarをつくる

Twitterのuser_timelineからiCalendarを生成するスクリプトを書きました。 ものすごいシンプルなもの。

ダウンロード

user_timeline_to_ical.rb

つかいかた

ruby user_timeline_to_ical.rb Twitterのユーザ名

とすると、標準出力にiCalendar形式で最近20件のイベントが出てきます。 出力をリダイレクトして、iCalやGoogle CalendarといったiCalendar形式をimportできるソフトウェアに食わせてみましょう。

スクリーンショット

iCalから: Twitter to iCalendar-01

Google Calendarから: Twitter to iCalendar-02

制限事項

  • 10/09現在、Twitter APIでuser_timelineにpageパラメータを渡すことができなくなっているため、20件以上のメッセージを取り出すことができません。
  • iCalendarの性質上、イベントが同じ時間帯に密集していると非常にみづらいです。たまにしかTwitterにpostしない人にはちょうどいいかも。

今後

トラッキング専用のアカウントにdirect messageをなげれば、あとから自分が何やっていたかをiCalendarで取得できるWebサービスをつくるつもりです。redmineのプロジェクトでつくってます。 これができれば、すべてのpostを表示せずにすむようになるし、iCalからsubscribeしたりできて、だいぶ時間家計簿として見易くなると期待しています。

2007.10.23 追記 : つくりました

背景

レバレッジ時間術 を読んでて、時間家計簿をつけようと思いました。 ちょうど最近Twitterを始めてて、これは時間をトラッキングしとくのにちょうどいいのでは、と思いついた次第。

ソース

いちおう貼りつけときます。

#
# create iCalendar from recent 20 user_timeline.
#
# usage
#   ruby user_timeline_to_ical.rb [username] > some.ics
#
require 'rubygems'
require 'json'
require 'icalendar'
require 'uri'
require 'open-uri'
require 'kconv'
require 'nkf'
require 'logger'
 
KCODE = 'u'
 
# XXX:
#   quick fix to avoid charset crash
module Icalendar
  class Component < Icalendar::Base
    def print_properties
      s = ""
 
      @properties.each do |key,val|
        # Take out underscore for property names that conflicted
        # with built-in words.
        if key =~ /ip_.*/
          key = key[3..-1]
        end
 
        # Property name
        unless multiline_property?(key)
           prelude = "#{key.gsub(/_/, '-').upcase}" +
 
           # Possible parameters
           print_parameters(val)
 
           # Property value
           value = ":#{val.to_ical}"
           escaped = prelude + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
           #escaped = value
           #s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH # XXX : quick fix to avoid charset crash
           s << escaped << "\r\n"
           s.gsub!(/ *$/, '')
         else
           prelude = "#{key.gsub(/_/, '-').upcase}"
            val.each do |v|
               params = print_parameters(v)
               value = ":#{v.to_ical}"
               escaped = prelude + params + value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
               s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
               s << escaped << "\r\n"
               s.gsub!(/ *$/, '')
            end
         end
      end
      s
    end
  end
end
 
class UserTimeLine
  attr_accessor :id, :since
  URL = 'http://twitter.com/statuses/user_timeline/'
 
  def initialize(id)
    @id = id
    #@log = Logger.new('debug.log')
  end
 
  def get(url)
    JSON.parse(open(url).readlines.join).sort { |a, b| a['id'] <=> b['id'] }
  end
 
  def url(*arg)
    url = URL + @id + '.json'
    return url if arg.empty?
 
    hash = arg[0]
    args = {}
 
    args['page'] = hash[:page] if hash[:page]
    if hash[:since]
      if String == hash[:since].class
        args['since'] = URI.escape(hash[:since].split.join('+'))
      else # should be Number
        args['since_id'] = hash[:since]
      end
    end
    arg = args.collect { |k, v| [k, v].join('=') }.join('&')
 
    url + '?' + arg
  end
 
  def to_ical
    msgs = get(url).collect {|x| [x['text'], x['created_at']]}
    cal = Icalendar::Calendar.new
 
    (msgs.size-1).times do |i|
      cal.event do
        dtstart  DateTime.parse(msgs[i][1]).to_ical(true)
        dtend    DateTime.parse(msgs[i+1][1]).to_ical(true)
        summary  msgs[i][0]
      end
    end
 
    cal.to_ical
  end
end
 
 
utl = UserTimeLine.new(ARGV.shift)
print NKF.nkf('-w -Lw', utl.to_ical)

レバレッジ時間術―ノーリスク・ハイリターンの成功原則 (幻冬舎新書 ほ 2-1)
本田 直之
幻冬舎 (2007/05)
売り上げランキング: 359
おすすめ度の平均: 4.5
5 効率アップのシステム化
4 時間管理ができるとカッコいい!
4 マンガ本からの脱却を目指す方への最初の一冊

RubyCocoa で QuartzComposer CustomPatch (2)

とりあえず、できました。

スクリーンショット

QCRubyPatch QCRubyPatch-2

サンプル

input, output ともにStringのPortを1つずつもち、入力にある”Objective-C”を”Ruby”に正規表現で置換するというだけのサンプルを書きました。

RubyPatch (binary & source) rev3 (i386 Binary)

RubyCocoaをUniversal Binary でビルドしていなかったため、いまのところIntel Mac だけで動きます。

RubyCocoa の svn trunk、 MacBook Kuroで動作確認をしています。

何をしているか

  1. Patchがロードされるとき、すなわち “registerNodesWithManager” が呼ばれるときに、RBBundleInitを呼んでRubyCocoaの準備をする
  2. Patchの動作を記述するObjective-CのクラスをProxyとして、実際の動作を記述するRubyクラスのインスタンスを保持させる
  3. Rubyクラスは、Objective-CクラスからPortをもらってくる
  4. Patchを実行するイベントがきたら、Rubyクラスのインスタンスに対して処理を移譲

といったところ。

実際のコードは、RubyQC::Trac にあるのでご覧ください。

このアプローチの問題点

  • いちいち全メソッドを Objective-Cのクラス → Rubyのクラス に投げるよう書くのがめんどう
  • Portの記述がObjective-Cのクラスにしか書けない (みたい) なので、Portを加えたり減らしたりするたびにObjective-C/Rubyコードともに変更しないといけない。わすれそう。

OSX::QCPatchクラスをRubyクラスで継承できて、さらにPortの情報もRubyクラスで書けるようになれば、これらの問題は解決しそうです。

課題

QCPatch.execute は、3つの引数をとるのですが、Rubyクラスのメソッドにどうやって3引数を渡すのか分からない…

18:03 追記

MLで、LimeChatの中の人に教えてもらいました。

objc_method :execute_time_arguments, %w|char id double id|
def execute_time_arguments(fp8, fp12, fp20)
  ...
end

のようにすればいいよ、とのことで、ばっちり動きました。ありがとうございます。


とはいえ、CustomPatch がRubyで書けるようになったのは大きな進歩です。ApacheReaderPatch とかも、ずいぶんと簡単に書けそう。

ApacheLogPatch 0.1

ApacheLogPatch というものをつくりました。 Apacheのログを、Quartz Composer で派手に tail -f するというものです。 パス、リモートホストのアドレス、リファラー、検索キーワードが表示されます。

Apacheの設定ファイルを書き換えたりと動くようにするまでの敷居が妙に高いわりには、動いたときの「おー、ふーん」感がなんともいえませんが、動かしてみようという方はどうぞぜひ。

自宅サーバをMacで動かしている方という、たいへん狭いターゲットを狙いました。 スクリーンセーバーとして動かしておき、自分のサーバに人がどんな検索キーワードできているのか、なんてことを夕食どきにご飯を食べながら眺めてみるのも一興ではないでしょうか。

ビデオとスクリーンショット

ビデオは、本当はDVカメラで撮影したちゃんとしたものがあったのですが、どうもFirewireからの取り込みができず挫折して vnc2swf を使いました。ので、フレームレートが悲惨なことになっており、本当にこれ、動かして楽しいの? みたいなことになってます。楽しいんですよ。

Python版のvnc2swfで録り直しました。だいぶ見れるレベルになったのでは。

QuartzComposer の CustomPatch で Signal を生成する方法

QuartzComposer の Patch には、Signal という 一瞬だけ True になったあとまたすぐ False になるような Port をもつものがあります。たとえば、StopWatch とか、Counterとか。

では、どのようにして CustomPatch で Signal を生成すればよいか、いろいろ試行錯誤してみたので、メモを残しておきます。

Port

@interface SomePatch : QCPatch {
  QCBooleanPort *outputSignal;
  ....
}

のように、Boolean な Port を宣言しておきます。

Configuration

まず、CustomPatch の timeMode を 1 にして、常に Patch が実行されるようにしておきます。

+ (int)timeMode { return 1; }

としておけばよいでしょう。

ここらへんの詳細については、QCPatch Configuration に書かれてあります。

Signal を発生させる

1〜2回、execute が実行される間に outputSignal の値が True になっていれば、Signal が発生したとみなされるようです。

- (BOOL)execute:(id)fp8 time:(double)fp12 arguments:(id)fp20 {
  static int count = 0;
 
  // signal hack
  if (TRUE == [outputSignal booleanValue]) {
    if (1 == count++) {
      [outputSignal setBooleanValue:FALSE];
      count = 0;
    }
  }
 
  return YES;
}

みたいにしました。[outputSignal setBooleanValue:TRUE] にするのは、どこか別のスレッドにてやっています。

まとめ

俺Patchで Signal が送れるようになれば、データが変わったタイミングを後段のPatchに伝えることができて、なにかと使えそうです。RSS Feed Patch や、Image Downloader も、FeedやImageのダウンロードが終わったタイミングをSignalで通知しているわけで。

おまけ

QuartzComposer の CustomPatch をデバッグするには、NSLog が便利です。 というか、つくった Patch は plugin 形式なので、いつものようにデバッガを直接起動することができないので。

NSLogのログは、 /Library/Logs/Console/”自分の uid”/console.log に出力されるので、tail -f などとして眺めるとグッドです。

Home > Tags > hack

Feeds

Return to page top