札幌Ruby会議02で発表しました。

ビデオを見返すと、かなり時間オーバーしてるんですね。反省します。

録画ビデオ(2分40秒くらいから)

札幌Ruby会議02 ビジネスセッション1 「大学教員はレシピ先輩たりうるのか 」

Mac OS Xでrvm

Mac OS XにふつうにRubyをインストールするときと同じで、readlineについて指定する必要がある感じです。twitterでぼやいていたら、kakutaniさんが教えてくれました!

rvm だと irb で日本語入力できてない……  #rvm

http://twitter.com/noplans/status/3597014333

@noplans -c でconfigureのオプションわたせそうな雰囲気でした

http://twitter.com/kakutani/status/3597037089

というわけで、

% rvm use ruby -v 1.9.1 -c --with-readline-dir=/opt/local

とかやって使えるようになりました。これでtermtterも大丈夫です。

TwitterのBot

gistから貼るのってどうやるのかわからないのでテストを兼ねて投稿します。便利なBOT - TwitterまとめWikiからボットを抜き出しているだけですー。

なんか見づらいなー。普通に貼った方が良いかもしれん。……配色のせいだった。配色を変えたら見やすくなった。

# twitter bots list
require 'nokogiri'
require 'open-uri'

BASE = 'http://usy.jp/twitter/index.php?%E4%BE%BF%E5%88%A9%E3%81%AABOT'

doc = Nokogiri::HTML(open(BASE))
list = doc.xpath('//a[@rel="nofollow"]')
         .map{|x| x['href']}
         .select{|link| %r|\Ahttps?://twitter\.com/| =~ link}
bots = list.map{|link| link.gsub(%r|\Ahttps?://twitter\.com/(\w+)?/?\z|, '\1')}.sort

[kaigifreaks] IRC画面をスクリーンに映すときに join や quit メッセージなどを表示させたくない

LimeChat for OSXの話。
ThemeのCSSを適当に作って、

.line[type=system], .line[type=join], .line[type=part], .line[type=kick],
.line[type=quit], .line[type=kill], .line[type=nick], .line[type=mode],
.line[type=topic], .line[type=invite], .line[type=wallops] {
  display: none !important;
}

とかするといいよ。

RubyKaigi2009行ってきた

日本Ruby会議2009(RubyKaigi2009)にスタッフ(KaigiFreaks)として参加しました。配信および録画が主な仕事です。そのため、担当の部屋以外の発表はまったく見ることができなかったのですが、IRCtwitterでRubyKaigi2009への意見/不満をできるかぎりすくい取ろうとしてずっとモニタリングしていたため、会議の雰囲気は伝わってきた感じでした。Buzztter rocks!

札幌に戻ってきてから角谷さんの "Take the Red Pill" を観たのですが、これを生で観てたら後は仕事にならんかっただろうなー、と思います。それくらい私にとってはストライクな内容でした。

まだKaigiFreaksとしての仕事は終わってないので、これくらいで。
i_ogi さんと wakuteka さんと組んで仕事をしたのですが、みなさん素晴らしすぎて楽させていただきました。ほんとに i_ogi さんは god でした!

日本PostgreSQLユーザ会北海道支部 / Ruby札幌合同セミナーで発表しました

しょぼい発表ですみませんでした。あとでコードも置いときます。
(追記2: ニコニコ動画の動画も貼りました(3/7))
D
(追記: コード置きました(3/1))

「日本PostgreSQLユーザ会北海道支部 / Ruby札幌 合同セミナー 2008-02-16」 dara さんの公開マイリスト - ニコニコ動画 に動画も上がる予定です。

発表のときに見せてたコード

スライドの「実際作る」以降で使用したコードです。

tumblr

一応、その前で説明してた

を使用するコードになってます。

  1. 私をフォローしてくれてる人たち(followers)の中に、
  2. 私がフォローしていない人たちがいるので、それをフォローする

プログラムを書きました。
User モデルのテーブルはこんな感じ。名前と URL、follow してるかどうかのフィールドがあります。

class Migration < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :name, :string
      t.column :url, :string
      t.column :following, :boolean
    end
  end
  def self.down
    drop_table :users
  end
end

User モデルは下のような感じです。

class User < ActiveRecord::Base
  validates_uniqueness_of :name

  def self.find_by_name_or_create(name)
    self.find_by_name(name) || self.create(:name => name) unless name.empty?
  end

  def before_save
    self.following ||= :false
  end
end

tumblr クラスを作りました。当日の発表では説明してません。#follow あたりはかなり決め打ちです。

class Tumblr
  class LoginError < Exception; end
  class Following < Array
    def initialize(array=[])
      self.replace array
    end
  end

  @@base_url = "http://www.tumblr.com"

  def initialize(user, pass)
    @ua = WWW::Mechanize.new
    env = ENV['http_proxy'] || ENV['HTTP_PROXY']
    if env
      http_proxy = URI(env)
      @ua.set_proxy http_proxy.host, http_proxy.port
    end
    login(user, pass)
  end

  def set_proxy(host, port)
    @ua.set_proxy host, port
  end

  def dashboard(num=0)
    @ua.get("#{@@base_url}/dashboard/#{num}")
  end

  def following
    url = "#{@@base_url}/following"
    doc = Hpricot(@ua.get(url).body)
    t = Tumblr::Following.new(pickup(doc)).each do |user|
      user.following = :true
      user.save
    end
    t
  end

  def followers
    url = "#{@@base_url}/followers"
    doc = Hpricot(@ua.get(url).body)
    t = Tumblr::Following.new(pickup(doc))
  end

  def follow(user=nil)
    if user
      name = user.name
      url = user.url
      page = @ua.get(url)
      f = page.iframes.first
      link = f.src
      page = @ua.get(link)
      f = page.forms.first
      @ua.submit(f)
      puts "Now you follow #{name}: #{url}."
      user.following = :true
      user.save
    end
  end

  def login(user, pass)
    page = @ua.get("#{@@base_url}/login")
    f = page.forms.first
    f.email, f.password = user, pass
    result = @ua.submit f
    raise Tumblr::LoginError, "wrong username or password" if result.forms.size > 0
  end

  private
  def pickup(doc)
    a = []
    (doc/"#following"/"div.username"/"a").each do |element|
      user = User.find_by_name_or_create(element.inner_html)
      user.url = element.get_attribute("href")
      user.save
      a << user
    end
    a
  end
end

実際に動かすコードです。

ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :database => 'db/development.sqlite3',
  :timeout => 5000
)
user_account = Pit.get("tumblr.com",
                       :require => {
                          :login => 'E-mail address',
                          :password => 'Password'})
t = Tumblr.new(user_account[:login], user_account[:password])
following = t.following
followers = t.followers
followers.each do |follower|
  unless following.include? follower
    puts "#{follower.name} (#{follower.url}) さんをまだフォローしてません。"
    puts "フォローしますか? (y/n)"
    line = gets
    if /\AY|y\Z/ =~ line.chomp
      t.follow follower
    end
  end
end
twitter

twitter の方は twitter4R 使いました、というだけのコードです。あとはこれを RubyCocoa から使う感じです。

class Client
  def initialize
    @twitter = Twitter::Client.new(Pit.get("twitter.com"))
    @last_updated = 10.minutes.ago.utc
  end

  def message(text='')
    puts "posted: #{text}" unless text.empty?
    @twitter.status(:post, text) unless text.empty?
  end

  def timeline_diff
    diff = nil
    begin
      diff = @twitter.timeline_for(:friends, :since => @last_updated)
      @last_updated = Time.now.utc
    rescue Twitter::RESTError => re
      case re.code
      when '304'
        puts re.to_s
      end
      @last_updated = Time.now.utc
    rescue Timeout::Error => re
      pp re
    end
    diff || []
  end
end