ずっと君のターン

2013-12-06

pry-parsecom作りました

| 08:12 |  pry-parsecom作りました - ずっと君のターン を含むブックマーク

最近お仕事でParseを使っています。parse.comはいわゆるBaaSで、多言語のライブラリやブラウザから使える管理ツールも揃ってて手っ取り早く動くものを公開したいときにはとても便利なんですが*1、運用でデータストアをどうこうしたいときは基本ブラウザ上でデータブラウザを使うしかありません。もしくは、APIを利用して独自のツールを作るか。

まぁデータブラウザもものすごく悪いという感じでもないですが、可能であればコマンドラインで操作したい。rails consoleみたいに。もしくはpry-rails。

ということで作りました。

http://github.com/technohippy/pry-parsecom

$ pry-parsecom
[1] pry(main)> login-parse
Email for parse.com: andyjpn@gmail.com
Password for parse.com: 
logged in
[2] pry(main)> show-applications
Email for parse.com: andyjpn@gmail.com
Password for parse.com: 
    Name   | Using
  ================
  FakeApp  |
  FakeApp2 |
(cached at: 2013-11-18 13:59:48 +0900)

起動してログインするとアプリの一覧が見られます。

[3] pry(main)> use-application FakeApp
The current app is FakeApp.
[4] pry(main)> show-classes
   Name   |    Class   
  =====================
  Comment | Comment    
  Post    | Post       
  _User   | Parse::User
(cached at: 2013-11-18 13:59:48 +0900)
[5] pry(main)> show-schema Post
    Name   |       Type
  ============================
  author   | pointer<_User>
  body     | string
  comments | relation<Comment>
(cached at: 2013-11-18 13:59:48 +0900)

使用するアプリを選択すればクラスの一覧や、クラスのスキーマも確認できます。

[6] pry(main)> posts = Post.find :all
=> [---
__type: Post
author:
  __type: Pointer
  className: _User
  objectId: ZybBXQEIjI
body: Hello
comments: <Ralations>
...snip...
[7] pry(main)> posts[0].body = 'World'
=> "World"
[8] pry(main)> posts[0].save

use-applicationコマンド実行時にParseのクラスに対応するRubyクラスを定義するので、手間いらずで検索や新規作成・更新・削除いろいろできます。Parseオブジェクトの操作は、合わせて自作したparsecomライブラリを使ってます。

http://github.com/technohippy/parsecom

game_score = GameScore.find_by_id 'Ed1nuqPvcm'
game_score.score = 73453
game_score.save

ParseのREST APIを使ってできることは、特殊なのを除いてだいたいできるはず。

game_scores = GameScore.find :where => proc {
  subquery = subquery_for :Team
  subquery.where {column(:winPct).gt(0.5)}
  subquery.key = 'city'
  column(:hometown).select(subquery)
}

使いやすいかどうかは別にして、サブクエリも発行できるライブラリは他にあんまりなさそう。

seans_score = GameScore.new 'score' => 1337, 'playerName' => 'Sean Plott'
zerocools_score = GameScore.new 'score' => 1338, 'playerName' => 'ZeroCool'
Parse.batch do
  seans_score.save
  zerocools_score.save
end

バッチリクエストも割と簡単。

[9] pry(main)> logout-parse
logged out
[10] pry(main)> exit

APIキーをローカルに保存するので、気になる人は終了前にログアウトしておいた方がいいかもしれません。

$ gem install pry-parsecom

で使えるようになります。軽い用途にはわりと便利に使えると思うのでよろしければどうぞ。

http://github.com/technohippy/pry-parsecom

*1:実際にプロダクションで使うととてもとても面倒くさいですが、それはまた別の話

2013-02-16 寒風

LLVMで作る日本語プログラミング言語

| 22:41 | LLVMで作る日本語プログラミング言語 - ずっと君のターン を含むブックマーク

https://github.com/technohippy/Kaleidoscope.ja

以前から日本語プログラミング言語って作ってみたくて、でもただのトランスレーターならともかく、まっとうにコンパイル出来てネイティブでサクサク動くようなのはどうやって作ったらいいか分からないどころか、どの辺から勉強に手をつければいいのかすら分からなくて放置してたわけです。

ところがまぁ世の中良くしたもので、LLVMというものを使えばフロントエンドを作るだけで、ややこしいところは良しなに処理してくれるそうじゃないですか。しかも最近本が出たばかり。これが日本語の予約語とか関数名・変数名とかを扱えるなら、いろいろ捗りそう。ということで試してみました。

結論から言えば、タイトルのとおり、LLVMは日本語も問題なく使えるみたいです。

サンプル: カレイドスコープ

ということで、さっそく実際に動くものを作ります。

LLVMの本家ページのチュートリアルにKaleidoscopeという言語の実装例があります。KaleidoscopeはC言語に似た簡易言語で以下のようなことができます。

  • if文による分岐
  • for文による繰り返し
  • 関数定義
  • 単項演算子・二項演算子の定義
# Compute the x'th fibonacci number.
def fib(x)
  if x < 3 then
    1
  else
    fib(x-1)+fib(x-2)

# This expression will compute the 40th number.
fib(40)

http://llvm.org/docs/tutorial/LangImpl1.html#the-basic-language

これを日本語プログラミング言語にしてみることにしましょう。

まず、フロントエンドがC++だと日本語を扱うのが大変そうなので(というかそもそもC++知らないので)Rubyに移植します。

https://github.com/technohippy/Kaleidoscope.rb

面倒くさいので細かい説明はなし。C++のチュートリアルをほぼ行単位でRubyにしてるだけなので、中身の詳細については本家チュートリアルを見てください。

この中のlangimpl6.rbに以下の改造を加えます。

  1. 日本語を識別子とみなすように
  2. 予約語を日本語に変更

ソースコードの関連する部分はこう。日本語識別子はすごく適当なのできっといろいろ漏れてます。

def isalpha c; isalnum(c) && !isdigit(c) end 
def isalnum c; c =~ /^[^\s();:,=#!<>+\/\*\-\|\&\.]$/ end
return TOK_DEF if $IdentifierStr == '次の関数を定義'
return TOK_EXTERN if $IdentifierStr == '次の関数を利用'
return TOK_IF if $IdentifierStr == 'もし' 
return TOK_THEN if $IdentifierStr == 'が真なら' 
return TOK_ELSE if $IdentifierStr == 'そうではなければ' 
return TOK_FOR if $IdentifierStr == '次の条件で' 
return TOK_IN if $IdentifierStr == '以下を繰り返す' 
return TOK_BINARY if $IdentifierStr == '二項演算子' 
return TOK_UNARY if $IdentifierStr == '単項演算子'

これだけでだいたい日本語プログラミング言語として動きました。簡単ですね。

サンプルのサンプル: マンデルブロ集合

先の日本語プログラミング言語が動作することを示すために、サンプルとしてマンデルブロ集合を表示してみようと思います。

ただ、いまのところ画面に表示するための命令がなにもありません。これだけは如何ともしがたいので、まずはprintdとputchardという組み込み関数的なものを使えるようにします。

まず、C++で件の関数を定義して

#include <cstdio>
  
//===----------------------------------------------------------------------===//
// "Library" functions that can be "extern'd" from user code.
//===----------------------------------------------------------------------===//
  
/// putchard - putchar that takes a double and returns 0.
extern "C"
double putchard(double X) {
  putchar((char)X);
  return 0;
} 
  
/// printd - printf that takes a double prints it as "%f\n", returning 0.
extern "C" 
double printd(double X) {
  printf("%f\n", X);
  return 0;
} 

ビットコードに変換した後で

$ clang++ -emit-llvm -S print.cpp -o print.ll
$ llvm-as print.ll

langimpl6.rb(改)内でデフォルトでそれらが使えるようにLLVM::Module.newではなく、LLVM::Module.parse_bitcodeを使うように変更します。

#$TheModule = LLVM::Module.new "my cool jit"
$TheModule = LLVM::Module.parse_bitcode 'print.bc'

Kaleidoscopeの改造はこれで終わりです。この上で動作するマンデルブロ集合を表示する日本語プログラム(抜粋)は次のような感じになります。(一番最後に全文を載せています)

# ..前略..

次の関数を定義 密度を表示する(密度)
  もし 密度 > 8 が真なら
    次の文字コードを持つ文字を表示する(32)  # ' '
  そうではなければ もし 密度 > 4 が真なら
    次の文字コードを持つ文字を表示する(46)  # '.'
  そうではなければ もし 密度 > 2 が真なら
    次の文字コードを持つ文字を表示する(43)  # '+'
  そうではなければ
    次の文字コードを持つ文字を表示する(42); # '*'

# ある位置が発散するかどうかを決める
次の関数を定義 発散するか?(実数 虚数 繰り返し数 実数初期値 虚数初期値)
  もし 繰り返し数 > 255 | (実数*実数 + 虚数*虚数 > 4) が真なら
    繰り返し数
  そうではなければ
    発散するか?(実数*実数 - 虚数*虚数 + 実数初期値,
                    2*実数*虚数 + 虚数初期値,
                    繰り返し数+1, 実数初期値, 虚数初期値);

# ..後略..

早速実行してみましょう。

f:id:technohippy:20130216002417p:image

ちょっとわかりにくいですが、マンデルブロ集合です。

ということで、LLVMを使った日本語プログラミング言語はたぶん実現可能ですよ、というお話でした。


# 論理否定 単項演算子
次の関数を定義 単項演算子!(値)
  もし 値 が真なら
    0
  そうではなければ
    1;

# 負 単項演算子
次の関数を定義 単項演算子-(値)
  0-値;

# > を < と同じ優先順位で定義
次の関数を定義 二項演算子> 10 (左辺値 右辺値)
  右辺値 < 左辺値;

# 論理和 二項演算子
次の関数を定義 二項演算子| 5 (左辺値 右辺値)
  もし 左辺値 が真なら
    1
  そうではなければ もし 右辺値 が真なら
    1
  そうではなければ
    0;

# 論理積 二項演算子
次の関数を定義 二項演算子& 6 (左辺値 右辺値)
  もし !左辺値 が真なら
    0
  そうではなければ
    !!右辺値;

# 同値比較
次の関数を定義 二項演算子 = 9 (左辺値 右辺値)
  !(左辺値 < 右辺値 | 左辺値 > 右辺値);

# 順次実行。右の値を返す
次の関数を定義 二項演算子 : 1 (式12) 式2;


次の関数を利用 putchard(char);
次の関数を定義 次の文字コードを持つ文字を表示する(文字コード)
  putchard(文字コード);

次の関数を定義 密度を表示する(密度)
  もし 密度 > 8 が真なら
    次の文字コードを持つ文字を表示する(32)  # ' '
  そうではなければ もし 密度 > 4 が真なら
    次の文字コードを持つ文字を表示する(46)  # '.'
  そうではなければ もし 密度 > 2 が真なら
    次の文字コードを持つ文字を表示する(43)  # '+'
  そうではなければ
    次の文字コードを持つ文字を表示する(42); # '*'

# ある位置が発散するかどうかを決める
次の関数を定義 発散するか?(実数 虚数 繰り返し数 実数初期値 虚数初期値)
  もし 繰り返し数 > 255 | (実数*実数 + 虚数*虚数 > 4) が真なら
    繰り返し数
  そうではなければ
    発散するか?(実数*実数 - 虚数*虚数 + 実数初期値,
                    2*実数*虚数 + 虚数初期値,
                    繰り返し数+1, 実数初期値, 虚数初期値);

# イテレーションを抜けるのに必要だった繰り返しの回数を返す
次の関数を定義 発散するかの確認を開始する(実数 虚数)
  発散するか?(実数, 虚数, 0, 実数, 虚数);

# 指定された2次元の範囲内でマンデルブロ集合を計算してプロットする
次の関数を定義 マンデルブロ(xの最小値 xの最大値 xのステップ   yの最小値 yの最大値 yのステップ)
  次の条件で y = yの最小値, y < yの最大値, yのステップ 以下を繰り返す (
    (次の条件で x = xの最小値, x < xの最大値, xのステップ 以下を繰り返す
       密度を表示する(発散するかの確認を開始する(x,y)))
    : 次の文字コードを持つ文字を表示する(10)
  )

# 指定した倍率で指定された位置のマンデルブロ集合をプロットする
次の関数を定義 マンデルブロ集合を表示する(開始実数値 開始虚数値 実数値の倍率 虚数値の倍率)
  マンデルブロ(開始実数値, 開始実数値+実数値の倍率*78, 実数値の倍率,
             開始虚数値, 開始虚数値+虚数値の倍率*40, 虚数値の倍率);

マンデルブロ集合を表示する(-2.3, -1.3, 0.05, 0.07);

2013-02-08 天気はいいけど肌寒い

きつねさんはおしえてくれないruby-llvm

| 23:06 | きつねさんはおしえてくれないruby-llvm - ずっと君のターン を含むブックマーク

今年のテーマ(の一つ*1)はLLVMにしようと思い立ってちびちびお勉強してたんだけど、タイムリーなことに本日達人出版会さんから素晴らしい本が出版されまして。さすが達人出版会。いいとこついてくるわー。ていうか一週間前に欲しいってtweetしたばっかだわー。

http://tatsu-zine.com/images/books/68/cover_s.jpg

http://tatsu-zine.com/books/llvm

ただまぁ残念なことに?こちらの本もそうなんですけどLLVMのチュートリアルはたいていの場合CとかC++の知識必須だったりするわけです。いや、読めと言われたら読むけど、正直CもC++もどっちもよく知らないからあんまり頭に入って来なくてつらい。せっかくLLVM(llc?)でコンパイルできるわけだし、フロントエンドの実装はもっと軽い言語でいいじゃない。例えばRubyとか、もしくはRubyとか。

と嘆いてたらruby-llvmというものを見つけました。名前の通りRubyでLLVMをなんかぐりぐりできるライブラリです。同僚の石川先生もコントリビュートしてるので、きっとすばらしいものです。触り始めたばっかりでよく知らないけど。

まぁそういうことで、llvmとruby-llvmとセットで覚えるかと、ここしばらく本家チュートリアルのKaleidoscopeをruby-llvmに移し替えてました。元のチュートリアル(C++)の流れをなるべく変えないようにRubyに移植してたから、ruby-llvmの使い方としてはあんまりよくない感じかもしれないけど。

LLVMの日本語の情報が少なすぎるので、ホントはできたサンプルを元に本家のチュートリアルの簡略版でも書こうかと思ってたんだけど、きつねさんが出たのでなんかもういいや。

ソースは公開しておくので、よかったら何かの参考にしてください。

https://github.com/technohippy/Kaleidoscope.rb

あ、そうだ。一点だけ。

今のところhomebrewでインストールされるruby-llvmはLLVM3.0じゃないとだめっぽいので、brew install llvm --shared する前に、ここの手順で3.0を使うように設定しましょう。怠るといろいろエラーが出ます。

*1:あとWebGLと、次点でDartlangとRails再入門

2013-01-18 氷がずっと残ってる

Smalltalk風Ruby

| 22:34 | Smalltalk風Ruby - ずっと君のターン を含むブックマーク

f:id:technohippy:20130118191803p:image:w300

https://twitter.com/technohippy/status/291697606638305280

とか言うことをつぶやきつつ考えたんですが、","に目を瞑ればRubyは1.9から key:val 形式のHashリテラルをキーワード引数っぽく使えるし、->{...} 形式のProcリテラルもあるしで、これってすでにほぼSmalltalkじゃないですか。

# Ruby
ret = ->{ 
    obj.message 'arg1', arg2:'arg2', arg3:'arg3'
}.call
"Smalltalk"
ret := [
    obj message: 'arg2' arg2: 'arg2' arg3: 'arg3'.
] value.

どっちがどっちか区別つかないレベル。

で、この際なので足りない部分を少し補ってRubyをもっとSmalltalkっぽく書けるようにと、小さなライブラリを作成しました。

https://github.com/technohippy/smalltalkable

require 'smalltalkable'

Object.subclass :Counter,
    instanceVariableNames: 'value step',
    classVariableNames: 'instanceCount',
    poolDictionaries: '', 
    category: 'Category-Name'

これを使うとクラス定義はこんなふうに書けます。

Counter.compile '
    initialize value, step:step
        @value = value
        @step = step'

メソッド定義はこんな感じ。文字列で渡すのがSmalltalk流。(嘘

counter = Counter.new 0, step:2

メッセージ送信はこう。

String.smalltalkize :rjust => [:rjustWidth, :padding]
'foo'.rjustWidth 10, padding:'*' #=> '*******foo' 

ちなみに既存のメソッドをSmalltalk風に再定義することもできます。

->{counter.value < 20}.while_true ->{
    (counter.value % 3 == 0).if_true ->{
        puts counter.value
    }
    counter.next
}

もちろん if や while もSmalltalkっぽく書けます。

$ sudo gem install smalltalkable

ということで、Smalltalkを愛してやまないけど仕事で已む無くRubyを使っている人はこのSmalltalkableで心の平穏を取り戻していただければと思います。

2012-12-24 いい天気でした

クリスマスRuby

| 00:46 | クリスマスRuby - ずっと君のターン を含むブックマーク

というような経緯があり、今年はRubyでPerlをぶっ潰すことにしました。具体的には今年はRubyを弄るかわりにRubyっぽくPerlを書くためのツールを作ってみました。これでPerlプログラムを生まれる前に消し去りたい。なんか近頃そういうの流行ってるっぽいし。opalとかopalとか。

https://github.com/technohippy/sapphire

簡単な実行例はこんな感じ。

# misc/write_file.rb
require 'path/class'
require 'autodie'

dir = dir '/tmp'
file = dir.file 'file.txt'
file_handle = file.openw
list = %w(a list of lines)
list.each do |line|
  file_handle.print "#{line}\n"
end
$ bin/sapphire misc/write_file.rb | perl
$ cat /tmp/file.txt
a
list
of
lines

ただ、これだとシンプルすぎてRubyだかなんだかわからない上に、ただの文字列置換みたいなのでもうちょっと大きいサンプルを作りました。

SchenkerというPerl版SinatraのようなものをRubyっぽくで書き直して、それをPerlに変換してから実行してみます。不毛に見えるけど、自分の英作文が正しいかどうかを確かめるために、英日機械翻訳した結果を日英機械翻訳してオリジナルと比較するような、まぁなんかそういう感じ。*1

Rubyっぽく書きなおしたもの(の一部)。*2

# https://github.com/technohippy/sapphire-sample/blob/master/Schenker/src/Schenker.rb
class Schenker < Exporter
  include 5.00800
  include Any::Moose
  include Carp, %w(croak)
  include Scalar::Util, %w(blessed)

  # ...snip...

  @@App = nil
  @@AppFile = nil
  # ...snip...
  @@EXPORT = %w(
    helpers Before error not_found define_error
    # ...snip...
  )

  def import(__no_self__)
    pkg, file = caller
    croak <<-END_MSG if defined? pkg and pkg == 'main'
Can't use Schenker in the 'main' package.
Please use Schenker in your package.
    END_MSG

    unless defined? @@App
      @@App = pkg
      @@AppFile = file
    end

    self.class.export_to_level 1, @_
    any_moose.import into_level: 1
  end

  def unimport
    caller = caller()
    any_moose.unimport
    no strict 'refs'
    @@EXPORT.each do |method|
      delete ("#{caller}::".to_deref)[method]
    end
  end

  # ...snip...

  def make_stash(*args)
    stash = self
    ->{
      arg_size = scalar args
      if arg_size == 0
        stash
      elsif arg_size == 1
        stash[args[0]]
      elsif arg_size % 2 == 0
        args_hash = args.to_hash
        args_hash.each do |key, val|
          stash[key] = val
        end
      else
        croak 'usage: stash key or stash key => val;'
      end
    }
  end

  def session
    croak 'cannot call session in not running server.'
  end

  def make_session
    if options.sessions
      ->{request.session}
    else
      ->{croak "session is disabled. To enable session, set 'sessions' option true."}
    end
  end

  def helpers(__no_self__, *args)
    croak 'usage: helpers name => code' unless scalar(args) % 2 == 0;
    helpers = args.to_hash
    helpers.each do |name, sub|
      @@App.meta.add_method name, sub
    end
  end

  def Before
    code = self 
    croak 'code required' unless code
    croak 'code must be coderef' unless ref(code) == 'CODE'
    push @@Filters, code
  end

  # ...snip...

  configure do
    set 'environment', ENV['SCHENKER_ENV'] || 'development'
    disable 'sessions'
    enable 'logging'
    # ...snip...
    set 'manager', nil
    set 'keeperr', nil
    set 'encode', {
      'encode' => 'utf-8',
      'decode' => 'utf-8'
    }
    set 'session_options', {
      'state' => {
        'class' => 'Cookie',
        'args' => {
          'name' => 'schenker_sid'
        }
      },
      'store' => {
        'class' => 'OnMemory',
        'args' => {},
      }
    }
    tt_options 'ENCODING', 'utf-8'

    # for prove
    if ENV['HARNESS_ACTIVE']
      set 'server', 'Test'
      set 'environment', 'test'
      disable 'run'
    end

    # for mod_perl
    if ENV['MOD_PERL']
      set 'server', 'ModPerl'
      disable 'run'
    end

    Schenker::Options.parse_argv

    configure 'development' do
      Before do
        headers 'X-Schenker', @@VERSION
      end
      error do |__self__|
        warn self
        status 500
        content_type 'text/html'
        body :$self.stack_trace.as_html 'powered_by', 'Schenker' # TODO
      end
      # ...snip...
    end
  end

  no Any::Moose
  self.meta.make_immutable
end

なんかいろいろPerlっぽいとこが漏れてる気がしますが、とりあえず上記はRubyとして解釈は可能。これを以下のコマンドで変換すると

$ bin/sapphire "Schenker/src/**/*.rb" -O out

こうなります。

use strict;
use warnings;
{

    package Schenker;
    use base 'Exporter';
    use 5.008;
    use Any::Moose;
    use Carp qw(croak);
    use Scalar::Util qw(blessed);
    # ...snip...
    our $App         = undef;
    our $AppFile     = undef;
    our @EXPORT      = ( 
        "helpers",      "Before",  "error",         "not_found",
        # ...snip...
    );  

    sub import {

        my ( $pkg, $file ) = caller();

        if ( ( defined $pkg ) and $pkg eq "main" ) {
            croak(
"Can't use Schenker in the 'main' package.\nPlease use Schenker in your package.\n"
            );
        }

        unless ( ( defined $App ) ) {
            $App     = $pkg;
            $AppFile = $file;
        }

        __PACKAGE__->export_to_level( 1, @_ );
        any_moose()->import( { into_level => 1 } );
    }

    sub unimport {
        my $self   = shift;
        my $caller = caller();
        any_moose()->unimport();
        no strict "refs";
        for my $method (@EXPORT) {
            delete( ${ "" . $caller . "::" }->{$method} );
        }

    }

    # ...snip...

    sub make_stash {
        my $self  = shift;
        my @args  = @_;
        my $stash = $self;
        sub {

            my $arg_size = scalar(@args);
            if ( $arg_size == 0 ) {
                $stash;
            }
            elsif ( $arg_size == 1 ) {
                $stash->{ $args[0] };
            }
            elsif ( $arg_size % 2 == 0 ) {
                my %args_hash = @args;
                while ( my ( $key, $val ) = each(%args_hash) ) {
                    $stash->{$key} = $val;
                }

            }
            else {
                croak("usage: stash key or stash key => val;");
            }

        };
    }

    # ...snip...

    configure(
        sub {
            # ...snip...
            set(
                "session_options",
                {
                    "state" => {
                        "class" => "Cookie",
                        "args"  => { "name" => "schenker_sid" }
                    },
                    "store" => { "class" => "OnMemory", "args" => {} }
                }
            );
            tt_options( "ENCODING", "utf-8" );

            if ( $ENV{"HARNESS_ACTIVE"} ) {
                set( "server",      "Test" );
                set( "environment", "test" );
                disable("run");
            }

            if ( $ENV{"MOD_PERL"} ) {
                set( "server", "ModPerl" );
                disable("run");
            }
            Schenker::Options->parse_argv();
            configure(
                "development",
                sub {

                    Before(
                        sub {

                            headers( "X-Schenker", $VERSION );
                        }
                    );
                    error(
                        sub {
                            my $self = shift;
                            warn($self);
                            status(500);
                            content_type("text/html");
                            body( $self->stack_trace()
                                  ->as_html( "powered_by", "Schenker" ) );
                        }
                    );
                    # ...snip...
                }
            );
        }
    );
    no Any::Moose;
    __PACKAGE__->meta()->make_immutable();
}

1;

辛うじてPerlです。見た目が変わっただけだとよく分からないと思うので、さらにこのSchenker改を使ったアプリを作って

class MyApp
  include Schenker

  get '/' do
    'Hello, world!'
  end

  get '/hello/:name' do |args|
    "Hello, #{args['name']}!"
  end
end

変換した後で

$ bin/sapphire myapp.rb -o myapp.pl
$ cat myapp.pl
use strict;
use warnings;
{

    package MyApp;
    use Schenker;
    get(
        "/",
        sub {

            "Hello, world!";
        }
    );
    get(
        "/hello/:name",
        sub {
            my $args = shift;
            "Hello, " . $args->{"name"} . "!";
        }
    );
}

1;

実行してみます。

$ perl -I out/src myapp.pl 
== Schenker/0.01 has taken the stage on 4567 for development with backup from ServerSimple
HTTP::Engine::Interface::ServerSimple : You can connect to your server at http://0.0.0.0:4567/

http://localhost:4567/

f:id:technohippy:20121221221013p:image

無事にウェブアプリが起動されました。

http://localhost:4567/hello/sapphire

f:id:technohippy:20121221221012p:image

pathパラメーターもOK。

http://localhost:4567/unknownpath

f:id:technohippy:20121221221010p:image

エラー画面も。

・・・

ということで、PerlをRubyっぽく書くためのツールの紹介でした。RubyでPerlをぶっ潰すと言うよりも、RubyがPerlにぶっ潰されている感がないでもないけど (゚ε゚)キニシナイ!!

*1西口さん、公開許可ありがとうございます

*2:全部を見たければhttps://github.com/technohippy/sapphire-sample/tree/master/Schenker/src:titile=こちら]をどうぞ