仙台Ruby会議02

rubyとビジネスっていうテーマで田舎でビジネスしたり、東京でビジネスする際のいろいろなはなしを聞けて楽しかったです。
いなかにこだわる理由みたいな話で、ずっと優秀な後輩とかと話したいのに東京に行ってしまって寂しいのでなんとかしたい
って話は自分もそう思っている部分があったので、思わずグッと来てしまいました。そのへんツボなのでやめてくれというかんじで(しかも泣けるという)。


それで、商品の本を貰ってしまったので、オレ言語のライブラリ構築のために参考にしようと思います。
あと、サイン貰ったんだけど、1人だけ名のでって話をしてて、ほかの人は北海道に来て貰ってくださいといわれたのでなにかの機会があれば、
北海道に行ってサイン貰おうと思います。


というこういう状況を打破しないと非常によくないので、すこしまともな服を買おうかなと思った今日この頃です。
あと、できるだけどうでもいいような日常的話題を楽しく話す技術を身に着けようと思いました。


ということで、Rubyでの自分のネタっていうとRuby風言語を作って発表とか思ってたのだけど、
時間とやる気がなかった、(たぶん特にやる気のほう)

class Object
	def eval(env)
		self
	end
	def _(a,op,b)
		Exp.new(a,op,b)
	end
end

class Exp
	def initialize(a,op,b)
		@a = a
		@op = op
		@b = b
	end
	def eval(env)
		if (@a == :self) then
			env.send(@op, @b.eval(env))
		else
			env.instance_variable_get(@a).send(@op,@b.eval(env))
		end
	end
end
class A
	def p(s)
		print s
	end
end

p binding

@a = A.new
_(:self, :p, "hello").eval(self)
_(:@a, :p, "hello").eval(self)

こんなコードを書いていて終わってしまってました。
何やりたかったかというと、構文木オブジェクトを作って、evalしてやると動くぜー。
っていう感じの仕組みを作ってオレ言語を構文木に変換、実行、イエイ!!ってやつだったんですけど。

でも、RubyJavaやらPHPといっしょで、メソッドと変数メンバの空間が別ってのを実装するところで止まって終わってました。
たぶん、気力あるときに1日あればそれっぽくバグのあるやつが作れると思うのだけど、そこらへんの理解が足りないので2、3ヶ月いやもっとそれ以上
な感じになってました。


今日はなしたこと

1.スカイプみたいなので駄弁るシステム上でイングリッシュボックスいうのを作って、
  話した英語はドラゴントークだかなんだかみたいなので、自動的にテキスト化され、翻訳されて、なんちゃって日本語に変換されて聞けたり、
  逆に日本語で話すとドラゴントークみたいなのでやっぱり、テキスト化され翻訳されて、なんちゃって英語になって聞けたりしたら良いんだろうねと。
  オレは、別にシステム化する必要はないと思うんだけど、誰か作ったらきっと楽しいんじゃないかと。たぶん使い物にはならないと思うけど。
  使い方によっては使えるのではないかと。

2.仙台というか東北にひとつRubyな面白い技術会社が欲しいなと。思い付きじゃなくて願望ですけど。

3.Railsのフィルターの仕組みはきっと全部やってしまうので、効率よくない場面もありそうだから、何とかきれいだけど高速にしてほしいなということを言葉わるーく話してしまった。すいません。悪気があったわけではないのだけど、なんか、そうなってしまったという。
たぶん、周りに不満が多いのを我慢してるからなのか、性格が悪いのか。。。

とにかく、Rubyは美しいと同時に問題もいろいろあって、その辺をツッこむと面白いけど、難しいので大変だけど話す価値はあると思うのですけどってかんじでした。mod_rubyかなにかのapache上で動くなにかは、たまに再起動をサーバ何台かあるうちの1個を落として、みたいなことをタイミングずらしてやるとかしてるって話を教えてもらったりしてなるほどーっと思いました。よく理解してないのですけど、プロセスが複数に分かれててそれがapache上で常駐しており、適当なメモリ食ったら、処理してないときに再起動かければ、問題ないってことっぽかったです。ゴミっぽいやつは消してってゴミっぽくないやつは残しておくんだけど、超GCが必要なタイミングがあったら、再起動というのであれば、JVM起動しっぱなし超巨大メモリで1分停止とかよりずっといいだろうなぁっと。っというのをD言語のtangoあたりが実装してあって、mod_dみたいなのがapache上であれば、高速でいいのかもなぁっと思いました。


あと、リレーショナルデータベースはテーブルを正規化するのがいいってはなしだけども、最近出てきた、name - valueだかなんかのDBとかは、正規化とかできないけど、どうやったらうまく動くのか見たいな話をしたら、ホントに共通に必要なDBはレプリケーションしてほかは、別DBでやるとかするといいとか教えてもらってなるほどなと思いました。

あと、自分が一番下手でいたいみたいな話がなるほどなぁっと思いました。それは地方では結構難しいことなのかもなぁっと。


ああ、あと、翻訳掲示板とか作ってみたのがあったので、遊んでみてね。っと。

http://labs.s-sd.org/enjpbbs/index.php

これをtwitterとかで翻訳発言とかruby逆引きレシピ見つつ作って見れたらいいのかなと。

イングリッシュボックスの話のシステムの今だとrubyクライアントとかこんなかんじだよなぁっと。

ctwitというrubyで作られたコンソールtwitterクライアントをいじってgoogleapiを呼んでってかんじで。

#!ruby -Ks 

require 'net/http'
require 'rexml/document'
require 'time'
require 'kconv'
require 'json'

class Twitter
  #このクラスのメソッドにはすべて文字列を渡す
  SERVER='twitter.com'

  attr_reader :username,:password

  def initialize
    @id_array=[]
  end

  def login(username,password)
    @username=username
    @password=password
  end

  def get_timeline(filename)
    req=Net::HTTP::Get.new("/statuses/#{filename}")
    req.basic_auth(@username,@password)
    xml=Net::HTTP.start(SERVER) do |http|
      resp=http.request(req)
      raise 'リクエストに失敗' unless resp.code=='200'
      resp
    end
    doc=REXML::Document.new xml.body
    tls=[]
    @id_array.clear
    @id_array<<0
    i=0
    doc.elements.each('statuses/status') do |stat|
      output=""
      output<<"[#{(i+1).to_s}]"
      stat.elements.each('user/screen_name'){|name| output<<name.text<<":"}
      stat.elements.each('text'){|text| output<<text.text.tosjis}
      stat.elements.each('created_at'){|time| output<<" (#{Time.parse(time.text).strftime "%Y/%m/%d(%a) %H:%M:%S"})"}
      stat.elements.each('id'){|id| @id_array<<id.text}
      tls<<output
      i+=1
    end
    tls
  end

  def get_timeline_with_count(filename,count=nil)
    if count==nil then get_timeline(filename)
    elsif count.to_i>200 then get_timeline("#{filename}?count=200")
    else get_timeline("#{filename}?count=#{count}")
    end
  end

  def get_direct_message(filename,count)
    if count.to_i>200 then filename+="?count=200"
    else filename+="?count=#{count}"
    end
    req=Net::HTTP::Get.new "/#{filename}"
    req.basic_auth @username,@password
    xml=Net::HTTP.start(SERVER) do |http|
      resp=http.request req
      raise 'リクエストに失敗' unless resp.code=='200'
      resp
    end
    i=0
    doc=REXML::Document.new xml.body
    dms=[]
    doc.elements.each('directmessages/directmessage') do |dmes|
      output=""
      output<<"[#{(i+1).to_s}]"
      dmes.elements.each('sender/name'){|name| output<<'From'<<name.text}
      dmes.elements.each('recipient/name'){|name| output<<' to '<<name.text<<':'}
      dmes.elements.each('text'){|text| output<<text.text.tosjis}
      dmes.elements.each('created_at'){|time| output<<" (#{Time.parse(time.text).strftime "%Y/%m/%d(%a) %H:%M:%S"})"}
      tls<<output
      i+=1
    end
    tls
  end

  def public_timeline #ログインしなくても使える
    get_timeline "public_timeline.xml"
  end

  def friends_timeline(count=nil)
    get_timeline_with_count "friends_timeline.xml",count
  end

  def user_timeline(username,count=nil)
    get_timeline_with_count "user_timeline/#{username}.xml",count
  end
  
  def replies(count=nil)
    get_timeline_with_count "mentions.xml",count
  end

  def direct_messages(count=nil)
    get_direct_message "directmessages.xml",count
  end

  def my_messages(count=nil)
    get_direct_message "directmessages/sent.xml",count
  end

  def send_message(user,message)
    message=message.toutf8
    req=Net::HTTP::Post.new "/directmessages/new.xml"
    req.basic_auth @username,@password
    submit=URI.escape(message)
    Net::HTTP.start(SERVER) do |http|
      resp=http.request(req,"user=#{user}&message=#{message}")
      raise "リクエストに失敗" unless resp.code=='200'
    end
  end

  def submit(status,repto=nil)

    status=status.toutf8
    submit=URI.escape(status)
    js = Net::HTTP.get_response(URI.parse("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=ja%7Cen&q=" + submit))
    decoded = JSON.load(js.body)
    status = status + decoded["responseData"]["translatedText"]
    req=Net::HTTP::Post.new "/statuses/update.xml"
    req.basic_auth(@username,@password)
    submit=URI.escape(status)
    Net::HTTP.start(SERVER) do |http|
      if repto==nil then resp=http.request(req,"status=#{submit}")
      else
        repto=repto.to_i
        resp=http.request(req,"status=#{submit}&in_reply_to_status_id=#{@id_array[repto]}") if 0<repto&&repto<=@id_array.size
      end
      raise "リクエストに失敗" unless resp.code=='200'
    end
  end

  def follow(username)
    req=Net::HTTP::Post.new "/friendships/create/#{username}.xml"
    req.basic_auth @username,@password
    Net::HTTP.start(SERVER) do |http|
      resp=http.request req
      raise "リクエストに失敗" unless resp.code=='200'
    end
  end

  def remove(username)
    req=Net::HTTP::Post.new "/friendships/destroy/#{username}.xml"
    req.basic_auth @username,@password
    Net::HTTP.start(SERVER) do |http|
      resp=http.request req
      raise "リクエストに失敗" unless resp.code=='200'
    end
  end

  def fav(favto) #ふぁぼる
    favto=favto.to_i
    unless 0<favto&&favto<=@id_array.size
      raise "リクエストに失敗"
    else
      req=Net::HTTP::Post.new "/favorites/create/#{@id_array[favto]}.xml"
      req.basic_auth(@username,@password)
      Net::HTTP.start(SERVER) do |http|
        resp=http.request req
        raise "リクエストに失敗" unless resp.code=='200'
      end
    end
  end
end

class TwitterConsole
  COMMANDS=['login','friends_timeline','public_timeline','user_timeline','replies','direct_messages','my_messages','send_message','submit','follow','remove','fav','exit','quit']

  def initialize
    @twitter=Twitter.new
  end

  def login
    print 'ユーザー名を入力:'
    name=gets.chomp!
    print 'パスワードを入力:'
    pass=gets.chomp!
    @twitter.login(name,pass)
  rescue
    puts "ログインに失敗しました。"
  end

  def friends_timeline
    if @arg.size>0 then @twitter.friends_timeline(@arg[0]).each{|t| puts t}
    else @twitter.friends_timeline.each{|t| puts t}
    end
  rescue
    puts "タイムラインの取得に失敗しました。"
  end

  def public_timeline
    @twitter.public_timeline.each{|t| puts t}
  rescue
    puts "タイムラインの取得に失敗しました。"
  end

  def user_timeline
    if @arg.size==1 then @twitter.user_timeline(@arg[0]).each{|t| puts t}
    elsif @arg.size>1 then @twitter.user_timeline(@arg[0],@arg[1]).each{|t| puts t}
    else puts "引数が足りません。"
    end
  rescue
    puts "タイムラインの取得に失敗しました。"
  end

  def replies
    if @arg.size>0 then @twitter.replies(@arg[0]).each{|t| puts t}
    else @twitter.replies.each{|t| puts t}
    end
  rescue
    puts "タイムラインの取得に失敗しました。"
  end

  def direct_messages
    if @arg.size>0 then @twitter.direct_messages(@arg[0]).each{|t| puts t}
    else @twitter.sent_messages.each{|t| puts t}
    end
  rescue
    puts "ダイレクトメッセージの取得に失敗しました。"
  end

  def my_messages
    if @arg.size>0 then @twitter.my_messages(@arg[0]).each{|t| puts t}
    else @twitter.my_messages.each{|t| puts t}
    end
  rescue
    puts "ダイレクトメッセージの取得に失敗しました。"
  end
  
  def send_message
    if @arg.size>0
      print "入力:"
      @twitter.send_message(gets,arg[0])
    else puts "引数が足りません。"
    end
  rescue
    puts "ダイレクトメッセージの送信に失敗しました。"
  end

  def submit
    if @arg.size>0
      print "入力:"
      @twitter.submit(gets,@arg[0])
    else
      print "入力:"
      @twitter.submit(gets)
    end
  rescue
    puts "つぶやきの送信に失敗しました。"
  end

  def follow
    if @arg.size>0 then @twitter.follow(@arg[0])
    else puts "引数が足りません。"
    end
  rescue
    puts "フォローに失敗しました。"
  end

  def remove
    if @arg.size>0 then @twitter.remove(@arg[0])
    else puts "引数が足りません。"
    end
  rescue
    puts "リムーブに失敗しました。"
  end

  def fav
    if @arg.size>0 then @twitter.fav(@arg[0])
    else puts "引数が足りません。"
    end
  rescue
    puts "ふぁぼるのに失敗しました。"
    p $!
  end

  def run
    loop{
      print "#{@twitter.username}>"
      @arg=gets.split(/\s/)
      match=COMMANDS.grep(/^#{@arg.shift}/)
      if match.length==0 then puts COMMANDS.join(' ')
      elsif match.length==1
        if match[0]=='exit'||match[0]=='quit' then exit
        else __send__ match[0]
        end
      else puts match.join(' ')
      end
    }
  end
end

tc=TwitterConsole.new
tc.run