2010-09-05

GAE アプリによるスタイルシートの切り替え

簡単そうだと思ったのでやってみた。実際、簡単だった。要した時間のほとんどは Python の文法を調べることだった。

今回の切り分けでは、以下のようにリクエスト URL に応じて共通スタイルと機器用スタイルに分け、さらに機器用スタイルではユーザエージェントを使って実際の機器に合わせたスタイルに切り替えている。

リクエスト URL返されるデータを収めたファイル
http://stylerepo.../logrepo/common logrepo_common.css
http://stylerepo.../logrepo/device ユーザエージェントに応じて logrepo_ipad.csslogrepo_imac.css に切り替える

つまり、以下のように 2 つのリンクを書くだけでデバイスに応じたスタイルに切り替えられるわけだ。

(スタイルへのリンク方法)
<link type='text/css' rel='stylesheet' href='http://stylerepo.../logrepo/common' />
<link type='text/css' rel='stylesheet' href='http://stylerepo.../logrepo/device' />

切り替えの仕組み

今回の変更で、stylerepo の構成は以下のようになっている。

(stylerepo アプリ構成の一部)
/stylerepo/
    +-- app.yaml
    +-- index.yaml
    +-- logrepo/
    |   +-- __init__.py
    |   +-- base.py
    |   +-- common_handler.py
    |   +-- default_handler.py
    |   +-- device_handler.py
    |   +-- logrepo_common.css
    |   +-- logrepo_ipad.css
    |   +-- logrepo_iphone.css
    |   +-- logrepo_mac.css
    +-- main.py
    +-- stylesheets
        +-- mac.css

前回の状態から logrepo ディレクトリが増えた。また、app.yaml にも手が入っている。

app.yaml

共通スタイルと機器用スタイルの切り替えはリクエストする URL を替えることで行う。この仕組みは GAE に用意されていて、設定を app.yaml に記述すれば良い。

app.yaml では、リクエストの URL パターンごとに処理を行うハンドラを記述する。この URL パターンの記述には正規表現が使えるためコンパクトかつ高機能な記述が行える。たとえば、以下のような記述。

(app.yaml より)
- url: /logrepo/(common|device)
  script: logrepo/\1_handler.py

この指定は リクエスト URL が /logrepo/common または /logrepo/device で終わっていた場合に、続くハンドラに処理させることを意味する。ここで commondevice の選択は正規表現になっていて、実際のリクエストにふくまれている文字列(commondevice) がスクリプト名の指定中では \1 として参照されている。すなわち、/logrepo/common でアクセスされたときには logrepo/common_hander.pyに、/logrepo/device としてアクセスされたときには logrepo/device_handler.py に、それぞれ処理が任せられる。

以下に今回の app.yaml の全体を示す。

ユーザエージェントの判別

機器の判別には HTTP のリクエストヘッダにふくまれているユーザエージェント文字列を使う。JavaScript で判別する方法でもお馴染のものだ。

以下がその部分のコードになる。

(device_handler.py より)
 1: class DeviceHandler(base.BaseHandler):
 2:     def request_device(self):
 3:         agent_string = self.request.headers['User-Agent']
 4:         device = 'mac'
 5: 
 6:         if agent_string.find('iPad') > -1:
 7:             device = 'ipad'
 8:         elif agent_string.find('iPhone') > -1:
 9:             device = 'iphone'
10:         else:
11:             device = 'mac'
12:         return device
13: 
14:     def style_file(self):
15:         return 'logrepo_' + self.request_device() + '.css'

また、DeviceHandler が継承している BaseHandler はこうなっている。

(base.py より)
 1: class BaseHandler(webapp.RequestHandler):
 2:     def style_file(self):
 3:         return ''
 4: 
 5:     def get(self):
 6:         path = os.path.join(os.path.dirname(__file__), self.style_file())
 7: 
 8:         self.response.headers['Content-Type'] = 'text/css'
 9:         for line in open(path, 'r'):
10:             self.response.out.write(line)

get メソッドを見ればわかるように、スタイル定義自体はファイルとして用意したものをそのまま使っている。発展形として、CSS ファイルをテンプレートとして使い、内容の一部をコードで置き換える、というようなことも考えられる。たとえば、色の組み合わせを何通りか用意しておき、日付などに応じて入れ替える、とか。

こちら側と向こう側

ページを開こうとする機器に応じて見た目を調整したい場合、2 通りの方法が考えられる。1 つはこちら側(クライアント側)で JavaScript を使って切り替えるもの、もう 1 つが今回のように向こう側(サーバ側)でやるものだ。

クライアント側でやればサーバ(とアプリ)を用意する必要はない。JavaScript の動くブラウザだけあれば良いから手軽にできる。一方、サーバ側でやるメリットとしては、クライアントの環境によらない、かな。これは、クライアントが JavaScript の動かないブラウザやプログラムであっても対応できる、という意味だ。

ま、サーバでできること(リクエスト時に解決できること)はサーバでやってしまうべきかな。クライアント側(JavaScript)はもっと他のことで忙しいはずだろう?

関連リンク

関連記事

0 件のコメント:

コメントを投稿