読者です 読者をやめる 読者になる 読者になる

日報をしゃべらせて聴くアプリ「Nippou Player for esa.io」を作った

みなさん日報書いてますか。

私が所属する Misoca でも、全員が毎日日報を書いています。ただ、メンバーが増えてくると、全員の日報を読むことが地味に大変だったりします。

日報を「聴く」

しかし読みたい。

もし、音楽のように日報を聴くことができたら、聴きながら作業もできるし読む辛さもなくなるかも。というわけで、勉強がてらアプリを作ってみたので紹介します。

Nippou Player for esa.io

f:id:hidakatsuya:20170303004032p:plain:w350

特徴

GitHub - hidakatsuya/nippou-player: A client for playing NIPPOU in esa.io.

  • esa.io の日報を音声として再生することができるデスクトップアプリ
  • Mac, Windows, Linux をサポート(ただし、Mac 以外は動作確認してない)
  • Electron, Vue.js 2.x, Photon, Webpack, axios, karma, mocha などでできている
  • 音声の再生は Web Speech API を使っている

必要なもの

Nippou Player は esa.io 上の日報を再生するアプリなので esa.io のアカウントが必要です。

使い方

Releases at Github より、パッケージをダウンロードできます。起動すると、

f:id:hidakatsuya:20170303010337p:plain:w350

こんな感じの設定画面が表示されるので、それぞれ以下のように設定します。

Access token for esa API

Nippou Player は esa API を使って日報を取得するので、そのアクセストークンの設定が必要です。アクセストークンの発行方法は以下を参考にしてください。

https://docs.esa.io/posts/102#3-0-0

日報一覧のパス

esa 上の一日分の日報一覧のパスを設定します。

日報/2017/03/03/
  ├ hidakatsuya の日報
  ├ hoge の日報
       :

例えば、日報をこのようなカテゴリで管理している場合は esa検索クエリの書式 を使って以下のように設定します。

in:日報/YYYY/MM/DD

YYYYMM は日付書式キーワードで、日報一覧を取得する際に日付に展開されます。「2017/3/3」の日報を取得するときは、

in:日報/2017/03/03

となる具合です。

この展開は Moment.jsmoment#format() によって行なっています。使えるキーワードは以下を参考にしてください。

Moment.js | Docs

以上で設定は完了です。メイン画面に戻ると日報一覧が表示されるはずなので、再生ボタンを押せばしゃべり出すはずです。

バグを踏んだりわからないことがあったら

ソースコードGitHub に公開しています。Pull request お待ちしております。または @hidakatsuya に質問してもらっても構いません。

github.com

作ってみて

感想

  • 今回初めて Electron アプリを作ったけど、別の OSS (thinreports-editor) でも Chrome Apps から Electron へ移行しようとしているので良い練習になった
  • 日報の内容をほぼそのまま Web Speech API に食わせているので、スピーチがわりと微妙
  • Vue.js の Single File Component が非常にわかりやすくて良い。次は Vuex を使って書き換えていきたいと思ってる
  • vue-clielectron-vue というテンプレートを使うと、babel とか webpack 周りが設定済みで、すぐにアプリ本体の開発に集中できて良い。ちょっと設定いじろうとすると何やってるかわかんなくて辛い部分もあるけど
  • UI は Photon が便利だった。が、微妙に足りてないところがある。メンテされていないのも気になるけど簡単で素晴らしい

今後

  • スピーチが微妙。ちょっと調べた感じ、やりようはありそうなので気が向いたら改善したいと思っている
  • ユースケース的にモバイルアプリに適しているツールなので、次は Swift の勉強がてら iOS 版を作ってみるつもり
  • TravisCI による自動ビルドがうまくいってなくてなんとかしたい。もしくは WerckerCI を使ってみようかと

Thinreports for PHP 0.8.1 をリリースした

まずは、軽く Thinreports for PHP の説明を。

Thinreports for PHP とは

Thinreports は、もともと Ruby 向けの帳票ツールとして作られた OSS で、私自身その開発者の一人だったりします。GUI ツールの Thinreports Editor で帳票レイアウトを作成し、Ruby ライブラリ thinreports-generator を使ってレイアウトをロードして PDF を生成します。

オープンソース PDF 帳票ツール for Ruby, Rails | Thinreports

Thinreports for PHP は、その thinreports-generator の PHP 実装となります。alpha リリースを経て、2015年11月に最初の正式版 0.8.0 をリリースしました。

github.com

こちらもご覧ください。

hidakatsuya.hateblo.jp

開発体制とか

開発は私一人でやってます。なかなかモチベーションが上がらず、今回のリリースも気がつけば1年越し。開発コミュニティの場としては、 Gitter の Community があります。開発に参加したい方、Thinreports に興味がある方は Gitter を覗いてみてください。

0.8.1

Bugfix が中心のマイナーアップデートです。詳細はこちらをどうぞ。

Release 0.8.1 · thinreports-php/thinreports-php · GitHub

変更点

ざっとご紹介しておきます。

#18 - Fix strokes and fills are not drawn correctly

  • 四角形・楕円形の背景色が PDF に描画されない
  • 四角形・楕円形・線形の色を none に設定しても PDF に描画されてしまう
  • テキストの色を none に設定しても PDF に描画されてしまう

これらの問題点を修正しています。結構クリティカルなやつです。

#19 - 0.8.x never supports .tlf generated with Thinreports Editor 0.9+

Thinreports Editor 0.9 以降、レイアウトファイル .tlf の内部フォーマットが大幅に変更されました。

github.com

Thinreports for PHP 0.8.x ではこの新しいフォーマットに未対応で、0.8.0 で新しいフォーマットの .tlf を読み込もうとすると、普通にプログラムエラーでコケてしまいます。そのため、 0.8.1 で IncompatibleLayout 例外をスローして原因をわかりやすくしました。

Next Major Version 0.9.0

現在開発中の 0.9.0 について少しだけ。

0.9.0 では、Thinreports Editor 0.9+ の新しいレイアウトフォーマットのサポートが中心となります。もちろん、古いレイアウトフォーマットへの互換性も維持します。

Support Thinreports 0.9 · GitHub

年内にリリースできればいいなー、とは思ってます。はい。

最後に

各種ツッコミをいただけるだけでモチベーションも上がって開発スピードが(きっと)上がります。興味のある方はぜひお試しください。

Thinreports 0.9.0 で .tlf ファイルの差分が取りやすくなった

2016.6.1 Thinreports 0.9.0 をリリースしました。

Thinreports 0.9.0 is out! | Thinreports - オープンソース PDF 帳票ツール for Ruby, Rails

0.9.0 以降の Editor では、.tlf ファイルが新しい形式で保存されるようになります。これによって、いわゆる「Diff 辛い問題」が解消されます。どういうことか簡単に紹介します。

古い形式

例えば、 test.tlf を Editor で編集して、一つのテキストブロックの文字色を #000000 から #ff0000 へ変更し上書き保存したとします。 0.8.0 以前の古い保存形式の場合、普通に diff を取ると次のような結果となります。

--- a/test.tlf
+++ b/test.tlf
@@ -1 +1 @@
-{"version":"0.8.2","config":{"title":"Test","option":{},"page":{"paper-type":"A4","orientation":"portrait","margin-top":"20","margin-bottom":"20","margin-left":"20","margin-right":"20"}},"svg":"<svg width=\"595.2\" height=\"841.8\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"none\" viewBox=\"0 0 595.2 841.8\"><g class=\"canvas\"><g class=\"s-text\" stroke-width=\"0\" fill=\"#000000\" fill-opacity=\"1\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-id=\"\" id=\"goog_45091349\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"72\" x-height=\"20.5\" x-left=\"20\" x-top=\"20\"><rect class=\"s-text-box\" stroke=\"none\" fill=\"#000000\" fill-opacity=\"0.001\" width=\"72\" height=\"20.5\" x=\"20\" y=\"20\"/><text class=\"s-text-l0\" xml:space=\"preserve\" stroke=\"none\" fill=\"inherit\" fill-opacity=\"1\" text-decoration=\"none\" x=\"20\" y=\"36\">\u30bf\u30a4\u30c8\u30eb</text></g><!--SHAPE{\"type\":\"s-tblock\",\"id\":\"text\",\"display\":\"true\",\"desc\":null,\"multiple\":\"false\",\"valign\":\"\",\"line-height\":\"\",\"line-height-ratio\":\"\",\"box\":{\"x\":20,\"y\":56,\"width\":164.1,\"height\":20.5},\"format\":{\"base\":\"\",\"type\":\"\"},\"value\":\"\",\"ref-id\":\"\",\"overflow\":\"\",\"word-wrap\":\"break-word\",\"svg\":{\"tag\":\"text\",\"attrs\":{\"x\":20,\"y\":72,\"xml:space\":\"preserve\",\"kerning\":\"auto\",\"letter-spacing\":\"normal\",\"id\":\"goog_45091350\",\"fill\":\"#000000\",\"fill-opacity\":\"1\",\"font-size\":\"18\",\"font-family\":\"IPAMincho\",\"font-weight\":\"normal\",\"font-style\":\"normal\",\"text-anchor\":\"start\",\"text-decoration\":\"none\"}}}SHAPE--><!--LAYOUT<g xmlns=\"http://www.w3.org/2000/svg\" class=\"s-tblock\" x-format-type=\"\" x-value=\"\" x-format-base=\"\" x-ref-id=\"\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-multiple=\"false\" id=\"goog_45091350\" x-id=\"text\" fill=\"#000000\" fill-opacity=\"1\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"164.1\" x-height=\"20.5\" x-left=\"20\" x-top=\"56\"><rect class=\"s-tblock-box\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"0.2\" width=\"164.1\" height=\"20.5\" x=\"20\" y=\"56\"/><text class=\"s-tblock-id\" font-size=\"10.5\" font-family=\"Helvetica\" font-weight=\"normal\" font-style=\"normal\" text-decoration=\"none\" text-anchor=\"start\" kerning=\"auto\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"1\" x=\"24\" y=\"67\">text</text></g>LAYOUT--></g></svg>","state":{"layout-guide":[]}}
\ No newline at end of file
+{"version":"0.8.2","config":{"title":"Test","option":{},"page":{"paper-type":"A4","orientation":"portrait","margin-top":"20","margin-bottom":"20","margin-left":"20","margin-right":"20"}},"svg":"<svg width=\"595.2\" height=\"841.8\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"none\" viewBox=\"0 0 595.2 841.8\"><g class=\"canvas\"><g class=\"s-text\" stroke-width=\"0\" fill=\"#000000\" fill-opacity=\"1\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-id=\"\" id=\"goog_45091349\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"72\" x-height=\"20.5\" x-left=\"20\" x-top=\"20\"><rect class=\"s-text-box\" stroke=\"none\" fill=\"#000000\" fill-opacity=\"0.001\" width=\"72\" height=\"20.5\" x=\"20\" y=\"20\"/><text class=\"s-text-l0\" xml:space=\"preserve\" stroke=\"none\" fill=\"inherit\" fill-opacity=\"1\" text-decoration=\"none\" x=\"20\" y=\"36\">\u30bf\u30a4\u30c8\u30eb</text></g><!--SHAPE{\"type\":\"s-tblock\",\"id\":\"text\",\"display\":\"true\",\"desc\":null,\"multiple\":\"false\",\"valign\":\"\",\"line-height\":\"\",\"line-height-ratio\":\"\",\"box\":{\"x\":20,\"y\":56,\"width\":164.1,\"height\":20.5},\"format\":{\"base\":\"\",\"type\":\"\"},\"value\":\"\",\"ref-id\":\"\",\"overflow\":\"\",\"word-wrap\":\"break-word\",\"svg\":{\"tag\":\"text\",\"attrs\":{\"x\":20,\"y\":72,\"xml:space\":\"preserve\",\"kerning\":\"auto\",\"letter-spacing\":\"normal\",\"id\":\"goog_45091350\",\"fill\":\"#ff0000\",\"fill-opacity\":\"1\",\"font-size\":\"18\",\"font-family\":\"IPAMincho\",\"font-weight\":\"normal\",\"font-style\":\"normal\",\"text-anchor\":\"start\",\"text-decoration\":\"none\"}}}SHAPE--><!--LAYOUT<g xmlns=\"http://www.w3.org/2000/svg\" class=\"s-tblock\" x-format-type=\"\" x-value=\"\" x-format-base=\"\" x-ref-id=\"\" kerning=\"auto\" letter-spacing=\"normal\" x-display=\"true\" x-multiple=\"false\" id=\"goog_45091350\" x-id=\"text\" fill=\"#ff0000\" fill-opacity=\"1\" font-size=\"18\" font-family=\"IPAMincho\" font-weight=\"normal\" font-style=\"normal\" text-anchor=\"start\" text-decoration=\"none\" x-width=\"164.1\" x-height=\"20.5\" x-left=\"20\" x-top=\"56\"><rect class=\"s-tblock-box\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"0.2\" width=\"164.1\" height=\"20.5\" x=\"20\" y=\"56\"/><text class=\"s-tblock-id\" font-size=\"10.5\" font-family=\"Helvetica\" font-weight=\"normal\" font-style=\"normal\" text-decoration=\"none\" text-anchor=\"start\" kerning=\"auto\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"1\" x=\"24\" y=\"67\">text</text></g>LAYOUT--></g></svg>","state":{"layout-guide":[]}}
\ No newline at end of file

これは辛い。主な原因は以下の通りです。

  1. 改行が取り除かれ、一行になっている
  2. レイアウトデザインは SVG がそのまま保存されている

(2) について少し説明が必要だと思います。.tlf の中身は JSON ではありますが、図形の位置やスタイルなどのデザインの状態は、SVG 形式で文字列として保存されています。下記は古い形式の .tlf の中身をわかりやすく整形したものですが、"svg" キーの値がそれです。

{
  "version": "0.8.2",
  "config": {
    "title": "Report Title",
    "page": {
      "paper-type": "A4",
      "orientation": "landscape",
      "margin-top": 0.0,
      "margin-bottom": 0.0,
      "margin-left": 0.0,
      "margin-right": 0.0,
    }
  },
  "svg": "<svg><g class=\"canvas\"><!--LAYOUT<rect x-id=\"rect_id\" x=\"100.0\" y=\"100.0\" width=\"200\" height=\"200\"/>--><!--SHAPE{ "type": "s-text", "id": "rect_id", "display": "true", "svg": { "attrs": {...} }}SHAPE--></g></svg>"
} 

当然、文字色の情報も "svg" キーの中のどこかに記録されていますが、単純な diff でその変更箇所を探すことは現実的ではありませんでした。

新しい形式

一方、新しい形式で diff を取ると以下のようになります。

--- a/test.tlf
+++ b/test.tlf
@@ -41,7 +41,7 @@
           "IPAMincho"
         ],
         "font-size": 18,
-        "color": "#000000",
+        "color": "#ff0000",
         "text-align": "left",
         "vertical-align": "top",
         "line-height": "",

一目瞭然。

新しい形式の詳細は下記で詳しく説明しています。興味がある方はどうぞ。

Diff が取れて何が嬉しいのか

いろいろあると思いますが、

  • git などの VCS で tlf ファイルの変更履歴が管理しやすくなる
  • Pull Request 等、コードレビューが捗る

などでしょうか。コードレビューが捗る、というか可能になったことは個人的にも本当に助かります。

まとめ

新しい形式に変更した理由は他にもありますが、その一つとして「Diff 辛い問題の解消」について説明しました。 今回の変更によって、Thinreports を使った開発が少しでもやりやすくなれば幸いです。

Thinreports for PHP を使って PHP7 で帳票や PDF を作る

これは PHP Advent Calendar 2015 21日目の記事です。

Thinreports for PHP については、まだ alpha 版だったときに下記 Qiita エントリーでも紹介していましたが、先日 0.8.0 正式版をリリースしたので PHP7 でのサンプルコードや、alpha 版からの変更点などを含め、改めて紹介したいと思います。

qiita.com

Thinreports for PHP とは

まず、Thinreports (シンレポーツ)について説明する必要があります。

Thinreports

Thinreports は、2010年ごろオープンソースとしてリリースされた、Ruby 向けの帳票・PDFツールです。下記のような特徴があります。

  • 国産 OSS
  • レイアウトデザインツール Thinreports Editor と、レイアウトファイルを読み込んで PDF を生成する Ruby 向けライブラリ Thinreports Generator for Ruby がセット

詳しくは 公式サイト や下記 GitHub をご覧ください。

github.com

Thinreports for PHP

今回紹介する Thinreports for PHP は、この Thinreports における PDF 生成ライブラリの PHP 実装ということになります。もちろん、オープンソースとして開発が進められています。

github.com

下記のような特徴があります。

  • バージョン 0.8.0 (2015.12.21 現在)
  • PHP 5.3 以降と 7 に対応
  • 日本語標準対応(IPAフォントをビルトイン)

PDF 生成までの流れ

Thinreports は mPDF のような HTML to PDF ではなく、別のアプローチを採用しています。大まかには下記のような流れで PDF を生成します。

  1. デザインツール Thinreports Editor を使って帳票レイアウトファイルを作成
  2. レイアウト作成時に、値を埋め込みたいところに名前付きの領域(テキストボックスみたいなもの)を定義しておく
  3. 作成したレイアウトファイルを Thinreports for PHP で読み込んで、名前付きの領域に値を埋め込むなどして PDF を生成するコードを書く

Hello World

では実際にやってみます。今回はせっかくなので PHP7.0.1 で試してみます。Thinreports for PHPREADME.md でも簡単に説明していますので、そちらも参考にしてください。

準備

PHP7.0.1 と Composer がインストールされていることが前提です。これらのインストール手順については割愛します。PHP については、サポートしている 5.3+ であれば問題ありません。

Step1. Thinreports Editor をインストール

Editor は Chrome アプリとして動作します。Google Chrome をインストールしていない場合はインストールしてください。Chrome の準備ができたら Chrome ウェブストア (無料)にアクセスして、右上の "CHROME に追加" をクリックすると Thinreports Editor がインストールできます。 このとき、 Google アカウントは必ずしも必要ではありません。

Step2. 帳票レイアウトファイルを作成

まず、Thinreports Editor を起動して、新規作成ボタンから新しいレポートを作成します。ここでは、用紙サイズや余白などはデフォルトのままとして OK をクリックします。

f:id:hidakatsuya:20151221000936p:plain

続いて、下記のようにテキストブロックを貼り付けて、フォントサイズを 48 にして、ID を hello_world としておきます。

f:id:hidakatsuya:20151221001123p:plain

保存ボタンを押すと、保存先を指定するダイアログが表示されるので、作成したレイアウトファイルを任意の場所に保存します。ここではデスクトップに hello_world ディレクトリを作成して、その中に layout.tlf という名前で保存したとします。つまり ~/Desktop/hello_world/layout.tlf に保存したことになります。

Step3. thinreports-php/thinreports-php のインストール

$ cd ~/Desktop/hello_world
$ composer require thinreports-php/thinreports-php

Step4. PDF 生成コードを書く

以下の内容で ~/Desktop/hello_world/hello_world.php を作成します。

<?php
require 'vendor/autoload.php';

$report = new Thinreports\Report('layout.tlf');

# 1st page
$page = $report->addPage();
$page->item('hello_world')->setValue('Hello World');

# 2nd page
$page = $report->addPage();
$page('hello_world')->setValue('Hello Thinreports')
                    ->setStyle('color', '#ff0000');

$report->generate('hello_world.pdf');

Step5. 実行する

$ cd ~/Desktop/hello_world
$ php hello_world.php

成功すると ~/Desktop/hello_world/hello_world.pdf に以下のような PDF ファイルが作成されます。

f:id:hidakatsuya:20151221011227p:plain

Quick Reference

この他にも色々なことができます。まだ公式リファレンスをまとめていないので、ここで逆引き形式の簡単なリファレンスをまとめておくことにします。

けい線や図形、テキストのスタイルを動的に操作する

Editor で描画した図形やテキストのスタイルを PHP で動的に変更することができます。

<?php
// なんでも
$page->item('any_object')->hide();
$page->item('any_object')->show();
$page->item('any_object')->setVisible(true);

// 四角形, 楕円形, 線形
$page->item('rect_id')->setStyle('border_width', 1)
                      ->setStyle('border_color', '#0000ff')
                      ->setStyle('fill_color', '#ff0000')

// テキスト, テキストブロック
$page->item('text_id')->setStyles(array(
    'color'       => 'blue',
    'align'       => 'center', // left, center, right
    'valign'      => 'bottom'  // top, center, bottom
    'font_size'   => 20,
    'bold'        => true,
    'italic'      => false,
    'linethrough' => true,
    'underline'   => false
));

複数のレイアウトを組み合わせる

Editor で複数のレイアウトを作成し、それらを使って一つの PDF ファイルを作成することも可能です。例えば、表紙のレイアウト cover.tlf と内容のレイアウト body.tlf で表紙付きの PDF を作成する場合は以下のように書きます。

<?php
# 内容のレイアウト body.tlf をデフォルトレイアウトとして指定
$report = new Thinreports\Report('body.tlf');

# 表紙のレイアウト cover.tlf でページを作成
$cover = $report->addPage('cover.tlf');
$cover('title')->setValue('Title');

# 内容のレイアウト body.tlf でページを作成
# `#addPage` の第一引数を省略した場合は、デフォルトレイアウトでページを作成する
$body = $report->addPage();
$body('content')->setValue('何かの内容');

$report->generate('result.pdf');

PDF データを取得する

PDF ファイルを作るのではなく、PDF データを取得することができます。

<?php
# :
$report->generate(); # => PDF data

日本語を出力する

Thinreports は、標準で日本語出力に対応しているため、レイアウト作成時に日本語フォントを設定すれば正しく出力されます。

f:id:hidakatsuya:20151221015157p:plain

ただし、選択できる日本語フォントは IPAフォントのみであることに注意してください。現状、任意のフォントを指定することはできません。

以上です。その他にも

  • ページ番号ツールのフォーマットを変更する
  • ページ毎に、ページ数のカウントから除外する
  • 空白のページを追加する
  • 簡易書式の設定を動的に変更する
  • 一覧表っぽいものを作る

など、まだまだいろいろな機能がありますが、これらについてはまた別の機会で紹介したいと思います。 本当は laravel で簡単な帳票アプリを作る方法を紹介したかったのですが。これについてもまた別の機会に。

最後に

いかがでしたでしょうか?PHP7 で実際に動かしたのは今回初めてだったのですが、ちゃんと動くようで安心しました(テストコードはもちろんパスしてる)。ぜひ、PHP7 と共に触っていただけると嬉しいです。不具合などは GitHub へどうぞ。

明日は nori0620 さんです。

参考リンク

株式会社 Misoca に入社しました

こんにちは、@hidakatsuya です。

2015年10月1日、 株式会社 Misoca に入社しました。

www.misoca.jp

Misoca では、島根県出雲大社があって砂丘が無い方)からリモートで 請求書作成サービス「Misoca(ミソカ)」 の開発に携わっています。詳しいことについては Misoca開発ブログ など、また別の機会にまとめるとして、今回は報告までにしておきたいと思います。

今後とも Misoca 共々よろしくお願いいたします。

誰?

最後に簡単に自己紹介しておきます。

github.com

fixtures のグループを作ることができる fixture_group という gem を作った

github.com

Rails には fixtures というテストデータを管理する機能が標準であります。今回作った fixture_group はその fixtures の拡張で、名前の通り、fixtures のグループを作ってグループ単位で fixtures をロードしたりできるようになるライブラリです。

使い方

README.md の説明そのままですが、例えば次のような感じで使うことができます。

fixtures グループの構成:

Rails.root/
  |- app/
  |- config/
  |    :
  `- test/
    |- unit/
    |    :
    `- fixtures/
      |- group_a/
      |  |- users.yml
      |  `- items.yml
      `- group_b/
         |- subgroup_b1/
         |  |- users.yml
         |  `- items.yml
         `- subgroup_b2/
            |- users.yml
            `- items.yml

テストコードで fixtures をロードする:

class FooTest < ActiveSupport::TestCase
  fixture_group 'group_a', :all

  def test_index
    # :
    assert_equal users(:user_a), @user
  end
end

class HogeIntegrationTest < ActionDispatch::IntegrationTest
  fixture_group 'group_b/subgroup_b1', :users, :items
  # :
end

README でもこの程度しか説明していないので、詳しい使い方は下記テストコードを見てください。ちなみに、関連とか ERB など、通常の fixtures の機能も普通に使えると思います。

fixture_group/test at master · hidakatsuya/fixture_group · GitHub

インストール

Gemfile に下記の行を追記:

gem 'fixture_group', group: :test, require: false

インストール:

$ bundle

test_helper.rb などで読み込む:

require 'fixture_group'

既知の問題点

通常、fixtures は test/fixtures のようなディレクトリ内にテーブルごとの yml ファイルを作成すると思います。その場合、実テーブルとそれに対応する yml ファイルは 1:1 なので、fixtures 標準では一度ロードしたテーブルデータはキャッシュされる仕組みになっています。

しかし、fixture_group を使う場合、実テーブルと yml ファイルが 1:1 にならないため、強制的に標準のキャッシュ機能を off にする必要がありました。厳密には、キャッシュさせないようにしてるのではなく、 このコード でやってるように、キャッシュされてるかどうかの問い合わせに対して常に No と返事する*1ようにモンキーパッチしています

ベンチマークしてないのでどの程度かわかりませんが、fixture_group を使わない場合に比べるとほぼ間違いなく遅くなると思います。良い方法は全く思いつかないですが、解決できるといいなぁと思ってはいます。

*1:fixture_group 管理外の yml は除く