daily gimite RSSフィード

2011/07/24

[] Yuyake - ブラウザ上でRubyでWebアプリ開発 20:31

色々未完成なのですが、放置気味だったのでとりあえず現状を公開することにしました。

Yuyake

ブラウザ上でRubyでWebアプリを開発してそのまま実行できる、というものです。フレームワークSinatraのみをサポートしてます。利用にはGoogleアカウント*1が必要です。

おことわり

実験的なサービスなので、突然落ちたり、サービス終了したり、データが消えたりする可能性があります。また、Yuyake上の全アプリケーションがAppEngineの無料クオータを共有することになるので、誰かが使いすぎるとサービス全体が落ちます :) まあ落ちてもいいのですが、わざと負荷をかけないようにしてください。

そんな感じでいまいち実用的ではないのですが、AppEngineで動くので、自分のアカウントで自分専用のやつを立ち上げることができます(ソースがあれば。近日公開予定)。その方法なら、使い物になるかもしれません。

利用可能なRubyの機能

FileとかSocketとか以外のRubyの組み込みクラス/標準ライブラリは使えるはずですが、サンドボックスのせいでところどころ動きません。データベースは現状使えませんが、Yuyake::SStore, Yuyake::AStoreというkey/valueストアが使えます。自動生成されるソースのコメント参照。

実装

Cloud9 IDE(これはnode.js用)のRuby版(&しょぼい版)みたいな感じです。Herokuが昔Railsで似たようなことをやっていた気がするのですが、今見るとローカルで書いてアップロードする方法しか見当たらない…。

実装はAppEngine+JRubyです。*2ユーザのコードはjavasandというJRubyのサンドボックスの中に入れて実行しています。

エディタの部分はCloud9 IDEのエディタ部分が単独で公開されているので、それを使っています。各種言語のシンタックスハイライトとかオートインデントとかしてくれて、いい感じです。

*1Google+アカウントではない。

*2:たまにやけに反応が遅いのはAppEngine+JRubyの起動が遅いせい。

2011/05/14

[][] Fiberを使ってem-http-requestとかを同期的に呼び出す 21:09

EventMachineの関数(em-http-requestとか)を多用すると、コールバックだらけになって訳が分からなくなるのが欠点です。

Ruby 1.9のFiberを使うと、em-http-requestみたいな非同期関数を同期的に呼ぶことができます。em-synchronyというライブラリがそのようなラッパを提供してるのを見つけました。

require "em-synchrony"
require "em-synchrony/em-http"

p EM::HttpRequest.new("http://www.google.com").get.response
p EM::HttpRequest.new("http://www.yahoo.com").get.response

こうするとhttp://www.google.comhttp://www.yahoo.comを順にロードします(ロード中もちゃんとEventMachineが動きます)。これはFiberの中で実行する必要があります。方法としては、EM.synchronyを使う方法:

EM.synchrony do
  p EM::HttpRequest.new("http://www.google.com").get.response
  p EM::HttpRequest.new("http://www.yahoo.com").get.response
end

と、自分でFiberを作る方法:

EM.run do
  ...
  Fiber.new {
    p EM::HttpRequest.new("http://www.google.com").get.response
    p EM::HttpRequest.new("http://www.yahoo.com").get.response
  }.resume
  ...
end

があります。Sinatraで使う場合はrack-fiber_poolというのを使うと、リクエスト処理全体をFiberで囲ってくれるようです。

require "sinatra"
require "em-synchrony"
require "em-synchrony/em-http"
require "rack/fiber_pool"

use(Rack::FiberPool)

get("/") do
  res1 = EM::HttpRequest.new("http://www.google.com").get.response
  res2 = EM::HttpRequest.new("http://www.yahoo.com").get.response
  res1 + res2
end

async_sinatraを使うのと同じことが、もっと楽に書けるわけですね。sinatra-synchronyというのもあるようですが、こっちはちゃんと見てないのでよくわかりません…。

ちなみに上のような例では(よく分からない例ですが)同時並行でロードするとベターなわけですが、そういう時のためにFiberを使って同時並行で実行するeachを作ってみました。

em-fiber-utils

require "em-synchrony"
require "em-synchrony/em-http"
require "em-fiber-utils"

EM.synchrony do
  urls = ["http://www.google.com", "http://www.yahoo.com"]
  EM::FiberUtils.concurrent_each(urls) do |url|
    # ここが同時並行で実行される。
    p EM::HttpRequest.new(url).get.response
  end
  # 両方終わるとここに来る。
end

2011/04/17

[][] jruby-jars-*.gem のビルド方法 16:11

jruby-jars-*.gem をビルドする方法のメモ。

$ git clone git://jruby.org/jruby.git jruby
$ cd jruby
$ ant
$ ./bin/jruby bin/gem install rake rspec
$ ant gem
$ ls dist
jruby-complete-1.6.1.jar       jruby-jars-1.6.1.gem
jruby-complete-1.6.1.jar.md5   jruby-jars-1.6.1.gem.md5
jruby-complete-1.6.1.jar.sha1  jruby-jars-1.6.1.gem.sha1

2009/09/26

[] HpricotからNokogiriに移行するときの罠(特にXML名前空間) 20:30

HpricotからNokogiriに移行しようとしていくつか罠にはまったのでメモしておきます。

基本的には

  • require "hpricot" → require "nokogiri"
  • Hpricot(html) → Nokogiri::HTML(html)
  • Hpricot::XML(xml) → Nokogiri::XML(xml)

と書き換えるだけで、運が良ければそのまま動くと思います。

Nokogiri(text)というのもあるのですが、これはXMLかHTMLかを自動判定するらしく、失敗することもあるのでお勧めしません。

NokogiriはHpricotと違ってXML名前空間をきちんと解釈するので、XML名前空間を使ったXMLを解析する場合には注意が必要です。XML名前空間を使ったXMLというのは、以下のようにxmlnsなんとかというのが入っているやつです。

<feed xmlns="http://www.w3.org/2005/Atom"
    xmlns:gs="http://schemas.google.com/spreadsheets/2006">
  <entry>
    <gs:cell>hoge</gs:cell>
  </entry>
</feed>

この例だとdoc.search("entry")とかdoc.search("gs:cell")では何も引っかかりません。それぞれ以下のように書く必要があります。

namespaces = {
  "atom" => "http://www.w3.org/2005/Atom",
  "gs" => "http://schemas.google.com/spreadsheets/2006",
}
doc.xpath(".//atom:entry", namespaces)
doc.xpath(".//gs:cell", namespaces)

いくつか注意点。

  • doc.search("gs:cell", namespaces)だとCSSセレクタだと思われてしまう(searchはXPathとCSSセレクタのどっちも受け付ける)ので、xpathメソッドを使う必要があります。
  • XPathの文法だと単に"gs:cell"だと現在のノードの直接の子ノードしか対象にならないので、子孫ノードも対象にするには.//を付けます。

要するに面倒くさいです。こっちの方がXML的に正しい実装ではあるんでしょうけど…。XML名前空間って、やりたい事はわかるし、便利なときは便利なんでしょうけど(XHTMLの中に直接SVGを書くとか)、大抵のときは話を面倒くさくしているだけのような…。

という点が主な原因で、実はgoogle-spreadsheet-rubyはとりあえずNokogiriへの移行を見送りました。

  • NokogiriはRuby 1.9のString#encodingをちゃんと設定してくれる(Encoding::UTF_8になる)
  • Nokogiriの方が速いらしい
  • MechanizeもNokogiriに移行したらしい
  • Hpricotは作者(_why氏)が行方不明?

というあたりから、Nokogiriに移行した方がいいような気はするのですが。

2010/9/24追記: CSSセレクタ(cssメソッド)を使うと名前空間のゴチャゴチャがなくなる(見た目通りの名前空間を書けばいい)と教えてもらいました。上の例ではそれぞれdoc.css("entry"), doc.css("gs|cell")になります。名前空間の区切りが|になるのはちょっと慣れないですけど。google-spreadsheet-rubyは現在はNokogiriに移行し、この方法を使っています。あと、NokogiriのString#encodingはちゃんと何も指定しなくてもEncoding::UTF_8になるようになっていたので、本文を修正しました。

2008/12/24

[][] Google SpreadsheetのRubyライブラリ 16:55

を作ったので公開しました。

Google Spreadsheet Ruby

インストール方法:

$ sudo gem install google-spreadsheet-ruby

使用例:

  require "rubygems"
  require "google_spreadsheet"

  # ログイン。
  session = GoogleSpreadsheet.login("username@gmail.com", "mypassword")

  # http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
  # の最初のワークシート。
  ws = session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg").worksheets[0]

  # A2セルの内容を取得。
  p ws[2, 1] #==> "hoge"

  # セルの内容を変更。ws.save()を呼ぶまで変更はサーバに反映されないので注意。
  ws[2, 1] = "foo"
  ws[2, 2] = "bar"
  ws.save()

Google SpreadsheetのRubyライブラリはすでにいくつかあったのですが(gdata-rubyroo)、

  • インタフェースがいまいち
  • 複数のセルへの変更をいっぺんにサーバに送る機能がない
  • 以前、ちょっと必要になっていい加減に作ったやつがあった

などの理由で、前に作ったやつを整理・拡張して公開しました。

今回初めてgithubのgemパッケージ化機能を試してみました。svn レポジトリを github に移行させてみるが参考になりました。

mandrakenaomandrakenao 2011/05/24 21:57 これは使いやすいです。助かります。