FlaskでLESSをあつかう方法を検討した結果watchdogかgrunt.jsでいいことになった

Flask+Twitter Bootstrapで作ることが非常に増えている。LESSを弄りたいときに

<link rel="stylesheet/less" href="less/style.less">
<script src="js/libs/less-1.2.1.min.js"></script>

とかやればいいんだけど、IEだとレンダリングがやたらと遅くなるので本番環境ではcssにコンパイルすることになるため、この部分を書きなおすのは面倒くさい。

だったら開発環境でも最初から更新時にCSSコンパイルするとか裏でよろしくやってくれる方法を導入したいなぁということで調べてみた。

これらは拡張子がcssのurlにアクセスした時にlessファイルを探してcssファイルが存在しなかったり、lessが変更されている(更新日時とかハッシュを見ている)ときにコンパイルする用になっている。before_requestのところで処理している。

難点は、cssとlessが同じディレクトリにあることを決め打ちしているところかなぁ。僕の場合bootstrapは別に管理しているのでコンパイル先を自分で決められないのは困る。

もう少し統一的に管理できそなうなものにFlask-Funnelというものがあったが、ちょっと面倒くさそう。これだったらGruntとかYeomanみたいなnode.js製の管理ツールでもいいかなぁと。

結論

Initializrのbootstrapだったら変更検知してstyle.lessをコンパイルすればいいのでwatchdog使えばいいかなぁ。grunt.jsでもいいや。

Githubのtwitter bootstrapだったらMakefileにwatchがついているのでそれを使ってもいいなぁと。ちなみにカスタマイズはここらへんを参考にしている。

FlaskのViewのメタプログラミングの例がわかりやすかった。

MethodViewのコードを読んでたら、例としてわかりやすかったのでメモ。どういう動きをするのかはあらかじめ「Pythonのメタプログラミング (メタクラス) を理解したい人のための短いコード片と禅問答」を読んでおくと良いと思う。

class MethodViewType(type):
    def __new__(cls, name, bases, d):
        rv = type.__new__(cls, name, bases, d)
        if 'methods' not in d:
            methods = set(rv.methods or [])
            for key in d:
                if key in http_method_funcs:
                    methods.add(key.upper())
            if methods:
                rv.methods = sorted(methods)
        return rv

class MethodView(View):
    __metaclass__ = MethodViewType

    def dispatch_request(self, *args, **kwargs):
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method
        return meth(*args, **kwargs)

clap your hands say d'n'b

この前の。

Blind Jealousy / Unquote
Austere / Escher
The Moment (feat. DRS) / Enei
Shimizu / June Miller feat Rolling Maffia
Elephants / Enei
Letting go / Linden
Lubov Moya (Blu Mar Ten Remix) / Unquote & Molecular Structures
Forgotten Roads / Fracture & Neptune
Driftwood (Feat. Noisia) / Icicle
Thought Reform / Mark System
Reflection / Subwave
Midnight Nation / Optiv
Quartz Controlled / June Miller & Mindmapper
It Aint The Weather / Seba
Espionage / Future Signal
RoMZ08 / サイケアウツ
Chasing Dreams / Mark System
Sound of the Rain / Royalston

ProductName MEDICAL MIX (MIXED BY JOE SYNTAX)
VARIOUS ARTISTS
OCTAVE / Med School Music / 1280円 ( 2012-11-07 )


スクラムとは何か(アジャイル開発)

僕の場合アジャイル開発という大雑把な括りで理解しているので、具体的な開発手法名はなかなか覚えにくい、というよりそれすらちょっと抽象的な表現なので分かりにくいと言ったほうが適切か。

スクラムとは、会社を機能単位に分割した階層や組織ではなく、どこをとっても会社のビジョンに向かった判断・行動パターンを共有する自己相似の知識創造活動であり、それを実践する人びとである

本書を読みきれば、上の定義があーなるほどと理解できるようになる。あとは、自分のフィールドに翻訳する必要があるのだが、カルチャーを変革する系の手法なので、ボトムアップな取り組みだけではうまくいかないだろうし、上の理解が必須かなぁと思った。なのでマネジメント層に読んでもらいたいなぁと。

  • アジャイルは顧客満足を実現する手段であり可能性のひとつ
  • アジャイル開発では知識の外部への伝播が語られていない(これは欠点)
  • PDCAのPの前にはSECIのSを置くべき

スクラムを実践するには組織を変えていく情熱が必要である

これがなー、厳しい。リーンスタートアップみたいなやり方は時間がかかるしなぁ。

第三部の「アジャイルと開発とスクラムを考える」の野中郁次郎先生の考察が非常に深かった。アジャイルでは知識の外部への伝達が弱いのは技術者の流動性が高すぎるからだと考察している。流動性がそれほど高くない僕のいる業界なんかでは暗黙知の共有とか形式知化なんかは非常に重要なことなので、本書のオリジナルの論文(日本の製造業)と対比しながらの考察は理解が非常に深まった。

以下、製薬業界用メモ。

ユーザーストーリーのフォーマット

  • <ユーザーの役割>として
  • <機能>が欲しい
  • なぜなら<機能の価値や目的>だからだ

動態担当として、logPを下げてほしい、なぜなら膜透過性が改善するからだ

みたいな、なぜ今そういう化合物を作っているのか?というストーリーを張っておかないと、振り返った時に個々の化合物が合成された背景を掴めないし、そのやり方がうまくいきそうかどうなのかが外部から判断できない。

ケミストの「うまくいったから、結果としてうまくいったんだ」というようなくだらないトートロジーに付き合わされるのはかんべんしてほしい、というのはみんなの願いであろう(ケミストそれ自身の願いでもある気がするw)。

プロダクトバックログ

プロダクトバックログとは、実現するかもしれない未来の詰め合わせである。創薬的には合成してもいいかなぁと考えている案のリストと考えることができる。リードオプティマイゼーションにおいてはそういうアイデアリストをプロジェクトごとにまとめて管理して確度の高そうな案から作っていくというやり方がプロジェクト的に理想的かなと思っているんだが5年くらい前からトライしているんだけどうまくいっていない。色々理由はあると思うんだけど知財的なことを考えると誰が作ったかが重要視されるのでまぁしょうがないのかなぁと。

とか考えるとUK-QSARでみかけたAZのスライドはまさにバックログを実装していてすごいなぁと。

Flaskでセッションが入れ替わるというバグに悩まされた

13.02.27

バグの発生箇所を掴んだ。OpenIDサーバーだった。開発環境ではエラーが出なくて本番環境でのみ出現するのでサーバーを疑っていたのだけど違った。

本番環境でデバッグログ出したら

Error attempting to use stored discovery information: Claimed ID does not match (different subjects!)
Expected <wrong-openid> got <right-openid>
Attempting discovery to verify endopoint
Performing discovery on <wrong-openid>

というメッセージが出ていた。

OpenIDサーバーから戻ってきた所でおかしくなっているようだ。referrer出力するようにしたらOpenIDサーバーが認証して戻すところで入れ替わっていた。

なんでそうなるのかワケがわからないが、OpenIDサーバーを再起動したらとりあえずは治った。ploneでも似たような症状を見つけたが、Flaskのせいではないということが分かったので一安心。

仕様をきちんと押さえておくか、本を読んでおくかしておいたほうがいいけど、とりあえずはいいや。

ProductName OpenID: the Definitive Guide
David Recordon
Oreilly & Associates Inc / ?円 ( 2012-04-30 )


13.02.26

初回のログイン時じゃなくて、クッキーがエクスパイアしたあとにアクセスしてもセッションの入れ替わりが起こるみたいだ。スレッドかなぁと思ったけど、帰りの車のなかでよく考えてみたらやっぱ違うよなぁと思った。

セッションのデータがサーバー側に残っていてクッキーが送られないときにそのデータが使われているんじゃないかなぁ。

sessionをよく読んでみよう。

13.02.25

なおってなかった。

open-idサーバーで認証するところまではちゃんとユーザーが認識されているんだけど、アプリケーションにリダイレクトされたところで、ユーザーのセッションが違うユーザーのものに入れ替わってしまうというパターン。

  • バージョン0.8でも0.9でも起きる
  • 開発環境では起きないが、本番環境(Gunicorn)で起きる
  • IE8でのみ確認されている

多分ブラウザは関係ないと思う。

@app.before_requestにセッションが残っているっぽいので@oid.loginhandlerにアクセスする時に@app.before_requestのユーザーをチェックする処理をとばしてログイン処理をすればいいような気がしてきたのであとでやってみる。


Flask-OpenIDでログインするとなぜか初回に別アカウントになってしまい、一度ログアウトしてから再ログインすると正常にログインできるというよくわからないバグが頻発していて悩んでいた。しかもdevelopmentでは全く再現できず、production(Flask+Gunicorn+Supervisord)でしか起こらないので厄介だった。

Flaskのバージョンを0.8から0.9にあげてみたけどなおらなかったので、調べたら既に報告されていた。

最初のスレッドは、「ユーザー付きあわせて違っていたらセッション破棄するぜ」みたいな解決方法だったのでちょっと使えなかったが、二番目のスレッドでsessionインターフェースを自前で用意したらなおったという報告があったので試してみた。

Flask-OpenIDでこのスニペットをそのまま利用するとopenid.yadis.manager.YadisServiceManagerがJSONシリアライズできないというエラーが出た。これはpython-openidに由来する問題らしく、コメントで指摘されているようにpickleでdumpすればsave_sessionはできるようになる。

しかし、今度はopen_sessionでコケる。managerがstrだ云々っていうエラーなのでpickle.loadsがうまくいってないようだ。Flask-OpenIDのソース読むとSessionWrapperで' p'っていうkeyのDictionaryを作っているので、Sessionのほうもそのようにしてみた。

Better Client-side sessionsをちょっと変えたらうまくいった。

from werkzeug.datastructures import CallbackDict
from flask.sessions import SessionInterface, SessionMixin
from itsdangerous import URLSafeTimedSerializer, BadSignature
import pickle

class ItsdangerousSession(CallbackDict, SessionMixin):

    def __init__(self, initial=None):
        def on_update(self):
            self.modified = True
        CallbackDict.__init__(self, initial, on_update)
        self.modified = False

class ItsdangerousSessionInterface(SessionInterface):
    salt = 'cookie-session'
    session_class = ItsdangerousSession

    def get_serializer(self, app):
        if not app.secret_key:
            return None
        return URLSafeTimedSerializer(app.secret_key,
                                      salt=self.salt)

    def open_session(self, app, request):
        s = self.get_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            return self.session_class()
        max_age = app.permanent_session_lifetime.total_seconds()
        try:
            data = s.loads(val, max_age=max_age)
            data = data[' p']
            for k in data:
                if k.startswith('yoc') or k.startswith('lt')\
                                       or k.startswith('_openid_consumer_last_token')\
                                       or k.startswith('_yadis_services__openid_consumer_'):
                    data[k] = pickle.loads(data[k])
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        if not session:
            if session.modified:
                response.delete_cookie(app.session_cookie_name,
                                   domain=domain)
            return
        expires = self.get_expiration_time(app, session)
        d = {}
        for k in session:
            if k.startswith('yoc') or k.startswith('lt')\
                                   or k.startswith('_openid_consumer_last_token')\
                                   or k.startswith('_yadis_services__openid_consumer_'):
                d[k] = pickle.dumps(session[k])
            else:
                d[k] = session[k]
        val = self.get_serializer(app).dumps({' p': d})
        response.set_cookie(app.session_cookie_name, val,
                            expires=expires, httponly=True,
                            domain=domain)

ベトナムの経済

ベトナムの経済

  • ドンの信用は低い
  • 鋼鉄業は未熟
  • トラディショナルトレードが高くモダントレードは低い
  • 農村出身者でビジネスマナーに乏しい

ProductName ベトナム経済の基礎知識
守部 裕行
ジェトロ / 2100円 ( 2012-12 )


Nodeクックブック

一応クックブックという体裁をとっているんだけど、ストーリー性があるし、細かい点はミニレシピという形で記述されているので、ノードでサーバー立ててプロダクションまでもっていくというような内容になっている。もちろんクックブックなのでNodeの基礎的な事柄は知っているという前提。

1361792675

本書は、Node.jsを使っての簡単なWebサーバの構築にはじまり、Webアプリケーションやコマンドラインアプリケーションの構築、そして自作Nodeモジュールの開発までをスムーズに学習できるように構成しています。 MySQLやMongoDB、CouchDB、Redisといった各種データベースとのインタフェイスや、WebSocketの組み込み方、SMTPなどのプロトコルとのインタフェイスといった基本的な事項を学びます。 さらにデータストリームの処理方法やセキュリティを考慮した実装、そして開発したアプリケーションを本番環境で動作させるまでのさまざまなレシピを掲載しています。

Expressでウェブサービスを作りたいのであれば買いだと思う。Node.jsだけでウェブサーバーを実装する方法(1,2章)は丁寧に解説されていてわかり易かったし、4章のデータベース接続もMySQL,MongoDB,Couch,Redisの説明がひと通りされているし、5章ではwebsocketも軽く触れられいてる(今は開発の止まっているnowjsも)。

やはり6,7章のExpressとセキュリティの話題が一番おもしろくて、どういったあたりに気を使えばいいのかが丁寧に解説されていて良かった。

いまメインで使っているFlaskでなぜかセッションが入れ替わるという問題を解決できなくて悩んでいるので、次にサービス作るんだったら Expressでもいいかなぁと思い始めている。YesodとかSnapという選択肢もあるんだけど、Stylus+nibとかSass+Compassみたいなフレームワーク使いたいので悩んでいる。

LESSというよりbootstrapは乗っかっている時にはスイスイ進むんだけど流儀から外れることをやろうとすると途端に牙をむく感じだしなぁ。CSSとかHTMLのフレームワークはRoRみたいなWAFと違ってすぐに手を入れたくなるだろうから、あんまりフルスタックすぎるのもどうなのかなぁと思ったりもする。いじるのが前提でミニマルで拡張しやすいほうがいいんじゃないかなぁと。

ProductName Nodeクックブック
David Mark Clements
オライリージャパン / 3570円 ( 2013-02-23 )


ちなみに9章のテストの話はいまいちだった。今読んでいるtestable javascriptのほうが楽しい。

ProductName Testable JavaScript
Mark Ethan Trostler
Oreilly & Associates Inc / 2640円 ( 2013-01-31 )


最近覚えたSQLAlchemyのTips

先週は、レガシーなOracleデータベースと格闘していた(文字エンコーディングの件もその一つ)。

__table__と__tablename__の違い

__table__は適切に設定済みのテーブルに対して使うらしい。自分でごちゃごちゃと設定したい場合は__tablename__と__table_args__を使うべし

複合プライマリーキーでのone-to-many

主キーが複合プライマリーキーの場合はそれぞれにprimary_key=Trueをつけるが、外部キーで一対多の関連付けをしたい場合には __table_args__で外部キー設定をしておく。

class Protocol(Base):
    __tablename__ = 'tblprotocol'
    id = Column("id", String(32), primary_key=True)
    version = Column("version", String(11), primary_key=True)
    name = Column("name", String(255))

class Experiment(Base):
    __tablename__ = 'tblexperiments'
    __table_args__ = (ForeignKeyConstraint(['protocolid','protocolversion'],
                                      ['tblprotocol.id', 'tblprotocol.version']), {})
    id = Column("id", String(32), primary_key=True)
    protocolid = Column("protocolid", String(32))
    protocolversion = Column("protocolversion", String(11))
    name = Column("name", String(255))
    protocol = relationship("Protocol", backref="experiments")

そもそも主キーの無いテーブル

主キーの無いテーブルはエラーが出るので、適当に複合キーを設定してプライマリーキーにしてしまう。

その他

SQLもORマッパーもわかっていればバランスの取れた設計になっていいのではなかろうかと思った。ORマッパー側から考えたほうがER図が綺麗になりそうだし。

ProductName 達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)
ミック
翔泳社 / 2520円 ( 2008-02-07 )


経済指標の簡単な説明

鉱工業生産指数って今でもモノづくりの実態をあらわしているのかな?

その他の指標

  • 毎月勤労統計調査
  • 企業物価指数と消費者物価

Persistentでキーから検索する方法

昨日の三島Haskell無名関数の会(mishima.hs)で「PersistentのKeyってどうやって作るの?」っていう話になって、宿題として持ち帰ったので朝から調べていた。

Keyは

type Key val = KeyBackend (PersistEntityBackend val) val

で、KeyBackendのデータコンストラクタがKeyなので、PersistValueのデータコンストラクタで包めばいいみたい。

ひとつレコードを作って、キーを指定して年齢を変更した後、キーでレコードを取ってきて年齢が変わっていることを確認するサンプル。SQLitのキーがInt64なのでこのサンプルではPersistInt64データコンストラクタを使っている。

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)
import Database.Persist.Store

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String default="Japan"
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    kzfmId <- insert $ Person "kzfm" (Just 20) "Fuji"
    liftIO $ print kzfmId
    kzfm <- getJust kzfmId
    liftIO $ print kzfm
    kzfm2 <- replace (Key (PersistInt64 1)) (kzfm {personAge = Just 36})
    liftIO $ print kzfm2
    kzfm3 <- getJust (Key (PersistInt64 1))
    liftIO $ print (personAge kzfm3)

どういうメソッドがあるのかはDatabase.Persist の関数群を簡単に説明が参考になる。