FlaskではじめるTDD

今までTDDをやったことなかった。これからはちゃんと保守可能なコードを書いていきたいという 気持ちが生まれたのでテストコードを書くことに決めた。とりあえず単体テストから始めていく。

実装

import json

@app.route("/api/users/hello")
def hello():
    d = {
        "name":"hello"
    }
    return Response(json.dumps(d),mimetype='application/json')

テスト

import ast
from flask import Flask
import json
import unittest

class ReqTestCase(unittest.TestCase):

    def setUp(self):
        self.app = Flask(__name__)
        self.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/app.db"
        self.db = SQLAlchemy(self.app)


    def test_hello(self):
       """
      /api/users/helloにGETリクエストを投げたら、helloが返ってくる。
       """
        with self.app.test_client() as client:
            res = client.get("/api/users/hello")
            res.headers['Content-Type'] = 'application/json'
            data_str = res.data.decode("utf-8")
            data_dict = ast.literal_eval(data)
            self.assertEqual("hello",data_dict.get("name"))


 if __name__ == "__main__":
        unittest.main()

今までリクエスト周りのテストは書いてなかったけど、 こうやってリクエスト周りのテストを書くと純粋なデータのやり取りの実装から進めていくことができて変更に対応しやすいコードが書けそうな感じがある。

ビューとデータの密接な結合が生じているとテストコードを書きにくいことを改めて実感するいい機会になった。

Fabricを使って、ローカル側からリモート側のプロジェクト側にてgit pullを実行させる。

今まで、このブログは以下の方法で運用していた。

  • ローカルでmarkdownファイルを作る。
  • bitbucketのほうにpushする。
  • リモートサーバーにログインして、git pullを実行。

でもこの方法だと、毎回サーバーにログインする必要が出てきて面倒。 そこでPythonのFabricというライブラリを使って

fab push

を行うだけでリモート側で git pull をしてくれるコードを書いた。

プロジェクトのルートディレクトリに以下のfabfile.pyを置くだけ。

#coding: utf-8
from fabric.api import run,env,local,settings
from fabric.operations import sudo
from fabric.context_managers import cd
import os

def try_cmd(host_type,cmd):
    run_cmd = host_type(cmd)

def host_type():
    run("uname -s")

from fabric.api import env, run
env.use_ssh_config = True

env.user = "your name"
env.hosts = ["your server's ip address"]

def push():
    with settings():

        #ローカル側の操作

        try_cmd("git commit -am 'update'")
        try_cmd(local,"git push origin master")

        #リモート側の操作

        with cd("/your/project's/path"):
            try_cmd(sudo,"git pull origin master")

これで幸せの実現ができた。

s3cmdで快適にs3にアップロードする

例えばtmpフォルダーの中に入っているpngファイルをhogehogeというバケットに保存したいときはターミナルに

s3cmd put  tmp/img/*.png s3://hogehoge

とタイプすれば、すべての画像をuploadできる。

上の方法だと、httpによるアクセスが許可されていないので、許可させるには

s3cmd put --acl-public tmp/img/*.png s3://hogehoge

とすればよい。

UILocalPushNotificationの使い方

定期的に通知をiPhoneにPush通知を送信する方法を調べていたんだけど サーバーを介さなくてもUILocalNotification を使用するとできた。下のサンプルコードだと、毎日23:00にPush通知を送信する例。

 NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *now = [NSDate date];
    NSDateComponents *componentsForFireDate = [calendar components:(NSYearCalendarUnit | NSWeekCalendarUnit |  NSHourCalendarUnit | NSMinuteCalendarUnit| NSSecondCalendarUnit | NSWeekdayCalendarUnit) fromDate: now];
    // 時間帯を固定
    [componentsForFireDate setHour: 23] ; 
    [componentsForFireDate setMinute:0] ;
    [componentsForFireDate setSecond:0] ;
    [componentsForFireDate setTimeZone:[NSTimeZone defaultTimeZone]];
    UILocalNotification* notifyAlarm = [[UILocalNotification alloc]
                                        init];
    // 一日ごとに通知を送る
    notifyAlarm.repeatInterval = NSDayCalendarUnit;

    notifyAlarm.fireDate = [calendar dateFromComponents:componentsForFireDate];
    NSLog(@"fireDate is %@",notifyAlarm.fireDate);
    notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
    notifyAlarm.alertBody =  @"hello";

    //UILocalNotificationを実行する
    [[UIApplication sharedApplication] scheduleLocalNotification:notifyAlarm];

便利ですね。

Flaskにてrequest.methodでPUTの判定をできるようにする

ハマったのでメモ。

PUTメソッドを使うにはサーバーサイドの方で、

from werkzeug.wrappers import Request
from werkzeug.wsgi import get_input_stream
from werkzeug.formparser import parse_form_data
from io import BytesIO

class MethodRewriteMiddleware(object):
    def __init__(self, app, input_name='_method'):
        self.app = app
    def __call__(self, environ, start_response):
        if environ["REQUEST_METHOD"].upper() == "POST":
            environ["wsgi.input"] = stream = \
                BytesIO(get_input_stream(environ).read())
            formdata = parse_form_data(environ)[1]
            stream.seek(0)
            method = formdata.get("_method","").upper()
            if method in ("GET","POST","PUT","DELETE"):
                environ["REQUEST_METHOD"] = method
        return self.app(environ,start_response)
....

app = Flask(__name__)
app.config.from_object(config)
app.wsgi_app = MethodRewriteMiddleware(app.wsgi_app)

みたいにしてView側では

    <form method="POST" action="/hoge">
    <input type="hidden" name="_method" value="PUT"/>
    <h1><input name="title" type="text" value="" size="100"></input></h1>
    </form>

のような感じで呼ぶとrequest.methodにPUTの値が入るようになる。

@app.route("/hoge")
def hoge():
    if request.method == "PUT":
        return "called put method."

これで綺麗にリクエスト処理を書けるようになった。Flaskは楽しい!