今時の若者風にvimのステータスをオシャレにしようと思ってvimプラグインのairlineを導入してみました。

vim-airline/vim-airline: lean & mean status/tabline for vim that's light as air

しかし、screenの上で動かすと全くカラフルにならずがっかり・・・。

https://gyazo.com/1319ee0e60af81843aaa05304c441953

256色対応するにはtmuxに移行しなきゃということなのか…?(screenの設定をtmuxに移すのが面倒な老害)

もしくは何らかのオプション付きで再コンパイルが必要か…?

と思ったらHigh Sierraのデフォルトのscreenでも~/.screenrcに下記を追加したら行けました。

term "screen-256color"

https://gyazo.com/c5a63beb261ea09ccac354b762a21dc6

やったね。

まだscreenで行けてよかった…。

今までの流れはこうでした。

  1. @machidaがslimでモックとして作る。
  2. 僕がjsにするためにhtmlに翻訳してfoo.vueに移す。

これがクッソ面倒だったのですが、pugならslimとほとんど同じなのでコピペでいけました。

$ yarn add pug

templateタグにlang="pug"を追加する。

app/javascripts/packs/components/foo.vue:

<template lang="pug">
  header.content
    h1 トップページ
</template>
<script>
いろいろ
</script>
<style>
いろいろ
</style>

https://gyazo.com/749f392120b56c47372ffa524b5f06b1

「ヒューッ!」

webpackerの設定は一切無し。

RubyMineを使ってるなら、pugプラグインを入れればシンタックスハイライトもちゃんと動きますね。

railsのcreated_atupdated_atnot nullなので「無くてもいいけどあってもいい」かな?ぐらいのときに面倒。

他からデータを入れる時に困る。

embulk-filter-add_timeプラグインで追加できる。

$ embulk gem install embulk-filter-add_time
in:
 ...

filters:
  - type: add_time
    to_column:
     name: created_at
     type: timestamp
    from_value:
     mode: upload_time
  - type: add_time
    to_column:
     name: updated_at
     type: timestamp
    from_value:
     mode: upload_time

out:
  ...

rubocopプラグインを入れたが下記のようなエラー。

ググって出て来るのはGemfileにrubocopを入れてないというエラーで俺の場合は違う。

https://gyazo.com/6640aec9d7216b15f5bcc1c296ca046b

ProcessNotCreatedException: Failed to run RuboCop command - is it installed? (SDK=/usr/local/var/rbenv/versions/2.3.1/bin): Cannot run program "rubocop" (in directory "/Users/komagata/dev/src/github.com/fjordllc/kowabana"): error=2, No such file or directory

.ktとい拡張子はkotlinかな?javaやkotlinのデバッグはやりたくないな。

RubyMineはしばらく置いておこう。

Failed to run RuboCop command - is it installed? (SDK=/usr/local/var/rbenv/versions/2.3.1/bin): Cannot run program "rubocop" (in directory "/Users/komagata/dev/src/github.com/fjordllc/kowabana"): error=2, No such file or directory
com.intellij.execution.process.ProcessNotCreatedException: Cannot run program "rubocop" (in directory "/Users/komagata/dev/src/github.com/fjordllc/kowabana"): error=2, No such file or directory
    at com.intellij.execution.configurations.GeneralCommandLine.createProcess(GeneralCommandLine.java:358)
    at org.jetbrains.plugins.ruby.ruby.run.RubyCommandLine.createProcess(RubyCommandLine.java:64)
    at io.github.sirlantis.rubymine.rubocop.RubocopTask$runViaCommandLine$1.invoke(RubocopTask.kt:173)
    at io.github.sirlantis.rubymine.rubocop.RubocopTask$runViaCommandLine$1.invoke(RubocopTask.kt:25)
    at io.github.sirlantis.rubymine.rubocop.RubocopTask.parseProcessOutput(RubocopTask.kt:67)
    at io.github.sirlantis.rubymine.rubocop.RubocopTask.runViaCommandLine(RubocopTask.kt:173)
    at io.github.sirlantis.rubymine.rubocop.RubocopTask.run(RubocopTask.kt:60)
    at io.github.sirlantis.rubymine.rubocop.RubocopAnnotator.doAnnotate(RubocopAnnotator.kt:157)
    at io.github.sirlantis.rubymine.rubocop.RubocopAnnotator.doAnnotate(RubocopAnnotator.kt:24)
    at com.intellij.codeInsight.daemon.impl.ExternalToolPass.b(ExternalToolPass.java:212)
    at com.intellij.codeInsight.daemon.impl.ExternalToolPass.access$500(ExternalToolPass.java:44)
    at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.run(ExternalToolPass.java:160)
    at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:320)
    at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:310)
    at com.intellij.util.ui.update.MergingUpdateQueue.lambda$flush$1(MergingUpdateQueue.java:260)
    at com.intellij.util.ui.update.MergingUpdateQueue.flush(MergingUpdateQueue.java:274)
    at com.intellij.util.ui.update.MergingUpdateQueue.run(MergingUpdateQueue.java:229)
    at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:222)
    at com.intellij.util.Alarm$Request$1.run(Alarm.java:378)
    at com.intellij.util.Alarm$Request.run(Alarm.java:389)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:227)
    at com.intellij.util.concurrency.BoundedTaskExecutor.runFirstTaskThenPollAndRunRest(BoundedTaskExecutor.java:178)
    at com.intellij.util.concurrency.BoundedTaskExecutor.access$000(BoundedTaskExecutor.java:40)
    at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:197)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Cannot run program "rubocop" (in directory "/Users/komagata/dev/src/github.com/fjordllc/kowabana"): error=2, No such file or directory
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    at com.intellij.execution.configurations.GeneralCommandLine.startProcess(GeneralCommandLine.java:368)
    at com.intellij.execution.configurations.GeneralCommandLine.createProcess(GeneralCommandLine.java:354)
    ... 28 more
Caused by: java.io.IOException: error=2, No such file or directory
    at java.lang.UNIXProcess.forkAndExec(Native Method)
    at java.lang.UNIXProcess.<init>(UNIXProcess.java:247)
    at java.lang.ProcessImpl.start(ProcessImpl.java:134)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
    ... 30 more

YosemiteプリインストールMac miniの話。OSアップグレードはクリーンインストール派だから他は知らん。

rbenvでcompleteが見つからないと言われる

.rbenv/libexec/../completions/rbenv.bash:16: command not found: complete
# ~/.zshenv:
-if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
+if which rbenv > /dev/null; then eval "$(rbenv init - zsh)"; fi

こう変えればOK。

brewのrbenvのインストール場所が変わった

# ~/.zshenv:
-export RBENV_ROOT=/usr/local/opt/rbenv
+export RBENV_ROOT=/usr/local/var/rbenv

/usr/local/opt/rbenvから/usr/local/var/rbenvに変わってるので注意。

vundlerの設定の書式が変わった

vimのプラグイン管理プラグインのvundlerの設定の書き方が変わってる。

# ~/.vimrc:
set nocompatible
filetype off

set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

Plugin 'gmarik/Vundle.vim'

Plugin 'vim-ruby/vim-ruby'

call vundle#end()

Vが大文字になってるし、BundleだったのがPluginになってる。

powがrbenvで設定してるrubyを読まない

# ~/.powconfig 
export PATH="/usr/local/var/rbenv/shims:/usr/local/var/rbenv/bin:$PATH"

.powconfigを書けば良い。

雑感

俺の仕事に支障がない状態(railsアプリが立ち上がる)にするまでにつっかえたところはこのぐらい。

一応bootstrap.shというのを用意してbrewfile.shというのを呼んでソフトをあらかた入れるようにしてるけど、結局OSアップグレードするぐらい時間が立ってるといろんなツールに非互換な変更が入ってるからある程度手動が入っちゃいますね。

C-iだと物足りないので。

uncrustify

brewでCLI版のuncrustifyが入るけど、Xcodeプラグインとして提供されているBBUncrustifyPlugin-Xcodeが便利でした。

Uncrustify Selected Lines

このプロジェクトをビルドするとプラグインディレクトリに自動で入るのがちょっと嬉しい。

XcodeのKey Bindingからは設定できないっぽいのでSystem PreferencesのKeyboard Shortcutsから設定しました。

捗るわあ。

小さいrailsサービスあるある

railsで何かサービスを作ったとする。ExceptionNotificationも入れた。NewRelicで5分毎に死活監視もしてる。なのに「落ちてるみたいです」とTwitterで言われる。

nginx + unicornの組み合わせでアクセスが増えた時、大抵真っ先に起こるのがunicornのtime out。

2013/02/08 18:36:10 [error] 20932#0: *3506622 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 〜ってやつ。

重いDBのクエリとかでアクセスをさばくスピードより接続数が増えてきて起こる。railsアプリまで処理が行かないのでExceptionは起こらない。NewRelicで監視してるURL以外で起きたり時々起きたりしてるからこちらが気付かない。

Nginxのエラーログを監視する

要はNginxのエラーログも監視しないといけない。syslog(rsyslog)でもplainなfileを監視したりalertメール飛ばしたり、複数台のログを1台に集約したりプラグインでできるけど、不必要なエラーログをフィルタリングしたりの設定が少し面倒くさい。

nagios or ganglia or monit入れる?この程度のサービスでfluentd使うのも大袈裟だしなあ。

logentriesを使う

logentriesLogglyとか沢山あるログ収集系のWebサービスです。多分これ系のどのサービス使っても同じ事できますが、LogglyだとAlertingは別サービスになってたりしてメンドイ。logentriesはWeb UIはイマイチですが設定は簡単だしalertメール飛ばすだけなら十分便利です。

logをlogentriesに送る方法は色々ありますが、専用agentを入れるのが一番簡単です。



$ su -
# echo 'deb http://rep.logentries.com/ squeeze main' >/etc/apt/sources.list.d/logentries.list
# gpg --keyserver pgp.mit.edu --recv-keys C43C79AD && gpg -a --export C43C79AD | apt-key add -
# apt-get update
# apt-get install python-setproctitle logentries
# le register
# apt-get install logentries-daemon

NginxのErrorログを監視。

# le follow /var/log/nginx/error.log --name Nginx-Error

しばらくするとこんな感じでログが集まってくる。

nginxのerror logは普通に404とかのエラーもあるので全部をalertメールしてたらウザいのでパターンにタグをつけます。

そしてそのタグに対してAlerts設定でメールを飛ばせばOK。「1時間に10回以上起きたら」とか「1時間に1回しか送りません」とかもWebから設定できるので簡単。

これで

「お客さんのエラー体験 = 俺らにAlertメール」

の図式に(常識的なメール数の中で)なるのでみんなで頑張ってエラー減らしまっしょい!という雰囲気になって良いです。

祝Github導入

お陰様でsvnからGithubに移行。気軽にブランチ作れてとっても快適。lingrやjenkinsとの連携もやっぱりスムーズ。@hrysdとはしゃぎました。

cakeplusプラグイン

i18n関連が面倒だと書いたらコメント欄でk1LoWさんが教えて下さったcakeplusプラグインを入れてみた。超改善された。あざーす!

レガシー認証

以前も書いたが、全コントローラー、全アクションに認証処理がベタ書きされてる。そこでその処理をLegacyAuthComponetという名前で分けた。

<?php
/**
 * 旧認証
 *
 * 古い認証機能をラップしたもの。
 * 管理者ログインしていない場合はログイン画面に
 * リダイレクトする
 *
 * 使い方:
 * class FooController extends AppController
 * {
 *     public $compornents = array('LegacyAuth');
 * }
 */
class LegacyAuthComponent
{
    public $name = 'LegacyAuth';
    public $controller;

    public function startup(&$controller)
    {
        $this->controller =& $controller;
        $this->adminLoginRequired();
    }

    public function adminLoginRequired()
    {
        if (isset($_SESSION['ses_login']['id'])) {
            $id = $_SESSION['ses_login']['id'];
        } else {
            $id = null;
        }

        if (!($id > 0)) {
            $this->controller->Session->setFlash(__('admin_login_required', true));
            $this->controller->redirect('/login/');
        }
    }
}
?>

プログラマーを求めて

一人だけでいいんだけどとにかくPHPプログラマーが見つからない。PHPの老舗、アシアルさんに相談したが、直近では空いている人がいないとのこと。しかし同じくPHP老舗であり、PHPCon2012でもニアミスしていたアリウープの柏岡様を紹介していただいた。

なんとこのブログの記事を読んでいたとのことで、ぶっちゃけたところからお話することができてとても話が速かった。しかしアリウープさんもお忙しいとのことでまだ決まるかどうかわからない状態。

それとは別で以前、ちょっと一緒にやっていくのは難しそうと書いた会社様より、「具体的に何が悪いのか、どういう人材を探しているのか説明して欲しい」との電話があり、上司の方とお話した。

どうも、こちらの話しているユニットテスト/自動テストと先方の話しているテストが違っているような気がする。

「よくわからないけど要はそういうツールがあるんですよね?だったらうちもできますよ。普通やりますよね、自動テスト」という感じ。

「でも先日見せてもらったコードにはユニットテスト無かったですよね?」(ついでにいうと使ってるライブラリはPEAR DBとSmartyだけでしたよね?)というようにちょっと話が噛み合わなかった。

僕らはそんなにすごいプログラマーを募集してるわけじゃないんです。ただ、プライベート(仕事外)でコードを書いてるPHPプログラマーだったら誰でも歓迎なんです。

しかしSIerから離れて随分経つので忘れていました。世の中にはプライベートでコードを書かないプログラマーが沢山いるという事実を・・・。

レガシーPHP改善日記

この日記が終わるかもだと・・・?

ハハハ、あんなメール、俺にとってはご褒美だ。

関連:レガシーPHP改善日記シリーズ

(レガシー改善に関しては文章が長くなるのでいつもと違って、〜だ、〜である口調で書きます。)

bake

CakePHPははじめてなのでところどころハマったが基本的なCRUDは出来た。というかbakeで殆ど生成されるのだが。CakePHP1.3のbakeはデフォルトでページングとカラム毎のソート機能が入っている。

しかし、「データがたくさんある時ページ分けして欲しい」というタスクが幾つかRedmineにあったし、ソースをみても既存の部分はbakeを使った形跡は無い。俺が初bakeか。

i18n

bakeにともなってi18nも使った(bakeで生成されるソースにはi18n前提だし)。現状、poファイルが無い。俺が初i18nか。

% cake bake i18n extract

こんな感じでソースからpotファイルを生成する。app/vendorsにPHPExcelというプラグインがあって、それの処理に死ぬほどメモリと時間がかかる。app/vendorsを除外する方法は無いものか。

ソースを見ると上記にpotを既存poにマージする機能があるっぽいが、やり方がわからなかったのでとりあえず初回はmsginitした。

macではデフォルトでgettextが入っているが、% brew link gettextしないとパスが通らないところが若干罠だった。

既存ソースに何故か

__('')
msgid ""
msgstr ""

みたいな空文字を翻訳している箇所が5箇所ぐらいあってmsginitが出来ない。どういう理由でこういうコードがあるのか不明だが、とりあえず消した。

validateエラーメッセージのi18n

modelでvalidateエラーメッセージをi18nしようとしたがsyntax errorが出る。何故?と思ったがphpではrubyと違ってインスタンス変数の定義は定義であって評価じゃない。初期値は書けるが関数の実行はできない。

要は下記はOKだが、

class User {
    public $name = 'komagata';
}

下記は駄目ということだ。

class User {
    public $length = strlen('komagata');
}

rubyを始めた頃はattr_accessorを見て「クラス定義内も普通に式が評価されるのか、凄いなー!」なんて思ったはずだが、それが当たり前になりすぎてそうじゃない言語が沢山あることを忘れていた…。(array()はOKなのでarrayは関数じゃなくてリテラルなのかな?)

class RegularOrder extends AppModel {
    public $validate = array(
        'interval' => array(
            array(
                'rule'    => 'numeric',
                'message' => __('must be number', true)
            )
        )
    );
}

何故これがsyntax errorなんだー!・・・みたいな。

FormHelperへの疑問

CakePHP1.3のbakeが生成するコードはFormHelperを使っている。railsのscaffoldと比較して疑問点がいくつかあった。

bakeが生成するviewのコードは下記のようになっている。(分かりやすく色々端折ってます)

<?= $form->input('interval') ?>

でも実際は下記のようにモデル名も書かないと動かない。

<?= $form->input('RegularOrder.interval') ?>

formはmodelを知ってるんだから自動で付けてくれてもいい気がするが、PHP4もサポートするせいでモデル名がわからないのかもしれない。それだったらPHP4サポートを切ったCakePHP2系だったら改善されてるのかも。

レガシー改善パターン "Partial Proxy Pattern"

既存のレガシーの中にリーダブルコードを書いていく為のショーモナイテクニックに勝手に名前を付けていきたいと思います。

既存のテンプレートの中の共通化したい部分はこんな感じで書かれてる。

<body>                      
  <?php include('../views/head-menu.ctp');?>
    <h2 class="title_1">注文入力</h2>

ファイル名と置き場所が分かり辛い。include(!)ではなく、CakePHPのElementを使って欲しい。かといって既存部分をいじったら他で何が起こるか分かったもんじゃない。怖い。

app/views/elements/admin/menu.ctp:

<?= $this->element('../head-menu') ?>

そこでこんな感じで本来置きたい場所に一旦既存のテンプレを読み込むだけのProxy的なElementを作る。そうすると、新しく実装するviewはリーダブルでいられる。

app/views/layouts/admin.ctp:

<?= $html->doctype('xhtml-trans') ?> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <?= $html->charset() ?>
    <title><?= $title_for_layout ?></title>
    <?= $html->css('admin') ?>
    <?= $scripts_for_layout ?>
  </head>
  <body>
    <?= $this->element('admin/menu') ?>
    <div id="content">
      <?= $content_for_layout ?>
    </div>
  </body>
</html>

レガシー改善パターン "CSS Proxy Pattern"

同じく、既存のCSS(style.css)はゴチャゴチャなので新しく作るview用のCSSはリーダブルにしたい。そこでこう!

app/webroot/css/admin.css:

@import url('style.css');

/* ここに新しいviewで必要なcssを書いてく */

レガシーCSSとリーダブルCSSを切り分けることが出来る。

絶賛募集中

僕と一緒にレガシー改善をしてくださるプログラマーを募集しています。常駐・受託、開発会社・フリーランス、派遣・バイト問いませんので是非お願いします。というか助けて欲しい・・・。詳しくはこちら

関連:レガシーPHP改善日記シリーズ

jitter + growlもいいけど、vimでもっと素早く直感的にエラーを知りたい。

そんな時はsyntastic

" .vimrc
Bundle 'scrooloose/syntastic'
:BundleInstall

syntasticはIDEみたいにエラーがあったら左にマークを表示してくれる。デフォルトでファイル保存時に走るので便利。

Terminal — vim — 88×26

coffeescriptは最初から対応してて、coffeeコマンドでsyntaxをチェックしてくれる。プラグイン形式になっているので自分で書けば何でも行けるハズ。coffeelintが走るようにしたいなあ。