スキップしてメイン コンテンツに移動

第20回北海道開発オフに参加してきた

2011/1/15に第20回北海道開発オフが開催されたので参加してきた。

今回は第20回記念ということで共同開発をやってみようということに。Ruby on RailsチームとGoogle App Engine(Python or Java)にわかれて共通の使用のWebアプリケーションを作ることに。僕はGoogle App Engineチームに(半ば強制的に)。

Webアプリの仕様は

  1. ユーザーのログインができること
  2. リンクでキャラクター(冒険者)のパラメータが変化すること

メンバーで話し合って結局Pythonで行くことに。@shuji_w6eさんを中心に最初はGoogle App EngineでのHello Worldから簡単にモデルとテンプレートを作成して、webappフレームワークを使用したアプリを作成。午後からは目標となるアプリケーションの作成を進めた。途中リファクタリングなども盛り込んでくれて結構勉強になった。

仕様の一つであるユーザーのログインはGoogle App EngineのGoogleアカウント認証を利用することに。app.yamlに以下のようにloginの設定を記述するのが簡単。

- url: /.*
  static: boukensya_main.py
  login: required

アプリケーション作成は上記にあるboukensya_main.pyに処理を記述することとなる。

URLパターンによって処理を分けるのに、以下のようにリストを作成してお決まりのメソッドに渡せば分岐される。

application = webapp.WSGIApplication([
    ('/home', HomePage),
    ('/action/(.*)', ActionPage),
    ('/.*', IndexPage),
], debug=True)

URLのパターンは正規表現で定義できる。IndexPageやMainPageは、後述する分岐された各処理の内容が記述されるクラス

BasePegeクラスはテンプレートファイル名とデータを受け取ってお決まりのレンダリングメソッドを呼ぶメソッドを用意。IndexPageやMainPageクラスでBasePageを継承し、テンプレートファイル名を渡す。

class BasePage(webapp.RequestHandler):
  def handle(self, templateName='index.html', data=None):
    path = os.path.join(os.path.dirname(__file__), 'template', templateName)
    self.response.out.write(template.render(path, data))

class IndexPage(BasePage):
  def get(self):
    self.handle()

class HomePage(BasePage):
  def get(self):
    user = users.get_current_user()
    key_name = user.user_id() + '_0'
    chara = Character.get_by_key_name(key_name)
    if chara == None:
      chara = Character(key_name=key_name, usr=user, name='default name')
      chara.put()
    self.handle('home.html', {'user': user, 'chara': chara})

class ActionPage(BasePage):
  def get(self, action_id):
    user = users.get_current_user()
    key_name = user.user_id() + '_0'
    chara = Character.get_by_key_name(key_name)
    if action_id == '0':
      chara.str += 1

冒険者モデルとしてCharacterクラスを定義、各パラメータ(体力・知力など)をプロパティとする。

class Character(db.Model):
  usr = db.UserProperty(required=True)
  name = db.StringProperty(required=True)
  sta = db.IntegerProperty(default=10)
  str = db.IntegerProperty(default=10)
  dex = db.IntegerProperty(default=10)
  agi = db.IntegerProperty(default=10)
  int = db.IntegerProperty(default=10)
  chr = db.IntegerProperty(default=10)
  arc = db.IntegerProperty(default=0)

  def __str__(self):
    return self.name

ActionPageではアクションIDによってパラメータが変化する処理を定義する。アクションIDは簡易的に~/action/n のURLでnの値によって変化するパラメータが変わるようにした。
実際にパラメータを変化させるのはモデル側に実装した方が良かったかもしれない。

あとはindex.htmlやhome.htmlをテンプレートとして用意すれば一応このアプリケーションは動作する。テンプレートでは{{}}で渡されるデータをあらわす。以下はhome.htmlのCharacterクラスのstrプロパティを表示しているところ。HomePageクラスでのself.handleメソッドで'chara'という名前でCharacterオブジェクトを渡しているのでテンプレートでは{{chara}}でその値を扱うことができる

<tr><th>筋力</th><td>{{chara.str}}</td></tr>

ローカル環境で動作を見てみる。今まで作成したファイルがboukensyamakerフォルダにあるとした場合、以下コマンドで開発用サーバーが起動、ブラウザでhttp://localhost:8080/ にアクセスする。

> dev_appserver.py boukensyamaker

※GoogleAppEngineSDKをインストールしたディレクトリにパスが通っていること。インストール時にチェックしていれば通っているはず

正常に動作することが確認できたら早速サーバーにアップロードしてみる。

> appcfg.py update boukensyamaker

上記コマンドでデプロイができたらもうAppEngine上で動作する。僕は以下にデプロイした。ちょっとお遊びで「ビールを飲む」アクションも追加している。ただし飲みすぎ注意。

http://boukensyamaker.appspot.com/

コメント

コメントを投稿

このブログの人気の投稿

VirtualBoxのUbuntu 18.04でNAT + ホストオンリーアダプター

VirtualBoxのVMにインストールしたUbuntu Server 18.04で、ネットワーク割当を「NAT」+「ホストオンリーアダプター」にする場合の設定についてメモ。 VMを作るとき、ゲストOSからインターネットに繋がって、かつホスト-ゲスト間でもつながる環境にしたいとき、VMのネットワーク割当は手っ取り早いのは「ブリッジアダプター」なんだけど、会社のネットワークでは都合がわるかったりするので「NAT」+「ホストオンリーアダプター」にしている。 アダプター1は「NAT」を選択 アダプター2は「ホストオンリーアダプター」を選択 ゲストOSであるUbuntuのネットワーク設定は、NATに該当するNICはDHPCで、ホストオンリーアダプターに該当するNICは固定IPとする。 Ubuntu 18.04ではネットワーク設定はNetplanで行う(Ubuntu 17.10から変わったようだ)。編集する設定ファイルは「/etc/netplan/50-cloud-init.yaml」 network: ethernets: {NATのNIC}: addresses: [] dhcp4: true optional: true {ホストオンリーアダプターのNIC}: addresses: [{固定IPアドレス}] dhcp4: no version: 2 ポイントは、ホストオンリーアダプターに該当するNICの固定アドレスを設定するときに、 ゲートウェイを設定しない こと。 以前に QiitaのVirtualBoxでNAT + Host-Only Network環境を構築する を参考にしていたのに、今回18.04で設定する際にすっかり忘れてはまってしまった。 IPアドレスは「192.168.0.2/24」のようにサブネットマスクも指定する。 NICに指定するデバイス名は以下コマンドで表示されるlogical nameが該当する。 > lshw -class network たいていは「enp0s3」「enp0s8」となるようだ。 設定ファイルを保存したら...

画像を切り取って保存する

画像処理において、画像の一部を切り出すことをトリミングというらしい。画像編集ソフトなんかにも大抵「トリミング」機能が実装されているだろう。 そんな画像を切り取る処理を.NETでやってみる。 .NETでは画像を扱う際にはBitmapクラスを使用する。BitmapクラスのCloneメソッドでサイズを指定すれば、そのサイズのBitmapオブジェクトができるので、それを保存すればよい。Cloneメソッドの引数はRectangle構造体とPixelFormat列挙体。 Rectangle構造体とは四角形の位置とサイズを表したもの。元画像の左上を(0, 0)として(X座標, Y座標, 幅, 高さ)の値でRectangle構造体を作成して渡す。PixelFormat列挙体はヘルプを見てもよくわからなかった。とりあえずは元画像の値をそのまま渡す。 例えば photo by tsukacyi のようなユッケの画像をトリミングするプログラムは以下のように書ける '元画像 Dim source As Bitmap source = New Bitmap("source.jpg") '切り取るサイズ Dim rect As Rectangle rect = New Rectangle(30, 80, 400, 320) '切り取り後の画像 Dim trimed As Bitmap trimed = source.Clone(rect, source.PixelFormat) '保存 trimed.Save("trimed.jpg") source.Dispose() trimed.Dispose() トリミング後の画像は以下の通り。

Microsoft.VisualBasic.dllを参照しない(その1)

VB.NETには、VB6に実装されていたCIntなどの型変換関数やLeft、Rightなどの文字列関数が使用できる。これらはMicrosoft.VisualBasic名前空間で定義されている。 個人的にはこれらはVB6との互換性の為に用意されたものと考えていて、あまり使用しないようにしている。名前付けのガイドラインにも沿っていないため、他の.NETなメソッドと並ぶと違和感があるというのもある。 注意:このエントリはVisual Basic 2005 Express Editionと.NET Framework2.0 SDK環境を元に書いています。 そんなわけでチーム開発の場合に、これら関数を使用しないよう、Microdoft.VisualBasic.dllを参照しないプロジェクトを作成しようと考えた。 Visual Studioのプロジェクトのプロパティから「参照」タブを開いてみると、すでに参照しているdllにMicrosoft.VisualBasic.dllが含まれていない。  下の一覧にImportされる名前空間が表示されているが、そこには含まれている。 Importされる名前空間からチェックをはずし以下のソースを書いてみる Public Class VisualBasicTest Public Shared Sub Main() Dim i As Integer = CInt("1991") System.Console.WriteLine(i) End Sub End Class だが、ビルドは通って実行もできてしまう。 逆アセンブリしてみる。 スタートメニューから「Microsoft .NET Framework SDK v2.0」→「Tools」→「MSIL 逆アセンブラ」を起動して、ビルドでできているexeを開く。すると、MANIFESTの部分を見てみると下記記述が見つかる。参照しているようだ。 .assembly extern Microsoft.VisualBasic { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) .ver 8:0:0:0 } きっとVisual Studioが...