セキュリティ・ネクストキャンプ2020の応募課題をこっそり晒してみる

セキュリティ・キャンプというイベントがあり、これはIPAという組織が毎年開催している合宿形式でセキュリティを学べるイベントです。参加資格を得るためには応募課題に合格する必要があり、課題は以下のような自由記述形式です。

2020年度 セキュリティ・ネクストキャンプ 応募課題

以下について,フリーフォーマットで自由に記述し回答してください.

■ あなたに関する問い
あなたは今までどのようなことをやってきましたか.どのようなことができて,どのようなことが得意で,どのようなことに自信がありますか.
どのようなものを作りましたか.どのような情報を発信してきましたか.
どのようにしてそうしたことをやってきましたか.なぜ,そのようなことをやってきましたか.やってきてどう思いましたか.
参加できた場合,セキュリティ・ネクストキャンプにどのようなことを期待し,どのようなことをやってみたいですか.

■ 課題への姿勢に関する問い
自身で何らかの技術的な疑問を設定し,その疑問を解決しようと取り組み,その過程を示すことで,自身の技術力や課題に取り組むやりかたを説明してください.
(疑問の例:実行ファイルはどのような構造になっているのだろう? lsコマンドは何をしているのだろう? pingコマンドを実行すると何が起きるんだろう? といったようなことです)
設定する疑問は何でも構いませんし,解決しなくても構いません.
解決できたかどうかではなく,いかに課題に取り組むかという点を評価します.

■ 興味ある分野に関する問い
セキュリティ・ネクストキャンプの講義の一覧を見て,その中から興味のある講義を選び,その講義で扱うテーマに対して自分が考えること,興味,疑問,課題,自分なりの考察などを説明してください.
その分野について知識があるかどうかではなく,いかに興味や疑問を持ち,課題を考え,自分なりに調べて考察するかといった点を評価します.

■ その他に関する問い
その他,アピールしたい点などあれば自由記述で回答してください.

 

 (出典:

セキュリティ・ネクストキャンプ2020 オンライン 応募要項 :IPA 独立行政法人 情報処理推進機構 の 応募課題ファイルの一覧 のとこにあるファイル)

僕はめでたく合格してセキュリティ・ネクストキャンプ2020に参加できました。(ネクストキャンプというのは普通のセキュリティ・キャンプ全国大会より上の年齢層向けのものです。)キャンプ自体は今年はオンラインで行われ、無事終了しました。

ところで、「セキュリティキャンプ 応募課題」とかでググると回答を公開している方が何人かいらっしゃいます。どうせですし未来の応募者の参考になるかもしれないので僕も公開しちゃおうと思います。

まず最初に考えたこと

セキュリティ・ネクストキャンプ2020 オンライン 参加を目指す方へ:IPA 独立行政法人 情報処理推進機構

ここに選考の基準があります。引用しますと、

基本として、加点法で判断します。
自身の技術力ややってきたこと、発信してきたこと、何ができるかといったことなど、アピールしたい点を存分に記述してください。
多く書くことにより、「文章が長過ぎる・まとまりが無い・重複している・冗長」などとして減点するようなことはありません。 失敗した経験や落選の結果を書くことや、「文章が洗練されていない」「誤字脱字がある」などによる減点もありません。 アピールをいっぱい書いて、得をすることはあっても、損をすることはありません。自身がアピールしたいことを思い付くままにいっぱい書いて、アピールしてください。

「やる気があります」「もしも参加できたら頑張ります」というのは、書くだけならばすぐにできるので、アピールにはなりません。
いかにやる気があるか、参加できたらいかに頑張ることができるか、ということを示してください。「やる気があり、今までこうしたことをやってきています」「このようにいままでずっと手を動かしてきています」「今までこのような活動を継続してきています」ということを書くといいでしょう。それが、やる気を証明することになります。

課題については、課題が解決できたかどうかの○×や正誤で判断するのではなく、課題を解決しようとする過程や未知のことにどのように取り組むか、新たなことに挑戦しようとする姿勢を見ます。
このため課題は解決できなくても構いません。課題に対する正答を求めているわけではありません。途中経過でも構いませんので、いかに取り組んだか、難しい課題にこのように取り組んでここまではわかった、という、その過程を書いてください。

手を動かしてみてください。ネット検索で調べたことを張り付けるだけではなく、本当にそうなのかという疑問を持ち、自分で手を動かして、見て、試し、調べ、本当にそうなのかと深堀りして考えてみてください。ネット検索で出てきたことは、はたして本当なのでしょうか? 深堀りして理解し納得できているでしょうか? 自分の眼や手で確認したでしょうか? 他人に説明できるでしょうか? そして、そうしたことを書いてください。課題解決に対する、そのような姿勢を見ます。

 また、ネクストではないキャンプの応募課題晒しのブログを1つだけ読みました。とはいえちゃんとは読んでなくて雰囲気だけ感じ取りましたが。

これら2つの情報ソースから、僕は以下の結論を引き出しました。

「とにかくいっぱい書いて自分の思考力を見せつければok」

ということでそういう方針で文章を書くことにしました。具体的には思考過程をほぼ抑制ゼロの垂れ流し式でいっぱい書きました。この書き方はかなりスピードが出て好きなので、個人的にはやっててかなり楽しかったです。普通はもうちょい抑制を利かせて整ったものを書く必要があるので……。というわけでその結果が以下です、どうぞ。長いので全部追わずに雰囲気だけ参考にしていただければ。今見返してコメントも付けました。

(ちなみに、あまりにも恥ずかしい部分をちょっと削ったので以下は提出したものの完全な復元ではないです。)

あなたに対する問い

  とりあえず東京大学理学部情報科学科を卒業しました。これは一通りの基礎知識を備えているアピールですのでこれ以上触れません。

では特に何をしていたかです。私はシステムソフトウェアに関心があります。これはなぜかというと、この説明が結構難しいというか、感覚的に好きだからです。もう少し掘ってみると、このレイヤが好きな理由の一つはこのレイヤーが一番「本質的な仕組みっぽい」と感じているからだと気づきます。仕組みというのは何かを実現させるための機構や方法のことを指します。ここでいう実現されるべき何かというのはコンピュータをコンピュータたらしめるということです。システムソフトウェアの下のレイヤが入れ替わってもそれは「別の部品から作られたコンピュータ」ですし、システムソフトウェアの上のレイヤがなくてもそれは「やることのないコンピュータ」ですが、システムソフトウェアがそれぞれの部品、インタフェースを繋げて統合しなければそれはコンピュータとは呼べないのではないかと思っていて、そういう意味でシステムソフトウェアが自分にとっては一番本質的だと感じます。そして本質的なものは好きです。そういう理由で好きなんですが、でもハード屋からしたら多分ハードが一番コンピュータにとって本質的なわけで、結局これは感覚に過ぎないということはわかっています。

 なぜのところだけ長々言いましたがもう少し言います。具体的作業としては私は仕様書を読むのが好きです。これも理由はわからなくて、結局これも単に好きだから好きとしか言えない気はします。幼少期、私は家の電話機の説明書を繰り返し読んで暗記していた(そしてそれを親に言いまくって辟易させていた)という話があって、つまりそういうのを好む性向があるということでしょう。これは具体的に何をやってきたかにつながります。卒業論文では、組み込みデバイス上でのOSの開発をテーマとして行いました。ここでいうOSというのはほとんどベアメタルプログラミングに近い代物となり、この研究の実装フェーズの結構な割合は仕様書を読んで理解することに費やされました。あるいは、私は30日OS本を写経的に使ってフルスクラッチOS(https://github.com/iwannatto/x86os)を作っていたのですが、ここでもやっぱり仕様書を読むのが好きで、本ではマジックナンバーを置いてサラッと流しているFAT12フォーマット特有の部分も仕様書を探してきて本での記述と仕様書との対応関係を見たりしていました。要は仕様書を読むのが好きなのでそういうことをやってきたし、そういう意味で興味のあることは進められるしそこは自信があると言いたいです。こういうことはやってきてやっぱり楽しいなあと思います。好きがモチベーションでやっているので。

 発信はtwitter(@iwannatto)と、github(https://github.com/iwannatto)と、非常に低い頻度で書くブログ程度でしょうか。http://iwannatto.hatenablog.com/entry/2018/12/11/000009 ←これとか。文章を書くこと自体はかなり好きなので発信に抵抗はないんですが好きすぎて結構のめってしまうのでむしろ控えていて、控えすぎているという感じです。

 参加できた際に期待していることはやっぱり最初に述べた好きの理由の延長線上にあって、「本質的な部分の仕組みの理解」です。 組込みソフトウェアプラットフォームの理解というテーマはもう名前からしてそれ自体じゃんという感じはするのですが、これは単にどういうソフトウェアプラットフォームがあるかを単に知るというだけにはとどまらないと考えています。なぜなら、「その下にどういうハードウェアがあって」「どういうインタフェースを上に提供することが求められているのか」という上下の理解を必然的に伴うはずであるからです。そしてそれは最高なのでぜひやりたいです。

(いま自分でコメント)

いやまあなんでもいっぱい書くってなったらこうなるんだけどなんか恥ずかしいですね

課題への姿勢に対する問い

 普段調べてはいないけど考えてみたら気になるかもというテーマを例を見ながら適当に考えた結果、「プログラムを実行するときは何が起こるだろうか?」ということが気になりました。この疑問を解決することを課題としましょう。

 課題を前にしてまずやることは具体化や細分化だと思います。今回は課題のステートメント自体がふわっとしているのでまず具体化。とりあえず調べやすそうなlinuxってことにすると、「./hoge (hogeはelf)したとき何が起こるかを追ってみる」と具体化できる。自分の元々の知識から、「./hogeするのはシェル上なのでシェルが何かを呼んでいる」「何かというのはおそらくローダで、なんかいい感じに仮想アドレス上に配置してくれる」「配置が終わったら実行をexecveとかでやる」みたいな流れが想像できました。あとはこれのそれぞれが合っているか、流れが合っているかを確認して、自分が納得できる程度に詳細を詰められれば一応課題クリアということになるでしょう(到達点がふわっとしてますが、まあもともと課題のステートメントがふわっとしてるので……)

 とりあえず「bash プログラム 実行 流れ」とかで検索。それっぽいタイトルのページが出てくる。https://language-and-engineering.hatenablog.jp/entry/20110617/p1 そういえばexec以外にforkも大事だったなあと思い出す。ありがとう。僕の認識は割と合ってそうだな。このページは割とbashとかOSがやってくれることを記述している寄りなので探していることそのものではないけど、こういう雰囲気系のページを何個か見て雰囲気あってそうなことを確認する。

 雰囲気を確認したけど核心っぽい記事は見当たらないので、次にやれることとしてbashソースコードを読んでみる。http://git.savannah.gnu.org/cgit/bash.git/tree/ これか。execute_cmd.cとかいうめちゃくちゃそれっぽい名前のファイルがあるので見る。とりあえずexecveで検索すると何個かヒットする。

/* Execute a simple command that is hopefully defined in a disk file
somewhere.

1) fork ()
2) connect pipes
3) look up the command
4) do redirections
5) execve ()
6) If the execve failed, see if the file has executable mode set.
If so, and it isn't a directory, then execute its contents as
a shell script.

 なるほど。実際コードを軽く見てみると最終的にexecveを呼ぶっぽくなっているのでやっぱりこのコメントの流れはあってそうだと確認できた。

 ところでloadって書いてないけどローダはどこにいったんだろう?というかローダってそもそもなんだ?という気持ちになったのでいったんローダで検索をかける。https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%80 wikipedia。「ローダ (loader) は、プログラムやデータなどを、外部(他のコンピュータや外部記憶装置)からインタフェースなどを通して受け取り、主記憶装置やワークエリアに置く(ロードする)プログラムである。なお、それに加えてプログラムを起動できる状態にし、それを実行するというスタートアップなどと呼ばれる機能まで混同している者も多い。」らしい。混同してたな。まあでもメモリに置ければ良いのか。そしてこの記事の最下行にはこう書いてある。「Unixでロードを実行するのは、システムコールのハンドラ execve() である。」マジか。なんか別のローダってやつがあるのかと思ってた。もちろんwikipediaなので眉唾の気持ちを持ちつつ、でもexecveがうろ覚えだったのでここでようやくman page of execveを見る。
int execve(const char *filename, char *const argv,
char *const envp
);
あー、ファイル名、引数、環境変数だけで実行できちゃうのか。これは確かにメモリ配置とかはexecveの中でやってるなあ。

 というわけでlinuxのexecveを読む。といっても完全に辿れるわけではない(時間もないし。)https://sugawarayusuke.hatenablog.com/entry/2018/03/01/002131 それっぽいのを見つけた。先人の知恵は完全に活用しましょう。個人的な関心はメモリ配置周りなので、そのへんのことを多少わかればよしとします。読んでいく。なんかすごい複雑で長い。メモリ配置周りを全部追っても結構な分量になってしまうので、個人的に特に気になっていたメモリをどこからどうやって引っこ抜いているのかだけ調べることにする。execveがやっていることの大半は実はelfからデータを引っこ抜いて計算なりなんなりをして正しい配置先アドレスなりを求めている過程のようだ。本質っぽいのはelf_mapという名前の関数でelfの中身をプロセスの中にmmapしてるところ。あーそうか、forkした時点でプロセス空間としてメモリを持っているのか……。

 ここまでわかったことをいったんまとめると、「./hogeをするとシェルから渡された適切な情報とともにforkとexecveが行われる」「fork自体がアドレス空間をカーネルから持ってくる」「execveの中身で配置先とかを一生懸命計算して配置し、実行する」という流れらしい。ところでこれらのシステムコール呼び出しはstraceで辿れるはず。printf(“hello, world!\n”); return 0;するだけの実行ファイルhogeを作って、strace ./hogeをやるとこうなった。(完全ではないですが部分的な注釈を勝手に入れました。)

./hogeの起動。キャッシュに共有ライブラリの場所を聞く(たぶん)。

execve("./hoge", ["./hoge"], 0x7fff83631d60 /* 59 vars */) = 0
brk(NULL) = 0x556fb153c000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=92454, ...}) = 0
mmap(NULL, 92454, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f775e98d000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)

共有ライブラリを引っ張ってきて配置する。fd=3のやつがそれで、MAP_PRIVATEで引っ張ってきていて実質コピーなのでcloseしても大丈夫なんだと思う。細かくは見ないが無名領域を作ったり適宜権限(あるいは保護)を設定していたりしている。

openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f775e98b000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f775e38c000
mprotect(0x7f775e573000, 2097152, PROT_NONE) = 0
mmap(0x7f775e773000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f775e773000
mmap(0x7f775e779000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f775e779000
close(3) = 0

メモリ領域の保護を設定。

arch_prctl(ARCH_SET_FS, 0x7f775e98c4c0) = 0
mprotect(0x7f775e773000, 16384, PROT_READ) = 0
mprotect(0x556fafb0f000, 4096, PROT_READ) = 0
mprotect(0x7f775e9a4000, 4096, PROT_READ) = 0
munmap(0x7f775e98d000, 92454) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
brk(NULL) = 0x556fb153c000
brk(0x556fb155d000) = 0x556fb155d000

プログラム本体の実行と終了。

write(1, "hello, world!\n", 14hello, world!
) = 14
exit_group(0) = ?
+++ exited with 0 +++

こんな感じ。上でまとめた、「execveの中身で配置先とかを一生懸命計算して配置し、実行する」という流れは確認できたはず。あとexecveの前に何をやっているかも確認したかったのでstrace bash -c ./hogeをやったんだけどforkをしている形跡がない。もしや-cで渡したコマンドが単一だったらforkしないのかと思い、代わりに
strace -f -F bash -c "./hoge; echo fuga"
をやると以下のようになった(抜粋)

clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f2c1e01ca10) = 4980
strace: Process 4980 attached
[pid 4979] rt_sigprocmask(SIG_SETMASK, , <unfinished ...>
[pid 4980] getpid( <unfinished ...>
[pid 4979] <... rt_sigprocmask resumed> NULL, 8) = 0
[pid 4980] <... getpid resumed> ) = 4980
[pid 4979] rt_sigprocmask(SIG_BLOCK, [CHLD], <unfinished ...>
[pid 4980] rt_sigprocmask(SIG_SETMASK,
, <unfinished ...>
[pid 4979] <... rt_sigprocmask resumed> , 8) = 0
[pid 4980] <... rt_sigprocmask resumed> NULL, 8) = 0
[pid 4979] rt_sigprocmask(SIG_SETMASK,
, <unfinished ...>
[pid 4980] rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, <unfinished ...>
[pid 4979] <... rt_sigprocmask resumed> NULL, 8) = 0
[pid 4980] <... rt_sigaction resumed> {sa_handler=SIG_DFL, sa_mask=
, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4979] rt_sigprocmask(SIG_BLOCK, [CHLD], <unfinished ...>
[pid 4980] rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, <unfinished ...>
[pid 4979] <... rt_sigprocmask resumed>
, 8) = 0
[pid 4980] <... rt_sigaction resumed> {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4979] rt_sigaction(SIGINT, {sa_handler=0x55f026652160, sa_mask=
, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, <unfinished ...>
[pid 4980] rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, <unfinished ...>
[pid 4979] <... rt_sigaction resumed> {sa_handler=SIG_DFL, sa_mask=
, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4980] <... rt_sigaction resumed> {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4979] wait4(-1, <unfinished ...>
[pid 4980] rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=
, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, {sa_handler=SIG_DFL, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4980] rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=
, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, {sa_handler=SIG_IGN, sa_mask=, sa_flags=SA_RESTORER, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4980] rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=
, sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f2c1d630fd0}, {sa_handler=0x55f026655790, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f2c1d630fd0}, 8) = 0
[pid 4980] execve("./hoge", ["./hoge"], 0x55f026bb07b0 /* 59 vars */) = 0

 長々。要はforkではなくcloneをして、その後シグナル割り込みの設定をして、4980で./hogeを実行する(4979はwaitする)という流れらしい。cloneのフラグはCLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDなので単に親を起こすのとidを知るためっぽくて、メモリ空間の共有はやっていない。なのでたぶんcloneがそのままメモリ空間の確保になっているはず。

 とりあえずこんなものにしておきます。まだ掘る余地はかなりあるのですが、結構キリがない感じになってしまうので……。一応何か課題があったときの取り組み方を示すという目的はある程度達成できたと思います。ここまでのことをまとめると、「./hogeをするとシェルから渡された適切な情報とともにforkとexecveが行われる」「fork自体がアドレス空間カーネルから持ってくる」「execveの中身で配置先とかを一生懸命計算して配置し、実行する」という流れであると検索により推測し、straceでこれらを実際に確認した(forkはcloneでしたが)というのが今回行ったことのまとめになります。

 掘る余地がかなりあると言いましたが、具体的にはここから十分時間が与えられた場合に何をするかだけ説明しておきます。プログラムの実行時に何が起こるかというのが自分で設定した課題ですが、特に実行時にメモリがどうなるのかに興味が出てきています。これはつまり、各プロセスがどのようにメモリを管理しているか、それがforkやらなんやらのときにどうなるか、という2点を理解すればよいのだと考えます。そのため、まずcloneの実装を探すと思います。古いQ&Aですが 、https://unix.stackexchange.com/questions/87551/which-file-in-kernel-specifies-fork-vfork-to-use-sys-clone-system-call にてcloneとかの本体はkernel/fork.cにあるdo_fork()であると示唆されていて、実際に https://github.com/torvalds/linux/blob/master/kernel/fork.c を見るとdo_forkはあるのでここから読んでいきます。これの過程で触っているプロセスの情報っぽい構造体が多分プロセスの持っているメモリ管理と関係しているはずなのでそこも探っていく、ということをやると思います。後はそもそも起動時にメモリをどうやっていて、initプロセスからのforkは普通のforkなのか何か違うのか……みたいなことも気になってはいます。

(今自分でコメント)

なが。あとこれほんとにやりながら書いたので今見ると何考えてるかわかりにくい部分がめっちゃありますね。審査してくださった人に申し訳なくなってきた。

ただまあこんなんでも通ったっていうのは結構重要で、やっぱり審査をする人は(選考のポイントで仄めかされているように)知識量ではなく掘るプロセスを重視しているんじゃないかなと思います。上の文章、知識があるかどうかで言ったら全然ないですからね、「なんもしらねーうおおお調べるぞなんかちょっとわかったわ」って言ってるだけなんで。セキュキャン通った人はみんな「自分は強くないけど通ったから臆せず応募しよう!」って言っていて、それは多分知識量は関係ないよという意味だと思うし、僕もセキュキャン出すか迷ってる人がいたら同じことを言います。

興味ある分野に関する問い

 「組込みソフトウェアプラットフォームを理解する」です。僕は低レイヤに触れること自体が興味なのでこのテーマ自体はもろに興味ありなんですが、考えることとなると難しい。組み込みに触れたのは卒業経験のときとベアメタルでラズパイを触ろうとしていたときで、どっちもベアメタル寄りだったのでソフトウェアプラットフォームの知識はそう多くないんですよね。というわけでとりあえず調べてみないと始まらない。調べて自分なりの考察を書く場所にします。

 とりあえず「組み込みソフトウェアプラットフォーム」という世界一安直なワードで検索すると、組み込みプラットフォームを提供するぞという会社のホームページが出てきました。各社言っていることは違いますが、共通してそうな部分を抜き出すと「顧客の開発の手間をなるべく減らせる、かつ合目的な開発基盤を提供する」ということを言っている気がします。ちょっと見えてきた気がする。この合目的という言葉にまとめた部分がポイントですね。組み込みにも当然汎用OSみたいなものはあるのですが、組み込みにおける合目的ってメモリを小さくすることだったりリアルタイム性の確保だったりの厳し目な制約になることがあって、それだと汎用OSでは足りないことがある。そういう場合にもスムーズに開発なりができるようなソフトウェア基盤を提供するよ、というのがこれらの企業のやっていることなのでしょう。
 
 そして上を起点に考えると、汎用OSももちろんソフトウェアプラットフォームの一環ですが、それだけじゃなくて組み込みデバイスがデバイス特有の働きをちゃんとできるようにしてあげるのが組み込みソフトウェアプラットフォームなんじゃないかなと勝手に想像しました。組み込みデバイスって制約が厳しい代わりにハードウェアのリソースを一目的に全振りできたりするわけで、そういうのをやりやすくするのが組み込み特有のソフトウェアプラットフォームなんじゃないかなと思いました。

 考察だけしてもつまらないので一つ適当な例を探してみます。上はOS寄りっぽいことを言ったのでミドルウェアを調べてみます。ファイルシステムミドルウェアっぽいので「embedded filesystem」とかで検索。https://www.keil.com/pack/doc/mw/FileSystem/html/emb_fs.html 一番上にこういうのが出てきました。Embedded File System (EFS)。ざっくり読むと、NOR flash device上で動くことを前提としたファイルシステムのよう。これこそ上で言った組み込み特有の合目的な開発基盤を提供するソフトウェアなんじゃないでしょうか。たぶんこういうのが他の分野でも他のデバイス向けでも色々ある、というのが組み込みソフトウェアプラットフォームの世界なのではと推測しています。

(いまコメント)

これも思ったことを書いただけですね。これ時効だと思うので言うんですが、実は応募時点ではネクストの6科目は全部やるものではなくいくつか選ぶものだと思っていたので絶対やると決めていた組み込みのやつのことしか書いてないです。まあ選んで書く課題なので結果なんも問題もなかったとは思うんですが。

この組み込みの授業は実際めっちゃ良くて、組み込みのソフトウェアプラットフォームの見通しを得られた上に実際に触れる仮想環境も用意してもらって実際に触れたという至れり尽くせりでした。まだやりたいことが残っているのでいつかやるという気持ちだけ今も持っています(結構経ってしまったけど)。

その他に関する問い

 アピールはそんなないです。なぜなら上でほぼ出し切ったからです。(ここに一文書いてたんですが省略。)あとはatcoderを一時期やっていて水色程度まではいきました。(https://atcoder.jp/users/iwannatto

(いまコメント)

いやだって実際そうやし……。書いて損することはないので書いたほうが良い気はしたんですが、まあ設問の書き方的にもないならないで良いかなと勝手に思ったのと、あと自分が一番強い部分はシステム系の内容を掘るところだと思っているので、それより弱い内容を書いてちょっと加点を狙うよりは上の課題で認められないなら諦めていいかなと思ったので。まあ僕がめんどくさがっただけなので書きたいことがあるなら是非書いたほうが良いとは思います

 来年度以降応募する人向けのアドバイス的ななにか

 こうして見るとなんか恥ずかしいですね、こんなに恥ずかしいのに公開している僕の勇気を讃えてほしい

応募基準にも仄めかされているのとキャンプの雰囲気からも感じ取れるんですが、全体的に器用にまとめるというよりはなんか強い興味みたいなのをドリャーとアピールする方針でいったほうがいいんじゃないかという気がします。

ただ、僕のやつは結構長くウオーって勢いで書いた感じそのままの文章なんですが、別にみんながこうする必要はなくて、各個人が最適なアピールの形態を選ぶのが良いと思います。僕は自分の脳の使い方的にもアウトプットの仕方的にもウオー形式が良いと思ったのでこうしましたが、もうちょい絞って書いたほうが上手くアピールできるという人はそうしてもいいと思います(文章を洗練しても加点されないと基準に書いてあるので、あくまで自分のやりやすさのためですが)。他にネクストで晒している人とかがいないのでわからないんですが、多分僕のは平均より分量が多いんじゃないかなという気もする(かなり疑わしい)(違うかも)ので、この量(10000文字くらい、まあコード引用削ると7000~8000文字くらい?)書くのが必須だということはない気がします。文字数を増やすためだけに無理に引き伸ばしてもあんまり意味がないと思うので、書きたいだけ書くのがいいんじゃないでしょうか。

あとそういえば僕が応募したのはネクストで、これは全国大会修了者くらいのレベル感の人を対象に想定しているらしいのですが、だからといって全国大会修了者である必要はないようです。ていうか僕は全国大会修了者ではないので。ネクストのメンバーには修了者も修了者でない人も両方いたので、たぶん加味とかもされてないです(それはそう)。

なんかこうそんな感じです このページに辿り着いてるってことは応募する気あると思うので頑張ってください、セキュキャン楽しいですよ 楽しいですよって言っといてキャンプ参加記みたいなのは書いていないな 気が向いたら書きますが向かないと書きません

(おわりです)

年を越せ

何がそんなにめでたいというんだ?無事に年越しを迎えられるのがめでたいのなら他の瞬間だって同じだ、毎分毎秒祝うべきじゃないのか?

人生の本質はたぶん忘却にあって、無事のありがたみを年越し一点に押し込めることによって他の時間はありがたくなく過ごすことができます。でも別に無事でいることだって本当の人生を思い出したらそんなにありがたいことじゃないので年を越した瞬間だったとて。それが理由で僕は年越しをそんなに珍重しないのではないだろうかとふと思う。

(年越しをそんなに珍重しないというのは完全に嘘で、毎年ジャンプしたり何だったりを一応していた。高校までは。大学に入って一人で過ごすようになってからはもはやどうでもよくなり、結局は横に人間がいたりいなかったりするのが理由なのか?でも僕の親はもう随分前から年を食っていて、1/1へとかわる記念すべき24:00はいつだって寝ていた。かつては叩き起こしたりしていたけど普通にかわいそうだったのでやめました、親は大事にしましょう。)

よいお年をって言ってるのも忘却の一種でしょう、よいお年をって今年一年だけでいいわけないだろ、良い人生をって言えよ。まあ人生が良かったところで何もないんですが……。これも一年単位なんですよ、来年のことは来年の正月に。セクションで人生を埋め尽くすことができるんですよ、知っていましたか?俺は知っていた。さっきようやく気がついたんだ。

人間が一年単位で忘却に押し込めることは単に利便性が理由だと思うんだけど、何やら伝統とか道徳を帯びるのは気に食わないですよね。僕はそうです、かなり気に入らない!こう、正月に初詣に行くと、『あの人は「ちゃんと」正月を祝う人だ』みたいな評価を得られるのおかしくない?俺は必ず道で見かけた蛇を蹴り殺している*1んだけど、これを話したところでこのエピソードに「ちゃんと」はつかない。なんでだよ。蛇が干支だからか?「ちゃんと」=「許容的な」で表層を、膨大な非許容的振る舞いを本質に社会を構成しているくせによく言いますね、あるいはそうだからそう言うのかもしれないけれど。死骸と歴史を中心核に、初詣を一番外側に持つ地球様の球体の上でのうのうと営む暮らしはいかがですか?暮らし自体はいいですよ、人生は最悪ですが、としか言えんなこんなもん。

本当は一年以外といわず全部忘れたいので酒を飲みたいです。いつか落ち着いたらいきましょうみなさん。蛇と蛇以外の大勢がもう死んでしまったか、あるいは僕らが殺しました。彼らはもう生きていられなかったけど、我々はせめて次の巳年くらいまでは生きていたいですね。そういえば四半期って言葉があるなあ、なんでもまずは四等分を目指すのが筋が良いのかもしれないとふと思った。そうなると言う言葉が一意になってしまう!まあ子年の最後の言葉としてはふさわしいのか*2

良いお年を。

(おわりです)

*1:0/0

*2:一意だけにって言ったら斜にタコ殴りにされそうだけどまっすぐ生きていきたいので言います、一意だけに。

書くことがない

 文章を書くのが好きな割には全然書いていない。
 理由としてまず忙しいというのがあるのだが、しかし空き時間をスマブラなりyoutubeなりに突っ込んでいることを考えるとこれは真の理由ではなくて、実際のところ僕は忙しいと感じているときに文章を書いてはいけないと思っている。こっちが真因だ。
 僕の中での知覚として、文章を書くというのは頭脳を消費する行動であって、そうすると課題なり研究なりのやるべきことが残っているときに文章を書いてはいけないという枷がかかっている。同様に趣味でプログラムを書くとかもそうで、そのせいで全然プログラムを書けていない。advent calendarだって一つも参加できなかった。その割にスマブラはうまくなっていくし、youtubeで見た音MADの集積で僕の脳は満たされていく。本当にこれで良いのか自分でも全くわからない。

 でも今日はなんかその知覚がバグったので久々に文章を書こうと思う。さらっと5000文字くらい書いたらやめにしよう。あと僕は文章を書くときに直前に読んでいた文章の文体に極めて(本当に極めて)強い影響を受けるのだが、今日は江國香織なのでマジで珍しい感じになると思う。

 書くということだけ決めて書き始めたので微塵もテーマがないので近況でも書こうので。多いね?大学院に入ってからは主に研究・バイト・落研の三本柱が全部中途半端になってよくわからない状況になり日々停滞している気がする。多分キャパをちょっと超えていてでも自分ながら情けないけどどれも手放せる気がしなくて、いつかなにかいくつかを握りしめながらどこかへ落ちていく気もする。代名詞の例文!下線を引きましょう。

 近況はこんなもんにして、そうすると書くことが本当にない。最近自覚したのだが、僕はメッセージの決まった文章を書くのは迷いなく楽しく書けるのだが、自分でテーマを見つける・考えるということが割と不得手だ。つまりレポートはいくらでも書けるが、小説なんかが全然書けない。クリエイティブの欠片もない!この歳になって主体性がないのかという悲しみがあるし、これが就活で今ガッツリと足を引っ張っている。でも仕方ないじゃないですか、まあ多少は訓練でどうにかなるけど訓練してこれだし、やっぱり人によって生得的な得手不得手はあるんですよ。仕事ができる程度には主体性がありますよのエピソードを生成して乗り切るしかないし、実際仕事で真の主体性がどの程度求められるかと考えると甚だ疑わしいのでまあそんなもんでいい気もする。仕事ってふつう抽象度の高低はあれど何らか目的が与えられるし目的に向かう行動を考えて実行するのは別にできるんですよ。その一部でしかない問題発見能力・状況改善への行動の迅速さのことを主体性って呼ぶの、頭悪いのでやめたほうがいい気がするんだけど。

 思えば僕はいつもテーマがないと嘆いていた気がする。小中高のとき自分の好きなものがよくわからなくて漫然と時間を過ごしていたらその時代は本当にそれで全部終わってしまった。高校3年生になって急に受験に目覚め、第一志望合格というテーマを見つけられたのは本当に幸運だった。高校3年生の受験勉強漬けの期間は間違いなく辛かったが、前よりいくぶんわかりやすかった。
 大学に入るとこれまた本当に何もなくなってしまった。受験のとき僕は物理に適性があると思っていたのだが、残念ながら二次試験の当日に勘違いに気づいた。こうしてまっさらな未定の一年生として入学した。
 適当に授業を漁った挙げ句に情報科学というテーマを見つけたのもやっぱりこれまた幸運だった、だったにすぎない。そして情報科学のなかから低レイヤというかりそめの方向性を見つけ、そこに邁進している風を装って今までのうのうと生きている。僕は自分のこれが本物ではないと知っているし、休み時間はコントローラーを握る。
 人生にテーマを見つけられたひとは無上に幸運だと思うし、早くそれになりたいと思っている。テーマがないとブログに書くことが自分語りしかない。いま記事一覧を見てげんなりしているところです!

 随分散漫だったがそろそろ畳もうと思う。5000文字ってさらっと書ける分量じゃねえよ。2500文字くらいしかないけど許してほしい。というかいま見返すと本当に散漫でひどいと思う。まあ僕の人生にくっついている細かいやるせなさをぽろぽろ払って拾い上げただけなので面白い形にならない。なんかテーマくれれば一塊を提示できると思うんでtwitterで投げてもらってもいいですよ。僕はこのようにして募り結局書かなかった過去をうずめてその上に生きています。

 ここまで読んでくれた人いらっしゃるかわからないんですけど、もしいらっしゃったら本当に感謝したいです。こんなものをここまで読んでくださるとは。そんな感謝の気持ちを込めて、一つだけ有益な情報を書かせていただきます。

お題「#買って良かった2020

 発表!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

 急に何?

 いや、なんかはてなブログって日替わりなのか何なのかわからないんですけどお題を出してくれてるんですよね。機械的に出されたお題なんてと思っていたけどそれでもあると凄く良いですね、縋っちゃうなあ。優秀賞はアマギフかなんかもらえるらしいのでこの文章を読んだ皆さんは各自この文章を優秀だと思ってください。

 ところで有益情報と申したのは嘘ではなくて、この枕はお題の通り僕のこの1年の買い物の中で間違いなく一番良いものでした。元々安い枕を使ってたせいかもしれないですが、寝付きが信じられないほど良くなった。毎日安定して体感半分程度の時間で寝られる。勿論人によって合う合わないあると思うのですが僕には凄まじい効果がありました。なんかストレートネックの人には特に良いらしいって噂を聞いたけど噂なので知りません。以上有益情報でした。いかガシガシ弟子でしたか?

 そんな感じです。もう書くことないな。というわけで、さっきまで散々言ってきたけどもう一度。

 書くことがない。

(おわりです)

人生で五指に入るボカロ曲

 今日めちゃくちゃ頭が痛くてなにもできない。なにもできないのでtodoとかは全部諦めて好きなことをやろうとした結果これをやることにした。結局僕は音楽の知識とかが今でも全然ないので音楽的なことは一切言えなくてしかも歌詞にもそんなに興味がなくて歌詞に感動したとかそういう事もあまり言えないので曲をただ並べるだけになるんだけど、まあなんとか並べた。全然関係ないんだけどこういう記事を本当はもっと書いていきたい、好きな曲なんてその時々で猛烈に移ろってゆくのでそれのスナップショットを撮っておくと後で嬉しくなったりするんじゃないかと思う。どうなんだろうね。でも自分の過去のブログとか見ないなあ。

 そういうわけで今この時点で選ぶ僕の人生で五指に入るボカロ曲を並べましたどうぞ。順番は思いついた順なので適当。6曲あります。

人造エネミー

https://www.nicovideo.jp/watch/sm13628080

カゲプロの曲の中では一番好きかなあ。カゲプロが流行ってた頃僕は逆張りオタクで、「ガキくせ〜〜〜〜」とかいいながら忌避していたせいでカゲプロの物語はほとんど追っていなかったし今でも知らないんだが、それとは関係ないものとして曲はかなり聴いていた。知ったのがカゲロウデイズだったこともあって最初の4曲を特に繰り返して聴いていて、その中で結局一番印象に残っているのがこれだったりする。曲全体に漂う寂寥感と虚無感と諦念がすごくいい。「それが最善策じゃないことを きっと君は知ってる」というフレーズは流石に心に残ってしまう。

めめめめめ

https://www.nicovideo.jp/watch/sm23815763

割とこう「とっておき」の曲だったりする。サビに入るまではしっとり抑えめという感じなんだけど、サビがめちゃくちゃいい。これも曲全体にかなり諦念のようなものが漂っていて、そういうのが好きなのかもしれない。この曲は大学に入ってから知ったんだけどどういう経緯だったか忘れてしまった。アイワナじゃないんだよな確か。知ってから結構長い期間これに支配されていたし、今でもこれは一番かもと思う。

とおせんぼ

https://www.nicovideo.jp/watch/sm7357463

これは思い入れがめちゃくちゃ深い曲、なんてったって一番最初に知ったボカロ曲なので。最初ボカロ曲というのは放送委員が流してくる耳障りかつ気まずくなる曲という忌避の対象だったんだけど、I wanna be the Revolutionをプレイしていたらこの曲に出会った上にめちゃくちゃハマってしまった。僕は結局アイワナプレイヤーとしては全然大したことなかったんだけど、耐久アイワナの動画を大量に見たおかげでそこから随分多くのボカロ曲を知ることができた。アイワナがなければ僕のボカロ趣味はなかったもしくは広がらなかったので、アイワナにはかなり深い感謝がある。曲自体は思い入れが深すぎてもうなんて言っていいかよくわからん。歌詞のニュアンス未だに全くつかめてないんだけど、つかめない人の曲なのかもしれない。wowakaさんは今でもすごく好きなアーティストです。

秘密

こういう曲名なのではなく単に人に教えないというだけです。じゃあ書くなよという感じですがその通りなのでその点に関して特に言うことはない。耐久アイワナ聴かなければなければ絶対知れなかった曲で、高校の頃はこれととおせんぼばっか聴いてた気がする。

マインドイブ | サイコセンス

https://www.nicovideo.jp/watch/sm32924916

https://www.nicovideo.jp/watch/sm30743292

これどういう意味かというと2つの曲です。いや絶海さんの曲の中から1曲かな〜と思ってたんだけど全然絞れんかった。僕にはすごく好きなアーティストが2人いて、wowakaさんと絶海さんです。この曲はどっちも歌詞は一昔前の厨二病かってくらい暗かったり悪魔的(そのまま)なんですが曲のテンションが異様に高くて、その齟齬がある種狂ったような印象をもたらしていてすごく好きです。あとこのキンキンするような感じの調声がめちゃくちゃ好き。どうしようね。

この曲も何で知ったか全然覚えてないなあ。大学に入ってからだし耐久アイワナではない。僕は高校までの頃はCD売ってるブースに行くとかそういう文化を知りもしなくてwowakaさんの姿を見られずじまいで後悔があるんだけど、その後悔を生かして絶海さんには超会議で一度お会いしたしサインCDも持っているので最強です。

 

こんな感じか。6曲出したけど実質5曲なのでセーフ。最初はもっと12曲くらい挙げてハミ出芸をしようとしてたんだけど「五指」を意識すると意外となかった。どうも僕の脳内では五指のごくわずか下の「五指ではないけど人生曲」みたいなとこに100くらいの曲がある感じの分布になってて、「これを入れるなら他100曲も入れる必要が出てくる」みたいな感じで外したのが多かった。その中でも、明らかにいい曲なのはわかってるけど思い入れ補正が足りない曲の群(砂の惑星ドラマツルギー、乙女解剖、ハウトゥー世界征服あたり)と、個人的にめちゃくちゃ気に入ってるんだけどこれは思い入れと思いたいだけなんじゃないかという謎の疑念がストッパーしてくる曲の群(さよならワンウェイハート、才能シュレッダー、グーパースターあたり)の2種類があるっぽい。自己分析ですね。まあこれ個人的思い入れ補正ランキングなので……。

どうでもいいけど最近僕はボカロをめっきり聴いていなくて(粗品の曲だけ聴いたけど)、自分の中の音楽を聴くためのリソースというのは全てytpmvに向けられている。のでめちゃくちゃ久々にボカロを聴いたんだけどめっちゃいいですね。また戻ってきてしまうかもしれなくてやばい。やばくないけど別に。まあ最近ボカロ聴いてないせいで思い入れ補正ランキングにするしかなくて無難な選曲になってしまったんだけど、本当は「今ハマっている曲」とかにしたほうがスナップショットという目的は果たせるしそっちのほうが面白い曲を紹介できるのでそういうのもやりたいですね。ちなみに僕の文章におけるやりたいというのはやらないという意味です。

本当にどうでもいいけどここまで書いたことにより頭痛が完全に治った。自分語りは頭痛にいいってマジ?どうせ書くならもうちょいマシなものを書きたいんだけどなあ、小説書くのとかやりたい……。ちなみに僕の文章におけるやりたいというのはやらないという意味です。

(おわりです)

OCaml print tips: %a, Format module

こんにちは納豆です。18erです。

この記事は

ISer Advent Calendar 2018 - Adventar

の11日目として書かれました。昨日はlmt-swallowさんの

SIS (SHE IS SUMMER) を語る – やっていく気持ち

でした。

概要

わりと19er向けのテーマなんですが、来年の関数・論理型プログラミング実験/コンパイラ実験のときに知っておくとちょっとだけ嬉しいかもという話をします。具体的には、その時使うであろうOCamlという言語で何かを出力する時に使えるちょっとした知識、「%a」と「Formatモジュール」のお話をします。今はそもそもOCaml触ってないしピンとこないよという場合でも、この記事の存在だけ覚えておくとちょっとしたお役立ちが生じるかもしれませんし、もしくは生じないかもしれませんね。排中律

導入

3年の冬学期にあるコンパイラ実験という講義で、「MinCamlを改造し、Syntax.t や Normal.t等の中間結果を出力できるようにせよ」という課題がありました。ここではこの課題のかわりに、Syntax.tをざっくり簡略化した以下の型を出力してみましょう。

type t = 
  | Int of int
  | Add of t * t
  | Sub of t * t

死ぬほど単純化しました。3人くらい死んだ。で、どういう出力が欲しいかというと、適度にインデントされてるやつが欲しいわけです。

ADD
  ADD
    INT 1
    INT 2
  INT 3

こういう感じですね。リアス式AST。これをocamlでどうやって実現するか?という話です。

そもそもの話として、ocamlにはvariantを出力するための汎用的な関数みたいなのが存在しません。しかも今回はいい感じにインデントもしたいわけです。そうすると当然、地道にパターンマッチしつついい感じに整形する関数を手書きすることになります。

let rec print_space n = Printf.printf "%s" (String.make n ' ')

let rec print_syntax_sub indent e =
  print_space indent;
  match e with
  | Int(i) -> Printf.printf "INT %d\n" i
  | Add(e1, e2) ->
      Printf.printf "ADD\n";
      print_syntax_sub (indent + 2) e1;
      print_syntax_sub (indent + 2) e2
  | Sub(e1, e2) ->
      Printf.printf "SUB\n";
      print_syntax_sub (indent + 2) e1;
      print_syntax_sub (indent + 2) e2

let print_syntax e = print_syntax_sub 0 e

(t型の値にeという名前を使っていますが、expressionのeです。)
そんでもって、これは特に問題なく動作します。ということはこれでいいわけでして、実際僕はこれと同じようなものを提出しました。
ただこのやり方だとAddとSubで同じようなことを書いているのがかなりキツいです。というのも、簡略化されていないSyntax.tというのは

type t = (* MinCamlの構文を表現するデータ型 *)
  | Unit
  | Bool of bool
  | Int of int
  | Float of float
  | Not of t
  | Neg of t
  | Add of t * t
  | Sub of t * t
  | FNeg of t
  | FAdd of t * t
  | FSub of t * t
  | FMul of t * t
  | FDiv of t * t
  | Eq of t * t
  | LE of t * t
  | If of t * t * t
  | Let of (Id.t * Type.t) * t * t
  | Var of Id.t
  | LetRec of fundef * t
  | App of t * t list
  | Tuple of t list
  | LetTuple of (Id.t * Type.t) list * t * t
  | Array of t * t
  | Get of t * t
  | Put of t * t * t
and fundef = { name : Id.t * Type.t; args : (Id.t * Type.t) list; body : t }

という感じ。AddSubFAddFSubと延々コピペもどきが続くことになり、まあ憂き目なわけです。

この重複を除きたいということで、「variant名の出力」と「インデントの整形」を分離するのは自然です。

let rec print_space n = Printf.printf "%s" (String.make n ' ')

let ename e =
  match e with
  | Int _ -> "INT"
  | Add _ -> "ADD"
  | Sub _ -> "SUB"

let rec print_syntax_sub indent e =
  print_space indent;
  match e with
  | Int(i) -> Printf.printf "%s %d\n" (ename e) i
  | Add(e1, e2) | Sub(e1, e2) ->
      Printf.printf "%s\n" (ename e);
      print_syntax_sub (indent + 2) e1;
      print_syntax_sub (indent + 2) e2

let print_syntax e = print_syntax_sub 0 e

こうなります。いい感じですね。ほんとはここで「これだとあんまよくないですね、こういう時に使えるのが……」みたいに話を繋げようとしたんですが予想外にいい感じだったのでどうしようかなと思っています。まあともかく、これをもうちょっとだけ簡潔にする余地があるんです。

%a

printfの引数たる「フォーマット文字列」においての話なのですが、%dで数字を文字列の中に埋め込めるのと同じく、実は%aで任意のオブジェクトを文字列の中に埋め込めます。ただ、そのためには出力用の関数を一緒に渡してやる必要があります。この関数は「第一引数にチャンネルを、第二引数にオブジェクトを受け取る関数」です。ここは具体例で済ませますが、

Printf.printf "%d" 1;;
Printf.printf "%a" (fun oc i -> Printf.fprintf oc "%d" i) 1;;

この2つが等価です。
さて、これを使って書くとこんな感じになります。

let rec space n = String.make n ' '

let ename e =
  match e with
  | Int _ -> "INT"
  | Add _ -> "ADD"
  | Sub _ -> "SUB"

let rec pr_e indent oc e =
  match e with
  | Int(i) -> Printf.fprintf oc "%s%s %d" (space indent) (ename e) i
  | Add(e1, e2) | Sub(e1, e2) ->
      Printf.fprintf oc "%s%s\n" (space indent) (ename e);
      Printf.fprintf oc "%a\n" (pr_e (indent + 2)) e1;
      Printf.fprintf oc "%a" (pr_e (indent + 2)) e2

let print_syntax e = Printf.printf "%a\n" (pr_e 0) e

%aをやるために無理やり部分適用とかしてしまった。わかりやすいかと言われるとあんま変わらないなという感じですが、まあ出力がフォーマット文字列と関係ないところで起こらないという点では統一感があるのかも。

Formatモジュール

上のやつでちょっと不満なのが、インデント情報が目につくというところですね。ここをもっと簡潔に書けないでしょうか。書けます。Formatモジュールを使いましょう。

Formatモジュールについてですが、「日本語の解説がマジで無い」という特徴があり、しんどいです。というわけで、英語の文書として公式チュートリアルの解説公式ドキュメントに当たります。前者は概要編、後者は詳細編という感じですね。以下あんま詳細には立ち入らないですが、概要編の中身を要約してお伝えします。

まずFormatモジュールのすごく重要な概念として、下の2つがあります。

  • boxes
    そのまんま「箱」です。箱は出力からは直接は見えない論理的な区切りで、入れ子にもできます。Formatモジュールは基本的に、「箱をopen」→「箱の中でいろいろ出力」→「箱をclose」という感じで使います。箱の嬉しいところは、「箱の中で改行すると箱の先頭と頭を揃えてくれる」「箱の中で改行したときの追加インデント量を箱ごとに設定できる」という点です。先のSyntax.tの出力に使えそうですね。
  • break hints
    これは「区切り」です。boxの中でbreak hintを出力すると、Formatモジュールがいい感じに判断して「改行」か「スペース」かを選んで出力してくれます。上の例だとあんま活きないですが、このいい感じの判断こそがFormatモジュールの売りみたいです。

いちいち箱をopenしてわちゃわちゃというのはめんどくさそうですが、実際にはFormat.printfとかの整形出力関数においてはこれらの動作を表すアノテーションがあるので、簡単なケースならラクできます。具体的には、 Format.printf "@[hoge@ fuga@]" とかやると、「箱をopen」→「print_string "hoge"」→「break hint挿入」→「print_string "fuga"」→「箱をclose」という意味になってくれます。

以下、これらについてもう少しだけ。

Boxes

箱には種類が4つあって、それぞれbreak hintsの扱いが違います。

  • horizontal(h)
    絶対改行しないマン
  • vertical(v)
    絶対改行するマン
  • vertical/horizontal(hv)
    改行なしで全部いけるなら改行しない、無理なら全部改行するという極端マン
  • vertical or horizontal(hov)
    右側スペースに余裕があるときは改行せず、余裕がなければ改行する。空気読めるマン

下のプログラムではvを使いますが、基本的にはhovが標準という想定らしいです。箱の種類はアノテーション@[<hov 1>みたいな感じで指定できます。(数字は「箱の中で改行した時の追加インデント量」です)。@[@[<hov 0>と等価です。

Break Hints

@;<n m>で、「改行されなければn個のspace、改行されたらm個のspaceで追加インデント」という指示となるbreak hintを挿入できます。この「改行された時のインデント量」はboxとは別に持っていて、実際に改行されたときはboxのものとの和になります。

あと@, ( = @;<0 0>), @ ( = @;<1 0>)というアノテーションもあります(後者は@の後ろにスペースです)。

使ってみる

let ename e =
  match e with
  | Int _ -> "INT"
  | Add _ -> "ADD"
  | Sub _ -> "SUB"

let rec pr_e ppf e =
  match e with
  | Int(i) -> Format.fprintf ppf "%s %d" (ename e) i
  | Add(e1, e2) | Sub(e1, e2) ->
      Format.fprintf ppf "@[<v 2>%s@,%a@,%a@]" (ename e) pr_e e1 pr_e e2

let print_syntax e = Format.printf "%a@." pr_e e

さっきocだった部分がppfになっているのは、Formatモジュールではout_channelの代わりにformatterというものを使って出力を行うようになっており、これの慣習的な名前がppf (多分pretty print function)だからです。formatterは出力先以外にも右側マージンの大きさとか同時にopenできる箱の総数とかFormatモジュール固有な情報を含んだ型みたいです。まあfprintfにそのまま渡せばいいので今回は関係ないですが。
あと@.は「箱を全部閉じてバッファをフラッシュして改行」を表します。

結果として、フォーマット文字列以外の部分は簡潔になったかなという気がします。でもフォーマット文字列がアノテーションでごちゃっとするので総合的には微妙な気がしますね。

最後に

有益情報を提供したかったのだが、思いの外使いやすくもなかったので豆知識紹介おじさんみたいになってしまった。
明日はtera_poonさんです。

(おわりです)

コミケに行ってきました(C94)

朝起きて、少し朦朧としている時間。それは、取り戻すための時間なのかもしれない。
空っぽで抑圧された鈍い頭に、自分の使命を蘇らせるための。

目覚めた瞬間の意識というのは、産まれたばかりと同じようなものなんだと思う。
なにもないし、なにもわかっちゃいない。
だから、束の間開放されていた自我を、ちゃんと自分に縛り付ける営みを。

8月10日、朝起きて、少し朦朧として。そして取り戻して、今日は何も予定がないことを知る。
どうするかな、と青い鳥を眺める。
そして、




コミケ行ったことねえから行ってみっか!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!」




と思いました。

思い立ったが吉日って言いますよね

いやまあ朝起きたら予定ないしコミケでも行ってみっかって気持ちになったというだけの話なんですが、国際展示場です。

まず駅に着くまでなんですが、もう既に人がいっぱいいる。最寄りじゃないとこの駅で列みたいなのができてやがる。
まあでもそんなにすごいことにはならず、国際展示場駅に到着。
展示場駅はなかなか凄く、人間が大量にいた。大量にいすぎてびっくりしたけど写真は撮りそびれた。ほげー。

ところでコミケの入場って「ザ・列」みたいなイメージをtwitter経由で持っていたのですが、実際のところそれは大量の開場待ち勢が入場し切るまでの10:00~12:00くらいの話で、それ以降は別に一切並ばずとも普通に入場できるんですよね。人がいっぱいいると言ったけど、通路がそれなりに広いので待つ感じではなかった。まあそういうわけでわっちゃわちゃですね。

ところでテンションが上りまくってツイートをいっぱいしていたので、ここからはツイートを軸に振り返っていきます。

コミケは暑いという100億回リピートされた話題があり、でも別に炎天下屋外待機勢がキツいというだけで入ってしまえばそんなでもないだろうと勘違いをしていたのですが、完全に甘かった。確かに屋内だし日光的なキツさはないのだけど、尋常じゃない量の人間がいるので人間の熱で暑いんですよね。あと普通の屋外の暑さとは違って妙に湿気のあるムワッとした暑さで、なんか体力を奪う感じがあるというか、これは慣れてないとだいぶ不快だった。慣れればそんなでもないんだけどね。ただ行ってみて初体験で予想外の現象が起きるとダメージがでかいという話があり、割と早い段階で消耗してしまった。ポカリもすぐ使い切った。
あと↑のツイートを見ればわかるように明らかに迷ってるんですが、これは会場mapとかを一切見ずに人の流れに身を任せていたからです。

周遊していたらブースに着きました。ブースっていうのはなんか机が大量に並んでいて人がモノ売ってるゾーンで、つまり本質です。 別の同人誌即売会には行ったことがあって、方向性というか質的雰囲気はそれとほぼ変わらなかったのですが、人間の量が段違いなのでそこは体験に違いを及ぼしますね。基本的にみんなエネルギーを放っているのですが、延々広がっているフィールドで大量の人間がエネルギーを放っていると謎の一体感が出ます。

これは知らん。

これはすごかった。200円。ちなみにこれラス1だったらしい。 どういう本かというと、はじめと終わりに1~2pくらいの漫画があり、中身は霊夢口調でびっしり字で民法の説明をしているというものです。「これアリなのか、同人の世界は本当になんでもあるな」という驚きの気持ちもあった。口調以外はほぼ完全に民法の話なのですが、ちゃんと初心者が読むのを想定した文章なのでなかなかわかりやすくためになります。これはかなりいい買い物をした。

この人は前からtwitterで知っていたのですが非常に好きです。2個目のツイートの右上のページとか特に好き。くすぐりのセンスというか、漫画の運び方みたいなのが好きです。つまり一番いいやつですね。冊子も別に書き下ろしがいっぱいあるやつではなかったのでお布施という気持ちを持っていきました。でもせっかく手に入ったのでtwitterでも読めるとはいえ何回も読み返している、買ってよかった。

ここからモリモリ周遊しました。コミケは大雑把に言って東棟と西棟に分かれていて、東を見尽くしました。 完成閾値バリだったので何も買わなかったけど、正直危ないシーンは何度かありました。僕は東方・vtuber・undertaleにかなり弱く、東方は規模のデカさにより「これを買っていたらキリがなくなる」バリアを使えたのですが、vtuberとundertaleにはそれが効かないのでやばかった。

あとこれは東→西への移動中の出来事です。


というわけで西をガン見していきます。 割と同人誌主体っぽかった東とは違い、西は「その他系」っぽい雰囲気でした。西の中で一番存在感がデカかったのは「音楽」で、とにかくCDをいっぱい売っていたのですが、これに関しては表紙買いを僕はしないし試聴もしなかったのでサーッと通過しました。あと西で存在感があったのは技術系書籍ですね。割とパソコン触ってる系の人たちが「サーバの作り方」とかそういう系統の本を出しているゾーンがあって、正直そこはなにか刺さるものがあれば買おうかなと思っていたのですが、なんだかんだニッチかつレベルの高いものが多くて普通に手が出なかった。ただ興味深いタイトルのものは結構多くて嬉しくなりました。このへんでぱてゼミのブースに行ったけどぱてさんがいなかったので、あとで来ようという思いを残して移動。

そんなこんなで西もざっくり一周。あと行っていないのは、西・東の企業ブースです。もう時間としてはギリでしたが、行きたいという気持ちがあったので西の企業ブースにgo。企業ブースは時間が長いっていうことを知らなかったのですが、企業ブースは時間が長かったので、まだやってました。企業ブースはでかくて色々あり、いかに自分が最新のアニメを知らないかということがビシビシわかりました。このへんで時間は切れたのですが、東の企業ブースに絶対行きたかったのでぱてゼミをスルーし移動。 そしてついに。

キルミーベイベーは神
ところでこのシャツを普段遣いできるようになればこれは浪費ではなくなるので、コーディネートを考えています。

企業ブースもガッツリ見たのでぱてゼミはもう撤収してるかな、でもぱてゼミのことだしなとか思いつつ16:40頃にぱてゼミのブースに行ったらいました。他がほぼ全部撤収完了してる中残っていたのでちょっと笑ってしまった。作詞をちょいやったパワーによりCDをゲット、そして帰路につく。

まあ基本的にこれで、金を持っていれば絶対もっと楽しいと思いました。

これは知らん 何言ってんだこいつ

感想

よく言われることですがお祭りでしたね。欲しくなりうるものが一手に集約され延々と広がり、しかも周りの人間の熱気がかなりあり、何かしらのスイッチが自分の中で入るのがわかります。12:00くらいから16:30くらいまでほぼ完全に歩き通しでしたからね。僕は普段全然歩く人間ではないのですが、最中は疲労感より自分の歩みを進める謎の力のほうが勝っていました。

合氣道会の合宿に行かずにこれに行っていたのでこういう事も言いました。
でもみんながハード合宿やってる中ひとりで全力エンジョイしていたのは普通に申し訳ない気持ちだったので、その後反省のツイートを致しました。

(おわりです)

railsゆるふわリハビリ開発日記

ほんとにゆるいので、学科民が読んだら薄味すぎて死ぬんじゃないかな。やめたほうがいいよ!(予防線)(学科民が怖い)
 
目次

なんの文章ですか

なんか気分になって軽いrailsアプリを作ってみたので、作業ログというか気持ちの日記みたいなのを公開してみます。マジでただの気持ちなので誰の役に立つかというのはよくわからないのですが、railsでなんかやってみたいけどイメージ掴めないなという人にはいいかもしれません。あとまあactive storage使ったのでそのへんかな。

なんのアプリですか

pdfをアップロードするとjpegに変換して一覧表示してくれる&最終閲覧順に勝手に並んでくれる というアプリです。気持ちとしてはスマホでpdf見ようとするとダウンロードされる上にその後ローカルで迷宮入りしてクソうざいという現象があり、webで一元管理できたらいいよなあという気分になったというのがあります。

……とか言いつつ、これ本気でやるならモバイルで開発してファイルのアップロードを楽にするべきなのでこういうwebアプリとしてやる意味は特になく、単にrailsの練習がしたかっただけです。railsはn年前くらいに一通り学んだのですがそれっきりで記憶が風化してしまったのでリハビリをしたく、また単純なCRUDを作るだけなのも味気ないなと思ったのでファイル処理でもやろうという気持ちになったというのがあります。まあただのトレーニングというわけですね。

ちなみにモノですが、こちらになります。
github: https://github.com/iwannatto/mypdfviewer
heroku: https://mypdfviewer.herokuapp.com/pdfs

※herokuの方は触る前に下の方にある注意事項を読んでください

環境構築編

まあ手持ちはmacだしできないってことはないやろ、粛々とやっていきまーす……

とかナメてたらドブった。どうもrubyのビルド時にopensslの場所を正しくお伝えしなければならない様子なのだが、./configureのオプションでお伝えしたはずなのに一切うまくいかない。いろいろ試してググってグネグネやってようやくrailsが入れられてセーフと思ったら、「rails s」をするとセグフォするという最悪の状況が出現。ここで更にググったけどなんかopenssl自体の入れ直しを迫られて非常に嫌になり、結局cloud9に逃げ込む。(もともとアカウントは持ってた)

cloud9にしたら1分でいけました。

結論:ローカルはクソ。時代はクラウド

アプリ全体について考えてみる編

要件としては「pdfをアップロードし、jpegに変換して表示できる」「最終閲覧順に並べられる」というものなので、これを満たすにはどうすればいいかを考えます。
まず最初の要件に関して、ファイルを受け取って保存できる必要がありますね。ちょっと調べてみると、最新のrails5.2には「active storage」というファイルを扱う用の枠組みがあることがわかりました。まあ普通にこれでオッケーそうだし、最新機能というのは響きがいいのでこれでいくことにしました。
もう一つ、pdf→jpegの変換ですが、これはimagemagickという一般的なソフトウェアで普通にいけるらしいということがわかりました。rubyからimagemagickを触れるようなgemがある(minimagick)ので、たぶんこれを使えばオッケーでしょう。
あとは最終閲覧順に並べるというのですが、これは単純に最終閲覧日時のカラムを用意すればそれでよし。
というわけでなんとなくやることが決まりましたね。

pdfを受け取る編

まあ骨組みから作っていくわけですが、railsはscaffoldingとかいうハイパー骨組み作成機能があり、ちょちょっとやるだけで基本的なCRUDを備えたMVCが全部できちゃうんですよね。

$ rails new mypdfviewer
$ cd mypdfviewer
$ rails g scaffold pdf name:string last_access:datetime
$ rails db:migrate

 これでもうできました。この段階ではまだnameとlast_accessしか扱えないのですが、それでももうこの時点でアプリケーションとしては動作するんですよね。ほえー。
というわけでCRUDができてしまったので、あとはこのpdfモデルでpdfファイルを扱えるようにしてやればよいです。
まずconfigを弄る必要があるんですが、なんか自動生成されたconfigそのままで問題ないっぽいのでよし。
モデルを弄るのは超簡単。

# app/models/pdf.rb内のクラス内に追記
has_one_attached :pdf

この1行を入れるだけでpdfという項目名でファイルを扱えるようになります。ウオー。
あとはformにfile_fieldを入れてファイルアップロードできるようにして、ストロングパラメーターの部分だけいじれば、
「@pdf = Pdf.new(pdf_params)」みたいな普通の記述でpdfモデルへのpdfファイルの紐付けができてしまいます。
というわけでこれにて終了。なんというか、圧巻の簡単さですね。すごいなあ。

ちなみに、DB触らなくていいの?となるかもしれないのですが、active storageはDBではない場所にファイルを保存するので、これ以上DBを触る必要がないんですよね。ただ、内部的にはメタデータを扱うためのDBを作るようなので、一度なんかのコマンドを走らせる必要だけあったかも(忘れた)。それと、今回はローカルに保存する設定になっていますが、その気になればS3とかが使えます。

あとついでに、ファイルの中身をpdfに制限したいので、モデルにバリデーターを作ったりもしました。これは後に間違いであったことが判明します。

pdf→jpeg変換編

とりあえずimagemagickを入れます。これ後で本番環境に入れるの忘れそうで怖いな。

$ sudo yum -y install ImageMagick

gemとしてはminimagickを使います。railsのscaffoldingで生成されたデフォルトのGemfileに書いてあるからという安直な採用理由で。

# Gemfileのmini-magickの行のコメントアウトを外す
$ bundle install

あとpdfモデルにjpegを付与できるようにしておく(モデルに1行追記

ここまでやれば、あとはminimagickを使って変換処理を書けばいいんですね。
……ところがここでハイパー詰まります。active storageにもminimagickにも不慣れなせいで、ファイルの取得・読み込ませ・変換 の各段階でガツンガツン引っかかった。
まず入力、imagemagickで拡張子変換をするのにはconvertコマンドを使うのだが、minimagickでそれに対応する(と思っていた)convertオブジェクトは入力でパス渡しをしなければいけない。ところがactive storageから取ってきたファイルはちゃんと使えるパスがないっぽく、わりと調べたが?????となり、結局今回はconvertオブジェクトは使えないということに。active storageからpdfを読んでそのバイナリを取り出してimageオブジェクトというのに渡し、そこでメソッドで変換するらしい。
取り出しにもなんか一癖あり、active storageへの保存メソッドにはストリームを渡す必要があるため割と考えなければいけなかった。最終的にはimageオブジェクトからはstring型でデータが取り出せるのを突き止め、そいつをrubyのstringioを使ってストリームにして渡しました。これstringioのことを元から知っていなければ詰んでいた感じがある。

require 'mini_magick'
require 'stringio'
binary = @pdf.pdf.download
pdf = MiniMagick::Image.read(binary)
jpeg = pdf.format('jpeg', 0)
sio_jpeg = StringIO.new(jpeg.to_blob)
@pdf.jpeg.attach(io: sio_jpeg, filename: '0.jpeg', content_type: 'image/jpeg')

というわけでどうにか変換処理には成功。ただ、デフォルトの変換というのは画質が悪く、この画質を調整するには変換時にdensityオプションを指定する必要があるんですね。というわけで、そこを改良。

……しようとしたところでまた詰まるんですね。オプションを足すなんて簡単にできるのでは?という僕の考えは非常に甘く、上のやり方ではdensityオプションを正しく指定できない。これは「imagemagickのコマンドにおいて、densityオプションは入力ファイルの前につける必要がある」というのと「imageオブジェクトのconvertメソッドを使う方法では入力ファイルの後に付けるオプションしか発行できない」というのが合わさりですね……。これできないの全然信じられなくて相当ググり回ったんだけど、不可能らしいということを確認するに終わりました。
まあそういうわけでどうすんのという感じだったのですが、ここでさっき切り捨てたconvertオブジェクトが使えました。imageオブジェクトをバイナリから読み込んだ後は実はパスが発行できるようになるので、これを渡せばok。convertオブジェクトは入出力両方パス渡しなので出力をどうするかと散々悩んだのですが、結局rubyのtempfileを使えばいいじゃんという話になりました(ここで1時間くらい悩んでいたのは普通にアホだった)。

require 'mini_magick'
require 'tempfile'
binary = @pdf.pdf.download
pdf = MiniMagick::Image.read(binary)
Tempfile.create(["", ".jpeg"]) do |jpeg|
  MiniMagick::Tool::Convert.new do |convert|
    convert.density(600)
    convert << pdf.path
    convert << jpeg.path
  end
  @pdf.jpeg.attach(io: jpeg, filename: '0.jpeg', content_type: 'image/jpeg')
end

まあなんとかできました。この辺の時間対成果の低さはやばかったんですができてよかった。
ちなみにこの後ファイル入力の仕方が変わるので結局全部パスになり、結局バイナリ関連で調べたことは全部無駄になりました。フエーン

最終アクセス時間

普通に書いたら終わった。
この辺は箸休めだった。

各種改良編

まあそういうわけで↑の段階で一応要件を満たしてかつ動くものは作れたので、あとは気になるところをちょこちょこ直す作業に入ります。ざっくり2つありました。

まず1つ目は、現行のコードだと pdf受け取る→active storageに保存→active storageからpdf取り出す→jpegに変換 という動きになっていて、これは自明に無駄なので直します。まあこれはそもそもリクエストデータの中にあるファイルをどうやって扱っていいか調べるのがめんどいからごまかして書いてただけなので、最初からやるべき作業とわかっていたやつ。
これも結局調べて数行書いただけ。結論から言うと、リクエストデータの中にあるファイルは一時ファイルとなって保存されており、そのパスが普通に手に入った。というわけでimagemagickにはパス渡しだけしてればよくなった。

もう1つはバリデーションに関して。railsではモデルにバリデーションを設定することができて、データをDBに保存する直前に自動で検証してダメだったら差し戻してくれます。というわけでpdfモデルのpdf(ファイル)にも.pdf以外を弾くという簡易な拡張子検証を入れておいたのですが、サーバのログを見ているとあることに気づきます。
pdfファイルに関しては、どう見てもデータ保存した後にバリデーションが動いているんですよ。
そういえばactive storage周りを調べている時に「active storageにはバリデーションがない」という文言はいくつか見ていて、これは「デフォルトでactive storage用のバリデーションマクロがないから手動で書かなきゃいけない」程度の意味だと思っていたんですが、どうも動きが根本的に違うようで。試してみた結果やはりバリデーションはダメで、「active storageに対応する属性はセットした瞬間にsaveされてしまう」「このsaveの際にバリデーションは働かない」という仕様のようでした。一応後からバリデーションを走らせることはできるけど、二度手間もしくは無意味になると。
railsは基本的に「コントローラでバリデーションすると分厚くなるからダメで、モデルに書きなさい」という思想だったのでうーんという感じでしたが、最終的には大人しくコントローラにバリデーションを載せたら終わりました。ちなみにrails開発陣によるとactive storageのバリデーションは普通に今後実装する予定らしいですね。まあそうか。

heroku編

herokuにあげようとしたらimagemagickが入ってないなと気づく。
HerokuでImageMagickのconvertができるまで
↑をまんまやって終了。先人は神
と思っていたが、クラッシュしたり何なりがあったのでProcfileを書いた。この辺よくわかっていない。

完成品

github: https://github.com/iwannatto/mypdfviewer
heroku: https://mypdfviewer.herokuapp.com/pdfs
一応上げてみました。どうぞ。

herokuの方の注意事項を諸々と。

  • ユーザーとかの概念がないのでファイルは全部完全に公開されます
  • 上げたファイルが保存されている保証が全然ないです
  • ある程度でかいファイルを上げるとなぜかクラッシュします(は?)(具体的には100KB前後とかにしておいてほしい)

というわけでまとめると、試してみるなら「誰に見られても問題ない&バックアップのある&100KBくらいの」pdfにしてください。
あとこれセキュリティとかその辺一切考えてないので完全に善意頼りですヨロシク。

ちなみに先ほどファイルの保存を保証しないと言いましたが、もっと具体的に言うとファイルは勝手にバシバシ消えていきます。
詳細はここに書いてあるのですが、要はherokuサーバにファイルとか作ってもアプリが再起動すると消滅するというheroku側の仕様です。つまりherokuでactive storageをlocalで使うのはそもそもできないので、本来ならS3とかのストレージサービスを使うべきという話です。ただまあ即消えるわけじゃないので動作確認ならなんとかなるし、何よりめんどいということでこのままいっています。
ちなみにちゃんとS3とかにしたい場合はアカウント取ってconfigいじればいけるっぽいです。

感想

すべてを覆い隠すフレームワークの息吹を感じる。
いやほんとこれで、webアプリを作ったかと言われればyesなんでしょうが、プログラミングをしたかと言われれば気分的にはnoなんですよね。人の書いたものを使っている感がすごいというか、クソでかいconfigを延々弄っている みたいな気持ちになります。
と言ってもこれは決してdisっているわけではありません。むしろこれはおそらくrailsの目指しているものなはずで、良いことだと思います。モノが秒でできてほしいという人間に対して最適な方法を提供していると言いますか、スタートアップでガンガン使われるのは伊達じゃないなあと。実際、「rails」でtwitter検索をかけてみると、なんていうかウォッとなるような人間がいっぱい登場するんですが、そういう人たちが開発に参加することができるようになるからこそ強いんだろうなあ。この面では結局圧倒的というか、railsすげーという気持ちになりました。
あと個人的に気になるところなんですけど、railsを極めるとどういうエンジニアになっていくんでしょうね。railsに精通していくのか、あるいはrails自体の開発に噛んでいくのか。ちょっとググった感じ、この記事みたいな感じの見通しなんでしょうかね。
まあともかく、作りたいものがあった時に強力なフレームワークであるのは間違いないなという気持ちになったので、なんかあったら今後もrailsで作ってみたいですね(ふわっふわ)。


(おわりです)