つよく、やさしく、泥臭く生きていくブログ

日常とポエムと、ときどき技術

herokuにperlアプリをデプロイする

herokuにperlのアプリケーションをデプロイしたい。
herokuじゃない普通の環境であれば、cpanfile でcarton installするんだけど、herokuだとできない。
ブラウザ上のコンソールでちょっと頑張ったけど上手くいかない。
ウンウン悩んでたけど簡単にできる方法があった

あとは、デプロイ元のgithubリポジトリのトップ に適当なapp.psgi と、cpanfileを置いて、herokuでデプロイする。
すると 、デプロイ時に、cpanfileでcarton install したみたいに local ディレクトリが作られている。やったぜ。

DBのリレーション

人と人の関係も難しいし、DBのリレーションも分からない。
ので基本を確認することにした。
だいたい社内のそういうのって既に良い感じに使えるように作られてて、それを使うかコピペして微修正するだけなので、細かいことはよく分かってなかった。

特に見たいのはリレーションのところで、SQLでいうとjoinの動作?なところ。

  • belongs_to
  • might_have

のあたり

この記事はPerlDBIx::Classなんかを使う感じのやつです。

準備

  • とりあえずMySQLで適当なテーブルを作ります。
  • 人の関係はよく分からないのでプリキュアで例示します。
mysql> use PRECURE;
mysql> SELECT * FROM Series;
+-----------+-----------+
| series_id | name      |
+-----------+-----------+
|         1 | hutariha  |
|         2 | max heart |
|         9 | smile     |
|        15 | hug       |
+-----------+-----------+

mysql> SELECT * FROM Precure;
+------------+-----------+----------------+
| precure_id | series_id | name           |
+------------+-----------+----------------+
|          1 |         1 | black          |
|          2 |         1 | white          |
|          3 |         2 | shiny luminous |
|         55 |        15 | amour          |
|        102 |        13 | mofurun        |
+------------+-----------+----------------+

  • 穴抜けがあるのはあえて。 全部は面倒くさい
  • このテーブルでは問題があるけどとりあえずスルー。

    • ブラックとホワイトはMHにも含まれてるはずだがそれが表現できない
    • レギュラープリキュアに含まれないキュアモフルンのプリキュアIDを100番台に設定しているが、10数年後にはレギュラープリキュアが100人を超えるので困る。
  • とりあえず使えるか確認

# DB接続
use My::Schema;

my $schema =  My::Schema->connect(
    "DBI:mysql:$database",
    $user,
    $password
) or die "cannot connect to MySWL: $DBI::errstr";

# 一人目のプリキュアの名前を取得してみる
my $precure_rs =  $schema->resultset('Precure');
my $precure = $precure_rs->search({
    precure_id => 1,
})->first;

print $precure->name;    # black

基本

  • プリキュア名からシリーズタイトルを取得する
    • 地道にやるとこうなる
# 名前でPrecureテーブルを検索
my $precure = $precure_rs->find({
    name => 'black',
});

# 検索結果の情報からシリーズIDでSeriesテーブルを検索
my $series = $series_rs->find({
    series_id => $precure->series_id,
});

say $series->name;    # hutariha
  • シリーズタイトルからそのシリーズのプリキュアを取得する
    • 地道にやるとこうなる
my $series = $series_rs->find({
    name => 'hutariha',
});

my $precure = $precure_rs->search({
    series_id => $series->series_id,
});

while (my $rs = $precure->next) {
    print $rs->name, "\n";
}

#----
# black
# white

さらにプリキュア名からそのプリキュアの所属するシリーズの全プリキュア名を取るってなると手間が多い

リレーション

  • リレーションを設定すると、前述のことがさらりとかけるようになる。
  • シリーズタイトルからそのシリーズのプリキュアを取得する
my $precure = $series_rs->find({
   name => 'hutariha'
})->precure;

while (my $rs = $precure->next) {
    print $rs->name, "\n";
}
# ----
# black
# white
my $precure = $precure_rs->find({
    name => 'white'
})->series->precure;

while (my $rs = $series->next) {
    print $rs->name, "\n";
}
# ----
# black
# white

リレーションの書き方

スキーマ情報を書いてるファイルにこんな風に追記する。

  • Series.pm
__PACKAGE__->has_many(  # SeriesはPrecure をたくさん持ってる
    # 'precure' はこのリレーションを使うときの名前なので何でもいい
    'precure' => 'DB3::DBIC::Schema::Result::Precure', 
    # 2つのテーブルを紐付ける条件。
    # foreign はここではPrecure。selfがSeries
    {'foreign.series_id' => 'self.series_id'}
);
  • Precure.pm
__PACKAGE__->belongs_to(    # Precureはシリーズに属する
    'series' => 'DB3::DBIC::Schema::Result::Series',
    { 'foreign.series_id' => 'self.series_id'}
);
  • 1対多の関係
    一つのシリーズに複数のプリキュアが居る。
    一人のプリキュアは一つのシリーズに属する。(今はMHや5GoGoは考えない)
    • has_many
      • シリーズ has_many プリキュア
      • これで返ってくるのはResultSetクラス??。many持ってるので当然ながら複数で返ってくる。
      • なので、->first->name したり、->nextでイテレートする。
    • belongs_to
      • プリキュア belongs_to シリーズ
      • これで返ってくるのは、Result::テーブル名 のクラス??。うーんつまりまあ一個だけ返ってくる。
      • なので、そのまま->name できる。
  • 1対1の関係?
    • might_have
      手元でやってるときはSQLを吐かせても、belongs_toとの違いがいまいち分からなかったのだけど、 プロダクションコードでSQLを吐くようにして見てみると、belongs_toのときはJOINで、might_haveのときはLEFT JOINされているのが確認できた。
      具体的に何をどうしたところでそのJOINが出てるのかまでは追えてないが、そんな感じ。
      名前的にも確かに、絶対両方あるよねだからJOINでいいよね、っていうのとまあ多分あるでしょでもないかもだからLEFT JOINにしとくねっていう感じ。

参考

use DBIx::Class; - 今日のCPANモジュール(跡地)

DBIx::Class::Relationship - テーブル間のリレーションシップ

第3回 DBIx::Classでデータベース操作(1):Perl Hackers Hub|gihyo.jp … 技術評論社

JOINによるテーブルの結合方法5種類を整理 - 具体例で学ぶ数学

JOINについて

そもそものSQLでJOINの動作を確認する。 シリーズIDで結合させる。

  • 内部結合ってやつ
mysql > SELECT * FROM Series se INNER JOIN Precure pr ON se.series_id=pr.series_id;
+-----------+-----------+------------+-----------+----------------+
| series_id | name      | precure_id | series_id | name           |
+-----------+-----------+------------+-----------+----------------+
|         1 | hutariha  |          1 |         1 | black          |
|         1 | hutariha  |          2 |         1 | white          |
|         2 | max heart |          3 |         2 | shiny luminous |
|        15 | hug       |         55 |        15 | amour          |
+-----------+-----------+------------+-----------+----------------+

両テーブルにあるものしか出ない。( smile は出ないしモフルンも出ない )

  • 外部結合ってやつ
mysql > select * from Series se LEFT OUTER JOIN Precure pr ON se.series_id=pr.series_id;
Enter password:
+-----------+-----------+------------+-----------+----------------+
| series_id | name      | precure_id | series_id | name           |
+-----------+-----------+------------+-----------+----------------+
|         1 | hutariha  |          1 |         1 | black          |
|         1 | hutariha  |          2 |         1 | white          |
|         2 | max heart |          3 |         2 | shiny luminous |
|         9 | smile     |       NULL |      NULL | NULL           |
|        15 | hug       |         55 |        15 | amour          |
+-----------+-----------+------------+-----------+----------------+

この場合は、Seriesテーブルは全部出す。が、Precureテーブルに無いものはNULLになる。 RIGHTの場合は、Precureテーブルのを全部出して、Seriesテーブルに無いものはNULLになる。

mysql> select * from Series se RIGHT OUTER JOIN Precure pr ON se.series_id=pr.series_id;
Enter password:
+-----------+-----------+------------+-----------+----------------+
| series_id | name      | precure_id | series_id | name           |
+-----------+-----------+------------+-----------+----------------+
|         1 | hutariha  |          1 |         1 | black          |
|         1 | hutariha  |          2 |         1 | white          |
|         2 | max heart |          3 |         2 | shiny luminous |
|        15 | hug       |         55 |        15 | amour          |
|      NULL | NULL      |        102 |        13 | mofurun        |
+-----------+-----------+------------+-----------+----------------+

次に考えたい

  • プリキュアのテーブル構成はより正しくはこうなるはず
    一人のプリキュアが複数シリーズに参加している。
    (といってもMHと5GoGoだけなんだけど )
    なので、シリーズテーブルとプリキュアテーブルの他に、それらを関連付けるためのシリーズ-プリキュア-関係テーブルが必要。
    っていう場合にどう書けばよいか。
シリーズテーブル
+-----------+-----------+
| series_id | name      |
+-----------+-----------+
|         1 | hutariha  |
|         2 | max heart |
|         9 | smile     |
|        15 | hug       |
+-----------+-----------+

プリキュアテーブル
+------------+----------------+
| precure_id | name           |
+------------+----------------+
|          1 | black          |
|          2 | white          |
|          3 | shiny luminous |
|         55 | amour          |
|        102 | mofurun        |
+------------+----------------+

シリーズ-プリキュア-関係テーブル
+------------+-----------+
| series_id | precure_id |
+------------+-----------+
|          1 |         1 |
|          1 |         2 | 
|          2 |         1 |
|          2 |         2 | 
|          2 |         3 | 
|          3 |         4 | 
+------------+-----------+ 

LimeChatで特定の人の発言に反応する

普段LimeChatをよく使う。slack何それ状態。

キーワードに反応する機能はあるけれど、発言内容しか引っかけてくれない。
ある人の発言は全てすぐに気付きたかったのだけど、「誰かが発言した」ことに反応するのが設定で無さそうだった。
ので、LimeChatスクリプト機能で書けた。
スクリプト機能についてはLimeChatのヘルプで分かりやすい。 ちなみに普段はバルーンはOFFにしている。

 function event::onChannelText(prefix, channel, text)
 {
  // チャンネルオブジェクトを取得
  var c = findChannel(channel);
 
   // 特定ユーザの発言に反応する
  if ( prefix.nick == '名前') {
    // キーワードに反応した状態にする
    c.highlighted = true;
    // ウィンドウをピコピコさせる
    window.blinkTitle();
    // バルーン出すなら
    showBalloon(channel, 'メッセージ');
  }
 }

追記
なんでこれが必要だったかというと、チームメンバ(2人)がチームのチャットで発言したのをすぐに捕捉したかったから。
明確に呼びかけの意思があるときは
これ確認お願いしますー >名前
っていう風に発言するので元々のキーワード機能で事足りた。
だけど、
なんかエラーメール飛んでますね
みたいなのにもすぐに反応していきたかったから。

Test::mysqld で copy_data_fromの機能を使う

雑なブログの方が毎日続いていて偉い。

最近はテストを書こうとしていて、そんな中でTest::mysqldを使ってみて躓いたところを書く。

そもそもTest::mysqld

めっちゃ便利感あって、DBに入れたり消したり更新したりするような処理をテストするときって、テストで使ったデータのお掃除が必要になる。
でもこいつを使ってやると、テストのたびに新しくmysqldを立ち上げ、その中でDBの処理を行い、終わったら綺麗に消えてくれる。

ただし、新しく立ち上がるmysqlは初期状態なので、テストするDBと同じようにテーブル作ったりデータを流し込むSQLを流し込んでやらないといけない。
が、copy_data_from というメソッドがあり、これを使うと、実際のデータから同じ環境を一発で用意してくれる。すごい。

この辺とか見てたわけです。 Test::mysqld 0.17 でテストがもっと簡単になる話 - kazuhoのメモ置き場

今回の要件

MyApp/DB.pmという自作モジュールがあり、DBへの接続とかユーティリティ的メソッドを持っている。こいつをテストしたい。

とりあえずテストコード

留意点はコード中にコメントで書いた

こいつをテストしたい
package MyApp::DB

# $ENV{TEST_DSN}があれば見るようにする。
# こうすることで、テストやデバッグの際に任意のDBへ接続させられる
my $schema;
if ( $ENV{TEST_DSN} ) {
    $schema = MyApp::DB::DBIC::Schema->connect( $ENV{TEST_DSN});
}
else {
    my @dsn = ('dbi:mysql:mydatabase', 'USER', 'PASS');
    $schema = MyApp::DB::DBIC::Schema->connect(@dsn);
}
test.t

use Test::More;
use Test::mysqld;
use DBI;
use MyApp::DB;

$ENV{DBI_PASS} = 'hogehoge'; # 読み込ませるDBに設定されているパスワードを書く

# test 用 mysqld 起動
my $mysqld = Test::mysqld->new(
    my_cnf => {
        'skip-networking' => '',
    },
    base_dir       => 'tmp/test_mysql',
    copy_data_from => '/hoge/foo/mysql',     # 実際のDBのディレクトリをコピーしてきたものでOK。ただし、mysql.sockファイルは削除しておく。
) or plan skip_all => $Test::mysqld::errstr;


# test 用 mysqld 用の dsn を環境変数に設定する
$ENV{TEST_DSN} = $mysqld->dsn(dbname => 'mydatabase');
#  こうしてMyApp::DBをnewすると、MyApp::DBに書いた通り$ENV{TEST_DSN} のdsnにつないでくれる。
my $db = MyApp::DB->new();

print $db->mymethod();

上記で、$dbからMyApp::DBのメソッドをテストしてやると、テスト用mysqlに対して読み書きしてくれる。

躓いたところとコメント

  • コピーしてきたデータの不備
    実際のDBのディレクトリをコピーしてきたものを使おうとすると、「そのようなファイルはありません」的なエラーが出ていた。
    動いているDBのディレクトリからコピッてきたため、mysql.sockファイルがあり、これのせいだった。
    コピーしてきた中のmysql.sockは削除しておく。
  • 環境変数には文字列しかダメ
    元々はdsnには上記の@dsnのように配列の形式でやるようにしていた。
    なので、$ENV{TEST_DSN} に元々と同じように配列を入れてやったり、配列のリファレンスを渡してデリファレンスして使ったりしようとしたがダメだった。
    どうやら、$ENV{hoge}には単純に文字列しか入れられないようだ。ほんまかいな。違ってたら教えてください。
    で、実際のところ、dsnには文字列の形で指定することもでき、
    $mysqld->dsn(dbname => 'mydatabase'); をprintしてみると分かるが、文字列の形で返されている。
    なので問題なし。
  • パスワード
    Test::mysqldはnewするとDBに接続する動作がある。
    初期状態だったら問題ないが、ccopy_data_from によって実際のDBの状態を作ってくれるため、 実際のDBのパスワードが無いと接続に失敗してこける。
    そういう場合のため、$ENV{DBI_PASS} に実際のDBのパスワードを入れておいてあげると、 DBIの動作の中でこいつを使ってくれる。

ちなみに、パスワード直書きが嫌だったので、とりあえずパスワードらしく入力させる方法はこれ。

system("stty -echo");    # 入力を表示しなくなる
print "Enter DB password: ";
my $passwd = <STDIN>;      # 標準入力させる
system("stty echo");      #入力表示を戻す
chomp( $passwd );

なにわPerl でコードレビューされてきた

aenikuy.hatenablog.com

終わった後の飲み会懇親会でいい話を聞いたうちの一つで、雑なブログを作った。
いい意味で雑。雑の半分は雅の半分。

なにわPerlに参加してきた

イベント参加の後はブログ書くっていうのはやっていきたい案件なので書きます。
今日はタイトルの通り、なにわPerl に参加してきました。
今回はコードレビューをする回ってことで、事前に告知されてるお題を解くプログラムを、事前 or 当日に書き、 それを著名なPerlモンガーにレビューしてもらうという夢のような回でした。
今回のレビュアーはpapixさんでした。
メインのレビュアーはそうだけど、少人数だったのもあって参加者お互いにあれこれ言い合ってて良かった。

お題がなかなか面白い内容で、僕は訳あって事前に書けず当日にプログラム書いたのだけど、 できれば事前にじっくり書いておきたかったです。

とりあえずかいつまんで書く

例外処理の褒められ

初手でだいぶ嬉しかったこと。
お題としては、入力値の範囲は問題内容で決まっており、その前提でプログラムを書けばOKである。 なので範囲外の入力値は考慮しなくてもOKである。
でも僕は、怖いしやらなきゃダメでしょって気持ちで、入力値を受け取った直後で値をチェックして範囲外ならdieさせてた。
ほとんど癖で、そういうチェックを書いた。

レビューされる直前になって、お題としては不要だったし書かなくてもいい処理を書くのはこの場合どうなんだろうということに思い当たり一瞬不安になった。
 ところが、僕のコード見て最初にその値チェックの行を「良い。大事。」と言ってもらえました。
問題を解くアルゴリズムがどうとかコードの可読性とかじゃなくて、そういう身に染みついてることを肯定されたのがマジで嬉しかったし、そういうところの重要性の認識が合ってたのもよかった。

Smart::Args がよさそう

上記の値チェックについて、papixさんがお勧めしていたモジュール。そういうチェックがすごい楽になるっぽい。触ってみよ。

テスタビリティ

papixさんのコードは処理の分離をしっかりやっていて、「一つのことを上手くやる」みたいなところがよかった。 で、そういう風に処理が分けられているのでテストがしやすいみたい。
テスト書きたい。

プロ

コードレビューじゃないんだけど、「プロってなんなんだ」みたいなエモーショナルなこと僕が言っちゃって、 それにpapixさんと主催者のtomchaさんが答えてくれた。
僕が理解できたことをとにかく文字にしておくと、

  • それを仕事にしてお金もらっていられているならプロ。
  • プロといっても2軍、1軍、メージャーリーガーとかはある
    1軍だとしてもホームラン王もいればヒット製造機もいれば守備めっちゃ上手いとかいろいろあるね
  • プロは早く8割(ある程度以上のライン)を満たすものを作れる
    早さってのはたしかにプロらしい。早く出せばフィードバックがもらえて改善していけるし改善していけば8割が9割にもなる。
    100%のものってのは無い。趣味の域だと99%を求めることもできるかもしれないけどそれってコストバランス度外視になっちゃってるからプロっぽくない。(ごく一部の分野しか考慮してないけど)
     早く及第点のものを作るっていうの、小林賢太郎の本にも書いてあった気がする。 あっちはたしか期日よりだいぶ前にお客さんに観せられる完成品を作り、残りの期間で完成品を更新するみたいな。

良い日だった。

columnコマンドが便利そう

columnコマンドが手軽に便利だった。

ちょっとperlでゴリゴリっと処理して画面に出力して、| tee でログファイルに書くっていうのちょくちょく使うんだけど、実は画面出力を見やすくするために、printするところで地道に整形(のようなこと)をしている。
でもそれって前読んだUNIXという考え方的には良くないなと思った。
人間にとっての見た目よりも、単純な形式で出力するだけのスクリプトにしておいて、人間にとっての見た目は

$ ./script.pl | tee hoge.log | column -t

ってするのがベストなんじゃないかと思った。

以下、覚え書き

$ cat hoge.txt
hoge1
hoge2
hoge3
hoge4
hoge5
hoge6
hoge7
hoge8
hoge9
hoge10
hoge11

こんな場合は

$ cat hoge.txt | column -c 30
hoge1   hoge5   hoge9
hoge2   hoge6   hoge10
hoge3   hoge7   hoge11
hoge4   hoge8 

-c で横幅を指定できる。
並びが縦に順番になってるのを、横に順番にしたい場合は -x をつけてこう。

$ cat hoge.txt | column -x -c 30
hoge1   hoge2   hoge3
hoge4   hoge5   hoge6
hoge7   hoge8   hoge9
hoge10  hoge11

それより便利なのがこういう場合

$ cat foo.txt
domain_name, ipaddress, comment, comment2
domain1, xxx.xxx.xxx.xxx, aaa, ii'ii
domain2, yyy.yyy.yyy.yyy, hogehoge,fooooooooooo
hoge.domain.com,     zzz.zzz.zzz.zzz, 0, 1

-s で区切り文字を指定
-t でカラム数をいい感じにしてくれる

$cat foo.txt | column -t -s ,
domain_name       ipaddress             comment    comment2
domain1           xxx.xxx.xxx.xxx       aaa        ii'ii
domain2           yyy.yyy.yyy.yyy       hogehoge  fooooooooooo
hoge.domain.com       zzz.zzz.zzz.zzz   0          1

余計なスペースがあって微妙なのでこう

cat foo.txt |column -t -s ,' '
domain_name      ipaddress        comment   comment2
domain1          xxx.xxx.xxx.xxx  aaa       ii'ii
domain2          yyy.yyy.yyy.yyy  hogehoge  fooooooooooo
hoge.domain.com  zzz.zzz.zzz.zzz  0         1

-s の指定でいい感じにできなさそうだったら前段でsedとかで区切りをいい感じに置換しておくとよさそう。

「UNIXという考え方」を読んだ

評判どおりのめっちゃ良い本だった。。。
まじで良いことしか書かれてない。いいことづくし。
もっと早くにちゃんと読んでればよかった。

そもそも、帯に書かれているように考え方のことが書かれている。だからUNIXに限らず当てはまることがたくさんありそう。

そういえば序盤の序盤で、初期のUNIXに貢献した人々の紹介として我らがラリー=ウォールの名前も上がってました。

UNIXの考え方として重要な9つの定理と、いくらか重要度の低い10の小定理が書かれている。そんな中から特にわかりみ溢れる箇所について適当に書く。

定理1: スモール・イズ・ビューティフル
定理2: 1つのプログラムには一つのことを上手くやらせる

この2つは特に引用されたりして目にする機会が多い気がする。 小さいものはわかりやすくて保守しやすく、1つのことに専念させれば堅牢で、他のプログラムと組み合わせやすい。

一つのことをうまくやるようにアプリケーションを書けば、それは必然的に小さなプログラムになる。

一つのことを上手くやるようにプログラムを作れないのであれば、おそらく問題をまだ完全には理解していないのだろう。

モジュールや関数を書く時は普段から意識してる点ではある。普段はできるだけ「◯◯する関数」と言えるレベルにしようと努めてる。「◯◯して△△する関数」だとまだ単純にできるだろうと思う。けれど難しいところで、◯◯と△△を分ければその分 sub hoge {の行と my $self = shift; の行が余分に増え、肝心の中身は 1行程度になるようなとき、分けるほどか?と悩む時がある。といいつつ、あとから見るときに分けるほどか?というほど単純な関数は見た瞬間何を返すかが分かりやすかったりして
つまり、やっぱりスモールイズビューティフルなのだなと。
そういえば、「これ分けたほうが良くない?」っていうレビューは受けたことあるけど「分けなくて良くない?」は無い気がする。

定理3: できるだけ早く試作する

アジャイル的というかプロトタイピング的というか、なんとなく最近ぽいなと勝手に思ってた考え方がもう既にずっと前から言われてたってことですな。
もう31ページと43ページから45ページをまるっと引用したい。
試作することで何がうまくいくかと、何がうまくいかないかが分かる。
どれだけ仕様書を書いてもどんなものが出来上がるのかは想像するしかないのに対して、作ってしまえばそこからイメージを膨らませてもらえるし、気に入らない部分を具体的に教えてくれる。
これは今の職場ではやりやすいことなので、これからもとにかく早くに見れる・触れる状態で確認してもらうようにしたい。

定理6: ソフトウェアの梃子を有効に活用する
よいプログラマはよいコードを書く。偉大なプログラマはよいコードを借りてくる 勝者となる条件は、車輪を発明し直すことではなく、既存の車輪を光らせることにある

これほんと最近思うことで、自分でガリガリとコードを書いて実現するんじゃなくて、既にあるソフトウェアやwebサービスや、もちろんAWSとかもそうなんだけど、それらを上手く組み合わせて使って、 その組み合わせる部分とかちょっとした部分だけを自分で書くっていうのを上手くやれるようにならないとイカンなって思う。

定理8 過度の対話的インタフェースを避ける

たぶんこの辺だったと思うんだけど、UNIXは「ユーザは自分が何をしているのか分かっている」ことを前提としているらしい。UNIX以外は、何をしているのか分かっていないユーザのためにたくさんのヘルプやメッセージをだしたりしている。だそうだ。
「ユーザは自分が何をしているのか分かっている」のあたりを上手く的確に捉えられるようになりたい。というのも、アプリケーションに過剰に注意書きを書きたくないから。

定理9: すべてのプログラムをフィルタとして設計する

これ読んで「たしかに…」と思った。今後プログラム書く時に意識したい。
プログラムは、何らかのデータを入力として受け入れて、何らかの形でデータとして出力するものである。
シェルのコマンドがパイプでどんどん繋げられるのを意識すると分かりやすいかもしれない。 出力されるのはデータであり、データはまたなんらかのプログラムの入力になりえる。

あー、めっちゃハッとさせられたんだけどこう書こうとすると上手く咀嚼できてない感がある。
この、出力がデータだという点が今まで曖昧だった気がする。データ入力→データ出力じゃなくて、データ入力→何か動く みたいな考え方だったと思う。
たとえば、DBに入れるプログラムがあったら、
データ → SQLでDBに入れる ってイメージしてたけど、
データ → SQLを通す → DBのデータになる ってイメージが良いのかも。(SQLからDBのデータの間も幾つかプログラムがあると思うけどそこは割愛)

めっちゃいい本だったしちゃんと読んだのでこれからは人に勧められる