人生シーケンスブレイク

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

NERDCommenterでfiletypeごとのコメントフォーマットを変更したい

SCSSを scss-lint でチェックしてるんだけど、デフォルトで推奨されているコメントが // となっている。 しかし、NERDCommenterコメントアウトした際には /* */ となっていて面倒なのでSCSSのデフォルトのコメント設定を変更したい。

.vimrc に以下のように追記する

let g:NERDCustomDelimiters = {
  \  'scss': { 'left': '//'}
  \}

まだ著作権表記で消耗してるの?

年末年始対応の時期ですね。

f:id:ShineSpark:20151216014450p:plain
svg画像を 著作権表示 - Wikipedia より。

企業で働くエンジニアは、あけおめ対策をはじめとした年末年始の対応準備に時間を割いていることでしょう。
その中でも、毎年恒例の著作権表記、特に年号更新の対応に追われる方が多いでしょう。

アレ、不要なので辞めましょう。

要点

  • 著作権表記は現在いちいち書く必要性はありません。
  • 年号表記も特に意味はありません。
    • そんな対応に時間をとられる必要はありません。辞めましょう。
  • もっと言えば "All rights reserved." も不要です。辞めましょう。

どゆこと?

そもそも著作権表記とは?

著作権表示(ちょさくけんひょうじ)は、著作物の複製物につける著作権者や著作物の発行年等に関する表示である。著作者が著作権を取得するため、著作物の創作のほか、何らかの手続き等(方式)が必要な法域においては、著作権表示は重要な意味があるが、現在は、ほぼ全ての法域で著作権は、著作物の創作とともに発生するので、重要性は失われている。
著作権表示 - Wikipedia より引用

日本国内に於いては、1971年7月24日にパリで改正された万国著作権条約に受諾しており、現在はこちらの内容に批准しています。 その第三条四項には以下のように定められています。

4  各締約国は、他の締約国の国民の発行されていない著作物を、方式の履行を要することなく保護するための法的手段を確保する。
千九百七十一年七月二十四日にパリで改正された万国著作権条約 - Wikisource より引用

ここでいう "方式" とは、著作権の無方式主義と方式主義のことです。

無方式主義 著作物を著作もしくは発表した時点で自動的に著作権が発生し、それ以外には何らの方式(又は手続)の履行を要求しない法制 方式主義 納入、登録、表示、官庁への納入、登録、登録手数料の支払い、自国内における製造もしくは発行などといった「方式」を履行することにより著作権の発生要件とする法制
著作権表示 - Wikipedia より引用

またまたWikipedia引用ですみません。ちなみに私はこの件について8年程前から疑問を抱き、毎年この時期になる度に当該Wikipediaページをwatchして参りました(編集は一切してない)。昔に比べると非常に明瞭になっており、私以外にもこの件で戦ってきた人が居るのかと思うと非常に感慨深いものがあります。 当時はガラケー全盛期の為、All rights reserved. を書くだけでフッターに無駄な改行が入ったのです。

要するに、「万国著作権条約に締約している国の国民は、何もせずとも著作物の権利を保護する法的手段を持っている。」ということになります。
何もせずとも、です。

つまり要らないのです。

ちなみに (c) 表記について

前述の通り不要なのですが、方式主義に於いて認められているのは © であり、(c) は認められていません。

その為、.txt ファイルとかに (c) と書いていても無効なので書くのは辞めましょう。

ちなみにAll rights reserved.は?

こちらは万国著作権条約には関係ありません。万国著作権条約制定前に策定されたブエノスアイレス条約という条約があるのですが、この条約では「権利を所有している」表記が必要でした(方式主義)。

アメリカ合衆国をはじめとした国々が締約し、「権利を所有している」表記として、All rights reserved. が使われました。 現在では、アメリカを含め殆どの国がベルヌ条約*1及び万国著作権条約にも締約している為、この表記は現在は不要です。

ちなみに、 日本はこのブエノスアイレス条約に締約していません。
したがって、 日本では全く意味がありません。
アメリカ企業の権利表記を日本企業がコピペしたものが蔓延したんでしょうねぇ...

All rights reserved. のデメリット

All rights reserved. の直訳は、「すべての権利を保有している」です。
イマドキのWebサービスに於いて、そのようなケースが果たして本当に成立しているのでしょうか?

  • CGM
    • ユーザが投稿した写真やデータ。規約にも運営側に帰属する旨記載して、規約との齟齬はありませんか?
    • ユーザが投稿した歌詞や第三者が著作権を保有している画像。これも運営者が権利を保有しているのでしょうか?
  • 外部データ
    • 他者からAPI取得 または購入したデータ
    • オープンデータ
    • OSSやライブラリ

意味の無い All rights reserved. を書くことで、逆に訴訟や炎上リスクを招く可能性もありそうです。

規約の話ではありますが、mixi規約改定時に問題になりましたね。
ミクシィの利用規約改定問題が示すCGM時代の権利処理のあり方 - ビジネススタイル - nikkei BPnet

他の例として、有名なCGMサービスとして ボケてがありますが、全ページのフッターに All rights reserved. と書いてあります。
しかし、利用規約には、

第17条 弊社の財産権
( 1 ) 利用者が送信(発信)したコンテンツおよび情報を除き、本サービスに含まれる一切のコンテンツおよび情報に関する財産権は弊社に帰属します。

と書いてあり、Allとは一体...うごごごごご...と思ったりします。*2

利用者が投稿したコンテンツおよび(他者の著作物を含む)画像の権利の帰属を宣言しない辺り、ボケてはしっかりしている方かも知れません。

お前専門家じゃねーだろ。ただのエンジニアがナマいってんじゃねーぞ。

はい。専門家ではありません。ぜひとも専門家からの意見もいただきたいところです。 弁護士ドットコム - 無料法律相談や弁護士、法律事務所の検索 でニュースとして取り上げていただけること、お待ちしております。

とはいえ根拠も無いままではアレなので、ここは最強法務部と名高い任天堂法務部のチェックを通ったであろう任天堂公式サイトを確認してみましょう。

f:id:ShineSpark:20151216005543p:plain
任天堂ホームページ|Nintendo より

お、超シンプルです。私が思う理想形です。

先日 サムスンから賠償金5億4800万ドルせしめた Appleはというと、

f:id:ShineSpark:20151216005906p:plain
Apple より

あ、年号もAll right reserved. がありました。

お次はGoogleです。
f:id:ShineSpark:20151216011814p:plain
Google より

本気で何も書いてない...

思ったよりバラバラの模様です。

対応

原理主義的な話を散々述べましたが、本懐は「毎年毎年、年号を更新するだけの無駄なクソ作業を無くしたい」です。
現実として、社内の一エンジニアや受託エンジニアでまるっと無くすというのは現実的ではないでしょう。

その場合の対応として、以下の解決策を検討しましょう。

動的ページ

Date() 関数などから現在の年号を表記するなどして動的に出力しましょう。

静的ページ

404.html とか 500.html などは、コンテンツなんてあってないようなページなので、© 会社名 または© 会社名 All rights reserved. にして年号は無くしましょう。

クライアント側で実行するJavaScriptでの年号出力は推奨しませんが、どうしても上長がOKしてくれない場合や、一時的なキャンペーンページなどの静的ページではJavaScriptでの実行もアリかもしれません。JavaScript無効設定での著作権表記について問題視する上長ならば、著作権表記自体の不要性やコストについても議論の余地がありそうです。

いずれにせよ手動更新は辞めましょう。

上長を説得する材料として

以下のサイトを判断材料にしてもらいましょう。

こんなことに時間を浪費しているうちはスピーディな開発なんて出来やしない。

*1:長くなるのでこのエントリでは言及しない。

*2:CGMとして有名な為取り上げましたが、All rights reserved.の含有する矛盾を問題提起するのが目的であり、ボケてサービスを批判する意図は全くありません。

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() メソッドを呼び出しているにすぎない。