メールで Twitter、リファクタリング続き
メールで Twitter
- Twitterへのメールからの投稿 - Rubyとか Illustratorとか SFとか折紙とか
- メールによる Twitterの参照 - Rubyとか Illustratorとか SFとか折紙とか
- メールで Twitter、リファクタリング - Rubyとか Illustratorとか SFとか折紙とか
機能追加の前のリファクタリングを続ける。
テスト或はビヘイビア(BDD)
テストやBDD(振る舞い駆動開発)なんだけど、実際にコマンドメールを送ってみて思ったように動くかどうかでやってます。テスト用のAPIサーバ作るとか考えるとちょっとやってらんないので実地動作でテストです。
ユニットテストとか考えるほどユニット化されていないというのもあります。
今回のリファクタリングでメソッドが増えるのでそろそろ考え時かも。
リクエストとレスポンスの処理
ソースを見てると Twitter::Statuses#updateメソッドと Twitter::Statuses#friends_timelineメソッドではツイッターAPIにリクエストを送る所が何か似てる。またその後の処理についても、レスポンスからメール作って返信するというのは追加したいほかのコマンドメールでも共通して使いそうだ。
なのでその辺を個別のメソッドに括りだす。
あと、記録(ログ)ファイルの辺りも。
というわけでソース
#!/usr/bin/ruby # -*- coding: utf-8 -*- require 'nkf' require 'net/http' require 'net/smtp' Net::HTTP.version_1_2 class TwitterError < StandardError; end class Twitter User = Struct.new :acount, :password Users= { '<投稿者メールアドレス>' => User.new('<ツイッターアカウント>', '<パスワード>') } Users['<二番目のメールアドレス>'] = Users['<投稿者メールアドレス>'] Address = ['twitter.com', 80] class Statuses Path = '/statuses/' Commands = Hash.new do |h, key| raise TwitterError, "invalid command '#{key}' called" end # Commands = Hash.new do |h, key| [ 'friends_timeline', 'timeline', 'user_timeline', 'mentions', 'update', ].inject(Commands){|cmds, cmd| cmds[cmd] = cmd.to_sym; cmds} def initialize(sender, recipient) @sender, @recipient = sender, recipient @user = Users[@sender] raise TwitterError, "sender '#{sender}' is not trusted user" unless @user end # def initialize(sender, recipient) def exec(lines) send Commands[@recipient.split('@').first.split('-').last], body(lines) end # def exec(lines) def update(lines) doing = lines[0].chomp response = request 'update', :post, :status => doing [doing, response.body].join "\n\n" end # def update(lines) def friends_timeline(lines) counting = lines[0].chomp count = /\A\d+/=~counting ? [counting.to_i, 200].min : 200 reply request('friends_timeline', :get, :count => count.to_s) end # def friends_timeline(lines) alias :timeline :friends_timeline def user_timeline (lines) end # def user_timeline (lines) def mentions(lines) end # def mentions(lines) private def body(lines) loop{ break lines if 1 == lines.shift.length} end # def body(lines) def request(to, post, args) req = if 'post' == post.to_s.downcase then Net::HTTP::Post else # req = if 'post' == post.to_s.downcase then Net::HTTP::Get end.new "#{Path}#{to}.rss" req.basic_auth @user.acount, @user.password arg_str = args.inject([]) do |res, k_v| res << "#{k_v[0]}=#{NKF.nkf '-Jw', k_v[1]}" res end.join('&') response = nil Net::HTTP.start(*Address) do |http| response = http.request req, arg_str end # Net::HTTP.start('twitter.com',80) do |http| return response end # def request(to, args) def reply(response) result = response.body.split("\n").select do |line| (line.include?('<description>')..line.include?('</description>')) ? true : false end.map do |line| line.sub(/^\s*<description>/, '').sub(/<\/description>\s*$/, '') end.map{|line| line.gsub(/&#(\d+?);/){ [$1.to_i].pack('U') }} Net::SMTP.start('localhost',25) do |smtp| smtp.send_mail <<-EOM, @recipient, @sender From: #{@recipient} To: #{@sender} Subject: #{@recipient.split('@').first} Date: #{Time.now} Message-Id: <#{Time.now.to_i}.#{@recipient}> #{NKF.nkf('-Wj', result.join("\n"))} EOM end # Net::SMTP.start('localhost',25) do |smtp| end # def reply(response) end # class Statuses end # class Twitter if $0 == __FILE__ then sender, recipient, = ARGV File.open('/home/<ユーザ名>/projects/'+Time.now.strftime('%Y%m%d%H%M%S.')+recipient, 'w') do |l| l.puts sender, recipient, '' begin # rescue TwitterError => evar l.puts Twitter::Statuses.new(sender, recipient).exec($stdin.readlines) rescue TwitterError => evar then l.puts evar, '', $stdin.read end # rescue TwitterError => evar end # File.open('/home/<ユーザ名>/projects/'+Time.now.strftime('%Y%m%d%H%M%S.')+recipient, 'w') do |l| end # if $0 == __FILE__