
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Ruby | monolog</title>
	<atom:link href="https://blog.monora.me/tag/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.monora.me</link>
	<description>monora log #=&#62; ものろーぐ</description>
	<lastBuildDate>Wed, 09 Dec 2020 15:58:57 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.1</generator>
<site xmlns="com-wordpress:feed-additions:1">21279848</site>	<item>
		<title>ICTSCのコンテストサイトを支えた技術</title>
		<link>https://blog.monora.me/2020/12/technology-that-supports-ictsc-contest-site/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=technology-that-supports-ictsc-contest-site</link>
		
		<dc:creator><![CDATA[きょんたん]]></dc:creator>
		<pubDate>Wed, 09 Dec 2020 15:00:34 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[ICTSC]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[whywaita Advent Calendar]]></category>
		<guid isPermaLink="false">https://blog.monora.me/?p=1653</guid>

					<description><![CDATA[<p>この記事は whywaita Advent Calendar 2020 の10日目の記事です。2週目です。来週何を書くか考えるだけで気が遠くなります。 昨日は @icchy さんで 「whywaitaであいうえお作文する [&#8230;]</p>
The post <a href="https://blog.monora.me/2020/12/technology-that-supports-ictsc-contest-site/">ICTSCのコンテストサイトを支えた技術</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></description>
										<content:encoded><![CDATA[<p>この記事は <a href="https://adventar.org/calendars/5082">whywaita Advent Calendar 2020</a> の10日目の記事です。2週目です。来週何を書くか考えるだけで気が遠くなります。</p>



<p>昨日は @icchy さんで 「whywaitaであいうえお作文する」でした。タイトルだけでだいたい想像できますね。</p>



<figure class="wp-block-embed-twitter wp-block-embed is-type-rich is-provider-twitter"><div class="wp-block-embed__wrapper">
<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><p lang="en" dir="ltr">whywaita: huge youth with aged insight to alcohol<br><br>(ref: <a href="https://t.co/dydlSQ25jD">https://t.co/dydlSQ25jD</a>) <a href="https://t.co/5ZLXsnMMM7">pic.twitter.com/5ZLXsnMMM7</a></p>&mdash; マットレス, がりょ1000 (@icchyr) <a href="https://twitter.com/icchyr/status/1336325587929976834?ref_src=twsrc%5Etfw">December 8, 2020</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div></figure>



<p>なるほど。</p>



<p><br />@icchy さんのおすすめインターネットコンテンツは<a href="https://alcoholog.hatenablog.jp/" title="https://alcoholog.hatenablog.jp/">飲酒日記</a>です。</p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-hatena-blog"><div class="wp-block-embed__wrapper">
<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="2020/12/01 - 飲酒日記" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Falcoholog.hatenablog.jp%2Fentry%2F2020%2F12%2F01%2F015540#?secret=kafpNdDwCr" data-secret="kafpNdDwCr" scrolling="no" frameborder="0"></iframe>
</div></figure>



<p>閑話休題。<br />アドベントカレンダー、書くネタはあれど頭を使うものばかりで泣きそうになったので、とりあえず過去の下書きを漁っていたらタイトルだけ書かれたこの記事の下書きを見つけました。<br />最近思うこともあったので、すでに忘れ去られ、誰もが忘れたままでいたいと思っていたICTSCのコンテストサイトの歴史を紐解いてみたいと思います。<br />whyawitaさんとの関わりもあるし、whywaita Advent Calendar的には100点満点のネタですね。よろしくお願いします。</p>



<span id="more-1653"></span>



<h3>ICTSCとは</h3>



<p>早速本題に入りたいのですが、そのためにはICTSCに関する簡単な知識が必要です。<br /><a href="https://icttoracon.net/archives/8570" title="https://icttoracon.net/archives/8570">ICTSC (ICTトラブルシューティングコンテスト)</a> は学生による学生のためのインフラ技術を競うコンテストで、最近は年1回開催されています。途中でナンバリングが回数から年号になるの、あるあるって感じですね。<br />予選もありますが、予選のことは一旦考えないこととして、本選の話をします。選ばれた本選の参加者は、当日の限られた制限時間内に与えられる問題へ挑むことになります。</p>



<p>問題は次のような形式で与えられます。</p>



<blockquote class="wp-block-quote"><p>「A社の技術者であるXさんは、自社の端末からあるWebサイトを開こうとするといつも失敗することに気が付きました。A社の社内ネットワークのトポロジは下の図のようになっており(省略)、ルータの管理コンソールへアクセスするために必要な情報はXXです。あなたはXさんに変わってこの問題を解決してください」</p><p>管理コンソールのIP: xxx.yyy.zzz.www<br />ユーザ名: admin<br />パスワード: nyannyan<br /><br />社内端末のIP, ユーザ名, パスワード: &#8230;</p></blockquote>



<p>参加者はこれらの情報を元に、持てる限りの知識と経験を活かして挑み、気合で問題を解決することになります。<br />(大会インフラとしてはVMがいて、仮想ないし物理のルータがいて……となりますが、今回の本題からはそれるので割愛します)</p>



<p>そうして、問題に不明点があれば質問をし (競技界隈では clar(ification) と呼ばれたりするみたいですね)、解決に必要な情報をまとめて回答します。基本的に回答はテキストで自由な形式で行われます。<br />運営は提出された回答を読み、必要があれば実際にVMへアクセスして回答の検証をし、採点を行います。<br />採点を行った結果、正答であれば点数が加算され、不正答であれば改めて参加者が挑戦する、というのが一連の流れになります。</p>



<h3>コンテストサイトの必要性</h3>



<p>ICTSCでは、約15問の問題が2日間かけて出題され、参加チーム数も15チーム近くに上ります。賞金が出るということもあり、運営は限られた時間内に公平性をできるだけ保ちつつ採点や質問対応といったタスクをこなさなければなりません。<br />15チームというのは意外と少なく見えますが、人数でいうと60人から80人近くおり、1つのチームでも分担して複数の問題に取り組んでいたりします。そうなると、意外と想像を絶する数の質問や回答が来ます。15チーム×15問で単純に225回答ですし。</p>



<p>私がICTSCの運営にジョインしたのは、遠い昔のICTSC5でした。当時はプロジェクト管理ツールである <a href="https://redmine.jp/" title="https://redmine.jp/">Redmine</a> を使って出題を行っていました。チームごとにプロジェクトを立て、問題ごとにIssueを立てて、そのコメントで質問や回答の管理をしていた気がします。<br />この構成で実際に開催を行ったところ、様々なトラブルが生じました。<br />質問や回答の状況が適切に把握できない(どの質問にまだ返答してないのか?採点は行われたか?)だけでなく、特定チームへの紙のトポロジ図の配布漏れ (これなんで紙で配ってたんでしたっけ?) などが生じました。</p>



<p>今思えばこれなんで起きたんだ?みたいなトラブルですね。Redmineを賢く使えば良かったのではないかと思ってきた……</p>



<p>さて、こういった課題に対処するために若者がやることは一つですね?<br />そう、<strong>「僕が考えた最強のコンテストサイトで解決しよう」</strong>です！</p>



<h3>コンテストサイトの開発</h3>



<p>さて、経緯はどうあれコンテストサイトの開発が始まります。正確にはすでに私の知らないところで一度頓挫していたのですが、忘れます。</p>



<p>今のコンテストサイトは様々あり(後述)、私が知らない構成になっているので、とりあえず私が知っている構成の最後の回のブランチへのリンクを貼ります。</p>



<p><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc2018">GitHub: ictsc/ictsc-score-server at ictsc2018</a></p>



<p>さて、途中で採用技術が二転三転している(なぜ?)ので、老害らしく順を追っていくことにしましょう。</p>



<h4>ICTSC6</h4>



<p>輝かしいコンテストサイト初回導入のときです。当時関わっていた人からはICTSC6の文字列すら見たくないという話も聞きます。当時の振り返りはこちら。</p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-monolog"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="bWqUq8X13t"><a href="https://blog.monora.me/2016/10/had-been-a-steering-committee-of-ictsc6/">第6回 ICTトラブルシューティングコンテストの運営委員をしていました</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="&#8220;第6回 ICTトラブルシューティングコンテストの運営委員をしていました&#8221; &#8212; monolog" src="https://blog.monora.me/2016/10/had-been-a-steering-committee-of-ictsc6/embed/#?secret=bWqUq8X13t" data-secret="bWqUq8X13t" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<p><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc6">https://github.com/ictsc/ictsc-score-server/tree/ictsc6</a></p>



<p>開発が始まりました、技術的には、特に面白みのない簡単な REST API + SPA 構成です。</p>



<p>私はフロントエンド技術には疎かったので、同じ運営学生のメンバーの方と、スポンサーの中の方々に協力して頂きました。<br />一体本業外のこんなことにどれだけの工数を割いていただいたのか、想像を絶する状況になっていたことだろうと思います。本当にありがとうございました。</p>



<p>特に、デザイナーの方にはふわっとしたユースケースから具体的な行動パターンの書き起こしから何から何までお世話になってばかりでした。特に、第6回は初めてコンテストサイトを導入するので何が起こるかわからない、というときに「じゃあ念の為に紙の解答用紙も作っておきますね!」とネタで解答用紙までデザインして渡していただいたときは笑いました。</p>



<p>さて、当時の採用技術は次の通りです。</p>



<ul><li>REST API<ul><li>Ruby</li><li>Sinatra (Ruby のシンプルなWebフレームワーク)</li><li>ActiveRecord (Rails で使われている O/Rマッパー)</li><li>(SQLite3)</li></ul></li><li>Web UI (SPA)<ul><li>Angular 2.0</li><li>RxJS 5.0</li></ul></li></ul>



<p>REST API の方は私がRailsの経験がほとんどなかったので必然的にこうなりました。Web UIはおまかせしたらこうなりました。<a href="https://github.com/ictsc/ictsc-score-server/blob/ictsc6/ui/package.json" title="https://github.com/ictsc/ictsc-score-server/blob/ictsc6/ui/package.json">package.json</a> 見るとわかるんですけど、Angular 2.0 って当時まだRCなんですよね。尖ってますね<br />RxJSが使われたコード、当時ちょっと弄ろうとしたら手も足も出なかったきおくがあります。今なら読めるかもしれない</p>



<p>ところで、当時のREST APIには致命的な設計ミスがあり、REST APIでN+1をしていました。例えば、参加者が問題文の書かれたページを開くと、次のようなアクセスが走ります。</p>



<ul><li><code>/problems/2</code></li><li><code>/problems/2/comments</code></li><li><code>/problems/2/issues</code></li><li><code>/problems/2/issues/37/comments</code></li><li><code>/problems/2/issues/39/comments</code></li><li>&#8230;</li><li><code>/problems/2/answers</code></li><li><code>/problems/2/answers/27</code></li><li><code>/problems/2/answers/27/comments</code></li><li>&#8230;</li></ul>



<p>うーんひどい、典型的なN+1ですね。</p>



<p>N+1 はデータ量が少ないと影響が見えにくいので、この問題は回答や質問量が増えてきた2日目に顕著になりました。<br />そして当時の私はDBにインデックスを貼るということを知らなかったらしい。いま schema.rb 見て吐き気が……</p>



<p>そして、運営は全ての参加者の回答や質問を見ることができる、つまりどうなるでしょうか? 大変なことになります。<br />10秒経っても帰ってこないAPIが乱舞して開発者ツールはさながらナイアガラの滝の様相を呈していました。同一IPに同時に張れるセッション数限界までリクエストが飛んでドーン、返ってきたら第2弾がドーン、もう見たくない……</p>



<p>当時は完全にテンパっていたのでAPI側の修正を入れる余裕はなく、フロントエンド側で急遽キャッシュ等の対応を入れてもらったものの、却って新しいリソースが見えなくなるなどして大変でした。</p>



<figure class="wp-block-image size-large"><img width="640" height="344" src="https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1-640x344.png" alt="" class="wp-image-2210" srcset="https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1-640x344.png 640w, https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1-320x172.png 320w, https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1-200x107.png 200w, https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1-768x412.png 768w, https://blog.monora.me/wp-content/uploads/2020/12/b0b2b8f0-2cc5-4725-ad4f-e39880c3d9d1.png 1278w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption>私が出題した問題の問題詳細ページ。VoIPに関するトラブルを出題しました。</figcaption></figure>



<p>そんなこんながありつつも、なんとか(?)大会は閉幕。次回への課題を残しつつ、コンテストは第7回へ続きます。</p>



<h4>ICTSC7</h4>



<p>知り合いのOB、みんなICTSC7の話しかしたがらんがち。<br /><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc7">https://github.com/ictsc/ictsc-score-server/tree/ictsc7</a></p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-monolog"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="bDdjtuEqVu"><a href="https://blog.monora.me/2017/03/had-been-a-steering-committee-of-ictsc7/">ICTSC7 の運営委員を務めました</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="&#8220;ICTSC7 の運営委員を務めました&#8221; &#8212; monolog" src="https://blog.monora.me/2017/03/had-been-a-steering-committee-of-ictsc7/embed/#?secret=bDdjtuEqVu" data-secret="bDdjtuEqVu" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<p>ICTSC6で辛酸を嘗めた私達[誰?]は、次こそは運営がトラブルシューティングしないコンテストにすべく様々な改善を行いました。</p>



<ul id="block-735dd9a3-4515-4519-855b-169cbe3c82ed"><li>REST API<ul><li>Ruby</li><li>Sinatra (Ruby のシンプルなWebフレームワーク)</li><li>ActiveRecord (Rails で使われている O/Rマッパー)</li><li>MySQL (MariaDBだったかも?)</li></ul></li><li>Web UI (SPA)<ul><li>Vue.JS 2.1</li><li>Vuex 2.0</li></ul></li></ul>



<p>API は N+1 問題を潰すべく、様々な対策が取られました。<br />その集大成が <a href="https://github.com/ictsc/ictsc-score-server/blob/ictsc7/services/nested_entity.rb" title="https://github.com/ictsc/ictsc-score-server/blob/ictsc7/services/nested_entity.rb">nested_entity.rb</a> です。この100行に満たないコードがこのコンテストサイトの真価といっても過言ではないでしょう。私はそう思います。<br />これは要するに「僕の考えた最強のGraphQLっぽいやつ」で、指定した関連リソースをいい感じに取ってきてくれます。<a href="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/controllers/problem.rb#L23">こうやって</a>使います。 こんな読みづらい使い方だからだめなんだわ。</p>



<p>これを使うと、上記のICTSC6の例が次の1リクエストで完結します。</p>



<ul><li><code>/problems/2?with=comments,issues-comments,answers-comments</code></li></ul>



<p>便利なのは分かりました。さて、ここで内部的には何をやっているのかというと、大きく次の2つのことを行っています。</p>



<ul><li>アクセスするユーザの権限によるRBAC (Role-Based Access Control) の適用</li><li>N+1を避けるクエリの生成</li></ul>



<p>コンテストサイトのユーザには、参加者や運営、スポンサーといったロールが割り当てられており、それぞれ見ることができる情報に差があります。例えば参加者が他チームの回答を見られては困りますね。</p>



<p>これ、すなわちRBACを実現するために、全てのロールに対してあらゆるモデルで読めるものを ActiveRecord の scope で定義しています。例えば<a href="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/db/model.rb#L421-L430" title="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/db/model.rb#L421-L430">こんな感じ</a>です。<br />scope とは何かというと、あらかじめ用意されたパラメータの指定可能なクエリといえばいいでしょうか。scope は <code>ActiveRecord::Relation</code> 型の値を返します。これは何かというと、ActiveRecord の抽象的なクエリ表現なので、これを更に合成したりできます。便利</p>



<p>クエリレベルでもN+1を避けるためにはどうしたら良いでしょうか? 気合でクエリを合成したらできそうです。<br />なんと ActiveRecord はクエリに <code>include</code> パラメータを渡すと勝手に合成してくれます。そうしましょう。<br />nested_entity.rb では、<a href="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/services/nested_entity.rb#L45-L47" title="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/services/nested_entity.rb#L45-L47">このあたり</a>で与えられたクエリパラメータをいい感じに ActiveRecord が解釈できる形に変換しています。</p>



<p>全てが ActiveRecord の世界で簡潔するので、RBACもN+1もクエリレベルで表現されます。これはすごくて、あとは全てのロールがアクセスできるリソースをActiveRecordのクエリで表現できれば大丈夫です。<a href="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/db/model.rb#L220-L247">つらい</a>、<a href="https://github.com/ictsc/ictsc-score-server/blob/d5fc08d0f0d889092f5390885fa9c7669a4d06a1/db/model.rb#L481-L513">二度とやりたくない</a>。</p>



<p>ちゃんとDBもMySQLにしてインデックスも貼ったので、APIも水平スケールできるようになりました。こうしてコンテストサイトの Iikanji Speed-Up に成功した我々は、その後数回に渡って予選敗退を繰り返しました。何が足りないん?</p>



<p>そして、フロントエンド担当のエンジニアの方の異常な熱意により、唐突に <a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc7/ui2" title="https://github.com/ictsc/ictsc-score-server/tree/ictsc7/ui2">ui2</a> ディレクトリが爆誕しました。突然のAngular引退宣言に人々は涙を禁じえませんでした。</p>



<p>そうして開催されたICTSC7は、少なくともコンテストサイトについては成功裏に終わったといっても過言ではないでしょう。</p>



<p>ユーザから見える部分としては、問題がこれまでの2日間の午前午後、計4パートでの固定出題から、問題を解くごとに次の問題が解けるようになったり、最初に問題を解いたチームに追加点数(first blood)を与えるようになったりという変化の方が大きかったかもしれませんが、これもコンテストサイトの貢献ということで。</p>



<p>ちなみにこの頃のスクリーンショットがこちら。</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="640" height="356" src="https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site-640x356.png" alt="ICTSC7 コンテストサイト" class="wp-image-1599" srcset="https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site-640x356.png 640w, https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site-200x111.png 200w, https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site-320x178.png 320w, https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site-768x428.png 768w, https://blog.monora.me/wp-content/uploads/2017/03/ictsc7-contest-site.png 1440w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption>ICTSC8におけるコンテストサイトのトップ画面</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" width="640" height="218" src="https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86-640x218.png" alt="" class="wp-image-2211" srcset="https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86-640x218.png 640w, https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86-320x109.png 320w, https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86-200x68.png 200w, https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86-768x262.png 768w, https://blog.monora.me/wp-content/uploads/2020/12/bf705b46-8524-43f3-a4b9-8c2698fd1a86.png 1398w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption>問題一覧画面の一部。このときは、出題された問題群ごとにちゃんと設定があり、その説明文が左に表示されていました。</figcaption></figure>



<h4>ICTSC8</h4>



<p>ICTSC8の記憶があまりありません。</p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-monolog"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="jTDaqetJT9"><a href="https://blog.monora.me/2017/10/had-been-a-steering-committee-of-ictsc8/">ICTSC8 の運営委員を務めました</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="&#8220;ICTSC8 の運営委員を務めました&#8221; &#8212; monolog" src="https://blog.monora.me/2017/10/had-been-a-steering-committee-of-ictsc8/embed/#?secret=jTDaqetJT9" data-secret="jTDaqetJT9" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<p><br /><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc8">https://github.com/ictsc/ictsc-score-server/tree/ictsc8</a><br />コミットログを見ると、たしかに差分はあるけどあまり変わってないですね。確かプッシュ通知を実装しました。CyberAgentのFRESHチーム?が作った <a href="https://github.com/opensaasstudio/plasma" title="https://github.com/opensaasstudio/plasma">plasma</a> を使っています。</p>



<p>ICTSC8では小さなバグフィックスやルール変更の対応等はあったものの、それよりはテストを書くことにご執心だったようです。RSpecの鬼になったkyontanの姿が垣間見えますね。増える緑の. (RSpecではテストケースが1つPASSするごとに.がプリントされた)にエクスタシーでも感じていたんでしょうか。<br /><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc8/spec/requests">https://github.com/ictsc/ictsc-score-server/tree/ictsc8/spec/requests</a><br />相変わらず<a href="https://github.com/ictsc/ictsc-score-server/blob/fb5884fc643facdd0a76cd54f2cbe3be0a58cce1/spec/support/api_helpers.rb#L25-L55" title="https://github.com/ictsc/ictsc-score-server/blob/fb5884fc643facdd0a76cd54f2cbe3be0a58cce1/spec/support/api_helpers.rb#L25-L55">へんなヘルパー</a>定義しててウケました。そんなんだからお前のコードは人に読まれない。</p>



<p>そうして満足したので運営委員を引退しました。それはそれとして、参加記を見ると毎回のように後悔が綴られていますね。</p>



<h4>ICTSC9</h4>



<p>関わっていないのでほとんど分かりません。</p>



<p><a href="https://github.com/ictsc/ictsc-score-server/blob/3730cfaa8d62b14a573a1e3a5998f045993e2504/services/nested_entity.rb#L16">https://github.com/ictsc/ictsc-score-server/blob/3730cfaa8d62b14a573a1e3a5998f045993e2504/services/nested_entity.rb#L16</a></p>



<p>ほんまにすまんて</p>



<h4>ICTSC2018</h4>



<p>運営に出戻ったものの何したのか覚えてません。コンテストサイトには手を出していないはず。</p>



<p>このあたりで API 向けの CLI が生えたらしい。今までシードデータ投入をSQLでやってたのを、 REST API 経由でやるようにしたんですかね。</p>



<p><a href="https://github.com/ictsc/ictsc-score-server/tree/ictsc2018/cli">https://github.com/ictsc/ictsc-score-server/tree/ictsc2018/cli</a></p>



<p>あとはしれっと Kubernetes 化とかされている。モダンなインフラってやつ</p>



<p>私が知るのはここまでです</p>



<hr class="wp-block-separator"/>



<h4>ICTSC2019</h4>



<p><a href="https://github.com/ictsc/ictsc-score-server/commit/5d5fd505944f7df9314b5e6378c770fbd9cb6182">Big Bang! · ictsc/ictsc-score-server@5d5fd50</a></p>



<p>唐突に api2 と ui3 が爆誕する。担当者のキレ具合が垣間見えますね。僕も同情します。</p>



<ul id="block-3550f266-a2d7-42cf-ace3-fdc2ecf144a8"><li>REST API<ul><li>Ruby</li><li>Rails 6.0</li><li>GraphQL</li><li>PostgreSQL</li></ul></li><li>Web UI (SPA)<ul><li>Nuxt.js 2.0</li><li>Apollo Client 2.6</li></ul></li></ul>



<p>ザ・モダン・Webアプリケーション大全。僕の心境としては「GraphQLもどきを作って公園で砂遊びしてたらいつのまにか本物のGraphQLで置き換えられていた、何が起きているのか(ry」って感じです。<br />DB が Postgres に変わったのはなんでなんでしょうね? 僕は理由を知らないので知っている人がいたら教えて下さい。</p>



<p>増えた機能が多すぎて良くわかりませんが、Webでだいたいのリソースが編集できるようになってたり、問題VMへのアクセスに必要な情報を専用のテーブルで管理できるようになったりしたようです。憶測ですが、VMをプロビジョニングするいい感じのツールと連携していい感じにできるんじゃないですかね。</p>



<p>こうして生み出したプロダクトが自分の手を離れて育っていく様子をまだ学生の身にして実感することができました。ハッピー。</p>



<h3>万物は流転する</h3>



<p>私事になりますが、先日<a href="https://www.janog.gr.jp/meeting/janog47/" title="https://www.janog.gr.jp/meeting/janog47/">JANOG47</a>？っていう日本最大級のネットワークエンジニアの集まり？ってやつでNETCONっていうあいしーてぃーえすしー？みたいなのをやるって言われてあれやそれやで運営委員になりました。</p>



<p>どうやらコンテストサイトが必要らしいんですよね。</p>



<p>………というわけで最近<a href="https://github.com/janog-netcon/netcon-score-server" title="https://github.com/janog-netcon/netcon-score-server">fork</a>しました。これどうしたらいいんですか?</p>



<p>そんなこんなで、泣きながら Ruby の GraphQL実装を調べています。ドキュメントの大切さが目に染みる。</p>



<p>明日は同期だったはずのくるとんさんです。ワンワールドサファイアステータスを求めて飛び回りたいという発言を最近目にしましたがお元気ですか?</p>The post <a href="https://blog.monora.me/2020/12/technology-that-supports-ictsc-contest-site/">ICTSCのコンテストサイトを支えた技術</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1653</post-id>	</item>
		<item>
		<title>総資産を Dr.Wallet から Slack へポストするようにした</title>
		<link>https://blog.monora.me/2017/07/post-total-assets-from-drwallet-to-slack/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=post-total-assets-from-drwallet-to-slack</link>
		
		<dc:creator><![CDATA[きょんたん]]></dc:creator>
		<pubDate>Sun, 09 Jul 2017 15:22:59 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Dr.Wallet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[総資産]]></category>
		<guid isPermaLink="false">https://blog.monora.me/?p=1614</guid>

					<description><![CDATA[<p>こんにちは。JST+11で生きているので昼です。 ところで最近は世界から10年遅れて Rails に入門したり、それとは関係なく Sinatra のアプリケーションに RSpec を書いてテストの重要性を感じながら七夕に [&#8230;]</p>
The post <a href="https://blog.monora.me/2017/07/post-total-assets-from-drwallet-to-slack/">総資産を Dr.Wallet から Slack へポストするようにした</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></description>
										<content:encoded><![CDATA[<p>こんにちは。JST+11で生きているので昼です。<br />
ところで最近は世界から10年遅れて Rails に入門したり、それとは関係なく Sinatra のアプリケーションに<br />
RSpec を書いてテストの重要性を感じながら七夕に笹の葉ラプソディを見たりしています。10年前って単語がよく聞かれる今日このごろです。</p>
<p>皆さんはおそらく何らかのチームでのコミュニケーションに Slack を使っていて、総資産を Dr.Wallet で管理されていて (ここで読者の8割が脱落)、その総資産を逐次仲間に共有したいですよね! (残存者0)</p>
<p>で、やっぱり皆さんも共有したいでしょう、というわけで作りました。</p>
<p><span id="more-1614"></span></p>
<p><small>(実は1年ぐらい前に作ってあったのですが、環境依存が辛かったので Docker に押し込む作業をしました)</small></p>
<p><a href="https://github.com/kyontan/assets2slack" target="_blank" rel="noopener noreferrer">GitHub kyontan/assets2slack</a></p>
<p>雑に <code>docker-compose run --rm crawler</code> とかやると共有できて便利です。</p>
<p><img loading="lazy" class="alignnone size-full wp-image-1629" src="https://blog.monora.me/wp-content/uploads/2017/07/2017-07-09-23.png" alt="assets2slack demo" width="290" height="269" srcset="https://blog.monora.me/wp-content/uploads/2017/07/2017-07-09-23.png 290w, https://blog.monora.me/wp-content/uploads/2017/07/2017-07-09-23-200x186.png 200w" sizes="(max-width: 290px) 100vw, 290px" /></p>
<p>おそらく最近のアプリケーションなので <code>docker-compose.yml</code> が置いてあります。ポジショントーク?じゃないですが、こういう環境作りが面倒くさいアプリケーションの共有には向いていると思います。<br />
1コンテナにまとめたかったのですが、少し面倒くさそうだったので投げました。PRお待ちしています。</p>
<p>実装は至極簡単に、 Ruby あるあるな capybara + selenium-webdriver で雑にクロールして Webhook で投げているだけです。技術に新規性はありません。</p>
<p>話が逸れますが、 Dr.Wallet のレシート人力OCRは精度も良いしおすすめです。あと、どうやら iOS より Android アプリの方が3倍ぐらい機能が充実していて便利です。NFC で Suica も読めるし。</p>
<p>皆さんもこれを使って総資産で殴り合っていきましょう。僕の総資産はたまにマイナスになります。</p>The post <a href="https://blog.monora.me/2017/07/post-total-assets-from-drwallet-to-slack/">総資産を Dr.Wallet から Slack へポストするようにした</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1614</post-id>	</item>
		<item>
		<title>shotgun じゃなくて rerun を使おうという話</title>
		<link>https://blog.monora.me/2016/03/use-rerun-with-tmux-not-shotgun/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=use-rerun-with-tmux-not-shotgun</link>
		
		<dc:creator><![CDATA[きょんたん]]></dc:creator>
		<pubDate>Sat, 26 Mar 2016 19:32:48 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[rerun]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[tmux]]></category>
		<guid isPermaLink="false">http://blog.monora.me/?p=1351</guid>

					<description><![CDATA[<p>Ruby で Rack アプリケーションを書いているときに、コード変更したら自動的にサーバー再起動したいという話。 今までは shotgun でやっていたのだけれど、これは毎回リクエストする度に変更の有無に関係なくサーバ [&#8230;]</p>
The post <a href="https://blog.monora.me/2016/03/use-rerun-with-tmux-not-shotgun/">shotgun じゃなくて rerun を使おうという話</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></description>
										<content:encoded><![CDATA[<p>Ruby で Rack アプリケーションを書いているときに、コード変更したら自動的にサーバー再起動したいという話。</p>
<p>今までは <a href="https://github.com/rtomayko/shotgun" target="_blank" rel="noopener noreferrer">shotgun</a> でやっていたのだけれど、これは毎回リクエストする度に変更の有無に関係なくサーバーを立ち上げ直すので、遅いという欠点があった。<br />
副次的な問題として、<a href="https://github.com/charliesome/better_errors" target="_blank" rel="noopener noreferrer">better_errors</a> で REPL が無効になるという欠点があった。(レスポンスを返すとそのコンテキストを捨ててしまうから?)</p>
<p>ずっと前からどうにかならないかなーと思っていたのでググったら <a href="https://github.com/alexch/rerun" target="_blank" rel="noopener noreferrer">rerun</a> というものを見つけた。<br />
Rack とか関係なくもっと汎用的なやつで、ファイル変更検知してサーバー上げ直すぞ! みたいなプロダクトらしい。</p>
<p>入れたら下のような感じで使える。便利。</p>
<p><code>RACK_ENV=development rerun -- rackup -o 127.0.0.1<br />
</code></p>
<p>調子に乗って通知有効化するぞ! というノリで <a href="https://github.com/julienXX/terminal-notifier" target="_blank" rel="noopener noreferrer">terminal-notifier</a> を入れてみたらサーバーが上がらなくなった。何事かと思ったら tmux 上で使おうとすると一癖あるらしく、<a href="https://github.com/julienXX/terminal-notifier/issues/115" target="_blank" rel="noopener noreferrer">こういうIssue </a>が立っていたので適当に読んだら解決した。</p>
<p>要するに、<code>brew</code> なりなんなりで、<code>reattach-to-user-namespace</code> をインストールして、<code>.tmux.conf</code> に下を書き加えるという話。</p>
<pre>
set -g default-command "which reattach-to-user-namespace &gt; /dev/null &amp;&amp; reattach-to-user-namespace -l $SHELL || $SHELL -l"</pre>
<p>再起動に掛かる時間が体感で倍ぐらいになったけれど、<code>better_errors</code>使えることで圧倒的効率アップみたいなところがあるので、トータルで幸福度が向上した気がする。</p>The post <a href="https://blog.monora.me/2016/03/use-rerun-with-tmux-not-shotgun/">shotgun じゃなくて rerun を使おうという話</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1351</post-id>	</item>
		<item>
		<title>Sinatra Modular-Application で configure が上書きされる問題の対処</title>
		<link>https://blog.monora.me/2016/03/problem-about-overwritten-configure-in-sinatra-using-modular-application/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=problem-about-overwritten-configure-in-sinatra-using-modular-application</link>
		
		<dc:creator><![CDATA[きょんたん]]></dc:creator>
		<pubDate>Tue, 15 Mar 2016 20:35:15 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[sinatra]]></category>
		<guid isPermaLink="false">http://blog.monora.me/?p=1322</guid>

					<description><![CDATA[<p>Ruby の Siantra で、Modular-Application を書いていたら良く分からない挙動にぶち当たって気が付いたら朝になっていたのでメモ。 app.rb require_relative "hoge"  [&#8230;]</p>
The post <a href="https://blog.monora.me/2016/03/problem-about-overwritten-configure-in-sinatra-using-modular-application/">Sinatra Modular-Application で configure が上書きされる問題の対処</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></description>
										<content:encoded><![CDATA[<p>Ruby の <a href="http://www.sinatrarb.com/" target="_blank" rel="noopener noreferrer">Siantra</a> で、Modular-Application を書いていたら良く分からない挙動にぶち当たって気が付いたら朝になっていたのでメモ。</p>
<p>app.rb</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ruby" data-enlighter-linenumbers="true">require_relative "hoge"

class App &lt; Sinatra::Base
  configure do
    disable :protection
  end

  use HogeRoutes
end
</pre>
<p>hoge.rb</p>
<pre class="lang:ruby" title="hoge.rb">class HogeRoutes &lt; Sinatra::Base
  get "/" do
     ...
  end
end
</pre>
<p>みたいな事をしていた。<br />
<code>disable :protection</code> をしているのにどうしても <code>Rack::Protection</code> が有効になるので、どうしてこうなるんだとあちこちのコードを追いかけたりした結果、<code>HogeRoutes</code> が <code>App</code> とは別にもう一度 <code>session, protections</code> 等のセットアップが行われるので、以下のように、 <code>use HogeRoutes</code> を <code>configure</code> より上に持ってくる必要があった。</p>
<p>app.rb</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ruby" data-enlighter-linenumbers="true">require_relative "hoge"

class App &lt; Sinatra::Base
  use HogeRoutes

  configure do
    disable :protection
  end
end
</pre>
<p><code>HogeRoutes</code> 内の <code>configure</code> で <code>disable :protection</code> とやってもいいが、こうするとクラスが増えた際にその分だけ書かないといけなくて大変。セッションみたいなアプリケーション全体で統一的な設定を持つ部分は親の <code>App</code> でやって良いんじゃないかと思う。</p>The post <a href="https://blog.monora.me/2016/03/problem-about-overwritten-configure-in-sinatra-using-modular-application/">Sinatra Modular-Application で configure が上書きされる問題の対処</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1322</post-id>	</item>
		<item>
		<title>sinatra-activerecord で seed_fu を使ってみた</title>
		<link>https://blog.monora.me/2015/10/use-seed_fu-in-sinatra-with-sinatra-activerecord/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=use-seed_fu-in-sinatra-with-sinatra-activerecord</link>
		
		<dc:creator><![CDATA[きょんたん]]></dc:creator>
		<pubDate>Tue, 13 Oct 2015 19:02:52 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[ActiveRecord]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[sinatra]]></category>
		<guid isPermaLink="false">http://blog.monora.me/?p=617</guid>

					<description><![CDATA[<p>Sinatra で、sinatra-activerecord を使っているときに、$ rake db:seed すると、既に存在するレコードが重複登録される。 これ、とても一般的な問題で、 Rails だと、seed_f [&#8230;]</p>
The post <a href="https://blog.monora.me/2015/10/use-seed_fu-in-sinatra-with-sinatra-activerecord/">sinatra-activerecord で seed_fu を使ってみた</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></description>
										<content:encoded><![CDATA[<p>Sinatra で、<a href="https://github.com/janko-m/sinatra-activerecord" target="_blank" rel="noopener noreferrer">sinatra-activerecord</a> を使っているときに、<code>$ rake db:seed</code> すると、既に存在するレコードが重複登録される。</p>
<p>これ、とても一般的な問題で、 Rails だと、<a href="https://github.com/mbleigh/seed-fu" target="_blank" rel="noopener noreferrer">seed_fu</a> というのを使って解決したりするらしい。</p>
<p>Rails (ActiveRecord 4.x) で動くなら Sinatra (sinatra-activerecord) でも問題なく動くとおもいきや、<code>$ rake -T</code> しても <code>db:seed_fu</code> が表示されない……</p>
<p>ソースやらなんやら見ながら唸っていたら、どうも Rake のタスク追加を、 <a href="https://github.com/mbleigh/seed-fu/blob/master/lib/seed-fu/railtie.rb" target="_blank" rel="noopener noreferrer">Railtie を使ってやろうとしているところ</a>を発見した。<br />
どうやら Rails だと、初期化時にここのコードが走るらしいのだけれど、Sinatra にそんなものはないので当然タスクは追加されない。</p>
<p>というわけで、<code>Rakefile</code> にベタ書きしてみる。<br />
メインのアプリケーションソースが <code>./app.rb</code> にあるということになっています。(適宜書き換えてください)<br />
あと、なぜか <code>&lt;&lt;</code> が <code>&lt; &lt;</code> に置き換えられたので修正してください。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ruby" data-enlighter-linenumbers="true">namespace :db do
  desc &lt; &lt;-EOS Loads seed data for the current environment. It will look for ruby seed files in (settings.root)/db/fixtures/ and (settings.root)/db/fixtures/(settings.environment)/. By default it will load any ruby files found. You can filter the files loaded by passing in the FILTER environment variable with a comma-delimited list of patterns to include. Any files not matching the pattern will not be loaded. You can also change the directory where seed files are looked for with the FIXTURE_PATH environment variable. Examples: # default, to load all seed files for the current environment rake db:seed_fu # to load seed files matching orders or customers rake db:seed_fu FILTER=orders,customers # to load files from settings.root/features/fixtures rake db:seed_fu FIXTURE_PATH=features/fixtures EOS task :seed_fu =&gt; :environment do

    if ENV["FILTER"]
      filter = /#{ENV["FILTER"].gsub(/,/, "|")}/
    end

    if ENV["FIXTURE_PATH"]
      fixture_paths = [ENV["FIXTURE_PATH"], ENV["FIXTURE_PATH"] + "/" + settings.environment.to_s]
    end

    SeedFu.seed(fixture_paths, filter)
  end

  task :load_config do
    require "./app"
  end
end

Rake::Task["db:seed_fu"].enhance(["db:load_config"])

SeedFu.fixture_paths = [
  Pathname(settings.root).join("db/fixtures").to_s,
  Pathname(settings.root).join("db/fixtures/#{settings.environment}").to_s
]
</pre>
<p>コードとしては、<a href="https://github.com/mbleigh/seed-fu/blob/master/lib/tasks/seed_fu.rake" target="_blank" rel="noopener noreferrer">seed_fu の rakeファイル</a> を元に、<a href="https://github.com/janko-m/sinatra-activerecord/blob/master/lib/sinatra/activerecord/rake/activerecord_4.rb" target="_blank" rel="noopener noreferrer">sinatra-activerecord から、データベースの接続設定を拾ってくる部分</a>を参考にしたりした。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ruby" data-enlighter-linenumbers="false">Rake::Task["db:seed_fu"].enhance(["db:load_config"])
</pre>
<p>の部分がミソで、これでアプリケーションを通してデータベースに接続する必要がある。ここで2時間ぐらいハマった。</p>The post <a href="https://blog.monora.me/2015/10/use-seed_fu-in-sinatra-with-sinatra-activerecord/">sinatra-activerecord で seed_fu を使ってみた</a> first appeared on <a href="https://blog.monora.me">monolog</a>.]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">617</post-id>	</item>
	</channel>
</rss>
