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

 今日めちゃくちゃ頭が痛くてなにもできない。なにもできないので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で作ってみたいですね(ふわっふわ)。


(おわりです)

今週の実績解除:12時半の秋葉原

2限と3限の合間に秋葉原に行ってきました。

f:id:iwannatto:20180701180735j:plain

こんな感じ。真昼なのでめちゃ暑かった。チャリで行けるらしいという説を聞き、時間とかをよく考えずに適当に出発。「とりあえず適当に直線的に進む→Google Mapを見る→適当に直線的に進む」を繰り返すという天才的なアルゴリズムで向かったのですが、異常なほど坂を上り下りすることになったのでこのアルゴリズムはバカだと思いました。なぜか帰路の3倍くらいの上り下りが発生した。バカすぎて逆に天才なのではという説はあります。

残念ながら滞在可能時間が短いということもあり、特に見物するでもなく用事を済ませ、飯を食べて帰りました。飯がこちらになります。週刊食べログ

f:id:iwannatto:20180701180938j:plain

アキバ盛りカレーという商品名で、完全に商品名インパクトにやられました。いやだって、「アキバ」という単語をそこにかけるか普通?そして妙に語感がいい。こういうパワーゴリ押し系ではない名前でインパクト出せるのはセンスなのでは?と思います。

味ですが、軍でした。カレーって基本的には統一・洗練っていうイメージの食べ物ではなくて、刺激物やうまみ等が各位暴れていくことによって人々を笑顔にする食べ物だと思ってるんですけど、その路線の極致みたいな味がしました。2色のカレー、両方スパイシーでかつ味が全然違うのでこの時点で大暴れという感じですし、具の統一感のなさもすごく良くて、手前から食べ進めていくと要所要所で攻めてくるんですけど、これはゲームで言うイベントですね。アキバ盛りってそういう意味なんだろうか?だとしたら深すぎる。とにかく広がりというか物量で叩き潰しに来る最高の味で、「これは袋叩きですわ」「多勢に無勢ですね」「こっちは一人しかいないんだけどこんなやる?オーバーキルじゃない?」みたいな感想が終始生えていました。途中で軍の視覚イメージとかも浮かんでたんですけど、今思うと槍投げで象を狩るアレの方が近いかなという気がします。

まあそんな感じです。店の居心地も良かったのでちょっとゆったりしてしまったんですが、出てからは多少急いで大学に戻ったので3限には-30分ほど余裕を持って間に合いました。

(おわりです)

今週の実績解除:起こりの鶏そば、肉の丼

どうぞ。

f:id:iwannatto:20180624133039j:plain

どうも唐突です。何かと言うと鶏そばを食べたよという話ですね。580円くらいだったっけな。味の感想ですが、パワーが十分ある、といった趣でした。いやね、もっと高い値段のヤツだとスープが異常洗練されて澄み切っており、「え、これ食べていいんですか?」みたいな見事な味がするんですけど、さすがにその域ではないという話です。ただ、鶏そばである以上は鶏の出力がないとお話にならないんですけど、そのパワーは十分すぎるほどぶっとばしてたな、という感じですね。麺はよく見る四角いやつで、安定感です。あと地味にチャーシューが薄いながらもキッチリした味を出していて、スープに食われてない存在感を放っており、職業意識を感じました。あとはゆずとか水菜とかがビジュアル的には出張ってますが、味的にはまあそれなりの役割でしたね(適当)。ともかく、600円切りにしてはやたらうまいなという印象です。

で、これどこのやつかというと、オリジンなんですよ。オリジン弁当のオリジンです。イートインがあったりして、オリジン"弁当"とは微妙に業態の違う店でしたけど(ここ見るとわかるんだけどオリジンの業態はいくつかある)。まあでも弁当とラーメンを並列して売っているという謎の雰囲気なのは変わりなく、いやお前なんでやねんと言いながら入店したんですけど、予想以上においしかった。流石にオリジンの店内でガチでラーメン作ってるはずはない(と思う)ので何らかのインスタントな方法で提供してるんだろうなと推測してるんですけど、スープに関してはインスタントでもかなりいい線いく時代になってるということでしょうね。コンビニ行くとたまに有名店再現カップ麺みたいなの売ってるんですけど、ああいう系もスープはかなりおいしいですからね。いい時代になったものだ。負けじと市井のラーメンも異常進化してほしいなあ。今はあんまその片鱗ないですけどね……。ラーメンに関しては未だに需要過多なのかもしれない。

次、どうぞ。

f:id:iwannatto:20180624135137j:plain

どうも先程です。いやこれつい2時間前に食べたばっかなんですよ。用事で池袋に行き、予定はなかったのですが吸い込まれました。左がハラミ、右がセセリです。890。味ですが、これがめっちゃおいしかった。説明すると、全体のテイストは「焼鳥丼」に類似しています。タレが甘辛いんですね。でもこっちのほうが焼鳥丼より圧倒的においしいと思う。今から焼鳥丼をdisるんですが、焼鳥丼ってどうしても上に載っている鶏肉が微妙なんですよ。食感としては存在感ありすぎて邪魔だし、味としてはパンチが足りなくて中途半端。タレとご飯それ自体に割とシナジーがあるのに鶏肉がそこにうまく噛めてないというか、だから焼き鳥屋での〆の地位に甘んじているんだろうなというか。(焼鳥丼ファンの皆さんには申し訳ない)(おいしいのはわかるし僕も好きなんだけど、ここが惜しいよねという感情)

で、今回の丼なのですが、それが解消されていましてね。ハラミは薄さがちょうどいいしセセリも邪魔にならない食感なんですが、両方とも旨味が十分なんですよね、完全に飯・タレ・肉の3点シナジーを形成している。どっちも焼肉屋/焼き鳥屋でしか食べる機会ないイメージでしたがこんなポテンシャルを持ってるとは。あと地味にスープが良くて、普通の店でセットとして付いてくる無料のスープって「誤魔化し」みたいな味がするんですけど、こっちはちゃんとしてましたね。まあこの味が店のやる気の現れなのか味の素の含有量の現れなのかについては判然としない。

そういう感じです。飯の話しかしてない。課題に追われて時間がないんですよね。ほんとはもっと出かけたり……は別にいいけど、せめて運動をしたいですね。体ボッキボキなので。来週は余裕ができるといいなあ(先週も言っていた)。

そうそう、最近食べログを書くことにちょっと興味を持っています。いや、別にまだ書いたことはないんですけどね。ちょっとこれを見てほしい。これすごくないですか? ちゃんと「店のレビュー」になってる部分、全体の4分の1くらいですよ。あとは昔の面白話と自分語りです。はっきり言って、「店のレビュー」としては完全に失格ですよね。

でもこのレビューには42人がいいねしており、この人それなりにフォロワー持ってるんですよね。この店のレビューとしても割と上の方に出てくるし、食べログとしては別になにもおかしくないんですよ。つまり食べログというのは純粋な店のレビューを投稿する場所ではなく、「店のレビュー、おもしろ情報、自分語りなどをうまくブレンドした何か」を載っける場所なんだろうなという。その観点で見ると件のレビューが(たぶん、それなりには)評価されているのもわかります。この人普通にスッキリ整った文章を書いてて自分語りも読みやすいし、41年前の東大の状況の記述も興味深い。それでいてギリギリ(本当にギリギリ)店のレビューであるという体裁を外さないラインで書いている。そしてタイトルも秀逸。これ本当にうまくやっていると思う。

そういうわけでこのカルチャー、本当に興味深いんですよね。やってみたいという気持ちだけはある。やってみたいという気持ちだけはあるというのは、僕にはよくあることなんですけどね。どうなるんだろう。とりあえず暇なときに食べログのアカウント作ろうかな。暇なときっていつ来るんだろうな……。

そうそう、今回の文章にはオチがないです。毎回オチがあると思うなよ。

(おわりです)

今週の実績解除:ベストセラー2冊買う

ベストセラーというとだいぶ語弊があって、実際には某サイトのローカルなランキングで1位2位というだけなんですけどね。別にベストセラーな本を買うのを今まで避けてきたわけではまったくないんですけど、ベストセラーであるという理由だけで本を買うのは初めてでした。表紙以外中身知らない状態で買いましたし(普段はやらない)。

で、どの本かっていうとこれですね。amazonリンクを貼っておきましょう。アフィリエイトォォォ *1

1日1ページ、読むだけで身につく世界の教養365

10年後の仕事図鑑

でまあ、読んでみた(前者は読み終わってないけど)感想なんですけど、どっちもあんまりって感じ。別にまあ悪い本ではないんですけどなんか合わねーなーって感じですね。余裕があればあとで書評っぽい記事を書くかもしれないけど余裕がないかもしれないので感じたことの概略だけ書いておこう。

まず教養のやつなんですけど、印象を一言で表すと「教科書」に尽きますね。情報を狭い紙面に詰め込もうとした際に生じがちなあの文体。興味をそそらない単語羅列、説明したという事実をギリギリ確保できる濃度の解説。教科書で見飽きた文体で、はっきり言って僕はあまり好きではない。ただ、そもそもタイトル的に明らかにこの本の趣旨がそれをやるところにあるのでdisるのはお門違いですよね。教科書をやるという観点で見るとやはり売れてるだけあって完成度が高いと思いますよ。文章に破綻してる感じは一切ないし、限られた紙面で要領よく知識を網羅するという点でもかなりうまいことやってるなという印象を受けます。コンセプトと噛み合う人間にとってはたまらない本でしょう。ちょっと面白いのが、解説がある程度薄いというのもあって、あんまり素養のない部分を読んでも面白くないんですよね。「哲学」とか「科学」とか「宗教」に関しては知識を再確認できたり「ほー、こんなのもあるんだ」という新たな知識を得られたりするんですけど、何の素養もない「音楽」とか「視覚芸術」とかに関しては知らない単語がモリモリ並んできて「なんやねん」という気持ちにしかなりません。教養を得るのにも教養が必要なんですね。厳しい世界だ。まあでももちろん色々書いてあるので、興味の種を拾うくらいなら素養がない分野でもイケると思います。

仕事図鑑に関しては、まあ既知のことが多いかなという感じですね。僕は割と普段からこういう情報技術寄りな視点での大まかな社会の潮流はなるべく見ようとしているので。そもそもこの2人twitterでよく見てるし。内容的に深いことは全然書いてなくて概略と気持ちだけ、要は「初心者向け」の本だなという気がします。まあ対談書き起こしの本なので元々そういうものなんですけど。ただそれにしても具体性がないというかありきたりなのが気にかかるんですけどね。なんというかふわっふわなメレンゲをめっちゃうまいこと作ったなという印象。ビジョン語りのこの2人のシナジーにはすごいものがあり、そこは素直に感心します。まあ鵜呑みにしないぞという気持ちがあれば素養として入れておくべきマインドセットではあると思うので、AIとかの概念に馴染みがない人には結構おすすめできるのかもしれないですね。あとこれは明確にこの本の「悪い」ところなんですが、注のクオリティがカスです。たぶん著者2人は全然この本の仕上がり見てなくて書き起こしとか補注は丸投げしてると思うんですけど、今回は完全に人選ミスでしょう。「レバレッジが効く」みたいな文脈のレバレッジという単語に対する注に「てこ」とだけ書いてあったの、さすがにひどいと思いますよ。

まあそういうわけで、ベストセラーでも別に個人的に満足いく本とは限らんなという話ですね。あたりまえ。満足いく本が読みたければちゃんと選ぼうという話です。ちゃんと選ぶというのが非常に難しく、時にランキングに流れちゃったりするのが人類なんですけどね。なんでも最初はそういうものだし、それでいいとも思いますけど。

ただこれ、「なぜこの本が売れているのか」ということを考え出すとだいぶ面白くなるんですよね。なんで教養のやつとか売れてるんだろう。買うのと読むのはイコールではないので、まずは「買う人の買う時の気持ち」になって考えることになるのですが、やっぱ期待しちゃうんじゃないですかね。いや、1日1ページで教養人になれるみたいな想像をしたら買っちゃう人類多いんだと思いますよ。僕は何かを勉強するとき、 ① 期限が迫り、ケツに火がつきます→② ファイアー というやり方が常套手段なので、これはまったく肌に合わないんですけど、たぶん「1日1ページ」という響きに対してえも言われぬ魅力を感じるジャパニーズはかなりいるんだろうなと勝手に思ってます。ホリエモンのやつに関しては、まあいつものホリエモンにかっこつけ落合陽一テイストが合わさり最強に見えるという感じですかね。ホリエモン、僕は別に彼の思想全肯定ではないのですが、おっさんの視点と進歩的な視点を両方持ってる上で実績があり喋りがうまいのでかなり強いなあという印象があります。

まあそんな感じかな。ちなみにこれ両方ともkindleで買ったんですけど、本屋に行かなくとも衝動買いできるのでkindleいいですよ。オススメです。下の方にリンク貼っときますね。アフィリエイトォォォ *2

(おわりです)

Kindle Paperwhite、電子書籍リーダー、Wi-Fi 、ブラック

 

*1:実際にはやってないので金は入ってきません

*2:やってない