0

大人のYAPCでスクリプト言語の勉強方法についてLTした


公序良俗に反する話が求められていたんだけど、ビビってあまり激しい話しなかった。でもおかげでスライドそのまま公開できる。

朝にスライド作って、LTするまですごい自信なかったから1日中ビール飲みまくってたんだけど、ウケてたからよかった。
内容は、俺がよく言ってる「ぶっこ抜き系ツールの自作はモチベーション保ちやすいし技術的にも色々な要素あるから新しい言語の勉強にいいよ」という話。



それとスライドに出てくる馬場くんと作ってるbabascriptIPA未踏ソフトウェアに採択されました
最近未踏は25歳までになったので俺年齢オーバーだからメンバーに入ってないけど開発には多少関わるつもりです(オープンソースソフトウェアなので)

0

twで写真を付けてtweetできるようになった

arms22さんの提案でCUIで使えるtwitter clientに写真アップロード機能が付きました

Tw: Twitter client on Ruby.

tw うまい --file=food.jpg

–fileオプションにファイル名を渡せば写真がpic.twitter.comにアップロードされつつ、tweetできる。



カメラ

Macではimagesnapをhomebrewで入れればCUIからiSightで写真を取れる
brew install imagesnap
imagesnap -w 2.0 capture.jpg


1行で写真を取ってtweetできる
imagesnap -w 2.0 capture.jpg && tw 定点観測 --file=capture.jpg --yes && rm capture.jpg


crontabに登録しておけば定期的に撮影tweetできる。
2時間おきに撮影するように設定してみた。撮る前にsayで「撮影します」って言わせる。
PATH=$HOME/.rbenv/shims:$PATH
LANG=ja_JP.UTF-8

0 */2 * * * say 撮影します && imagesnap -w 2.0 capture.jpg && tw 定点観測 --file=capture.jpg --yes && rm capture.jpg

やっぱこれ危ないからやめよう


橋本商会 » CUIでUSBカメラキャプチャ

0

Rackミドルウェアの作り方を勉強した

今スイスにいる。
行きの飛行機の中での勉強用にこのページを保存しておいて、Rack middlewareの作り方を学んだ。

第25回 Rackとは何か(3)ミドルウェアのすすめ:Ruby Freaks Lounge|gihyo.jp … 技術評論社

というのもSinatra::RocketIOをRack::RocketIOにしたいからなんだけど、Rack Hijack APIがよくわからない。(hijackについてはそのうち書く)


ソースコードはgithubに全部置いてある。
github.com/shokai/rack-plugin-study


Rack

Sinatra/Rails/Padrino等のRuby製webアプリケーションフレームワークと、
webrick/thin/mongrel/unicorn等などのRuby製webサーバーを接続するしくみがRackです。

Webアプリフレームワークが、どのwebサーバーででも動くように接続部分を抽象化してくれている。

以前rackの上にsinatra風のフレームワークを実装してみた時に、くわしく解説した。
SinatraっぽいWAFを作る、46行で


rack middleware

rack middlewareを作ると、WebサーバーがWebアプリケーションフレームワーク(WAF)とやりとりするデータを書き換えたりできる。
rack::auth系の認証プラグインとか、rack::cache系のキャッシュ系が有名。
どちらもリクエストとレスポンスの間をいじる処理で、プラグインをsinatraやrailsで実装するよりもrackでやったほうが楽だと思った。

rackの実装はほぼ安定していてWAFほどコロコロ変わったりしないし、rack middlewareとして実装しておけばrailsでもsinatraでもpadrinoでも使える。



rack middlewareを作って、sinatraアプリから使えるかどうか試した。

まずSinatraアプリを準備する

まず、単なるHTMLを返すSinatraアプリを作る。文章は感嘆符や句読点が色々欲しかったので、艦これwikiからお借りしました。


main.rb

get '/' do
'<p><img src="/shokai.jpg"></p>
<p><遠征の成功条件(暫定)></p>
<p>編成隻数</p>
<p>特定の艦種を一定数以上?(条件が無い遠征もある?要検証)</p>
<p>旗艦のLv</p>
<p>編成合計Lv?(下記参照、要検証) 敵地偵察作戦の場合、軽巡洋艦を1隻含む6隻構成、旗艦Lv20以上、編成合計Lvは駆逐艦のみで構成した場合に100Lv以上。</p>
<p>旗艦に軽巡を据える必要性は無く、旗艦:駆逐改でも成功することが確認されている。</p>
<p>また、編成艦船の最低Lvは関係無いことも判明している。</p>'
end

このmain.rbをconfig.ruからrequireして、rackupすればsinatraアプリが起動する。

config.ru

require 'rack'
require 'sinatra'

require File.expand_path 'main', File.dirname(__FILE__)

run Sinatra::Application

rackup config.ru -p 5000



アプリのレスポンスを書き換えるrack middleware


語尾がクマ語になるrack middlewareを作った。
https://github.com/shokai/rack-plugin-study/tree/master/kuma

こうなる


kuma_response_filter.rb

クラスを作って、initializeとcallだけメソッドを宣言する。
# -*- coding: utf-8 -*-
class KumaResponseFilter
def initialize(app)
@app = app
end

def call(env)
res = @app.call env # sinatraに処理させる
if res[1]["Content-Type"] =~ /^text\/.+/
res[2] = res[2].map{|body_part|
body_part.gsub(/([。!?])/){|s| "クマ#{s}"}
}
res[1]["Content-Length"] = res[2].map{|body_part| body_part.bytesize }.inject{|a,b| a+b }.to_s
end
return res
end
end
sinatraに処理させてから、そのレスポンスがtextの時のみ中身をいじってから返している。
感嘆符や句読点の前にクマを挿入するだけ。
Content-Lengthが変わるので最後に計算し直す。


config.ru

use クラス名 するだけでプラグインが有効になって、response bodyが書き換えられる。
require 'rack'
require 'sinatra'
require File.expand_path 'kuma_response_filter', File.dirname(__FILE__)
use KumaResponseFilter

require File.expand_path 'main', File.dirname(__FILE__)

run Sinatra::Application


JPEG画像をglitchしてから配信するrack middleware


https://github.com/shokai/rack-plugin-study/tree/master/image_glitch


JPEG画像がこうなる。glitchはmakimotoさんのスライドを参考にした

image_glitch.rb

Sinatraのレスポンスをみて、image/jpegだったらa/b置換glitchする。
module Rack
class ImageGlitch
def initialize(app)
@app = app
end

def call(env)
res = @app.call env # sinatraに処理させる
if res[1]["Content-Type"] == "image/jpeg"
body = []
res[2].each do |i|
body.push i.gsub('a', 'b')
end
res[2] = body
end
res
end
end
end


これも、config.ruで
use Rack::ImageGlitch
するだけで有効になる。


リクエストがアプリに届く前に修正するrack middleware


https://github.com/shokai/rack-plugin-study/tree/master/jpeg2jpg

試しに、 .jpg を .jpegや.JPEG と打ち間違えてもレスポンスが返ってくるプラグインを作った。

.JpEGとかでリクエストされても、Sinatraのlogには.jpgとしてリクエストされた、と表示される。
127.0.0.1 - - [09/Sep/2013 15:31:26] "GET /shokai.jpg HTTP/1.1" 200 - 0.0024


jpeg_request_filter.rb

PATH_INFO, REQUEST_PATH, REQUEST_URI全部書き換える。
# -*- coding: utf-8 -*-
class JpegRequestFilter
def initialize(app)
@app = app
end

def call(env)
# sinatraに処理させる前にrequestを書き換える
if env["PATH_INFO"] =~ /.+\.jpeg$/i
["PATH_INFO", "REQUEST_PATH", "REQUEST_URI"].each do |k|
env[k].gsub!(/\.jpeg$/i, ".jpg")
end
end
res = @app.call env # sinatraに処理させる
end
end


まとめ

リクエスト・レスポンスを加工するならrackでやったほうが楽な場合もある。

0

Rubyのrescueって1行で書くと値を返すのね・・

考えてみれば当たり前か。


シリアルポートからの入力を読むread関数が、たまにEOFErrorを投げてくるので無視したい。
しかしEOFError以外は握りつぶしたくない。
色々省略するけど、こういう感じで書いてたんだけど

require 'serialport'
serialport = SerialPort.new("/dev/tty.devicename", 9600, 8, 1, 0)

def read
serialport.read rescue EOFError ## たまに来るEOFErrorだけを無視するつもりだった
end

loop do
puts read
sleep 1
end


こういう実行結果になる。rescueで拾ったEOFErrorがreturnされてた。
EOFError
(デバイスからのデータ)
EOFError
EOFError
(デバイスからのデータ)
EOFError
(デバイスからのデータ)
EOFError


関数readを、こうして1行のrescueで書かないようにすればEOFErrorの時はnilが返る。
def read
begin
result = random_error
rescue EOFError
end
end


nilなので、改行がputsされる

(デバイスからのデータ)


(デバイスからのデータ)

(デバイスからのデータ)

0

arduino_firmata gemの起動が速くなった

Arduino Firmata on Ruby

必要ないsleep削って速くしたぜ!ってpull requestが来たんだけど、そのsleepは古いArduino(Duemillanove/Decimila等)をサポートするのに必要なんですって言ってrejectしようと思ったけどよく考えたら接続する前にUSBデバイスのファイル名でUNO以降/Duemillanove以前を判別できる事に気づいた。


このようにデバイス名でわかる。
Debian Linux(raspberry pi)の場合
/dev/ttyACM0 -> Leonard, Micro, UNO
/dev/ttyUSB0 -> Decimilla, Duemillanove


Mac OSXの場合
/dev/tty.usbmodem1234 -> Leonard, Micro, UNO
/dev/tty.usbserial-A1234 -> Decimilla, Duemillanove


新しいArduinoの場合、sleepを省略するようにした。
UNO以降のArduino + Ruby2.0の環境ではarduino_firmataの初期化プロセスが0.1秒ぐらいになった。Duemillanove以前のArduinoだったり、Ruby2.0以前の場合は3秒ぐらいかかる。


あと、at_exitを知らなかった。
at_exit使わずにtrapでSITHUPやSIGTERMを受信してたので、なんかシグナルを握りつぶしていたらしい。
Ruby exit, exit!, SystemExit and at_exit blunder | Big Fast Blog

このようにclass内でブロック渡ししておくと、プロセスが終了する時に呼び出される。

class MyClass
at_exit do
# threadを止めたり、シリアルポートを閉じる等の処理
end
end