2010/08/17

RSpecとAutotestとJRuby --1.9のメモ

autotestというかautospecしたいので設定メモ。Ubuntuのポップアップ(OSD)で結果を通知する。

#まずはZenTestと通知用のautotest-notifyosdというGemを入れる。 gem install ZenTest autotest-notifyosd --no-ri --no-rdoc #autotest-notifyosdはlibnotifyがいるらしい sudo aptitude libnotify-bin #autotest-notifyosdのセットアップ ~/.rvm/gems/jruby-1.5.1/gems/autotest-notifyosd-0.0.3/bin/enable-autotest-notifyosd

JRubyを1.9モードで起動するための設定。~/.autotestというファイルを作って以下を書き込む。

Autotest.add_hook :initialize do |at| def at.ruby "#{super} --1.9" end end

specディレクトリのあるところでautospecと実行すると、autospecが開始される。 specディレクトリ内のファイルに変更があるたびに、テストが実行され結果がOSDに表示されるようになる。

変更されたファイルだけテストしたいときはどうすればいいのだろう?

2010/08/16

RubySpecメモ

以下のメッセージがでたら./rubyspec_tempを消す。

----------------------------------------------------- The rubyspec temp directory is not empty. Ensure that all specs are cleaning up temporary files. -----------------------------------------------------

JRubyの1.9コンパチモードで実行するには-Tでjrubyに--1.9オプションを渡す。ついでに1.9用のコンフィグファイルを読ませる必要がある?

mspec -B ruby.1.9.mspec run -tj -T --1.9 .

encodingとか1.9からの機能に関するspecにはwith_featureっていうガードがかかっていて、ruby.1.x.mspecというコンフィグファイル内で有効無効を制御しているっぽい。RUBY_VERSIONをみて、どのコンフィグファイルを読むか制御していると書いているのだけど、jruby --1.9だとruby.1.9.mspecを読まないのか、encodingまわりなどのspecを実行してくれないようだ。

コンフィグファイル指定なしの場合

mspec -T --1.9 core/encoding/names_spec.rb jruby 1.5.1 (ruby 1.9.2dev trunk 24787) (2010-06-06 f3a3480) (Java HotSpot(TM) Client VM 1.6.0_20) [i386-java] Finished in 0.069000 seconds 1 file, 0 examples, 0 expectations, 0 failures, 0 errors

コンフィグファイル指定ありの場合

mspec -B ruby.1.9.mspec -tj -T --1.9 core/encoding/names_spec.rb jruby 1.5.1 (ruby 1.9.2dev trunk 24787) (2010-06-06 f3a3480) (Java HotSpot(TM) Client VM 1.6.0_20) [i386-java] .... Finished in 0.602000 seconds 1 file, 4 examples, 637 expectations, 0 failures, 0 errors

-tr19だとインタプリタがruby1.9という名前でないとだめっぽい。rvmとかで1.9を使っている場合rubyになるので、素直に-trでいいみたい。

2010/04/29

Android開発環境構築メモ

環境

  • Ubuntu 9.10
  • JDK1.6.0_12
  • Eclipse 3.5
  • Experia X10

Android SDKを入れる。ここからDL。現在2.1

好きなディレクトリに展開。PATHにtoolsを通す。

(必要なら)ソニエリのデベロッパーサイトからExperiaのSDKをDL。AndroidSDKのadd-onsディレクトリに展開。add-ons/XPERIA-X10_r1のようになる。

Eclipseを立ち上げてADTプラグインを入れる。マネージャでhttps://dl-ssl.google.com/android/eclipse/を追加。だめならhttp://dl-ssl.google.com/android/eclipse/としてSSLを使わないようにする。

Preference→AndroidでインストールしたSDKを指定してOK

Window→Android SDK and AVDでAVDマネージャを起動

InstalledPackageで好きなバージョンを追加。Experiaなら1.6(API Level4)を入れる。

VirtualDeviceでTargetをX10に指定してVirtualDevice(エミュレータ)作成。起動時にScale display to real sizeにしておくと、854x480ではなく実機の大きさで起動してくれるのでネットブックでも安心。
Androidのエミュレータは実機をそのままエミュレートするので、iAppliのようにアプリ実行ごと都度起動ではなく立ち上げっぱなしにして使うもの。

EclipseでPreference→AndroidでApplyを押すとTargetが再ロードされるのでX10を選択

New Project→Android Project。Project作成ダイアログが表示されるが、ネットブックとかだと画面の縦幅が足りずにBuild Targetが出てこない場合がある。Linuxの場合、Alt+F7で画面外にダイアログをずらして、Alt+F8で下にダイアログを伸ばすと必要な項目が出てくる。

Project Nameは好きな名前, Build Targetは実機に合わせる。X10とか。Application Nameは実機で表示される名前。package nameはJavaのパッケージ名。デフォルトパッケージは不可なので適当に。Create Activityは最初に呼ばれるActivityのクラス名。Activityについては公式ドキュメント等参照。Min SDK Versionはアプリで許容する最小のSDKのバージョン。API Levelで指定する。

できたらパッケージエクスプローラでプロジェクトを右クリックRunAs→AndroidApplicationで起動すればエミュレータにHelloworldが表示される。

An SDK target must be specified.

2010/04/03

TinyDSメモ(Query編)

Query

クエリ用クラス
内部で使っているので、appengine-apiに含まれるDatastore::Query辺りを合わせて読むと幸せになれそう

使い方

Person.query.sort(:user_name, :desc).filter(:user_name, '==', 'hoge').one

基本的に設定メソッドはselfを返すのでメソッドチェインが可能

メソッド

メソッド説明
initialize(model_class)コンストラクタ
Base.queryで取得するので基本使わない。
ancestor(anc)祖先を指定する。
anc:祖先。Base, String, Keyを指定可能。
filter(*args)フィルタを設定する。
argsはHashまたはパラメータ名, 演算子, 値の3組のどちらかを指定する。
Hashを指定した場合はname=>パラメータ名, value=>値のペアで「==」フィルタになる。
3組の場合に指定できる演算子は以下のとおり
  • ==
  • >
  • >=
  • <
  • <=
  • IN
  • !=
  • <>
sort(name, dir=:asc)ソートを指定する。
name:プロパティ名
dir:ソートの方向 :asc(昇順)または:desc(降順)を指定する。
デフォルトは昇順。
keys_only検索結果としてKeyだけを返すように設定する
count設定されている条件で取得できるエンティティの数を返す。
one設定されている条件で1件のエンティティを取得する。
複数ヒットする場合はTooManyResults例外、ヒットしなかった場合nilを返す
all(opts={})設定されている条件にヒットする全エンティティを取得する。
optsには:limit, :offset, :chunk, :transactionが指定できる。
:limit : クエリが戻す結果の上限
:offset : オフセット(スキップする件数)。スキップした数は:limitに影響しない
:chunk : 内部のチャンク方法を指定するらしいがよく分からない。
:transaction : クエリを実行するトランザクション。祖先のクエリ(ancestor queries)のみサポートしている。
指定しない場合、現在のトランザクション(current transaction)を使用する。
each(opts={})設定されている条件でヒットするエンティティにブロックで渡された処理を行う。
optの値はallと同じ
collect(opts={})設定されている条件でヒットするエンティティにブロックで渡された処理を行い、その結果を返す。
optの値はallと同じ
keys(opts={})指定された条件でヒットするエンティティのKeyを返す。
optの値はallと同じ

2010/04/02

TinyDSメモ(Base編)

JRuby on gae/jで使えるDatastoreのラッパーであるTinyDSの個人的メモ (0.0.3 pre)

間違っていたら指摘をお願いします。

TinyDS::Base

エンティティの基底クラス。

基本的な使い方

class Person << TinyDS::Base property :user_name, :string end # 作成 Person.create({:user_name => "hoge"}) # idから検索 hoge = Person.get_by_id(id) puts hoge.user_name #=> hoge

保存したいプロパティはproperty :カラム名, :型名で定義

プロパティの型

シンボル説明
string文字列 RubyのString(500byte以下)
integer整数 RubyのInteger
text長い文字列 RubyのString(DatastoreのTextクラス)
time日付 RubyのTime
listリスト RubyのArray
float浮動小数点 RubyのFloat *0.0.2にはない
booleanブール値 Rubyのtrue/false *0.0.2にはない

floatとbooleanを使うにはGithubから最新版を取得する必要がある。

予約されているプロパティ名

  • id: Keyのid
  • name: Keyのname
  • key: key
  • entity: Baseの中で使っている
  • parent_key: 親のKey
  • parent: 親

プロパティ名がcreated_atとupdated_atの場合はsaveを行う際に自動的に作成日と更新日時が設定される。

メソッド

メソッド名説明
initialize(attrs={}, opts={}コンストラクタ
attrs:このエンティティに設定したいプロパティのHash(key=>name, value=>値)
opts:オプションのHash :id, :name, :key, :parentと:entityが指定できる。
:id:このエンティティのidに指定したい整数値
:name:このエンティティのKeyのnameに指定したい文字列
:key:このエンティティのKeyに指定したいKey

:id, :name, :keyはいずれか1つだけ指定可能。2以上指定すると例外
:id, :name, :keyを指定しない場合は:id自動発番

:parent:Hashの値にエンティティを入れておくと、それを親エンティティとして作成する。
:entity:Hashの値にエンティティ(生のEntity)を入れておくと、それを元にエンティティを作成する。
:entityを指定しない場合、newで作成した場合は新規レコード扱い(new_record==true)になり、saveするまでDatastoreには格納されない。
self.property(name, type, opts={})プロパティを定義する。
name:プロパティの名前
type:プロパティの型
opts:オプション opts[:default]=デフォルト値とすることでデフォルト値を渡せる。
デフォルト値はProcオブジェクトでもよく、Procの場合はそれをcallした結果がデフォルト値になる。
self.property_definitions定義されているプロパティを取得する(key=>name, value=>PropertyDefinitionsのHash)
>self.property_definitions(name)nameで指定されたプロパティ定義を取得する。存在しない場合は例外
self.valid_property?(name, context)nameで指定されたプロパティが存在すればtrue。contextは使っていないっぽい
self.has_property?(name)nameで指定されたプロパティが存在すればtrue。
self.default_attrsデフォルト値の一覧を返す(key=>name, value=>デフォルト値のHash)
self.kindこのエンティティのKindを返す
keyこのエンティティのKeyを返す
idこのエンティティのidを返す
nameこのエンティティのnameを返す
parent_keyこのエンティティの親のKeyを返す
self.to_key(m)渡されたオブジェクトをKeyとして返す。mの種類によって以下の動作となる。
Key=>そのまま
Entity=>m.key
String=>KeyFactory.stringToKey(m)で生成したKey
Base=>m.key
それ以外=>例外
self.allocate_ids(num)Datastore.allocate_ids(kind, num)の結果(KeyRange)
self.allocate_idDatstore.allocate_ids(1).startの結果(Key)
self.create(attrs={}, opts={})エンティティの作成と同時にDatastoreに保存する。
引数はinitializeと同じ。保存する前にvalid?が呼ばれ、不正なエンティティの場合falseが返る。
self.create!エンティティの作成と同時にDatastoreに保存する。
引数はinitializeと同じ。保存する前にvalid?が呼ばれ、不正なエンティティの場合RecordInvalid例外がスローされる。
self.new_from_entity(_entity)既存のエンティティ(生Entity)からエンティティを生成する。
valid?このエンティティが有効かをチェックする。デフォルト実装では常にtrueを返す。createやsaveのときに暗黙的に呼ばれる。
new_record?新規レコード(まだDatastoreに格納されていない)の場合trueを返す。
saveDatastoreに保存する。保存する前にvalid?が実行され、不正なエンティティの場合falseを返す。
save!Datastoreに保存する。保存する前にvalid?が実行され、不正なエンティティの場合RecordInvalid例外がスローされる。
self.get!(key)指定したkeyのエンティティを取得する。エンティティが見つからない場合EntityNotFound例外をスロー
self.get(key)指定したkeyのエンティティを取得する。エンティティが見つからない場合nil
self.build_key(id_or_name, parent)Keyを生成する。
self._get_by_id_or_name!(id_or_name, parent)指定したidまたはnameのエンティティを取得する。エンティティが見つからない場合EntityNotFound例外
self.get_by_id!(id, parent=nil)指定したidのエンティティを取得する。エンティティが見つからない場合EntityNotFound例外
self.get_by_id(id, parent=nil)指定したidのエンティティを取得する。エンティティが見つからない場合nil
self.get_by_name!(name, parent=nil)指定したnameのエンティティを取得する。エンティティが見つからない場合EntityNotFound例外
self.get_by_name(name, parent=nil)指定したnameのエンティティを取得する。エンティティが見つからない場合nil
self.get_by_ids(ids, parent=nil)指定したidのエンティティを取得する。idsはidのArray。
self.get_by_names(names, parent=nil)指定したnameのエンティティを取得する。namesはnameのArray
self.queryQueryオブジェクトを返す。詳しくはQueryについてを参照。
self.countこのKindの件数を返す
destroyこのエンティティを削除する
self.destroy(array)指定されたArrayに含まれるエンティティを削除する
self.destroy_allこのKindのすべてのエンティティを削除する
read_onlyこのエンティティを読み取り専用にする
regetDatastoreから再度読み込み直す。トランザクション用らしい?
tx(opts={}, &block)トランザクション内でblockを実行する。
optsには:retritesと:force_beginが指定可能。
:retriesはトランザクションの再試行回数(Datastore.transaction(retries)を参照)
:force_beginはDatastore.current_transaction(nil)で強制的にトランザクションを開始する
attributes=(attrs)属性を設定する
set_property(k,v)プロパティを設定する
get_property(k)プロパティを取得する

2010/03/04

MockDesigner

Starアプリの画面モックを作るアプリをつくってみたので晒してみる。
主に大体の場所と、画面に文字列が入るかの確認用

イメージ

出来ること

  • ペイントちっくに画面を描ける
    • 線を引ける
    • 四角を描ける
    • 文字を描ける
  • 描いたモックを携帯で表示できる

置き場所

作成ツール
ビューワアプリ
一式
ソース

必要なもの

  • Java SDK6
  • iAppli Development Kit for Star 1.0以降

Designer使い方

  1. MockDesinerを展開
  2. java -jar MockDesiner.jar
  3. 適当につくる
  4. ファイルを保存する

Viewer使い方

  1. MockViewerを展開する
  2. Starのエミュレータを起動する
  3. プロジェクトの読み込みでMockViewerを読み込む
  4. プロジェクト→iアプリライブラリでlibのkxml2_2.3.0を読み込む
  5. resフォルダにDesignerで作ったファイルをsample.xmlという名前で放り込む
  6. ビルド
  7. 起動

今後やりたいこと(今できないこと)

  1. 画像を扱えるようにする
  2. テキストの文字サイズを変更
  3. テキストの縦位置調整
  4. 複数選択
  5. Undo
  6. ソースをきれいにする
  7. テスト

2010/02/07

appengine-jrubyとSinatraとRSpec

節操なくいろいろなものを試してみているこの頃。今日はSinatra。

AppEngineでいろいろなものを動かしてみてはいるんだけど、どのフレームワークもAppEngineへの対応するのがまだまだ試行錯誤的な感じでチュートリアルで書いてあることがすぐに陳腐化してしまっている気がする。

特にフルスタックな重量級フレームワークを動かそうとすると、大体がモデルからの自動生成がフレームワークの便利さの主たる部分になっているのでAppEngineを利用するとその旨味を活かしきれない。逆に、普段の開発では気にしないライブラリのロードやスレッド関連がAppEngineの制約にあわず、うまく動かない原因になったり調べるにも追いかけるのが大変だったりとめんどくさい。

AppEngineの勉強という意味でも最初は薄いフレームワークから始めるのがいいかなぁと思う今日この頃です。JDOとLowlevelAPIみたいなもんですね。

どのフレームワークもテストまわりがどうもうまく動かせたことがないのと、RSpec Bookを試してみたかったのでAppEngineでRSpecを動かして、TinyDSのサンプルをテストするのが目標。appengine-jrubyの時点で裏でゴニョゴニョしてて良くわからないのだけど。

環境はUbuntu 9.10。以下必要なもの。括弧内はうちの環境

  • ruby(MRI 1.8.7-p249)
  • rubygems(1.3.5)
  • jruby(1.4.0)
  • AppEngine SDK Java (1.3.0)

以下手順

  1. MRIでappengine-jrubyのgemをいれる。
  2. テンプレートを作る

    appcfg.rb generate_app shout

  3. Gemfileで必要なGemを設定

    # Critical default settings: disable_system_gems disable_rubygems bundle_path ".gems/bundler_gems" # List gems to bundle here: gem "appengine-rack" gem "sinatra" gem "tiny_ds"

  4. アプリを作る。shout.rb

    require 'sinatra' require 'tiny_ds' class Shout < TinyDS::Base property :message, :text property :created_at, :time property :updated_at, :time end helpers do include Rack::Utils alias_method :h, :escape_html end get '/' do @shouts = Shout.query.sort(:created_at, :desc).all erb :index end post '/' do shout = Shout.create(:message => params[:message]) redirect '/' end

    views/index.erb

    <html> <head> <title>Shoutout!</title> </head> <body style="font-family: sans-serif;"> <h1>Shoutout!</h1> <form method=post> <textarea name="message" rows="3"></textarea> <input type=submit value=Shout> </form> <% @shouts.each do |shout| %> <p>Someone wrote, <q><%=h shout.message %></q></p> <% end %> <div style="position: absolute; bottom: 20px; right: 20px;"> <img src="/images/appengine.gif"></div> </body> </html>

  5. config.ruを設定する。

    require 'appengine-rack' AppEngine::Rack.configure_app( :application => "shout", :precompilation_enabled => true, :version => "1") require 'shout' run Sinatra::Application

  6. 動作確認
    以下を実行してブラウザでアクセスしてみる。

    dev_appserver.rb .

  7. Spec用の前準備

    AppEnigneのローカル環境で動かすためには通常appcfg.rb run -S irbの様にappcfg.rbを通じて呼び出すのだけど、AppEngine上でRubyGemsが使えないので、使用するGemはBundlerという仕組みを通してひとつのJarに固められたものを使用するようになる。で、appcfg.rbを通すとRubyGemsが無効化されるようで(たぶんGemfileのdisable_rubygems)、appcfg.rb run -S specとかやってもundefined method `bin_path' for Gem:Moduleとかいわれて動かない。ここで、JRubyから直接動かせと書かれているのでその準備が必要。

    1. jruby側に必要なGemを入れる。
      Gemsファイルに追加したものとappengine-api、rspecが必要。

      jruby -S gem install appengine-api rspec sinatra tiny_ds rack-test

    2. specディレクトリを作る。
      mkdir spec
    3. ローカル環境用スクリプトを作る。
      基本はGAE/Jと同じくローカル環境が必要。ここを参照させていただき、最近のGAEに合わせてgetAttributesを追加。
      spec/local_gae.rb

      class LocalGAE require "java" import "com.google.apphosting.api.ApiProxy" import "com.google.appengine.tools.development.ApiProxyLocalImpl" import "com.google.appengine.api.datastore.dev.LocalDatastoreService" class GaeEnvironment include ApiProxy::Environment def getAppId; "JRubyLocal" end def getVersionId; "1.0" end def setDefaultNamespace(s); end def getRequestNamespace; nil end def getDefaultNamespace; nil end def getAuthDomain; nil end def isLoggedIn; false end def getEmail; nil end def isAdmin; false end def getAttributes; {} end end class EmptyApiProxyLocalImpl < ApiProxyLocalImpl end def self.setup(profile_dir=nil) profile_dir ||= "./_tmp_gae_profile" #puts "setup" ApiProxy.setEnvironmentForCurrentThread(GaeEnvironment.new) localImpl = EmptyApiProxyLocalImpl.new( java.io.File.new(profile_dir) ) ApiProxy.setDelegate(localImpl) proxy = ApiProxy.getDelegate proxy.setProperty(LocalDatastoreService::NO_STORAGE_PROPERTY, "true") end def self.teardown proxy = ApiProxy.getDelegate datastoreService = proxy.getService("datastore_v3") datastoreService.clearProfiles ApiProxy.setDelegate(nil) ApiProxy.setEnvironmentForCurrentThread(nil) #puts "teardown" end def self.run(profile_dir=nil) setup(profile_dir) yield(ApiProxy.getDelegate) teardown end end if __FILE__ == $0 LocalGAE.run do |proxy| p proxy end exit end

  8. specを作る。spec/shout_spec.rb

    #-*- coding: utf-8 -*- require 'local_gae' require 'shout' require 'spec' require 'rack/test' set :environment, :test describe 'The Shout App' do include Rack::Test::Methods def app Sinatra::Application end before do LocalGAE.setup end it "最初はShoutout!と表示されメッセージが登録されていないこと" do get '/' last_response.should be_ok last_response.body.should =~ /Shoutout!/ last_response.body.should_not =~ /Someone/ end it "Helloとポストするとそのメッセージが表示されること" do post '/', {:message => "Hello"} follow_redirect! last_response.should be_ok last_response.body.should =~ /Hello/ end after do LocalGAE.teardown end end

  9. 環境変数の設定。
    google-appengineのjarにパスが通っていないと動かないのでCLASSPATHを設定する。

    export CLASSPATH=$GAEJ/lib/impl/appengine-api-stubs.jar:$GAEJ/lib/impl/appengine-local-runtime.jar:$GAEJ/lib/impl/appengine-api.jar:$GAEJ/lib/impl/appengine-api-labs.jar

  10. 実行

    jruby -S spec spec/shout_spec.rb

うちの環境だとなぜかspec動かした後プロンプトに戻ってこないのでCtrl-Cで強制終了する必要があるのが残念。

2010/01/30

appengine-jrubyでRails2.3.5をWindowsで動かす

GAEでRailsを動かすRails 2.3.5 on App Engine (TinyDS)をWindowsでやってみた。

rails2_td_appengine.rbがcurlやらtailやら*nixのツール前提なのでRubyだけで書き直してみた。適当な直し。次のクローンはもっとうまくやってくれるでしょう。

#!/usr/bin/ruby # # Copyright:: Copyright 2009 Google Inc. # Original Author:: John Woodell (mailto:woodie@google.com) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'fileutils' require 'open-uri' def download(url, dest_file_name) open(url) do |src| open(dest_file_name, "wb") { |dest| dest.write(src.read) } end end FILE_BASE = 'http://appengine-jruby.googlecode.com/hg/demos/rails2/' MOD_FILES = %w{ app/controllers/rails/info_controller.rb config/boot_rb config/environment_td config/gae_boot_patch.rb config/initializers/gae_init_patch.rb config/database.yml config/initializers/session_store_rb config.ru } # Install Rails 2.3.5 FileUtils.touch 'config.ru' download("#{FILE_BASE}Gemfile_td", "Gemfile") system 'appcfg.rb.bat bundle --update .' # Remove dups and generate Rails app #FileUtils.rm 'public/robots.txt' system 'appcfg.rb.bat run -rthread bin/rails .' # Fetch configuration files FileUtils.mkdir_p 'app/controllers/rails' MOD_FILES.each do |path| download("#{FILE_BASE}#{path}", "#{path}") end # Merge configs into boot.rb open("boot", "wb") do |dest| open("config/boot.rb") do |src| dest.write(src.readlines[0,108]) end open("config/boot_rb") do |src| dest.write(src.read) end open("config/boot.rb") do |src| dest.write(src.readlines[-3,3]) end end #system 'head -n 108 config/boot.rb > boot' #system 'cat config/boot_rb >> boot' #system 'tail -n 3 config/boot.rb >> boot' FileUtils.rm 'config/boot_rb' FileUtils.mv 'boot', 'config/boot.rb' # Merge configs into environment.rb open("conf", "wb") do |dest| open("config/environment.rb") do |src| dest.write(src.readlines[0,30]) end open("config/environment_td") do |src| dest.write(src.read) end open("config/environment.rb") do |src| dest.write(src.readlines[-12, 12]) end end #system 'head -n 30 config/environment.rb > conf' #system 'cat config/environment_td >> conf' #system 'tail -n 12 config/environment.rb >> conf ' FileUtils.rm 'config/environment_td' FileUtils.mv 'conf', 'config/environment.rb' # Merge session_store initializer open("config/initializers/session_store.rb", "ab") do |dest| open("config/initializers/session_store_rb") do |src| dest.write(src.read) end end #system 'cat config/initializers/session_store_rb' + # ' >> config/initializers/session_store.rb' FileUtils.rm 'config/initializers/session_store_rb' # install the nulldb adapter system 'ruby script/plugin install http://svn.avdi.org/nulldb/trunk/' puts "##" puts "## Now type 'dev_appserver.rb .'" puts "##"

とりあえずプロジェクトが作れるところまでやった。続きの手順はまた今度やってみる。