Tinkererで静的サイト作る準備としてS3に同期するまでのGlup環境をつくった
背景
Tinkererで静的サイト(ブログ含む)を作るときに最近のWeb技術的に足りないものがあったのでGlupで補強した。
足りないもの
- S3同期
- minify(html, css, js)
できたもの
https://github.com/jbking/tinkerer-gulp-s3-template
Tinkererで静的サイト(ブログ含む)を作るときに最近のWeb技術的に足りないものがあったのでGlupで補強した。
https://github.com/jbking/tinkerer-gulp-s3-template
この記事はPython Advent Calendar 2014の7日目です。引き続きRedisネタです。
python-stdnetはPython製のRedisオブジェクトマッパーの一つで、よくあるオブジェクトリレーショナルマッパー(ORM)のパターンに非常に似たものになっています。
なんか前回の記事とかわりませんね。
このようにしてモデルを定義します。
from stdnet import odm class User(odm.StdModel): email = odm.SymbolField(required=True, unique=True) salt = odm.CharField() hash = odm.CharField() created_at = odm.DateTimeField(default=time.time)
SymbolFieldとCharFieldはどちらも文字列を格納する目的ですが、SymbolFieldがインデックスされ、CharFieldはインデックスされません。
あんまり前回の記事とかわりませんね。
定義したものはこのように使えます。
models = orm.Router('redis://localhost?db=1') models.register(User) user = models.user.new(email='user@host.com') user.salt, user.hash = gen_hash(password) user.save() user = models.user.get(email='user@host.com') at_gmail = models.user.filter(email__endwith='@gmail.com').all()
ルーターに対してモデルを登録してクエリでとってくるやりかたです。
こんなのがあります
この記事はPython Advent Calendar 2014の6日目です。
romはPython製のRedisオブジェクトマッパーの一つで、よくあるオブジェクトリレーショナルマッパー(ORM)のパターンに非常に似たものになっています。
なんかどこかでみたことありますね。
このようにしてモデルを定義します。
import rom # All models to be handled by rom must derived from rom.Model class User(rom.Model): email = rom.String(required=True, unique=True, suffix=True) salt = rom.String() hash = rom.String() created_at = rom.Float(default=time.time)
なんかどこかでみたことありますね。
定義したものはこのように使えます。
user = User(email='user@host.com') user.salt, user.hash = gen_hash(password) user.save() user = User.get_by(email='user@host.com') at_gmail = User.query.endswith(email='@gmail.com').all()
なんかどこかでみたことありますね。
http://qiita.com/zaru/items/6e57ab83a8e0e40f2115
IE6,7は死亡(もういいよね)
よくないことが稀にある。 IE8, 9ではXDomainRequestを使うことでCORSを実現できる。ただしその方法はIE7では使えない。
IE7では要件としてFlashが有効であるブラウザに限定できるならeasyXDMが使える。
http://easyxdm.net/wp/
なぜFlashが必要なのかは http://easyxdm.net/wp/2011/04/13/microsoft-security-bulletin-ms11-018/
よくあるユースケースとしてはjQueryのajaxでCORSをしたいということがある。
https://github.com/GyldendalDigital/jQuery-EasyXDM
モダンブラウザが普及した今日、いまだレガシーIEは死なず。 つまりそれはクロスブラウザ動作確認をIEでも行わなければならないことを意味する。
IEである。
モダンな開発者であれば仮想マシンは常識であるのでmodern.IE( https://www.modern.ie/ja-jp/virtualization-tools )を使用する。各ホストOS向けにイメージが配布されているのでそれをダウンロードすればよい。
ただし中身はIEしか入っていないWindows環境なので後述する方法でデバッグ環境を構築する必要がある。さもなければ死ぬ。
Microsoft Script Editorかそれが同梱されているMicrosoft Officeを所持しているならばそれをインストールして使用する。 所持していなければVisual Studio Express( http://www.visualstudio.com/downloads/download-visual-studio-vs) をインストールする。for Webあたりでよい。 ここで注意が必要なのはVisual Studio Express 2013のシステム要件は Windows 7 以上となっており、modern.IEのIE7のOSである Windows Vista では動作しない。あきらめてページ下部にあるVisual Web Developer 2010 Expressをインストールする。 なおこのインストールは時間がかかるのでインストーラーを走らせてから他の作業を並行するとよい。
ここまで整備すればあと一歩である。
レガシーなデバッグ環境にはdeminifyなどというモダンな機能はついていないのでminifyしていないコードを使うことを推奨する。
2日にわたる超チューニング祭 in ニコニコ超会議3で最速になってきました。参加された方々、スタッフの方々にはお世話になりました。ありがとうございました。
レギュレーションの確認
sftp用の秘密鍵がDropbox経由で各自に配布され、10時くらいに開始。コードを落そうとsftpでつなぎにいくが、ポートが標準ではないようでつながらない。 コード落としつつレギュレーションを検討した。
なにはともあれコードを動かす準備。
$ git init . $ git add * $ git commit -m init $ python -m SimpleHTTPServer
深く考えずにHTTPサーバを立ちあげられるPythonは良い環境。
こういうときの調査はChromeのDeveloper Toolsで性質を調べる。
まず始めに影響の大きかった2つについて検討した。
これらで2秒くらい改善した。
次に非同期でコンテンツを取得しているところの通信が気になったので調査した。 「注目のコンテンツ」という箇所が実際には広告で別のバックエンドから取得しているようだった。 ここでスタッフにソーシャルハッげふん、げふん、要件を定義してもらい静的コンテンツにしてしまっていいという答えをえた。 この改善で60msくらい減った。この変更はあとからQ&Aにのったけど見てなかった人とはここで差がでたと思う。
ここまでやるとおかしな接続はなくなってCSSファイルのサイズが気になってきた。 以前Qiitaでそんなこと書いてるようなことを覚えてたのでググった。
結果としてダメcommonだということが判明したので、それ中心にCSS全体として50%くらい?不要な記述があったので削った。そしたら必要なものもあったらしくレイアウト崩れてしまったのでインスペクタで確認しつつ部分的に戻した。
猫の日 #とは
/* 猫の日対応@2/22が終わったら消す */
CSSの中身はさわり始めると地獄になりそうだったので、見た目崩れていないことの確認以上は深入りをしなかった。
表示に必要のないJSを削除。 minify系のウェブサービスを使ってCSSとJSを縮小。 CSS Spriteを検討したが、詳しくなかったので今回はやめた。 サーバのnginxがそんなに性能よくなかったぽいのでTCPソケットを減らすために画像をdataスキームに埋めこむ。
たんぽぽわーくで日が暮れる。
15時くらいに中間計測があり、その時点での評価結果が出された。 今回は連番にIDが振られており、そのIDがそのまま評価用のURLにも使われていたので、かつレギュレーションになかったので、上位のサイトを検証しにいった。 速いところはみんな似たようなことやってた。CSS Spriteやってる人もいて尊敬した。
最速のページ
<html>niconico(C++)</html>
作業やってもよいレギュレーションだったけど、疲れたので寝た。もう若くない。
いいかげんたんぽぽ疲れしたのでgruntを調べる。 grunt-contrib-uglifyとgrunt-contrib-cssminが使えそう。
この日は競技時間が1時間半くらいしかないので大きな変更はあきらめて細かく修正することにした。 細かく成果物を生成するには手作業でminifyしてるとつらいのでgruntを導入した。 uglifyとcssminが他のminify系サービスより性能が良かったのが驚いた。
zepto外しのためにjQueryを想定しているコードの機能だけ抽出して代替コードを書きだす。が間に合わずに中止。 残り2分でhtml自体をminifyしてないことに気付く。ググったらgrunt-contrib-htmlminが。 とりあえず入れてREADME通りに設定して動かす。何か成功したっぽい。時間ないから 確認せずそのままアップロード
そして結果発表を待つ。
最速になりました。そして戀塚さんの目にとまったらしく審査員特別賞もいただくことになりました。
計測結果からの推測になってしまうのですが、LTE回線だったこと、計測は10回読んだ平均を使ったことから、 アセットをページに全て展開するのではなく、ある程度の粒度の少数のファイルに分けることでの並列の効率化が効果があったと思われる。あと AppCache これは計測方法が詳細不明だったので有効かどうかは不明だったけど、うまく刺さったようで爆速になった。たぶんこれが随分大きな要因。ただ個々のサイズが大きいとレンダリングが終わるまでに時間がかかるのは明白なので、極力サイズを減らす努力は無駄じゃなかったと信じたい。
そもそもチューニング厨は参加自粛するべきイベントだったのではないかと思い始めましたが、司会進行の方が私のいま住んでる地方ラジオに枠をもってるとか何かよくわからないつながりがあったり、表彰式に偶然ymotongpooがいたりで終始中々面白かったので良しとします。 再度になりますが、参加された方々、運営されたスタッフの方々、ほんとうにありがとうございました。 :)
センサー系のカンファレンスで参考になった内容をメモ
先日のGoken vol.14でJxckさんから良いことを聞いたのでメモに残す。
何はともかく定義を。
type rwcMock struct { bytes.Buffer Closed bool } func (m *rwcMock) Close() error { m.Closed = true return nil }
Goは、ある構造体の埋め込むことでその構造体のように振る舞え、そのオブジェクトに対してメソッドが定義されていたらインターフェースを満たすという機能がある。 bytes.Bufferの定義は、
```go:buffer.go type Buffer struct { // ... }
func (b *Buffer) Read(p []byte) (n int, err error) { // ... }
func (b *Buffer) Write(p []byte) (n int, err error) { // ... }
となり、これはio.ReadWriterインターフェースを満たす。 ```go:io.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type ReadWriter interface { Reader Writer }
そしてrwcMockはio.ReadWriteCloserインターフェースを満たす。
```go:io.go type Closer interface { Close() error }
type ReadWriteCloser interface { Reader Writer Closer }
## いかに使えるのか ここでnet.Connとos.Fileの定義の抜粋を記す。 ```go:net.go type Conn interface { Read(b []byte) (n int, err error) Write(b []byte) (n int, err error) Close() error // ... }
```go:os.go type File struct { // ... }
func (f *File) Read(b []byte) (n int, err error) { // ... }
func (f *File) Write(p []byte) (n int, err error) { // ... }
func (f *File) Close() error { // ... } ```
一目見て分かるように、これらのオブジェクトはio.ReadWriteCloserインターフェースを満たす。
つまり入出力でこれらのオブジェクトを使う箇所で型をio.ReadWriteCloserにしておけば、テストダブルとしてrwcMockを使えることを意味する。
Pythonにおけるfileオブジェクトの代わりにStringIOオブジェクトを渡して、内容を検査するのと同様なことがGoでもできる。
POSIXではもっとプリミティブなファイルデスクリプタ、型でいうとuintptrまで抽象化することが可能なのだが、現状のGoはソケットのファイルデスクリプタを公開しておらず、内部的にはnetFDとして定義しているのだが、そとからはos.Fileでしか操作することが出来ない。
ここまで書いてから気付いた。net.Connインターフェースには定義されていないが、TCP/IPやUnixドメインソケットのConnオブジェクトにFile()メソッドが定義されていて、os.Fileオブジェクトが返るようだ。 つまり、ソケットでできることはファイルオブジェクトででもできる。 どちらにせよrwcMockのテストにおける有用性は変わらない。