<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>橋本商会 &#187; Ruby</title>
	<atom:link href="http://shokai.org/blog/archives/tag/ruby/feed" rel="self" type="application/rss+xml" />
	<link>http://shokai.org/blog</link>
	<description>なんか作ったりした記録を忘れないうちに書くblog</description>
	<lastBuildDate>Tue, 07 Sep 2010 13:08:58 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<atom:link rel="hub" href="http://pubsubhubbub.appspot.com/" />
			<item>
		<title>26歳になったので</title>
		<link>http://shokai.org/blog/archives/5322</link>
		<comments>http://shokai.org/blog/archives/5322#comments</comments>
		<pubDate>Wed, 18 Aug 2010 10:12:56 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[Wikipedia]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5322</guid>
		<description><![CDATA[
8月15日に26歳になったので、新しい自分を探すためにtwitterのプロフィールを自動的に更新するようにした


人間以外にもなれる




ソースコードは全部githubに置いた
ランダムに適当な紹介文を取ってくる [...]]]></description>
			<content:encoded><![CDATA[<p>
<p>8月15日に26歳になったので、新しい自分を探すために<a href="http://twitter.com/shokai">twitterのプロフィール</a>を自動的に更新するようにした<br /><br />
<a href="http://www.flickr.com/photos/shokai/4903244203/" title="30ed9b71553d2f33922bc39e7368a008 by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4121/4903244203_4bb03acf09.jpg" width="253" height="225" alt="30ed9b71553d2f33922bc39e7368a008" /></a></p>
<p><a href="http://www.flickr.com/photos/shokai/4904183134/" title="838d669e96f189702c3f9844965ace8e by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4135/4904183134_77a40b2fe2.jpg" width="247" height="244" alt="838d669e96f189702c3f9844965ace8e" /></a><br /></p>
<p>人間以外にもなれる<br /><br />
<a href="http://www.flickr.com/photos/shokai/4903239191/" title="eac518c56870949065eeeed905d34ae4 by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4117/4903239191_61c18437a9.jpg" width="254" height="200" alt="eac518c56870949065eeeed905d34ae4" /></a></p>
<p><a href="http://www.flickr.com/photos/shokai/4903423129/" title="e85cb1527f60394255fe556f41cc3e62 by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4140/4903423129_e74e2c5a3c.jpg" width="251" height="354" alt="e85cb1527f60394255fe556f41cc3e62" /></a></p>
<p><a href="http://www.flickr.com/photos/shokai/4903820452/" title="9c4c1cb1c6fadac1ff09f120b5585892 by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4117/4903820452_2bd60a5065.jpg" width="264" height="276" alt="9c4c1cb1c6fadac1ff09f120b5585892" /></a></p>
<p><a href="http://www.flickr.com/photos/shokai/4903251901/" title="7d4fcd1cd71ba0e8693133439b4751eb by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4076/4903251901_af1e028e41.jpg" width="271" height="312" alt="7d4fcd1cd71ba0e8693133439b4751eb" /></a><br /></p>
<p>ソースコードは<a href="http://github.com/shokai/twitter-change-profile">全部githubに置いた</a><br /></p>
<p>ランダムに適当な紹介文を取ってくる<br /><br />
<a href="http://github.com/shokai/twitter-change-profile/blob/b04a978f51f173f8e9997b8f2f89984152061eb8/lib/wikipedia.rb">wikipedia.rb</a><br /><br />
<pre class="prettyprint"><br />
# -*- coding: utf-8 -*-<br /><br />
require 'rubygems'<br /><br />
require 'open-uri'<br /><br />
require 'uri'<br /><br />
require 'nokogiri'<br /><br />
require 'kconv'<br /><br />
require 'net/http'</p>
<p>class Wikipedia</p>
<p>  def initialize(agent_name)<br /><br />
    @agent_name = agent_name<br /><br />
  end</p>
<p>  def random<br /><br />
    get('特別:おまかせ表示')<br /><br />
  end<br /><br />
  <br /><br />
  def get(name)<br /><br />
    doc = Nokogiri::HTML open(URI.encode("http://ja.wikipedia.org/wiki/#{name}"), 'User-Agent' =&gt; @agent_name).read.toutf8<br /><br />
    <br /><br />
    title = doc.xpath('//title').first.text<br /><br />
    name = doc.xpath('//h1').first.text<br /><br />
    descriptions = doc.xpath('//div[@id="bodyContent"]//p').map{|i|i.text}<br /><br />
    {<br /><br />
      :title =&gt; title,<br /><br />
      :name =&gt; name,<br /><br />
      :descriptions =&gt; descriptions<br /><br />
    }<br /><br />
  end</p>
<p>end<br /><br />
</pre></p>
<p>このスクリプトをcronで定期的に実行して更新してる<br /><br />
<a href="http://github.com/shokai/twitter-change-profile/blob/1baaf76b52f9d377c39ce1ceb8c1edb232550f57/change-profile.rb">change-profile.rb</a><br /><br />
<pre class="prettyprint"><br />
#!/usr/bin/env ruby<br /><br />
# -*- coding: utf-8 -*-<br /><br />
require 'rubygems'<br /><br />
require 'twitter'<br /><br />
require 'yaml'<br /><br />
require File.dirname(__FILE__)+'/lib/wikipedia'<br /><br />
$KCODE = 'u'</p>
<p>begin<br /><br />
  conf = YAML::load open(File.dirname(__FILE__) + '/config.yaml')<br /><br />
rescue<br /><br />
  STDERR.puts 'config.yaml load error'<br /><br />
  exit 1<br /><br />
end</p>
<p>tw = Twitter::Base.new(Twitter::HTTPAuth.new(conf['name'], conf['pass']))<br /><br />
w = Wikipedia.new('shokai')<br /><br />
desc = nil<br /><br />
5.times do<br /><br />
  data = w.random<br /><br />
  <br /><br />
  desc = data[:descriptions].first<br /><br />
  desc.gsub!(/\[\d+\]/, '')<br /><br />
  tmp = desc.split(/(と?は)/)<br /><br />
  left = tmp.shift<br /><br />
  while left =~ /（[^）]+$/ do<br /><br />
    tmp.shift<br /><br />
    left = tmp.shift<br /><br />
  end<br /><br />
  desc = "#{conf['your_name']}#{tmp.join('')}".toutf8<br /><br />
  puts '-'*10<br /><br />
  print data[:name] + ' =&gt; '<br /><br />
  puts desc<br /><br />
  break if desc != conf['your_name']<br /><br />
end<br /><br />
exit if desc == nil or desc == conf['your_name']</p>
<p>tw.update_profile({'description' =&gt; desc})<br /><br />
</pre><br />
</p>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5322/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ZeroMQでOpenCV cvOpticalFlowのデータを配信する</title>
		<link>http://shokai.org/blog/archives/5311</link>
		<comments>http://shokai.org/blog/archives/5311#comments</comments>
		<pubDate>Wed, 11 Aug 2010 10:39:22 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[OpticalFlow]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[ZeroMQ]]></category>
		<category><![CDATA[画像処理]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5311</guid>
		<description><![CDATA[
1VQ9がZeroMQで遊んでたので、俺も橋本商会 » cvCalcOpticalFlowBMをZeroMQでpubしてみた。ZeroMQはなんか面倒な事を適当にやってくれるmessaging libraryで、色々な [...]]]></description>
			<content:encoded><![CDATA[<p>
1VQ9がZeroMQで遊んでたので、俺も<a href="http://shokai.org/blog/archives/4820">橋本商会 » cvCalcOpticalFlowBM</a>をZeroMQでpubしてみた。<a href="http://www.zeromq.org/">ZeroMQ</a>はなんか面倒な事を適当にやってくれるmessaging libraryで、色々な言語のバインディングが出ている。<br />
<br />
ZeroMQのpubはセンサーのデータとかを垂れ流しにするのに都合がよさそう。<br />
clientが何台いるかどうかを考えないで良いし、pub/subどちらが先に起動していても適当に接続処理をしてくれる。cookbookを見てるとmulticastやthread間通信にも使ってる。とりあえずセンサーデータ垂れ流しという用途に俺はよく使いそう。<br />
<br />
<br />
ソースコードは<a href="http://github.com/shokai/zeromq-study">githubに置いた</a>。<br />
他にも<a href="http://github.com/shokai/zeromq-study/tree/7ac74e64a0343ea87f55b94fb26baa29cf652668/count">単純なカウントアップのpub/sub両方をC++/C/Rubyで書いた（6種）</a>のと、<a href="http://github.com/shokai/zeromq-study/tree/7ac74e64a0343ea87f55b94fb26baa29cf652668/twitter-stream">twitterのstream APIをZMQ_PUBで中継する</a>のを作ってみた（解説：<a href="http://d.hatena.ne.jp/shokai/20100810/1281455530">zeromqインストール、twitter stream APIを中継 &#8211; 橋本詳解</a>）。特にstream APIのHUB的存在は便利。<br />
<br />
あと、<a href="http://mongrel2.org/">mongrel2</a>が<a href="http://nichol.as/zeromq-an-introduction">WebSocketやXMLSocketとZeroMQの接続をしてくれるようになる</a>らしくて期待してる。<br />
<br />
<br />
<br />
受信側<br />
<a href="http://github.com/shokai/zeromq-study/blob/d2f3e4f69881a517a9c0445372b7b366e0a74f7b/opticalflow/opticalflow_sub.rb">opticalflow_sub.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env ruby<br />
require 'rubygems'<br />
require 'zmq'<br />
<br />
ctx = ZMQ::Context.new<br />
sock= ctx.socket(ZMQ::SUB)<br />
sock.connect('tcp://127.0.0.1:5000')<br />
sock.setsockopt(ZMQ::SUBSCRIBE, 'opticalflow')<br />
<br />
loop do<br />
  puts sock.recv()<br />
end<br />
</pre>
<br />
<br />
送信側。これを適当なパソンコにUSBカメラ刺して動かしておけば、別のマシンから動きが取れる！!<br />
<a href="http://github.com/shokai/zeromq-study/blob/d2f3e4f69881a517a9c0445372b7b366e0a74f7b/opticalflow/opticalflow_pub.cpp">opticalflow_pub.cpp</a><br />
<pre class="prettyprint">
// http://opencv.jp/sample/optical_flow.html<br />
#include &lt;cv.h&gt;<br />
#include &lt;highgui.h&gt;<br />
#include &lt;cxcore.h&gt;<br />
#include &lt;ctype.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;iostream&gt;<br />
#include &lt;boost/format.hpp&gt;<br />
#include &lt;zmq.hpp&gt;<br />
<br />
using namespace std;<br />
using namespace boost;<br />
<br />
void detect_flow(IplImage *img, IplImage *img_p, IplImage *dst);<br />
zmq::context_t ctx(1);<br />
zmq::socket_t sock(ctx, ZMQ_PUB);<br />
<br />
int main(int argc, char* argv[]) {<br />
  IplImage *img = NULL;<br />
  CvCapture *capture = NULL;<br />
  capture = cvCreateCameraCapture(0);<br />
  //capture = cvCaptureFromAVI("test.mov");<br />
  if(capture == NULL){<br />
    cerr &lt;&lt; "capture device not found!!" &lt;&lt; endl;<br />
    return -1;<br />
  }<br />
<br />
  sock.bind("tcp://127.0.0.1:5000");<br />
<br />
  CvSize size = cvSize(320, 240);<br />
  IplImage *img_resized = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
  IplImage *img_gray = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_gray_p = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_dst = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
<br />
  char winNameCapture[] = "Capture";<br />
  cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);<br />
  <br />
  while (1) {<br />
    img = cvQueryFrame(capture);<br />
    cvResize(img, img_resized);<br />
    cvCvtColor(img_resized, img_gray, CV_BGR2GRAY);<br />
    cvCopy(img_resized, img_dst);<br />
    detect_flow(img_gray, img_gray_p, img_dst);<br />
    cvShowImage(winNameCapture, img_dst);<br />
    cvCopy(img_gray, img_gray_p);<br />
    if (cvWaitKey(10) == 'q') break;<br />
  }<br />
  <br />
  cvReleaseCapture(&amp;capture);<br />
  cvDestroyWindow(winNameCapture);<br />
  <br />
  return 0;<br />
}<br />
<br />
void detect_flow(IplImage *src_img1, IplImage *src_img2, IplImage *dst_img){<br />
  int i, j, dx, dy, rows, cols;<br />
  int block_size = 24;<br />
  int shift_size = 10;<br />
  CvMat *velx, *vely;<br />
  CvSize block = cvSize(block_size, block_size);<br />
  CvSize shift = cvSize(shift_size, shift_size);<br />
  CvSize max_range = cvSize(50, 50);<br />
<br />
  rows = int(ceil (double (src_img1-&gt;height) / block_size));<br />
  cols = int(ceil (double (src_img1-&gt;width) / block_size));<br />
  velx = cvCreateMat(rows, cols, CV_32FC1);<br />
  vely = cvCreateMat(rows, cols, CV_32FC1);<br />
  cvSetZero(velx);<br />
  cvSetZero(vely);<br />
<br />
  cvCalcOpticalFlowBM(src_img1, src_img2, block, shift, max_range, 0, velx, vely);<br />
  string result_str = string("");<br />
  for (i = 0; i &lt; velx-&gt;width; i++) {<br />
    for (j = 0; j &lt; vely-&gt;height; j++) {<br />
      dx = (int)cvGetReal2D(velx, j, i);<br />
      dy = (int)cvGetReal2D(vely, j, i);<br />
      cvLine(dst_img, cvPoint(i * block_size, j * block_size),<br />
              cvPoint(i * block_size + dx, j * block_size + dy), CV_RGB(255, 0, 0), 1, CV_AA, 0);<br />
      if(dx != 0 || dy != 0){<br />
	result_str += str(format("[%d,%d,%d,%d]") % (i*block_size) % (j*block_size) % dx % dy);<br />
      }<br />
    }<br />
  }<br />
  if(result_str.size() &gt; 0){<br />
    result_str = str(format("opticalflow %s") % result_str);<br />
    cout &lt;&lt; result_str &lt;&lt; endl;<br />
    zmq::message_t msg(result_str.size()+1); // ZeroMQ<br />
    memcpy(msg.data(), result_str.c_str(), result_str.size()+1);<br />
    sock.send(msg);<br />
  }<br />
}<br />
</pre>
<br />
<br />
<pre>
g++ -O opticalflow_pub.cpp -o opticalflow_pub.bin -I/opt/local/include/opencv -lcv -lcvaux -lcxcore -lhighgui  -I/usr/local/include /usr/local/lib/libzmq.a<br />
</pre>
<br />
<br />
これで動いた座標とその方向 [x,y,dx,dy] が連続で送られてくる。<br />
<pre>
opticalflow [48,216,4,-29][72,216,0,-29][96,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,216,0,-29][120,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,168,0,10][96,192,-10,-20][96,216,0,-29][120,192,0,10][120,216,0,-29][144,216,0,-29][168,216,0,-29][192,48,-10,0][192,216,0,-29][216,216,0,-29][264,216,-9,-29]<br />
opticalflow [96,168,0,10][96,192,-10,-10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,0,10][168,96,0,10][168,216,0,-29][192,72,0,40][192,96,0,-30][192,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,168,0,10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,10,0][168,96,0,10][168,216,0,-29][192,96,0,-30][192,216,0,-29][264,216,-9,-29]<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5311/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mongoid使ってみる</title>
		<link>http://shokai.org/blog/archives/5300</link>
		<comments>http://shokai.org/blog/archives/5300#comments</comments>
		<pubDate>Tue, 10 Aug 2010 14:12:55 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[mongoid]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5300</guid>
		<description><![CDATA[
mongo単体で使ってみててだいたい分かったので、mongoidというmapperを使ってみる。

mongoidの良いのは

  default値を入れておきたい場合も簡単に書ける。created_atとか。
  _ [...]]]></description>
			<content:encoded><![CDATA[<p>
mongo単体で使ってみててだいたい分かったので、mongoidというmapperを使ってみる。<br />
<br />
mongoidの良いのは<br />
<ul>
  <li>default値を入れておきたい場合も簡単に書ける。created_atとか。</li>
  <li>_idでdocumentを取り出すとき、素のmongoだとcollection.find_one(BSON::ObjectID(id))とかしないとならないけどmongoidだと_idに文字列でID入れればいい</li>
</ul>
とかがぱっと使ってみて思った。。<br />
そもそもこういうのmongoの機能にあるかもしれないけど。<br />
<br />
<br />
■ドキュメント<br />
<ul>
  <li><a href="http://d.hatena.ne.jp/shokai/20100709/1278649322">mongodbインストール &#8211; 橋本詳解</a> Macにインストールした</li>
  <li><a href="http://d.hatena.ne.jp/shokai/20100710/1278745488">Rubyからmongo使う &#8211; 橋本詳解</a></li>
  <li><a href="http://www.mongodb.org/display/DOCS/Ruby+Tutorial">Ruby Tutorial &#8211; MongoDB</a></li>
  <li><a href="http://api.mongodb.org/ruby/1.0.7/">MongoRuby-1.0.7</a> mongoドライバのドキュメント</li>
  <li><a href="http://d.hatena.ne.jp/babie/20100805/1280982678">ハンズオンで分かる MongoDB チュートリアル &#8211; ζ*’ワ’)ζ＜ちれすですの！</a> mongoのコンソールから使う</li>
  <li><a href="http://mongoid.org/docs/documents/">Mongoid Documentation: Documents</a> mongoid公式ドキュメント</li>
  <li><a href="http://d.hatena.ne.jp/babie/20100809/1281316973">Rails3 対応 MongoDB ORM、Mongoid 詳解―ドキュメント &#8211; ζ*’ワ’)ζ＜ちれすですの！</a> 公式ドキュメント翻訳中。すげー。</li>
</ul>
<br />
<br />
■インストール<br />
<pre>
sudo gem install mongoid<br />
</pre>
1.9.1を使う。&#8211;pre付けるとRails3対応の2.x系統が入る。<br />
<br />
<br />
■modelを作る<br />
適当にperson class作って、Mongoid::Documentにする<br />
<br />
person.rb<br />
<pre class="prettyprint">
require 'rubygems'<br />
<br />
class Person<br />
  include Mongoid::Document<br />
  field :fullname # 指定無しでtype=&gt;stringになる<br />
  field :username<br />
  field :age, :type =&gt; Integer<br />
  field :created_at, :type =&gt; DateTime, :default =&gt; Time.now<br />
end<br />
</pre>
string以外は型指定する。型はArray, BigDecimal, Boolean, Date, DateTime, Float, Integer, String, Symbol, Timeがある。<br />
→ <a href="http://mongoid.org/docs/documents/">Mongoid Documentation: Documents</a><br />
<br />
defaultで現在時刻を入れるようにした。<br />
<br />
<br />
■mongodbへ接続<br />
Mongoid.configureのブロック内で接続する。<br />
conf.masterに普通のmongoで接続してdbを指定した時の返り値(<a href="http://api.mongodb.org/ruby/1.0.7/Mongo/DB.html">Mongo::DB</a>オブジェクト)を与えれば、mongoidで使える。<br />
<pre class="prettyprint">
require&amp;nbsp;'rubygems'<br />
require&amp;nbsp;'mongoid'<br />
require&amp;nbsp;File.dirname(__FILE__)+'/person'<br />
<br />
Mongoid.configure&amp;nbsp;do&amp;nbsp;|conf|<br />
&amp;nbsp;&amp;nbsp;conf.master&amp;nbsp;=&amp;nbsp;Mongo::Connection.new('localhost',&amp;nbsp;27017).db('mongoid-test')<br />
end<br />
</pre>
<br />
<br />
<br />
■modelの操作<br />
新しいpersonオブジェクト作って保存<br />
<pre class="prettyprint">
person = Person.new(:fullname =&gt; 'sho hashimoto',<br />
                    :username =&gt; 'shokai',<br />
                    :age =&gt; 25)<br />
<br />
puts person.fullname<br />
puts person.age<br />
<br />
person.save<br />
</pre>
<br />
<br />
保存されてるか、mongoのコンソールで確かめる<br />
personで保存したら、自動的に複数形のpeopleになってた。ActiveRecordっぽい。<br />
<pre class="prettyprint">
% mongo<br />
MongoDB&amp;nbsp;shell&amp;nbsp;version:&amp;nbsp;1.4.4<br />
url:&amp;nbsp;test<br />
connecting&amp;nbsp;to:&amp;nbsp;test<br />
type&amp;nbsp;"help"&amp;nbsp;for&amp;nbsp;help<br />
&gt;&amp;nbsp;show&amp;nbsp;dbs<br />
admin<br />
chirpstream_shokai<br />
local<br />
mongoid-test<br />
people<br />
povietest<br />
test<br />
testdb<br />
&gt;&amp;nbsp;use&amp;nbsp;mongoid-test<br />
switched&amp;nbsp;to&amp;nbsp;db&amp;nbsp;mongoid-test<br />
&gt;&amp;nbsp;show&amp;nbsp;collections<br />
people<br />
system.indexes<br />
&gt;&amp;nbsp;db.people.find()<br />
{&amp;nbsp;"_id"&amp;nbsp;:&amp;nbsp;"4c61463c2f7306e9fe000001",&amp;nbsp;"created_at"&amp;nbsp;:&amp;nbsp;"Tue&amp;nbsp;Aug&amp;nbsp;10&amp;nbsp;2010&amp;nbsp;21:29:48&amp;nbsp;GMT+0900&amp;nbsp;(JST)",&amp;nbsp;"fullname"&amp;nbsp;:&amp;nbsp;"sho&amp;nbsp;hashimoto",&amp;nbsp;"username"&amp;nbsp;:&amp;nbsp;"shokai",&amp;nbsp;"age"&amp;nbsp;:&amp;nbsp;25&amp;nbsp;}<br />
{&amp;nbsp;"_id"&amp;nbsp;:&amp;nbsp;"4c614d652f73060653000001",&amp;nbsp;"created_at"&amp;nbsp;:&amp;nbsp;"Tue&amp;nbsp;Aug&amp;nbsp;10&amp;nbsp;2010&amp;nbsp;22:00:21&amp;nbsp;GMT+0900&amp;nbsp;(JST)",&amp;nbsp;"fullname"&amp;nbsp;:&amp;nbsp;"sho&amp;nbsp;hashimoto",&amp;nbsp;"username"&amp;nbsp;:&amp;nbsp;"shokai",&amp;nbsp;"age"&amp;nbsp;:&amp;nbsp;25&amp;nbsp;}<br />
</pre>
2回保存したから複数保存されてた<br />
<br />
<br />
■find<br />
探す。<a href="http://mongoid.org/docs/querying/">Mongoid Documentation: Querying</a>にqueryの書き方が載ってる。<br />
適当にユーザ名shokaiの最初の一件を取得して、表示する<br />
<pre class="prettyprint">
person = Person.first(:conditions =&gt; {:username =&gt; 'shokai'})<br />
puts person._id<br />
puts person.username<br />
puts person.created_at<br />
</pre>
<br />
他にも、全件とか色々な書き方ができる。<br />
<pre class="prettyprint">
person = Person.find(:first, :conditions =&gt; {:username =&gt; 'shokai'})<br />
person = Person.all(:conditions =&gt; {:username =&gt; 'shokai'}).first<br />
person = Person.first(:conditions =&gt; {:_id =&gt; '4c61463c2f7306e9fe000001'})<br />
</pre>
allで検索したら結果が1件しか無くても、collectionで返ってくる。eachで回せる。<br />
<br />
<br />
<br />
■modelに書いてない値を入れてみる<br />
modelにない、person.placeを入れてみる<br />
<pre class="prettyprint">
person = Person.new(:fullname =&gt; 'sho hashimoto',<br />
                    :username =&gt; 'shokai',<br />
                    :age =&gt; 25,<br />
                    :place =&gt; 'fujisawa')<br />
<br />
puts person.fullname<br />
puts person.age<br />
puts person.place<br />
<br />
person.save<br />
</pre>
普通に入ってた。このへんは複数人でやるときは何か考えないとならないな。<br />
でもクロールしてきた値とかを適当にどんどん入れてしまうのにはすごくいい。twitterのchirp streamとか。<br />
<br />
<br />
数値はlt,gtで大なり小なり条件指定できるらしい。<br />
あとmongodbは空間型があるはずだけどそれは使えないのかな？filedの型になかったけど。<br />
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5300/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ShinagawaSeaside</title>
		<link>http://shokai.org/blog/archives/5219</link>
		<comments>http://shokai.org/blog/archives/5219#comments</comments>
		<pubDate>Sun, 04 Jul 2010 17:09:53 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[ShinagawaSeaside]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[TokyoCabinet]]></category>
		<category><![CDATA[TokyoTyrant]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5219</guid>
		<description><![CDATA[
tokyo tyrantのサーバーを起動したり終了したりするrake taskを作った。

名前は、tokyotyrantの周辺のライブラリがmiyazaki resistanceとかそういう名前ばかりだったのでそうい [...]]]></description>
			<content:encoded><![CDATA[<p>
tokyo tyrantのサーバーを起動したり終了したりするrake taskを作った。<br />
<br />
名前は、tokyotyrantの周辺のライブラリが<a href="http://www.slideshare.net/tsukasa.oishi/miyazaki-resistance">miyazaki resistance</a>とかそういう名前ばかりだったのでそういう作法なのかなと思って天王洲アイルと迷いつつ品川シーサイドに決めた。<br />
<br />
<br />
■インストール<br />
<pre>
sudo gem isntall shinagawaseaside<br />
</pre>
<br />
<br />
■使う<br />
Rakefile<br />
<pre class="prettyprint">
require&nbsp;'rubygems'<br />
require&nbsp;'shinagawaseaside'<br />
<br />
ttdb&nbsp;=&nbsp;[&nbsp;{&nbsp;:name&nbsp;=&gt;&nbsp;'users',&nbsp;:port&nbsp;=&gt;&nbsp;20010},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;:name&nbsp;=&gt;&nbsp;'videos',:port&nbsp;=&gt;&nbsp;20011},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;:name&nbsp;=&gt;&nbsp;'comments',&nbsp;:port&nbsp;=&gt;&nbsp;20012}&nbsp;]<br />
<br />
ShinagawaSeaside::set_tasks(ttdb,&nbsp;:basedir&nbsp;=&gt;&nbsp;File.dirname(__FILE__)+'/ttdb')<br />
</pre>
ShinagawaSeaside::set_tasks するとrake taskが追加される。<br />
<br />
Rakefileのあるディレクトリの下に ttdb というディレクトリが作られて、<br />
その中にusers.tch, videos.tch, comments.tch というDBができる。pidはusers.pid, videos.pid, comments.pidの中に入る。<br />
<br />
<br />
<pre class="prettyprint">
% rake -T<br />
rake ttrestart  # restart TokyoTyrant server<br />
rake ttstart    # start TokyoTyrant server<br />
rake ttstop     # stop TokyoTyrant server<br />
</pre>
<br />
<br />
中身は<a href="http://d.hatena.ne.jp/shokai/20100703/1278187208">RakeでTokyoTyrant serverを起動/終了 &#8211; 橋本詳解</a>と大体同じ。(複数サーバー起動できるようにした)<br />
tokyotyrantをソースからインストールすると一緒に入る ttservctl を参考にした。<br />
<br />
<br />
<br />
タスクの名前は初期値がttstart, ttstopだけど、変更もできる<br />
<pre class="prettyprint">
ShinagawaSeaside::set_tasks(ttdb,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:basedir&nbsp;=&gt;&nbsp;File.dirname(__FILE__)+'/ttdb',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:start&nbsp;=&gt;&nbsp;'start',&nbsp;#&nbsp;set&nbsp;task&nbsp;name<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:stop&nbsp;=&gt;&nbsp;'stop',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:restart&nbsp;=&gt;&nbsp;'restart'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
</pre>
<br />
<br />
<br />
俺はyamlで設定ファイルを書いてそこから読み込むようにしている。そうするとアプリからも、どのDBがどのportにあるか見つけやすい。<br />
<br />
config.yaml<br />
<pre class="prettyprint">
ttdb&nbsp;:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name&nbsp;:&nbsp;users<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port&nbsp;:&nbsp;23240<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;name&nbsp;:&nbsp;videos<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port&nbsp;:&nbsp;23241<br />
</pre>
<br />
Rakefile<br />
<pre class="prettyprint">
require&nbsp;'rubygems'<br />
require&nbsp;'yaml'<br />
require&nbsp;'shinagawaseaside'<br />
<br />
begin<br />
&nbsp;&nbsp;conf&nbsp;=&nbsp;YAML::load&nbsp;open(File.dirname(__FILE__)+'/config.yaml')<br />
rescue<br />
&nbsp;&nbsp;STDERR.puts&nbsp;'config.yaml&nbsp;load&nbsp;error'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
ShinagawaSeaside::set_tasks(conf['ttdb'],&nbsp;:basedir&nbsp;=&gt;&nbsp;File.dirname(__FILE__)+'/ttdb')<br />
</pre>
<br />
<br />
<br />
■ソースコード<br />
githubに置いた<br />
<ul>
  <li><a href="http://github.com/shokai/shinagawaseaside">http://github.com/shokai/shinagawaseaside</a></li>
  <li><a href="http://rubygems.org/gems/shinagawaseaside">http://rubygems.org/gems/shinagawaseaside</a></li>
</ul>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5219/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>gemだけで画像をリサイズできるImageResizeを作った</title>
		<link>http://shokai.org/blog/archives/5213</link>
		<comments>http://shokai.org/blog/archives/5213#comments</comments>
		<pubDate>Sun, 27 Jun 2010 12:16:42 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[ImageResize]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[画像処理]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5213</guid>
		<description><![CDATA[
ImageResizeはImageMagickなどの外部プログラムに依存せずに、単体で画像をリサイズできる。ただしJavaの実行環境が必要。


SFCの「革新的ネットサービスの構築」という授業のTAをやっていて、昨日 [...]]]></description>
			<content:encoded><![CDATA[<p>
ImageResizeはImageMagickなどの外部プログラムに依存せずに、単体で画像をリサイズできる。ただしJavaの実行環境が必要。<br />
<br />
<br />
SFCの「革新的ネットサービスの構築」という授業のTAをやっていて、昨日と今日niftyに行って開発合宿をしていた。<br />
そこでいろいろあって、ImageMagickなしで画像のサムネイルを作る方法が無いのか模索していたら、<a href="http://rubygems.org/gems/ImageResize">ImageResize</a>というgemができた。<br />
<br />
■インストール<br />
<pre>
gem install ImageResize<br />
</pre>
<br />
<br />
■使う<br />
<pre class="prettyprint">
require 'rubygems'<br />
require 'ImageResize'<br />
<br />
# input, output, width, height<br />
Image.resize('big.jpg', 'small.jpg', 40, 40)<br />
</pre>
これで40&#215;40ピクセルに縮小される。<br />
縦横のアスペクト比が1:1ではない画像の場合、アスペクト比を保ったまま長辺の方にあわせて縮小する。<br />
<br />
<br />
■ソースコード<br />
githubに置いた。<br />
<a href="http://github.com/shokai/ImageResize-ruby">http://github.com/shokai/ImageResize-ruby</a><br />
<br />
<br />
■実装<br />
本体はJava。<br />
標準実行環境にJPEGやGIFやBITMAPなど様々な画像フォーマットの読み書き機能を含んでいる環境というと、Javaと.NETとopenFrameworksしか思いつかなかった。<br />
その中でいちばん色々なマシンにインストールされていそうで、配布が容易な物を選んだらJavaになった。<br />
<br />
<br />
Javaでの画像の扱いは.NET並に簡単。java.awtとjavax.imageioの下に色々充実しているのでそれらを使えばいい。<br />
<a href="http://github.com/shokai/ImageResize-ruby/blob/master/java/ImageResize.java">ImageResize.java</a><br />
<br />
<pre class="prettyprint">
import&nbsp;javax.imageio.*;<br />
import&nbsp;java.io.*;<br />
import&nbsp;java.awt.image.*;<br />
import&nbsp;java.awt.geom.*;<br />
import&nbsp;java.awt.image.*;<br />
import&nbsp;java.util.regex.*;<br />
<br />
class&nbsp;ImageResize{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;ImageResize(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String&nbsp;args[]){<br />
	if(args.length&nbsp;&lt;&nbsp;4){<br />
	&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("ImageResize&nbsp;in.jpg&nbsp;out.jpg&nbsp;320&nbsp;320");<br />
	&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);<br />
	}<br />
	ImageResize&nbsp;app&nbsp;=&nbsp;new&nbsp;ImageResize();<br />
	if(app.resize(args[0],&nbsp;args[1],&nbsp;Integer.parseInt(args[2]),&nbsp;Integer.parseInt(args[3]))){<br />
	&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(args[1]);<br />
	}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;boolean&nbsp;resize(String&nbsp;fname_in,&nbsp;String&nbsp;fname_out,&nbsp;int&nbsp;max_width,&nbsp;int&nbsp;max_height){<br />
	System.out.println(fname_in);<br />
	BufferedImage&nbsp;img&nbsp;=&nbsp;null;<br />
	try&nbsp;{<br />
	&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;=&nbsp;ImageIO.read(new&nbsp;File(fname_in));<br />
	}<br />
	catch&nbsp;(Exception&nbsp;e)&nbsp;{<br />
	&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
	&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;=&nbsp;null;<br />
	}<br />
	<br />
	int&nbsp;width,&nbsp;height;<br />
	if(img.getWidth()&nbsp;&lt;&nbsp;img.getHeight()){<br />
	&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;=&nbsp;max_height;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;=&nbsp;img.getWidth()&nbsp;*&nbsp;max_height&nbsp;/&nbsp;img.getHeight();<br />
	}<br />
	else{<br />
	&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;=&nbsp;max_width;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;=&nbsp;img.getHeight()&nbsp;*&nbsp;max_width&nbsp;/&nbsp;img.getWidth();<br />
	}<br />
	System.out.println(img.getWidth()&nbsp;+&nbsp;"x"&nbsp;+img.getHeight()&nbsp;+&nbsp;"&nbsp;=&gt;&nbsp;"&nbsp;+&nbsp;width&nbsp;+&nbsp;"x"&nbsp;+&nbsp;height);<br />
	<br />
	BufferedImage&nbsp;img_resized&nbsp;=&nbsp;new&nbsp;BufferedImage(width,&nbsp;height,&nbsp;img.getType());<br />
	AffineTransformOp&nbsp;ato&nbsp;=&nbsp;new&nbsp;AffineTransformOp(AffineTransform.getScaleInstance((double)width&nbsp;/&nbsp;img.getWidth(),&nbsp;<br />
										&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(double)height&nbsp;/&nbsp;img.getHeight()),&nbsp;<br />
						&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;null);<br />
	ato.filter(img,&nbsp;img_resized);<br />
	<br />
<br />
	String&nbsp;format&nbsp;=&nbsp;Pattern.compile("^.+\\.(.+)$").matcher(fname_out).replaceAll("$1");<br />
	boolean&nbsp;result&nbsp;=&nbsp;false;<br />
	try&nbsp;{<br />
	&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;ImageIO.write(img_resized,&nbsp;format,&nbsp;new&nbsp;File(fname_out));<br />
	}<br />
	catch&nbsp;(Exception&nbsp;e)&nbsp;{<br />
	&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
	&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;false;<br />
	}<br />
	return&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
</pre>
<br />
<br />
■参考<br />
<ul>
  <li><a href="http://d.hatena.ne.jp/toshyon/20060609/p1">Java で画像のリサイズ &#8211; toshyonのメモ書き</a></li>
  <li><a href="http://www.javadrive.jp/java2d/bufferedImage/index2.html">ImageIOクラスで画像ファイルを読み書きする &#8211; BufferedImageの使い方色々 &#8211; Java2D</a></li>
</ul>
<br />
<br />
今回は使わなかったが、<a href="http://java.sun.com/javase/technologies/desktop/media/jai/">Java Advanced Imaging (JAI) API</a>というのが標準実行環境には含まれていないけどSunが作っていて、かなり充実しているらしい。<br />
Javaランタイムも速くなってきているらしいしScalaで画像処理とかしてみたい。<br />
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5213/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>エディタ保存したらブラウザリロードするsinatra/auto-reloadを作った</title>
		<link>http://shokai.org/blog/archives/5155</link>
		<comments>http://shokai.org/blog/archives/5155#comments</comments>
		<pubDate>Sun, 23 May 2010 18:03:51 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[GreaseMonkey]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Sinatra]]></category>
		<category><![CDATA[sinatra-auto-reload]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5155</guid>
		<description><![CDATA[
エディタを保存したらブラウザを自動リロードするsinatraプラグインを作った。
これでサブモニタにchromeとJavaScriptコンソール置いておくと幸せになれる。
ソースはgithubに。

Google Ch [...]]]></description>
			<content:encoded><![CDATA[<p>
エディタを保存したらブラウザを自動リロードするsinatraプラグインを作った。<br />
これでサブモニタにchromeとJavaScriptコンソール置いておくと幸せになれる。<br />
ソースは<a href="http://github.com/shokai/sinatra-auto-reload">github</a>に。<br />
<br />
Google ChromeもしくはFirefox+Greasemonkeyで動作する。エディタには依存しない。<br />
sinatraアプリをローカルではなくサーバーで実行していてもリロードできる。<br />
<br />
<br />
<br />
■インストール<br />
<pre>sudo gem install sinatra-auto-reload</pre>
<a href="http://rubygems.org/gems/sinatra-auto-reload">rubygems.orgに置いた</a>ので、gemコマンドでインストールできる<br />
<br />
<br />
<br />
■使う<br />
sinatraアプリ内で読み込む<br />
<pre class="prettyprint">
require&nbsp;'sinatra/auto-reloader'&nbsp;if&nbsp;development?<br />
</pre>
if development? すると、shotgunか -e development オプションを付けて起動した時だけ有効になる。production環境には影響を与えない。<br />
<br />
<br />
<br />
■ブラウザ拡張をインストール<br />
localhost:4567 でアプリを起動しているとして、<br />
<a href="http://localhost:4567/sinatra_auto_reload.user.js">http://localhost:4567/sinatra_auto_reload.user.js</a> にgreasemonkeyスクリプトが生成される。<br />
Firefox 3.5+Greasemonkey、もしくはChrome 5.0のuserscriptとしてインストールできる。<br />
<br />
<br />
スクリーンショット<br />
<a href="http://www.flickr.com/photos/shokai/4632061741/" title="sinatra-auto-reload Firefox userscript by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4001/4632061741_23e4e4a5a7_o.png" width="445" height="346" alt="sinatra-auto-reload Firefox userscript" /></a><br />
<br />
<br />
ホスト名ごとに違うuserscriptとしてインストールされるので、調べ物して同じブラウザで他のページを開いても大丈夫<br />
<a href="http://www.flickr.com/photos/shokai/4632048509/" title="sinatra-auto-reload Chrome userscript by shokai, on Flickr"><img src="http://farm5.static.flickr.com/4029/4632048509_957aed8c97_o.png" width="634" height="197" alt="sinatra-auto-reload Chrome userscript" /></a><br />
<br />
<br />
<br />
■監視しないファイルを定義<br />
sinatraアプリ内でauto_reload_ignoresという関数を定義しておく<br />
<pre class="prettyprint">
def&nbsp;auto_reload_ignores<br />
&nbsp;&nbsp;[/db.*/,&nbsp;/config.yaml/,&nbsp;/log.*/,&nbsp;/pid.*/]<br />
end<br />
</pre>
配列内に正規表現で書いたファイルは、更新されてもリロードしない。logとかpidに反応しても困るので。<br />
<br />
<br />
ところでプラグインに変数を渡すのは、こういう方法でいいんですかね？もっとまともなやり方がある気がするんだけど。<br />
<br />
<br />
<br />
■しくみ<br />
sinatra/baseに新しくgetでアクセスできるページを追加し、 http://hostname:port/sinatra_auto_reload にファイルの最終更新日時が出るようにした。<br />
これをuserscriptからsetIntervalで監視して、更新があればリロードする。<br />
<br />
<br />
<br />
■参考<br />
JavaScript部分はこれを参考にした<br />
<a href="http://la.ma.la/blog/diary_200601201204.htm">最速インターフェース研究会 :: Firefoxでの開発を高速化する自動リロードスクリプト</a><br />
<br />
<br />
<br />
■オススメ<br />
sinatra-reloader か shotgun と一緒に使うのをおすすめします<br />
sinatraはサーバーを再起動しないとrubyのコードの変更を読み込んでくれないんだけど、こいつらを使うと毎回読み込み直してくれるようになる。<br />
<br />
<br />
■今後<br />
よく考えたらSub URIで動かしている場合に動かなそうだ。あとで修正する。<br />
<br />
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5155/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ArgsParserをrubygemsに登録した</title>
		<link>http://shokai.org/blog/archives/5096</link>
		<comments>http://shokai.org/blog/archives/5096#comments</comments>
		<pubDate>Mon, 15 Mar 2010 09:49:50 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[ArgsParser]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5096</guid>
		<description><![CDATA[
車輪の再発明臭がヤバイが、コマンドラインの引数のパーサを作ったのでrubygemsに登録した。はじめてgem登録した。

gemでインストールできる。

gem update --system
gem install  [...]]]></description>
			<content:encoded><![CDATA[<p>
車輪の再発明臭がヤバイが、コマンドラインの引数のパーサを作ったので<a href="http://rubygems.org/gems/ArgsParser">rubygemsに登録した</a>。はじめてgem登録した。<br />
<br />
gemでインストールできる。<br />
<pre>
gem update --system<br />
gem install ArgsParser<br />
</pre>
<br />
<br />
<a href="http://d.hatena.ne.jp/shokai/20100223/1266918579">newgemコマンドでgemを作ってrubygems.orgに登録</a>し、一応<a href="http://d.hatena.ne.jp/shokai/20100313/1268464378">rspecでtestも書いた</a>。<br />
gemはrubyforge.orgで公開するのが普通だったけど、2年ぐらい前からgithubでgemが作れる様になったので新しいプロジェクトはみんなgithubに行って、2009年秋？頃に<a href="http://gems.github.com/">githubでgemがビルドできなくなった</a>からかrubygems.orgでやってねという事になったらしい。<br />
<br />
<br />
なのでリポジトリはいつも使っているmercurialにしてbitbucket.orgで公開する事にした。<br />
<a href="http://bitbucket.org/shokai/argsparser-ruby/">shokai / argsparser-ruby / overview — bitbucket.org</a><br />
<br />
<br />
既にgemにはいくつかCのgetopt風なARGVのparserがあるけど、なんとなくインタフェースが好きじゃないので自分が欲しい物を作った。<br />
<br />
<ol>
  <li>&#8211;help -debug等のオプションの有無を判別する</li>
  <li>-x 320 -y 240 等のパラメータ(key value)を取得する</li>
  <li>-hと-helpのように省略名称が関連づけられていれば同じ物として扱う</li>
  <li>-helpと&#8212;helpのようなハイフンの数の違いを無視して、全て :help でアクセスできる</li>
  <li>ruby math.rb add -a 10 -b 25  の&#8221;add&#8221;のような、第一引数を取り出す</li>
  <li>パラメータ名にコメント文を付ける(-helpで起動した時の引数の説明文に使う)</li>
</ol>
という機能に絞ってある。<br />
<br />
<br />
<br />
parseすると、第一引数(String)とパラメータ(Hash)が返ってくる。第一引数がない（いきなりパラメータが来た）場合は、第一引数はnilになる。<br />
example.rb<br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
require&nbsp;'rubygems'<br />
require&nbsp;'ArgsParser'<br />
<br />
#&nbsp;必要なパラメータ名を登録する<br />
parser&nbsp;=&nbsp;ArgsParser.parser<br />
parser.bind(:help,&nbsp;:h,&nbsp;"show&nbsp;help")&nbsp;#&nbsp;name,&nbsp;shortname,&nbsp;comment&nbsp;for&nbsp;help<br />
parser.bind(:frame,&nbsp;:f,&nbsp;"frame&nbsp;image&nbsp;(required)")<br />
parser.bind(:message,&nbsp;:m,&nbsp;"message&nbsp;(required)")<br />
parser.bind(:size,&nbsp;:s,&nbsp;"size&nbsp;(required)")<br />
parser.comment(:min,&nbsp;"minimum&nbsp;size")&nbsp;#&nbsp;add&nbsp;comment&nbsp;for&nbsp;help<br />
parser.comment(:max,&nbsp;"maximum&nbsp;size")<br />
parser.comment(:debug,&nbsp;"debug&nbsp;mode")<br />
<br />
<br />
#&nbsp;parseして、引数が足りてるか、help表示指定が無いかチェック<br />
first,&nbsp;params&nbsp;=&nbsp;parser.parse(ARGV)<br />
<br />
if&nbsp;parser.has_option(:help)&nbsp;or&nbsp;!parser.has_params([:frame,&nbsp;:message,&nbsp;:size])<br />
&nbsp;&nbsp;puts&nbsp;parser.help<br />
&nbsp;&nbsp;puts&nbsp;'e.g.&nbsp;&nbsp;ruby&nbsp;example.rb&nbsp;-f&nbsp;frame.png&nbsp;-m&nbsp;"hello&nbsp;world"&nbsp;-s&nbsp;320x240&nbsp;-debug'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
if&nbsp;first<br />
&nbsp;&nbsp;puts&nbsp;'first&nbsp;arg&nbsp;:&nbsp;'&nbsp;+&nbsp;first&nbsp;#&nbsp;第一引数<br />
end<br />
<br />
if&nbsp;parser.has_param(:size)<br />
&nbsp;&nbsp;puts&nbsp;'size&nbsp;:&nbsp;'&nbsp;+&nbsp;params[:size]&nbsp;#&nbsp;-sでも-sizeでも&nbsp;:size&nbsp;でアクセスできる<br />
end<br />
<br />
#&nbsp;全ての引数を表示<br />
p&nbsp;params<br />
</pre>
<br />
<br />
実行してみる<br />
<pre>ruby example.rb hoge -f frame.png -m "hello world" -s 320x240 -debug</pre>
結果<br />
<pre class="prettyprint">
first&nbsp;arg&nbsp;:&nbsp;hoge<br />
size&nbsp;:&nbsp;320x240<br />
{:message=&gt;"hello&nbsp;world",&nbsp;:size=&gt;"320x240",&nbsp;:debug=&gt;true,&nbsp;:frame=&gt;"frame.png"}<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5096/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>モバイルSuicaの履歴をtwitterに流したかった</title>
		<link>http://shokai.org/blog/archives/5072</link>
		<comments>http://shokai.org/blog/archives/5072#comments</comments>
		<pubDate>Tue, 23 Feb 2010 08:38:03 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[mobilesuica]]></category>
		<category><![CDATA[nokogiri]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[WWW::Mechanize]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5072</guid>
		<description><![CDATA[
xtunnelのためにスクレイピングの勉強をしていて、Mechanize+hpricotからMechanize+nokogiriの組み合わせに乗り換えようと色々と使ってみている中でできた物のひとつ。
昔しゃお先生がやっ [...]]]></description>
			<content:encoded><![CDATA[<p>
xtunnelのためにスクレイピングの勉強をしていて、Mechanize+hpricotからMechanize+nokogiriの組み合わせに乗り換えようと色々と使ってみている中でできた物のひとつ。<br />
昔しゃお先生がやっていたのを俺もやりたくて3ヶ月ぐらい前に作ったけど、mobilesuica.comのおサイフケータイ使用履歴は1日一度早朝に更新される仕様に変更されたらしくボツになった。<br />
<br />
<br />
結局idやcssなどの手がかりが無くて手動で要素を取り出す事になり、nokogiriはHTMLタグを除去するのにしか使わなかった<br />
MobileSuica.rb<br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
require&nbsp;'rubygems'<br />
require&nbsp;'nokogiri'<br />
require&nbsp;'mechanize'<br />
require&nbsp;'kconv'<br />
<br />
module&nbsp;MobileSuica<br />
&nbsp;&nbsp;def&nbsp;MobileSuica.get(user,pass)<br />
&nbsp;&nbsp;&nbsp;&nbsp;agent&nbsp;=&nbsp;WWW::Mechanize.new<br />
&nbsp;&nbsp;&nbsp;&nbsp;agent.user_agent_alias&nbsp;=&nbsp;'Windows&nbsp;IE&nbsp;7'<br />
&nbsp;&nbsp;&nbsp;&nbsp;page&nbsp;=&nbsp;agent.get('http://www.mobilesuica.com/iq/ir/SuicaDisp.aspx?returnId=SFRCMMEPC03')<br />
&nbsp;&nbsp;&nbsp;&nbsp;login_form&nbsp;=&nbsp;page.forms_with(:name&nbsp;=&gt;&nbsp;'form1').first<br />
&nbsp;&nbsp;&nbsp;&nbsp;login_form.fields_with(:name&nbsp;=&gt;&nbsp;'MailAddress').first.value&nbsp;=&nbsp;user<br />
&nbsp;&nbsp;&nbsp;&nbsp;login_form.fields_with(:name&nbsp;=&gt;&nbsp;'Password').first.value&nbsp;=&nbsp;pass<br />
&nbsp;&nbsp;&nbsp;&nbsp;page&nbsp;=&nbsp;login_form.click_button<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;page.body.toutf8.split(/&lt;tr&gt;/).delete_if{|tr|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;!(tr&nbsp;=~&nbsp;/&amp;yen;/m)<br />
&nbsp;&nbsp;&nbsp;&nbsp;}.map{|tr|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tr.gsub(/\n/,"").split(/\r/)[0..5].map{|line|&nbsp;#&nbsp;月日,種別,利用場所,種別,利用場所,残額<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Nokogiri(line).inner_text.chomp.strip.gsub(/[\t　]/,"")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;end<br />
end<br />
</pre>
<br />
<br />
mobilesuicaのユーザ名、パスワードで履歴を2次元配列として取り出せる。<br />
<pre class="prettyprint">
require 'MobileSuica'<br />
MobileSuica.get("user", "pass")<br />
</pre>
月日、種別、利用場所、種別、利用場所、残額の順になる<br />
<pre>
01/30<br />
入<br />
川崎<br />
出<br />
横浜<br />
5,110<br />
01/30<br />
入<br />
相鉄横浜<br />
窓出<br />
川崎<br />
5,320<br />
</pre>
<br />
<br />
<br />
履歴のうち最新の駅名をtwitterに投稿する。-dつけて起動するとdaemonになる<br />
tweet-mobilesuica.rb<br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
require&nbsp;'rubygems'<br />
require&nbsp;'webrick'<br />
require&nbsp;'twitter'<br />
require&nbsp;'MobileSuica'<br />
<br />
def&nbsp;start(conf)<br />
&nbsp;&nbsp;loop&nbsp;do<br />
&nbsp;&nbsp;&nbsp;&nbsp;begin<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;first&nbsp;=&nbsp;MobileSuica.get(conf["suica_user"],&nbsp;conf["suica_pass"]).first<br />
&nbsp;&nbsp;&nbsp;&nbsp;rescue<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;first&nbsp;=&nbsp;nil<br />
&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;first&nbsp;!=&nbsp;nil<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last&nbsp;=&nbsp;first&nbsp;if&nbsp;!last<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;first&nbsp;!=&nbsp;last&nbsp;#&nbsp;1回前に取得した履歴と比較<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;first<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;'-'*10<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;first[1]&nbsp;==&nbsp;'入'&nbsp;&amp;&amp;&nbsp;first[3]&nbsp;==&nbsp;'出'&nbsp;#&nbsp;降車履歴の時<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message&nbsp;=&nbsp;first[4].chomp.strip+"なう&nbsp;(suica)"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;!(message&nbsp;=~&nbsp;/#{conf["ngwords"]}/)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;conf["nopost"]&nbsp;!=&nbsp;true<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;httpAuth&nbsp;=&nbsp;Twitter::HTTPAuth.new(conf["twitter_user"],&nbsp;conf["twitter_pass"])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tw&nbsp;=&nbsp;Twitter::Base.new(httpAuth)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tw.update(message)&nbsp;#&nbsp;twitter&nbsp;post<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;message<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last&nbsp;=&nbsp;first<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;sleep&nbsp;60*60*1.5&nbsp;#&nbsp;1時間半待つ<br />
&nbsp;&nbsp;end<br />
end<br />
<br />
conf&nbsp;=&nbsp;YAML::load&nbsp;open&nbsp;File.dirname(__FILE__)+'/config.yaml'<br />
if&nbsp;ARGV[0]&nbsp;==&nbsp;'-d'<br />
&nbsp;&nbsp;WEBrick::Daemon.start&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;start(conf)<br />
&nbsp;&nbsp;}<br />
else<br />
&nbsp;&nbsp;start(conf)<br />
end<br />
</pre>
<br />
<br />
設定ファイル。自宅の駅名などはngwordsに入れておく<br />
config.yaml<br />
<pre class="prettyprint">
#&nbsp;config.yaml<br />
#&nbsp;mobilesuica.com&nbsp;user/pass<br />
suica_user&nbsp;:&nbsp;'username@docomo.ne.jp'<br />
suica_pass&nbsp;:&nbsp;'12345678'<br />
<br />
#&nbsp;twitter&nbsp;user/pass<br />
twitter_user&nbsp;:&nbsp;'shokai'<br />
twitter_pass&nbsp;:&nbsp;'password'<br />
<br />
#&nbsp;postしない駅名を正規表現で<br />
ngwords&nbsp;:&nbsp;"(東京|横浜)"<br />
<br />
#&nbsp;for&nbsp;debug<br />
#nopost&nbsp;:&nbsp;"true"<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5072/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>wavファイルの音量を調整する</title>
		<link>http://shokai.org/blog/archives/5053</link>
		<comments>http://shokai.org/blog/archives/5053#comments</comments>
		<pubDate>Sat, 06 Feb 2010 06:28:55 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[sound]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[wav]]></category>
		<category><![CDATA[WavFile.rb]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5053</guid>
		<description><![CDATA[
音量の小さいwavファイルのボリュームを上げる。上げすぎて音割れしないようにする。

前に作ったWavFile.rbを使ったら簡単にできた
橋本商会 » Rubyでwavファイルをいじる WavFile.rbを作った
 [...]]]></description>
			<content:encoded><![CDATA[<p>
音量の小さいwavファイルのボリュームを上げる。上げすぎて音割れしないようにする。<br />
<br />
前に作った<a href="http://shokai.org/projects/ruby-wavfile/index.cgi/file/a324224bc098/WavFile.rb">WavFile.rb</a>を使ったら簡単にできた<br />
<ul><li><a href="http://shokai.org/blog/archives/4886">橋本商会 » Rubyでwavファイルをいじる WavFile.rbを作った</a></li></ul>
<br />
16ビットwavは+-32768、8ビットwavは+-128の範囲の配列で波形が表現されている。<br />
ソースのwavの波形を配列に取り出して、その中で最大の値を取りだし、全体を何倍すれば+-32768の間になるかの倍率を計算して全部かけ算すれば音量を調整できる。<br />
<br />
<a href="http://shokai.org/projects/ruby-wavfile/index.cgi/file/a324224bc098/maximizeVolume.rb">maximizeVolume.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
#&nbsp;wavの音量を最大に調節する<br />
require&nbsp;File.dirname(__FILE__)&nbsp;+&nbsp;'/WavFile'<br />
<br />
if&nbsp;ARGV.size&nbsp;&lt;&nbsp;2<br />
&nbsp;&nbsp;puts&nbsp;'ruby&nbsp;maximizeVolume.rb&nbsp;input.wav&nbsp;output.wav'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
in_file&nbsp;=&nbsp;ARGV.shift<br />
out_file&nbsp;=&nbsp;ARGV.shift<br />
<br />
format,&nbsp;data&nbsp;=&nbsp;WavFile::read&nbsp;open(in_file)<br />
<br />
puts&nbsp;format.to_s<br />
<br />
bit&nbsp;=&nbsp;'s*'&nbsp;if&nbsp;format.bitPerSample&nbsp;==&nbsp;16&nbsp;#&nbsp;int16_t<br />
bit&nbsp;=&nbsp;'c*'&nbsp;if&nbsp;format.bitPerSample&nbsp;==&nbsp;8&nbsp;#&nbsp;signed&nbsp;char<br />
wavs&nbsp;=&nbsp;data.data.unpack(bit)<br />
<br />
puts&nbsp;"このwav中の最大音量:&nbsp;#{wavs.max}"<br />
<br />
volume_ratio&nbsp;=&nbsp;32768/wavs.max.to_f&nbsp;if&nbsp;format.bitPerSample&nbsp;==&nbsp;16<br />
volume_ratio&nbsp;=&nbsp;128/wavs.max.to_f&nbsp;if&nbsp;format.bitPerSample&nbsp;==&nbsp;8<br />
puts&nbsp;"補正倍率:&nbsp;#{volume_ratio}"<br />
<br />
wavs_fixed&nbsp;=&nbsp;wavs.map{|w|<br />
&nbsp;&nbsp;(w*volume_ratio).to_i<br />
}<br />
puts&nbsp;"補正されたwav中の最大音量:&nbsp;#{wavs_fixed.max}"<br />
<br />
data.data&nbsp;=&nbsp;wavs_fixed.pack(bit)<br />
<br />
open(out_file,&nbsp;"w"){|out|<br />
&nbsp;&nbsp;WavFile::write(out,&nbsp;format,&nbsp;[data])<br />
}<br />
</pre>
<br />
<br />
使う<br />
<pre>
ruby maximizeVolume.rb input.wav out.wav<br />
</pre>
<br />
約24倍されてout.wavに保存された<br />
<pre>
フォーマットID: 1<br />
チャンネル数: 1<br />
サンプリングレート: 44100 (Hz)<br />
byte per sec: 88200<br />
bit per sample: 16 <br />
ブロックサイズ: 2<br />
このwav中の最大音量: 1335<br />
補正倍率: 24.5453183520599<br />
補正されたwav中の最大音量: 32768<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5053/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenCVで画像のサイズを求めるgearman workerを作って、Rubyから呼ぶ</title>
		<link>http://shokai.org/blog/archives/4987</link>
		<comments>http://shokai.org/blog/archives/4987#comments</comments>
		<pubDate>Sun, 03 Jan 2010 03:29:08 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=4987</guid>
		<description><![CDATA[
ファイル名を渡すと画像サイズを返すgearman workerを作った。

{"width":1600,&#160;"height":1200}

という風にJSON風に値を返す。


以前画像のだいたいの色を求めるg [...]]]></description>
			<content:encoded><![CDATA[<p>
ファイル名を渡すと画像サイズを返すgearman workerを作った。<br />
<pre class="prettyprint">
{"width":1600,&nbsp;"height":1200}<br />
</pre>
という風にJSON風に値を返す。<br />
<br />
<br />
以前<a href="http://shokai.org/blog/archives/4966">画像のだいたいの色を求めるgearman worker</a>を作ってたんだけど、C++で書いたworker側を単独で動作テストする事ができなくてどうしようか悩んだ。テスト用のclientと同時に作らなければならなくて、どちらにバグがあるのか切り分けるのが面倒だった。<br />
<br />
そこで、workerの起動時に<br />
<pre>
./imgsizeWorker --test "/path/to/imagefile.jpg"<br />
</pre>
という風にgearman clientから来る引数と同じ形式で渡すと、単体のプログラムとしても動作チェックできるようにした。<br />
<br />
<br />
こうすると普通にworkerとして起動する。<br />
<pre>
./imgsizeWorker -s localhost -p 7003<br />
</pre>
これはboost::program_optionsでやると便利だった。<br />
今後はこのコードを雛形にすればC++とOpenCVで高速に画像解析して、gearmand経由でスクリプト言語から呼び出しまくれる。<br />
<br />
<br />
■使ったライブラリ<br />
boostライブラリを中心にいろいろ使った。<br />
<br />
opencv1.0は画像サイズを取得する為だけに使った。GUIを使って無いのにcvLoadImage()のためだけにhighgui.hを読み込んでいる。<br />
<br />
boost::tupleとboost::tieを使うと返り値を複数返す関数が作れるので、画像の情報を返すのに便利。そういえばRubyでは普通に使ってたけどC#3.5やAS3.0ではタプル無かったな。欲しい。ASはArrayに何でも入れやすいからいいか。<br />
<a href="http://d.hatena.ne.jp/shokai/20091231/1262248887">boost::tupleで多値を受け取る &#8211; 橋本詳解</a><br />
<a href="http://d.hatena.ne.jp/shokai/20091231/1262249708">boost::tieでtupleを展開 &#8211; 橋本詳解</a><br />
<br />
boost::formatでprintf風にstd::stringをフォーマット。<br />
boost::program_optionsで引数をparseする。<br />
<a href="http://d.hatena.ne.jp/shokai/20090729/1248884601">boost::program_optionsでコマンドライン引数を読む &#8211; 橋本詳解</a><br />
<br />
rubyはxing-gearman-rubyを使った。<br />
<a href="http://shokai.org/blog/archives/4919">橋本商会 » Cでgearman workerを書いてRubyのclientから呼び出す</a><br />
<br />
<br />
■プログラム<br />
libgearmanのgearman_worker_add_serverはIPアドレスを渡さないとならないので、一応ホスト名を解決するようにした。<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/imgsizeWorker.cpp">imgsizeWorker.cpp</a><br />
<pre class="prettyprint">
//&nbsp;画像サイズを返すgearman&nbsp;worker<br />
#include&nbsp;&lt;stdio.h&gt;<br />
#include&nbsp;&lt;stdlib.h&gt;<br />
#include&nbsp;&lt;unistd.h&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;cv.h&gt;<br />
#include&nbsp;&lt;highgui.h&gt;<br />
#include&nbsp;&lt;boost/program_options.hpp&gt;<br />
#include&nbsp;&lt;boost/regex.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple_io.hpp&gt;<br />
#include&nbsp;&lt;libgearman/gearman.h&gt;<br />
<br />
using&nbsp;namespace&nbsp;boost;<br />
using&nbsp;namespace&nbsp;std;<br />
<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(string&nbsp;fileName);&nbsp;//&nbsp;画像のwidth,heightを返す<br />
string&nbsp;imgsize(string&nbsp;fileName);&nbsp;//&nbsp;gearman&nbsp;workerとしてclientに返すstringに整形する<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr);<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;{<br />
&nbsp;&nbsp;program_options::options_description&nbsp;opts("options");<br />
&nbsp;&nbsp;opts.add_options()<br />
&nbsp;&nbsp;&nbsp;&nbsp;("help,h",&nbsp;"helpを表示")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("server,s",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearmanサーバーのアドレス")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("port,p",&nbsp;program_options::value&lt;int&gt;(),&nbsp;"gearmanサーバーのport番号")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("test,t",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearman&nbsp;worker単体テスト用query");<br />
&nbsp;&nbsp;program_options::variables_map&nbsp;argmap;<br />
&nbsp;&nbsp;program_options::store(parse_command_line(argc,&nbsp;argv,&nbsp;opts),&nbsp;argmap);<br />
&nbsp;&nbsp;program_options::notify(argmap);<br />
<br />
&nbsp;&nbsp;if(!argmap.count("help")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(argmap.count("test")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"---test---"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;gearman_param&nbsp;=&nbsp;argmap["test"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgsize(gearman_param);&nbsp;//&nbsp;単体でworkerとしてのテスト<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}else&nbsp;if(argmap.count("server")&nbsp;&amp;&amp;&nbsp;argmap.count("port")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_st&nbsp;worker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_create(&amp;worker);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server&nbsp;=&nbsp;argmap["server"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;g_port&nbsp;=&nbsp;argmap["port"].as&lt;int&gt;();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;hostent&nbsp;*g_host&nbsp;=&nbsp;gethostbyname((char*)g_server.c_str());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server_addr&nbsp;=&nbsp;str(format("%d.%d.%d.%d")&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[0]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[1]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[2]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[3]);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_server(&amp;worker,&nbsp;g_server_addr.c_str(),&nbsp;g_port);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_function(&amp;worker,&nbsp;"img_size",&nbsp;0,&nbsp;job_imgsize,&nbsp;NULL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("---start&nbsp;worker&nbsp;(%s:%d)---")&nbsp;%<br />
		&nbsp;&nbsp;g_server_addr&nbsp;%&nbsp;g_port)&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(true)&nbsp;gearman_worker_work(&amp;worker);&nbsp;//&nbsp;workerとして待機<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;"server,portが必要です"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;opts&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;1;<br />
&nbsp;&nbsp;<br />
}<br />
<br />
//&nbsp;opencvで画像サイズを取得<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(string&nbsp;fileName){<br />
&nbsp;&nbsp;IplImage&nbsp;*img&nbsp;=&nbsp;cvLoadImage(fileName.c_str());<br />
&nbsp;&nbsp;if(!img){<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(-1,&nbsp;-1);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(img-&gt;width,&nbsp;img-&gt;height);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvReleaseImage(&amp;img);<br />
&nbsp;&nbsp;}<br />
}<br />
<br />
//&nbsp;画像サイズを取得してgearman&nbsp;serverに返すstringに整形する<br />
string&nbsp;imgsize(string&nbsp;fileName){<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;"";<br />
&nbsp;&nbsp;int&nbsp;width,&nbsp;height;<br />
&nbsp;&nbsp;tie(width,&nbsp;height)&nbsp;=&nbsp;get_size(fileName);<br />
&nbsp;&nbsp;if(width&nbsp;&gt;&nbsp;0&nbsp;&amp;&amp;&nbsp;height&nbsp;&gt;&nbsp;0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;+=&nbsp;str(format("{\"width\":%d,&nbsp;\"height\":%d}")	<br />
		&nbsp;&nbsp;&nbsp;&nbsp;%&nbsp;width&nbsp;%&nbsp;height);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;"error&nbsp;:&nbsp;image&nbsp;load&nbsp;error";<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;fileName&nbsp;&lt;&lt;&nbsp;"&nbsp;=&gt;&nbsp;"&nbsp;&lt;&lt;&nbsp;result_str&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;result_str;<br />
}<br />
<br />
//&nbsp;gearman&nbsp;worker&nbsp;job<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr){<br />
&nbsp;&nbsp;string&nbsp;fileName&nbsp;=&nbsp;(char*)gearman_job_workload(job);<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;imgsize(fileName);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;char&nbsp;*result&nbsp;=&nbsp;(char*)strdup(result_str.c_str());<br />
&nbsp;&nbsp;*result_size&nbsp;=&nbsp;result_str.size();<br />
&nbsp;&nbsp;*ret_ptr&nbsp;=&nbsp;GEARMAN_SUCCESS;<br />
&nbsp;&nbsp;return&nbsp;result;<br />
}<br />
</pre>
<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/Makefile">Makefile</a><br />
<pre class="prettyprint">
#&nbsp;Mac用Makefile<br />
SRC&nbsp;=&nbsp;imgsizeWorker.cpp<br />
DST&nbsp;=&nbsp;imgsizeWorker<br />
<br />
prefix=/opt/local<br />
INCPATH=$(prefix)/include<br />
LIBPATH=$(prefix)/lib<br />
<br />
CV_LIBS=&nbsp;-lcv&nbsp;-lcvaux&nbsp;-lcxcore&nbsp;-lhighgui<br />
BOOST_LIBS=&nbsp;$(LIBPATH)/libboost_program_options-mt.a<br />
<br />
GEAR_INCPATH=/usr/local/include<br />
GEAR_LIBPATH=/usr/local/lib<br />
GEAR_LIBS=$(GEAR_LIBPATH)/libgearman.a<br />
<br />
all:<br />
	g++&nbsp;-O&nbsp;$(SRC)&nbsp;-o&nbsp;$(DST)&nbsp;-I$(INCPATH)/opencv&nbsp;-L.&nbsp;-L$(LIBPATH)&nbsp;$(CV_LIBS)&nbsp;-I$(INCPATH)/boost&nbsp;$(BOOST_LIBS)&nbsp;-I$(GEAR_INCPATH)/libgearman&nbsp;-L.&nbsp;-L$(GEAR_LIBPATH)&nbsp;$(GEAR_LIBS)<br />
</pre>
<br />
<br />
client側。workerからの返り値の先頭にerrorが書いていなかったらJSONとしてparseする<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/testclient.rb">testclient.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
require&nbsp;'rubygems'<br />
require&nbsp;'gearman'<br />
require&nbsp;'json'<br />
<br />
if&nbsp;ARGV.size&nbsp;&lt;&nbsp;1<br />
&nbsp;&nbsp;puts&nbsp;'画像へのパスが必要'<br />
&nbsp;&nbsp;puts&nbsp;'ruby&nbsp;testclient.rb&nbsp;~/path/to/images/*.png'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
c&nbsp;=&nbsp;Gearman::Client.new(['localhost:7003'])<br />
taskset&nbsp;=&nbsp;Gearman::TaskSet.new(c)<br />
<br />
ARGV.sort{|a,b|&nbsp;a.split(/\//).last.to_i&nbsp;&lt;=&gt;&nbsp;b.split(/\//).last.to_i}.each{|name|<br />
&nbsp;&nbsp;puts&nbsp;"add&nbsp;task&nbsp;#{name}"<br />
&nbsp;&nbsp;task&nbsp;=&nbsp;Gearman::Task.new("img_size",&nbsp;name+"\0")<br />
&nbsp;&nbsp;task.on_complete{|result|<br />
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;!(result&nbsp;=~&nbsp;/^error/)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;"return:&nbsp;#{name}&nbsp;=&gt;&nbsp;"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;JSON.parse(result)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;"return:&nbsp;#{name}&nbsp;=&gt;&nbsp;#{result}"<br />
&nbsp;&nbsp;&nbsp;&nbsp;end<br />
<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;taskset.add_task(task)<br />
}<br />
taskset.wait(100)&nbsp;#&nbsp;wait&nbsp;100(sec)<br />
</pre>
<br />
<br />
&amp;をつけていくつかworkerを起動する<br />
<pre class="prettyprint">
./imgsizeWorker -s localhost -p 7003&#038;<br />
./imgsizeWorker -s localhost -p 7003&#038;<br />
</pre>
<br />
<br />
clientからtask登録。フォルダ内のjpgファイルを全部登録する<br />
<pre class="prettyprint">
ruby testclient.rb ~/Pictures/selected/*.jpg<br />
</pre>
サイズが返ってくる<br />
<pre class="prettyprint">
add task /Users/sho/Pictures/selected/a66dab3a.jpg<br />
add task /Users/sho/Pictures/selected/3ed6f38e.jpg<br />
add task /Users/sho/Pictures/selected/77ab53f0.jpg<br />
add task /Users/sho/Pictures/selected/889bd644.jpg<br />
add task /Users/sho/Pictures/selected/73177294.jpg<br />
return: /Users/sho/Pictures/selected/a66dab3a.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/3ed6f38e.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/77ab53f0.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/889bd644.jpg => {"height"=>800, "width"=>1280}<br />
return: /Users/sho/Pictures/selected/73177294.jpg => {"height"=>1200, "width"=>1600}<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/4987/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
