Shoyan blog

Guardでrubocopを自動化する

RubyのシンタックスチェックにRubocopを使っていて、リポジトリにpushした時にチェックするようにしている。
push→シンタックスエラー→直してもう1回pushというのがまどろこしいのでguard-rubocopを使ってファイルを保存する度にチェックするようにしてみた。
ストレスが軽減され、良い感じだったので紹介する。

guard-rubocopの導入

gurad-rubocopを導入するにはまず、rubocopを導入している必要がある。
この記事ではrubocopは導入済みという前提で進める。

gurad-rubocopの導入はいたって簡単。
3分で導入できるので是非やってほしい。

Gemfileに以下を定義する。

1
2
3
4
group :development do
  gem 'guard'
  gem 'guard-rubocop', require: false
end

bundle install を行うと、guardコマンドが利用できるようになる。
bundle exec guard init rubocopGuardfile が生成される。

これでインストール完了。

あとはターミナルで bundle exec guard コマンドを実行する。
その状態でファイルを変更するとそのファイルを対象にrubocopコマンドが実行される。

オプション

デフォルトだとgurad起動時にrubocopコマンドが実行されるようになっている。
起動時に実行したくない時はGuardfileに以下の設定を行えばよい。

1
guard :rubocop do

以下のように変更する。

1
guard :rubocop, all_on_start: false do

他にもオプションがあるので詳しくはREADMEを参照してほしい。

関連記事

Jekyllチュートリアル

最近、ブログをリニューアルしようとJekyllをちょこちょこ触っている。
ブログを作る上でJekyllで知っておいたほうが良いことをまとめていきたいと思う。

Jekyllとは

Jekyllは静的なサイトを作ることに特化したツールだ。
Ruby製でGithub pagesでも利用されている。
内部的にはLiquidが使われている。
Liquidはテンプレートエンジンで変数や制御文(foreachとか)を使うことができる。
Jekyllはそれをラップしたものだ。
Jekyllの何が便利かというと、サイトの設定をymlファイルで管理したりJekyllコマンドでMarkdownからhtmlファイルを生成したりすることができるという点だ。
また、サードパーティ製のテーマを利用できたりもする。

ドキュメント

Jekyllはドキュメントがなかなかしっかりしているので、公式のドキュメントを見るのがよい。

日本語版も用意してある。

まずドキュメントを探してみて、わからなければググるという感じで自分は調べている。

目次

  • これから追加していきます。

機械学習ことはじめ

機械学習をやるぞ!と息巻いてから半年が経とうとしている。
進捗はてんでダメ。ほとんど勉強せずに時間だけが過ぎてしまった。
さすがにこれではいけないと学習を始めた。

とりあえず学ぶにも何かしらの教材がないと効率が悪いと思い、基礎的な理論が勉強できそうな 「機械学習理論入門」を買った(半年前に)。
機械学習の理論を優しく書いてあるはずなのだが、高校数学の理解もままならない自分の数学力では理解に苦しみ、本棚の肥やしとなっていた。
この段階では機械学習にも色々種類があるんだなということがわかった。

次に機械学習理論の入門としてわかりやすそうなやる夫シリーズを読みだした。

まずは最小二乗法についての話が出てきた。
これはそれとなく理解できたので、最小二乗法を使って誤差を求めるプログラムをRubyで実装してみたりもした。

しかし、最急降下法でつまづいてしまった。
最急降下法に出てくる合成関数の微分がよく分からない

そもそも合成関数ってなんだというところで、合成関数について勉強した。
合成関数は関数の引数が関数になっている関数だった。

関数: y=g(x)
合成関数: y=g(f(x)) ←こういう感じで引数が関数となっている

やる夫シリーズを理解するには微分の知識が必要だということがわかったので、微分について勉強するために数学ガールを読んでいる。微分について会話形式で学んでいけるので、なかなかわかりやすい。

というところで、現状は微分を学んでいる。

だいぶ遠回りしている感がある。
道のりは険しい。

Railsで日付をいい感じ(スラッシュ区切り)に表示する

日付は日本だと /(スラッシュ)区切りが一般的なのでそのように表示したい。
しかし、RailsでDateオブジェクトやDateTimeオブジェクトを表示すると以下のような感じになってしまう。

1
2
3
4
5
pry(main)> Date.today.to_s
=> "2016-11-10"

pry(main)> DateTime.now.to_s
=> "2016-11-10T17:47:01+09:00"

strftimeメソッドで書式を指定すれば良いのだが、面倒だしイケてないように感じる。

Rails国際化APIのI18nにまさにというメソッドがあったので紹介する。
その名もlocalizeメソッドだ。
localizeメソッドはDateオブジェクトやDateTimeオブジェクトを現地のフォーマットに変換する。

1
2
3
4
5
pry(main)> I18n.localize(Date.today)
=> "2016/11/10"

pry(main)> I18n.localize(DateTime.now)
=> "2016/11/10 18:05:04"

ちなみにlocalizeの省略形で l というエイリアスが用意されている。

1
2
3
4
5
pry(main)> I18n.l(Date.today)
=> "2016/11/10"

pry(main)> I18n.l(DateTime.now)
=> "2016/11/10 18:07:24"

PHPUnitの後処理でテーブルをTRUNCATEする

PHPUnitでテスト用のレコードを作成するのだが、テストで作成したレコードが残ってしまい再度テストを行うと失敗するという現象に遭遇した。

以下のようにAppTestCase を作成してカスタマイズしている。
setup() メソッドで datasets ディレクトリにymlファイルがあればその内容でデータを作成している。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class AppTestCase extends PHPUnit_Extensions_Database_TestCase
{
    static private $_pdo = null;
    private $_conn       = null;
    private $_dataSet;
    private $_obj;

    protected function setUp()
    {
        $fixturePath = 'datasets/' . get_class($this) . '/' . $this->getName() . '.yml';
        if (file_exists($fixturePath)) {
            $this->_dataSet = new PHPUnit_Extensions_Database_DataSet_YamlDataSet($fixturePath);
        } else {
            $this->_dataSet = new PHPUnit_Extensions_Database_DataSet_DefaultDataSet();
        }
        parent::setUp();
    }

    public function getConnection()
    {
        if ($this->_conn === null) {
            if (self::$_pdo == null) {
                self::$_pdo = new PDO(DB_DSN, DB_USER, DB_PASSWD);
            }
            $this->_conn = $this->createDefaultDBConnection(self::$_pdo, DB_DBNAME);
        }
        return $this->_conn;
    }

    public function getDataSet()
    {
        return $this->_dataSet;
    }
}

PHPUnitのドキュメントにはTRUNCATEが実行されると書いてあるが、TRUNCATEはテストデータが作成される前に行われるので最後に作成されたレコードは残ってしまう。

MySQLサーバのクエリログは以下。
ドキュメント通りデータが作成される前にTRUNCATEが実行されている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2016-11-08T07:47:26.906619Z        26 Connect   root@192.168.75.91 on app_test using TCP/IP
2016-11-08T07:47:26.907174Z        26 Query     SET NAMES 'ujis'
2016-11-08T07:47:27.136497Z        27 Connect   root@192.168.75.91 on app_test using TCP/IP
2016-11-08T07:47:27.248506Z        28 Connect   root@192.168.75.91 on  using TCP/IP
2016-11-08T07:47:27.249034Z        28 Init DB   app_test
2016-11-08T07:47:27.256189Z        28 Init DB   app_test
2016-11-08T07:47:27.256837Z        28 Query     select * from users
2016-11-08T07:47:27.258065Z        28 Init DB   app_test
2016-11-08T07:47:27.258473Z        28 Init DB   app_test
2016-11-08T07:47:27.331138Z        27 Query     SET FOREIGN_KEY_CHECKS = 0
2016-11-08T07:47:27.331548Z        27 Query     TRUNCATE `users`
2016-11-08T07:47:27.349437Z        27 Query     SET FOREIGN_KEY_CHECKS = 1
2016-11-08T07:47:27.349922Z        27 Query     SHOW COLUMNS FROM `users`
2016-11-08T07:47:27.350848Z        27 Query     SHOW INDEX FROM `users`
2016-11-08T07:47:27.351590Z        27 Query     INSERT INTO `users`
                (`account_id`, `domain`, `created_at`)
                VALUES
                ('1', ‘example.com', '2016-11-02 18:40:10')
2016-11-08T07:47:27.354645Z        28 Init DB   app_test
2016-11-08T07:47:27.355089Z        28 Init DB   app_test
2016-11-08T07:47:27.355386Z        28 Query     select * from users
2016-11-08T07:47:27.360299Z        26 Quit
2016-11-08T07:47:27.391481Z        27 Quit
2016-11-08T07:47:27.431965Z        28 Quit

テストを実行した後にTRUNCATEするには getTearDownOperation メソッドを追加する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class AppTestCase extends PHPUnit_Extensions_Database_TestCase
{
    static private $_pdo = null;
    private $_conn       = null;
    private $_dataSet;
    private $_obj;

    protected function setUp()
    {
        $fixturePath = 'datasets/' . get_class($this) . '/' . $this->getName() . '.yml';
        if (file_exists($fixturePath)) {
            $this->_dataSet = new PHPUnit_Extensions_Database_DataSet_YamlDataSet($fixturePath);
        } else {
            $this->_dataSet = new PHPUnit_Extensions_Database_DataSet_DefaultDataSet();
        }
        parent::setUp();
    }

    public function getConnection()
    {
        if ($this->_conn === null) {
            if (self::$_pdo == null) {
                self::$_pdo = new PDO(DB_DSN, DB_USER, DB_PASSWD);
            }
            $this->_conn = $this->createDefaultDBConnection(self::$_pdo, DB_DBNAME);
        }
        return $this->_conn;
    }

    public function getDataSet()
    {
        return $this->_dataSet;
    }

    // テスト後にテーブルをTRUNCATEする
    public function getTearDownOperation()
    {
        return PHPUnit_Extensions_Database_Operation_Factory::TRUNCATE();
    }
}

あとは各UnitTestのtearDownメソッドで parent::tearDown() を実行する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class SampleTest extends AppTestCase
{
    public function setUp()
    {
        parent::setUp();
    }

    public function tearDown()
    {
        parent::tearDown();
    }
    .....
}

以下が変更後のMySQLのクエリログだ。
最後にTRUNCATEが実行されている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2016-11-08T08:43:49.367260Z       120 Connect   root@192.168.75.91 on app_test using TCP/IP
2016-11-08T08:43:49.367966Z       120 Query     SET NAMES 'ujis'
2016-11-08T08:43:49.626685Z       121 Connect   root@192.168.75.91 on app_test using TCP/IP
2016-11-08T08:43:49.743978Z       122 Connect   root@192.168.75.91 on  using TCP/IP
2016-11-08T08:43:49.744418Z       122 Init DB   app_test
2016-11-08T08:43:49.751453Z       122 Init DB   app_test
2016-11-08T08:43:49.752239Z       122 Query     select * from users
2016-11-08T08:43:49.758493Z       122 Init DB   aap_test
2016-11-08T08:43:49.759944Z       122 Init DB   app_test
2016-11-08T08:43:49.760656Z       122 Query     select * from users
2016-11-08T08:43:49.828928Z       121 Query     SET FOREIGN_KEY_CHECKS = 0
2016-11-08T08:43:49.829372Z       121 Query     TRUNCATE `users`
2016-11-08T08:43:49.852672Z       121 Query     SET FOREIGN_KEY_CHECKS = 1
2016-11-08T08:43:49.853435Z       121 Query     SHOW COLUMNS FROM `users`
2016-11-08T08:43:49.854605Z       121 Query     SHOW INDEX FROM `users`
2016-11-08T08:43:49.855462Z       121 Query     INSERT INTO `users`
                (`account_id`, `domain`, `created_at`)
                VALUES
                ('1', ‘example.com', '2016-11-02 18:40:10')
2016-11-08T08:43:49.863268Z       122 Init DB   app_test
2016-11-08T08:43:49.864301Z       122 Init DB   app_test
2016-11-08T08:43:49.864678Z       122 Query     select * from users
2016-11-08T08:43:49.869473Z       121 Query     SET FOREIGN_KEY_CHECKS = 0
2016-11-08T08:43:49.869882Z       121 Query     TRUNCATE `users`
2016-11-08T08:43:49.885689Z       121 Query     SET FOREIGN_KEY_CHECKS = 1
2016-11-08T08:43:49.888006Z       120 Quit
2016-11-08T08:43:49.914728Z       121 Quit
2016-11-08T08:43:49.941933Z       122 Quit