人生シーケンスブレイク

シーケンスブレイク(Sequence breaking、シークエンスブレイクとも)とは、テレビゲームにおいて開発が想定している攻略ルートを逸脱し、ショートカットする行為のことである。

gulp-sassで自動でSass/SCSSからcssファイルを生成する

フロントエンド開発に便利なNode.js環境で、ファイル変更時に自動でSass(SCSS)からCSSファイルを生成したい。

2016-06-17 追記

上位互換記事を書きました。

gulp-sassで自動でSass/SCSSからcssファイルを生成したり、他にもいろいろする - 人生リアルタイムアタック

準備

以下のようなディレクトリ構成を想定。

.
├── css
│   └── // cssファイルの生成先
└── assets
    └── sass
        └── main.scss

インストール

gulpとgulp-sassをインストールする

$ npm install gulp gulp-sass

gulpfile.jsの作成

まずは最も簡単な例として、*.scssに変更があれば自動で*.cssを生成する例。

var gulp = require('gulp');
var sass = require('gulp-sass');

// Sassコンパイルタスク
gulp.task('sass', function(){
  gulp.src('./assets/sass/**/*.scss')
    .pipe(sass())
    .pipe(gulp.dest('./css/'));
});

// watchタスク(**/*.scss変更時に実行するタスク)
gulp.task('sass-watch', ['sass'], function(){
  var watcher = gulp.watch('./src/sass/**/*.scss', ['sass']);
  watcher.on('change', function(event) {
    console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
  });
});

// gulpのデフォルト動作としてsass-watchを実行
gulp.task('default', ['sass-watch']);

実行

$ gulp

を実行するだけで、後はSCSS変更時に自動でCSSが生成されるようになる。 **/*.scss としている場合、hoge/hoge.scsscssの出力先も hoge/hoge.css としてくれる。親切。

出力フォーマットを変更したい。

gulpfile.js内の .pipe(sass()) を、 .pipe(sass({outputStyle: 'expanded'})) にすると、よくみるcssフォーマットで生成されるようになる。

構文ミス時にgulpごと落ちるのを何とかしたい

gulp-plumber を使う。

var gulp = require('gulp');
var sass = require('gulp-sass');
var plumber = require('gulp-plumber');

// sassコンパイルタスク
gulp.task('sass', function(){
  gulp.src('./src/sass/**/*.scss')
    .pipe(plumber()) // ←ここが追加
    .pipe(sass())
    .pipe(gulp.dest('./css/'));
});

// watchタスク(Sassファイル変更時に実行するタスク)
gulp.task('sass-watch', ['sass'], function(){
  var watcher = gulp.watch('./src/sass/**/*.scss', ['sass']);
  watcher.on('change', function(event) {
    console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
  });
});

gulp.task('default', ['sass-watch']);

こうするとエラー時にも落ちない。

browser-syncと併用したい。

var gulp = require('gulp');
var sass = require('gulp-sass');
var bs = require('browser-sync').create();

// browser-sync
gulp.task('bs', function(){
  var bsOptions = {}
  bsOptions.files = ['template/**/*.html', 'css/**/*.css'];
  bsOptions.port  = 3000;
  bs.init(bsOptions);
});


// sassコンパイルタスク
gulp.task('sass', function(){
  gulp.src('./src/sass/**/*.scss')
    .pipe(sass())
    .pipe(gulp.dest('./css/'));
});

// watchタスク(Sassファイル変更時に実行するタスク)
gulp.task('sass-watch', ['sass'], function(){
  var watcher = gulp.watch('./src/sass/**/*.scss', ['sass']);
  watcher.on('change', function(event) {
    console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
  });
});

gulp.task('default', ['bs', 'sass-watch']);

こんな感じかなー

FlaskでもHamlを使う - hamlish-jinja

Flask 最近書いてるんだけれども、今更閉じタグなんて書きたくないんじゃ!ということで、様々HTML Template触った結果最も書きやすかったHamlを使いたい。

ということで hamlish-jinja を使う。

使い方の基本

Flaskのdefault template engineであるjinjaのoptionsに、hamlish-jinjaを渡したclassを生成して実行する。

from flask import Flask, render_template
from werkzeug import ImmutableDict


class FlaskWithHamlish(Flask):
    jinja_options = ImmutableDict(
        extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_', 'hamlish_jinja.HamlishExtension']
    )
app = FlaskWithHamlish(__name__)


@app.route('/')
def index():
    return render_template('index.haml')

if __name__ == '__main__':
    app.run()

オプション

hamlish-jinjaには幾つかのオプションが用意されている。 以下はその一例。

  • app.jinja_env.hamlish_enable_div_shortcut = True
    • divタグを省略可能にする。div.classname を .classnameだけで記述可能になる
  • app.jinja_env.hamlish_mode = 'indented'
    • 出力HTMLにインデントをつける。開発中はこちらが良いだろう。

https://github.com/Pitmairen/hamlish-jinja#configuration

Syntaxについて

あくまでHamlishであって、Hamlと全く同じではない。

複数のAttributeを {} で記述できない。

Hamlで可能な記述(hamlish-jinjaでは不可)

    %meta {name:'viewport', content:'width=device-width, initial-scale=1'}

hamlish-jinjaで可能な記述

    %meta name='viewport' content='width=device-width, initial-scale=1'

!!!5 が適用されない。

他の方法でできるからいいじゃんという話になっている。

Added doctype strings. by totkeks · Pull Request #15 · Pitmairen/hamlish-jinja · GitHub

このケースでは、app.py側に以下のように書いたらいいじゃんという話。

import jinja2 

jinja_env.globals['html5'] = jinja2.Markup('<!DOCTYPE html>')

つまり、hamlとまったく同じ記述ができるというわけではなく、あくまでもhamlっぽく書けるという話。

コメント書式が異なる

hamlish-jinjaでのコメントは ; です。なんでや。

;Test comment

hamlからhamlishに変換したい

↓ を使いましょう。

hamlish-jinja/haml2hamlish.py at master · Pitmairen/hamlish-jinja · GitHub

まとめ

それでもhamlishなtemplateは記述量の少なさから得られる恩恵が大きいのでもうちょっと使って見る予定。 とはいえ、hamlと完全互換では無いので、そこの差異を認識した上で利用するのが望ましそう。

SSL証明書が正しくないサイトに対してPythonでアクセスする

HTTPSで提供しているサービスのローカル開発環境にPythonでアクセスを試みたらエラーが出た。

Traceback (most recent call last):
  File "/main.py", line 36, in <module>
    main()
  File "/main.py", line 18, in main
    soup = BeautifulSoup(opener.open(conf['login_url']).read().decode('utf-8'), 'html.parser')
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/urllib/request.py", line 465, in open
    response = self._open(req, data)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/urllib/request.py", line 483, in _open
    '_open', req)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/urllib/request.py", line 443, in _call_chain
    result = func(*args)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/urllib/request.py", line 1283, in https_open
    context=self._context, check_hostname=self._check_hostname)
  File "/usr/local/var/pyenv/versions/3.5.0/lib/python3.5/urllib/request.py", line 1242, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:646)>

PEP 0476に従い、Python2.7.9以降はSSL証明書が正しくない場合にはデフォルトでSSL認証エラーを出すようになった模様。 PEP 476 -- Enabling certificate verification by default for stdlib http clients | Python.org

開発環境にアクセスするだけのコードだったので、

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

上記を書いて実行したらアクセス可能になった。
セキュリティ上は安全と保証されないことを認識の上でご利用ください。

みんなのPython 第4版

みんなのPython 第4版

urllib.request.build_opener に User-agent を指定する

Pythonで要ログインのサービスをスクレイピングをする際にはbuild_openerを利用するが、User-agentも指定する必要があるケースがある。

通常のbuild_opener

# coding: utf-8
import http.cookiejar
import yaml
from bs4 import BeautifulSoup
from urllib import parse, request


def main():
    # yaml
    conf = yaml.load(open('conf.yaml').read())

    # login
    opener = request.build_opener(request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
    post = {
        'username': conf['username'],
        'password': conf['password']
    }
    query = urllib.parse.urlencode(post).encode('utf-8')
    opener.open(conf['login_url'], query)

    # 以下スクレイピング処理など...
    soup = BeautifulSoup(opener.open(conf['scrap_url']).read().decode('utf-8'), 'html.parser')

もしUser-agentの指定が必要な場合には以下のようにするとよい。

# coding: utf-8
import http.cookiejar
import yaml
from bs4 import BeautifulSoup
from urllib import parse, request


def main():
    # yaml
    conf = yaml.load(open('conf.yaml').read())

    # login
    opener = request.build_opener(request.HTTPCookieProcessor(http.cookiejar.CookieJar()))
    opener.addheaders = [('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36')]
    post = {
        'username': conf['username'],
        'password': conf['password']
    }
    query = urllib.parse.urlencode(post).encode('utf-8')
    opener.open(conf['login_url'], query)

    # 以下スクレイピング処理など...
    soup = BeautifulSoup(opener.open(conf['scrap_url']).read().decode('utf-8'), 'html.parser')

解説

urllib.request.build_opener()で作成したインスタンスOpenDirectorオブジェクト と呼ばれるものであり、urllib.request.urlopen()と引数、返り値が一緒である。*1 加えて、addheaders()でOpenDirectorインスタンスにヘッダーを指定できるので、こちらを利用してUser-agentを指定すればよい。

公式にも以下のURLに例が載っているので、こちらを参考にすると他の用途も掲載されている。 http://docs.python.jp/3.4/library/urllib.request.html#examples

*1:urlopen()はグローバルな OpenerDirector の open() メソッドを呼び出しているにすぎない。

Bowerを使ってフロントエンドのパッケージ管理を行う

先行きが若干不安だけど。 Bower is alive, looking for contributors · Bower blog

npm はpackage.jsonがカオスになっている為開発用ライブラリのパッケージ管理のみ、BowerはBootstrapなどのフロントエンドのライブラリパッケージ管理で使い分けしている。

Bower インストール

$ npm install bower

Bower によるパッケージインストール

$ bower install bootstrap

デフォルトでは bower_components/ ディレクトリにインストールされる。 bower_components/ を.gitignoreに追加しておくとgitリポジトリに外部ライブラリの歴史が残らず綺麗に管理できる。

bower.jsonの作成

$ bower init

コマンドを叩き、npm init 感覚で幾つかの質問に答えていくとbower.jsonが生成される。

bower.jsonに記述されたライブラリのインストール

$ bower install

インストール先の変更

.bowerrc に以下のように記述すると良い。

{
  "directory": "static/vendor",
  "json": "bower.json"
}

まとめ

Bowerを使う際には以下のファイルを追加する。

$ tree -a
.
├── .bowerrc # パッケージインストール先の指定
├── .gitignore # パッケージインストール先の除外設定を追加
└── bower.json # パッケージ管理ファイル