Runner in the High

技術のことをかくこころみ

OneTapBUYというのをやっている

 つい最近OneTapBUYのCMをテレビで観たので、ふと思い出したので書く。

f:id:IzumiSy:20170919100836p:plain:w300
つい最近のホーム画面

 今年(2017年)の5月くらいから始めて、ここまででだいたい10万くらい投資にまわした。画像の投資元本がぴったり10万じゃないのは、OneTapBUYの中に「利益分だけ売却」という機能があってそれを試したりしたから。

 僕自身がただの一般家庭出身の中堅大学文学部の学生でしかないので、ほとんどまわりに投資信託株式投資をしてます! という知り合いがいなかった。これがもっとリテラシや情報感度の高いコミュニティだとそういう人がいるんだろうな、とは思うものの、僕の周りには音楽をやってる奴かプログラミングをしている奴しかいないので、「お金」という観点では自分で勉強していくしかないなと思う。

 家が金持ちだとか、バイトで月30万くらい稼げる! みたいな人間であれば、もっと大きいお金を運用して経験を積めるのではないかと思うけれど、ただの大学生だと頑張って月2万くらいが積立の限界じゃないかなと思う。そういう意味では、もともとお金を持っている人間は、それだけプールの中から遊ばせられる割合が多く、結果的に金融リテラシが身につくのかもしれない(?)。一億持っていれば、そこから数百万運用するくらいなら、なんでもないはずだ。一方で、僕にとっての10万円は本当に大きなお金で、もしもそれを明日すべて失うのかもしれないと思うと、それはそれで損をした気持ちになるし、怖いなと思う。でも何かを怖いと思って挑戦をしないよりかは、損をしても「ああこういうものなんだな」と思うほうが僕には価値があると思った。ただ、見方によっては損をしたことからバイアスのある見方しかできなくなってしまうこともあるし、そこはできるだけフラットな見方のできる経験をしていきたい。

 ところで、そもそもOneTapBUYを始めたきっかけは、リンダ・グラットンの「LIFE SHIFT」を読んだからだ。著者が本の中で、これからの未来は金融リテラシの有無が貧富の差を大きく分けると言っていて、それがなぜかという詳しい理由は忘れたものの、ああたしかになとすごく納得してしまった。加えて、卒業論文に関する調べものをしていて、日本は若者があまり金融投資に関して積極的ではないというデータを見て、それがなぜなのか個人的にすごく気になったのだ。金融投資と一口に言われて僕自身が思い浮かべる率直なアイデアはFX破産や借金取り、元本棄損のような「怖い」ワードばかりだった。それは、ネットの影響やバイト先の先輩がFXでウン百万単位の借金を背負ったという話を聞いてきたからではないかと思う。これは僕だけではなく、僕の周囲の人間も軒並みそうなのではないかと思う。大学の知り合いに「OneTapBUYというのをやっているんだ」というはなしをすると、「なにそれ、なんか怪しくない?」「なんかマトモじゃなさそう」というようなことを口々に言った。僕自身も、たしかになんか胡散臭い名前のアプリではあるなと思う。普通に考えて株式投資なんてものがワンタップでやれていいはずはない。世間一般の若者からすれば、株というのはもっと難しくて、危ないものだからだ。そんなものには積極的にならない。

LIFE SHIFT(ライフ・シフト)

LIFE SHIFT(ライフ・シフト)

 OneTapBUYに関してググってみると、銘柄数が少ない、手数料が高い、などのデメリットを書き連ねているブログ記事がいくつも散見される。おそらく、そのような記事を書いている人たちの殆どが株で「儲けたい」「大勝したい」という気持ちをモチベーションにして金融投資をしているのだと思う。たしかにその観点は間違ってないし、僕も大金持ちになりたいという気持ちはある。とはいえ、それでもいきなり初めての人間がいくつもある証券会社を選び、そしてさらにそこから星の数ほどもある株式銘柄を選ぶというのはとても大変だ。誰でも、なにか初めてのことをするときその対象が複雑すぎると、どうしても足踏みしてしまうものだ。そういう意味でも、OneTapBUYは僕のような金融リテラシの低い若者の、最初のハードルをさげようとしている。UI/UXとして株取引というもののコアとなるエッセンスである「安く買って高く売る」という体験をいかに低いハードルで提供できるかというものが考えられていると思う。

 ちなみに、僕はあまりOneTapBUY自体は胡散臭いと思っていない。なぜならTechCrunchのスタートアップ・バトル2015で賞を受賞しているし、スタートアップとして比較的安心のできるベンチャーキャピタルから出資を受けているからだ。これが確実にお金を預ける先として安心かどうかという点に関しては人によって賛否両論あるかもしれないが、僕自身はさほど危惧していない。

f:id:IzumiSy:20170919112214p:plain:w300
はじめてちょっと利益が出始めたとき

validatable-recordについて

www.npmjs.com

 validatable-recordというモジュールをすこし前に作った。Twitterでは軽く流したが、実際にはこれを作ろうと思ったモチベーションが少なからずあったので、自分自身の学びやのちのちの振り返りのためにも、そのモチベーションについてここに書き残しておきたい。

背景

 このモジュールを作っていたとき、僕はチームでVue.jsを用いたSPAの開発をしていて、アプリケーション・フォームのバリデーション・ロジックの実装にとりかかろうとしていた。Vue.jsのバリデーション・モジュールの有名なものにはvue-validatorというものがあるらしく、検索すると上位にヒットし、スターも多い。作者もVue.js界隈ではよく知られているkazuponさんである。このモジュールのいいところは、モジュール自体の出来もさることながら、バリデーション・ロジックの実装自体を全てビューレイヤで完結させることができ、とてもシンプルに使いやすいところだ。Vue.jsというフレームワークの持つ小さなラーニング・コスト同様に、このモジュールもとても手に馴染みやすい。しかしながら、アプリケーションのアーキテクチャによっては、バリデーションという役割が、このモジュールによって果たされることがよくない場合もある。

バリデーション・ロジックという責務

 バリデーションをどこで行うか? という事柄には「常にこれ」という答えがない。ビューでバリデーションをすることもあるし、そうでない場合もある。Railsではビジネスロジックの一部としてモデルレイヤがバリデーションを扱う。このような、バリデーションがビジネスロジックの一部である、という観点はとても興味深い。というのは、例えばフォームのバリデーションには多くの場合ビジネスロジックが関わることが多いからだ。もちろん、インフラストラクチャ・レイヤの事情によって、ユーザ名を制限したり、文字数制限を課したりする場合もあり、こうしたバリデーションはどちらかと言えばインフラストラクチャ・レイヤの一部だ。だが、別の例としてECアプリケーションにおける特定の商品の注文数の制限などは、限りなくビジネスロジックに近い。なぜなら、これはこのアプリケーションのビジネス固有の事情によって生まれるバリデーションだからだ。

モデル・レイヤ

 アプリケーション開発において意識する必要があることは、そのアプリケーションがこれからもスケールする可能性があるということだ。個人で作っていたり、フレームワークの練習で作るアプリケーションはその限りではないが、一般的なアプリケーションは毎日のように変更が施され、複数人の開発者がコードに手を加える。とりわけ、フロントエンド・アプリケーションはビューに大きな変更が加わるケースが多く、その過程でコード内のロジックがビューやビューモデルに偏りがちになることが多い。レイヤード・アーキテクチャからフロントエンド・アプリケーションを考えると、ビュー/ビューモデルはプレゼンテーション・レイヤの一部であり、そこが多くを知っていることが良い方向に向かうケースは少ない。そのため、アプリケーションから、固有のビジネス・ルールを切り離したドメイン・レイヤがフロントエンド・アプリケーションにも存在しているというのはなんらおかしなことではないと言える。巷では、様々なエンジニアがFluxアーキテクチャにおいてドメイン・レイヤをどこに置くか、という話題について記事を書いているが、僕のケースでは、Immutable.jsのRecordを用いてストアの一部としてドメイン・レイヤを置いている(僕のアプリケーションは、React+Reduxの技術スタックではないが、以下のスライドの図は参考になる)。

f:id:IzumiSy:20170905193818p:plain
改善React道

 もし、ビュー・レイヤの中にバリデーション・ロジックを置くとしても、それはドメインに固有でないもののほうがよい。ドメインに固有のバリデーション・ロジックは必ずしもビュー・レイヤからの入力だけに使われるとは言い切れないからだ。APIサーバやデータベースなどのインフラストラクチャ・レイヤからのデータの取得もまた、アプリケーションにとっては「入力」となる。それらがビジネス・ルールに対して適切であるかというのはドメイン・レイヤにおけるバリデーションの責務だと言える。このような意味でも、フロントエンド・アプリケーションにおけるバリデーションはドメイン・レイヤに置かれるモデルの一部として扱われるのが適切であることが多い。

validatable-record

 Immutable.jsのRecordでモデルを定義する際には、最終的にはバリデーション・ロジックは自前で書かねばならない。npmで検索すれば、様々なバリデーション・モジュールがひっかかるので、そこから自前で作ることができる。しかしながら、validatable-recordを使えば、validate.jsのバリデーション定義を組み込んだイミュータブルなRecordを定義することができる。

const ManRecord = ValidatableRecord({
  name: null,
  age: null
}, {
  name: {
    presence: true
  },
  age: {
    presence: {
      message: "is invalid"
    }
  }
});

// Immtable.jsのRecordと同じように使える
const man = new Man({
  name: "Justine";
  age: 25
});
 
// もちろんRecordのメソッド群も使える
man.size        // 2
man.get('name') // "Justine"
man.get('age')  // 25

// バリデーション
man.validate() // == true

 設計の指針として、バリデーション・ロジックをどこに置くか、という点を考えておくことは、決して無駄骨になることはないし、息の長いアプリケーションを開発する際には、すこしでも時間をとって考えておきたい。validatable-recordはその中でドメイン・レイヤにおけるバリデーションを実装するとしたらこんな感じかな、という小さなアプローチを提案するものだった。僕らのところではこうしてるよ、こういうものを使ってるよ、などの話もいつかどこかの勉強会で聞けたらいいな、と思う。

GPD Pocket Ubuntu版が届いた

 IndieGoGoでUbuntu版をbackしていたのがとうとう届いた。Windows版は「もう届いた」「小さくて最高」みたいなツイートやブログエントリーがネット上でちらほらと散見されたのにもかかわらず、Ubuntu版は随分時間がかかっているんだなと思い若干本当に届くのか疑っていたが、とりあえず届いたのでひと安心した。GPDのロゴが散りばめられた非常にそれっぽい梱包でなかなかどうして悪くない。 f:id:IzumiSy:20170822101209j:plain

 Ubuntu版も箱自体はWindows版のものとさほど変わらないようだ。若干裏側の板の貼り付けが雑だが、それもそれで味があるというものだ。それにしてもGPDのロゴはかわいい。黒い箱にゴールドという配色に高級感を感じさせようという努力を感じる。 f:id:IzumiSy:20170822101619j:plain

 クリスマスプレゼントをもらったこどものように箱を開封して、さっそく端末のセットアップを始める。確か公式のアップデートでは、マウスコントローラのラバーにはいくつか交換できるものが同梱されるとかなんとか言っていたような気がするが、入っていなかった。なくてもさほど困るものではないのでいいとしよう。
 IndieGoGo上のアップデートでUbuntu版のデモをみたとき、あまりにもディスプレイの解像度が高すぎて、もしかしたら文字が読めないかもしれないと心配したが、まだ20代の僕にはどうにか読めるレベルで安心した。僕は小学生のころからPC漬けの毎日を過ごしていながら、いまだに両目とも1.5という比較的恵まれた眼を持っている。これに関しては両親に感謝するばかりだ。結局のところ、僕にはこれくらいの解像度のほうが丁度よい。 f:id:IzumiSy:20170822103841j:plain

不具合対応

 やはりというか、もともと予想していたとおり様々な不具合に遭遇した。基本的にはディスプレイ周りの不具合が多く、解決にはUbuntuに関するある程度の知識(たとえばログイン・マネージャがlightdmと呼ばれるものであると知っている、など)が必要になったので、確かに全くLinuxを知らない、という人にとっては優しい製品ではなかった。かと言って僕自身もそんなに詳しいわけでもないが、この21世紀の今、ある程度の情報であればGoogleの大いなるチカラを使ってどうにか見つけ出すことができる。また、GPD Pocket Windows版にわざわざLinuxディストリビューションを入れる世界中の酔狂なひとたちも、様々な情報をネット上で提供してくれているので、彼らの奉仕精神に感謝することにしよう。

不具合レスキューQ&A

  • 起動時のコンソールが横向きになってしまっている

 起動時にブートログが流れるが、それが横になってしまっている。これを治すためにはGRUBの設定ファイルからGRUB_CMDLINE_LINUX_DEFAULTのオプションを見つけ出し、そこへfbcon=rotate:1を追記する。もし設定ファイルを見つけ出せなければ、Grub Customizerなどを使ってもよい。ネットの他のブログエントリーにはこのオプションに加えてnomodesetも追記しているものを見かけるが、僕の場合は追記すると、ログイン後デスクトップのローテーションが効かなくなったため、ここでは勧めないことにする。

  • ログイン画面も横になってしまっている

 GPD Pocketで使われているログイン画面の設定を変更するには、ログイン・マネージャーであるlightdmに関するコンフィギュレーションを弄らねばならない。この不具合に対する最も簡単な解決策は~/.config/monitors.xml/var/lib/lightdm/.config以下にコピーすることだ。おそらくではあるが、この設定を行う前にはGPD側から配布されているUbuntu版の不具合解決に関するPDFを参照のこと、ログイン後のデスクトップをしっかりと正しいローテーションに変更する必要がある。
(参考)https://askubuntu.com/questions/408302/rotated-monitor-login-screen-needs-rotation

  • Unity Dashがおかしい

 Unity DashというのはデフォルトでUbuntuのUnityデスクトップに搭載されているアプリケーション/ファイルの横断検索機能のようなものだ。Macを使ったことがある人ならば、内臓機能のSpotlightやAlfredなどのアプリケーションを思い浮かべるだろう。僕はデスクトップでもUnityを大活用しているため、スーパーキーひとつで起動できるこの検索機能はとてもお気に入りだ。
 しかしながら、デフォルトではGPD PocketのUnity Dashはモードがデスクトップ用になっていないため、下記のようなシルエットになっており、あまりうれしくない。下の写真はネットから拾ってきたものだが、まさにこんな感じになる。 f:id:IzumiSy:20170822170015p:plain  これは実際には不具合ではなく、おそらくただのありがた迷惑だ。GPD Pocket自体はNetbook扱いなので、GPDは端末をNetbookとしてUbuntuのカスタマイズを行ったのではないかと考えられる。僕の場合はターミナルから以下のコマンドを実行して設定をデスクトップのものに戻した。

gsettings set com.canonical.Unity form-factor ‘Desktop’

(参考)https://ubuntuhut.wordpress.com/2014/10/02/change-unity-dash-overlay-size-in-ubuntu/

そのほか

 RedditでもLinuxをGPD Pocketでちゃんと動かそうと頑張っているひとたちがいて、Linux on GPD Pocketというスレを中心に活発な(?)議論が行われている。そうしたかいもあってか、親切なひとがgpd-pocket-ubuntu-respinというWindows版へのUbuntuのインストールの不具合も含めたさまざまなLinux on GPD Pocketのための応急処置の詰め合わせのようなリポジトリを公開している。たとえば僕の場合は、ログイン画面のローテーションがおかしい問題の解決にこのリポジトリを参考にした。おそらくGPD Pocketを手にする人々というのは、一般的にはアーリーアダプター、すなわち人柱的存在であるが、それでもこうした集合知の助けを借りられるというのは十二分に心強い。

さいごに

 ここまで長ったらしく不具合がどうのこうのと書いてきたが、端末自体は全く満足のいくもので、ひさしぶりにいい買い物をしたと思う。過去にNECから販売されていたLifetouch NOTEというクラムシェル型Android端末にも手を出したが、最終的にはあれ自体はあまり使い物にはならない端末だった。けれど、GPD PocketはAndroidではなくちゃんとしたLinuxで、無茶なハックをせずともvimを動かしたり、コーディングをすることができる。性能の差も大きい。予てからこのような端末を待っていただけに、手にしたこの喜びはひとしおだ。

フレームワーク・ライブラリを選ぶということ

とりあえず、ググったら大抵解決しそうなんでReduxを使おうと思います

 2016年の夏、僕はあるフリマアプリで有名なECスタートアップでインターン生として、商品を販売するユーザー向けのアプリケーションのプロトタイピングをしていた。ほとんどすべてのアプリケーション(ソフトウェア)開発のプロセスがそうであるように、僕もまたアプリケーション開発の前段階として「どのような技術スタックを採用するのか」という選択をするフェーズにさしかかった。Reactを採用するのか、Vueを採用するのか。Refluxを採用するのか、それともReduxを採用するのか。それぞれのフレームワーク/ライブラリに一長一短があり、それらの特徴や長所・短所を踏まえたうえで最も最適な選択が「技術選択」というタイミングで求められる。そこでぼくは、とりあえずRedux+Reactの組み合わせを採用した。なぜなら、それが一番情報量として多かったし、なにより困らなさそうだったから!

 多くの割合を占める技術選択の多くが、「それを使っておけばとりあえずは困らなさそうだから」という理由に始まると言ってしまっても、さほど多くの人は「?」という顔をしない。例外なくほとんどの人は大きな失敗だけは避けたいと考える。無名の誰が作ったか分からず、ドキュメントのほとんどないフレームワークや、数年間もメンテされていないライブラリを使うことで、アプリケーション開発にリスクが生まれるということは誰にでも分かる。ゆえに、多くの人が使っていて、ちょっとググるだけでブログ記事やStackoverflowの回答がポコポコひっかかるようなフレームワークは人気がでやすい。その人気は、なんらかの問題が起きても解決策はどこかに用意されているから、という安心感から来る。大抵の場合、この安心感はポジティヴに「エコシステムが成熟している」「採用事例が豊富」などと形容される。

 成熟したエコシステムや、豊富な採用事例がアプリケーション開発における技術選択の一要素になることは問題ではないし、間違いでもない。個々の組織の方針やなんらかのやむを得ないケースによる場合もある。しかしながら、どのような場合でも選択の理由としてそれらを受け入れることが正しいかどうかという点に応えることは簡単ではない。いかなるアプリケーションであってもMVCはスケールしないからFluxをアーキテクチャとして採用すべきなのか。Flux実装ライブラリとしてReduxを採用すべきなのか。日本で流行っていてネットに知見も多く転がっているからRoRを使うのか。RoRにデフォルトで乗っているからActiveRecordを使うのか。etc…

 生き馬の目を抜くこの世の中を生きる私達は、常日頃から選択を繰り返しながら生きている。だが、その中でも合理的選択には大きな負荷が伴う。選択の理由のほとんどは感情的で非合理的だ。深く思考せずにされる選択は、水のように重力に従って低いとこへと流れるままに流れる。そしてある日、自分たちのアプリケーションがフレームワークやライブラリに縛られ、毎日だましだましの小技やハックを繰り返しながら終わりのない成長痛に悩まされていることに気づく。アプリケーション開発における選択は、普段の私達の思考回路とは外れた場所にある。

 どんなフレームワークやライブラリにも(大抵の場合)なんらかの解決しようとした問題がある。合理的選択のヒントはその解決の対象となった「問題」がどのようなものであるのか、という点を深く理解し、自らのケースに応用できる可能性を探ることだ。TodoMVCなどを除いて、多くのケースでは、アプリケーションを開発するためにフレームワーク/ライブラリを使うのであって、フレームワーク/ライブラリを使うためにアプリケーションを開発するわけではない。もちろん、(たとえば僕のような)学生の個人開発や、ちょっとしたフレームワークの使い心地を試すときには、こんなふうにうるさく合理性に触れる必要はない。でも業務となれば話は別だ。開発者としてアプリケーションを作るということは、もっと大きな責任を抱えこむ。大抵の場合は他にも複数のステイクホルダーがいて、様々な利害関係が渦巻いている。それゆえに、簡単にアプリケーションをポイと捨てることはできない。常に変化し、スケールし続けるアプリケーションは、開発者よりもずっと長生き*1だ。それだけに、ステップの一つとして合理的なフレームワーク/ライブラリを選択をするという重要性は簡単に無視できるものではないし、少なくとも骨を折らねばらないものである。

 2016年の夏、ぼくはそんなことは考えていなかった。ただReactが超アツいと聞いていて、Fluxアーキテクチャを採用することがすごくイケてるとネットで見ただけで、実際はペアーズで出会った女の子に夢中*2だった。そういう意味では、2017年のいま、こういうふうに思えるようになったことは、なにかとても大事なことのような気がする。

参考文献

ビジネスとアプリケーション開発の両方からソフトウェア・アーキテクチャを考えるのによい本。この記事を書くきっかけになった。

*1:この場合は人間の寿命という尺度ではなく、一生同じ開発者がひとつのアプリケーションを開発しつづけないという意味において

*2:そのあとブロックされて凹んだ2016年の思い出

モデルの中でセッションを使う

ときたまセッションに関連するロジックをモデルの中に作りたいときがある。ところが、原則的にRailsではセッションをモデルの中で操作することができない。

この問題を解決する最も簡単な方法は、モデルに生やしたメソッドへセッションのインスタンスを渡す引数を追加し、それを経由してロジックを書くというものだ。

class User < ActiveRecord
  def your_method(session, count)
    session[:items_count] = count
  end
end

こうすることで、やりたいことは実現できる。

...
def update
  user.your_method(session, count)
end
...

SRP(単一責任の原則)に則る

これによって、実際にやりたいことは実現されたが、上のコードはSRP(単一責任の原則)から見て必ずしも正しいものではない。 基本的にはActiveRecordパターンであるRailsのモデルには、そのモデルが1:1で紐づく入出力すなわちデータベース・テーブルへの、アクセスオブジェクトとしての役割がある。セッションも見方を変えれば、小さく揮発性の高いストレージのひとつであるが、ひとつのモデルにいくつもの永続化のロジックが混入するのは責務の観点からみて適切ではない。こうした実装は、テストを書く際やアプリケーションのスケールの際に問題になりやすい。

こうした問題を解決するひとつの方法として、セッションを扱うロジックをカプセル化し、責務を分離することで、疎結合な実装にすることができる。

class TemporaryCart
  def initialize(session)
    @session = session
  end
  
  def add_item
    @session[:temprary_cart][:items_count] += 1
  end
end

TempraryCardクラスを実装したことによって、コントローラは以下のようになる。

...
def update
  TemporaryCart.new(session).add_item
end
...

セッションの永続化モジュールがモデルから分離されたことで、見通しの良いコードベースになった。モデルがそれ自体の正しい責務を守ることは「驚き最小の原則」にも則るという面で利点がある。

参考: Setting a Session Variable in a Model - Stack Overflow

コントローラを名前空間で分離して責務の分割をする

Deviseなどを使って、ひとつ以上のスコープを持つアプリケーションを開発している際に、RESTfulなコントローラとビューが1対1対応をしていると、コントローラがどうしても複数のスコープが絡みついた見通しの悪いコードになりがちである。

たとえば、ECカートを実装する例を考えるとする。まず、ChargesControllerがあって、newアクションとcreateアクションはUserスコープによって決済を行う、いわゆるカートの画面を表示にまつわるもの。そして、showアクションはAdminスコープのみでアクセスできるよう認証を実施し、決済情報の詳細を見れるものである。

class ChargesController < ApplicationController 
  def show
    authenticate_admin!
    ... 
  end
  
  def new
    ...
  end
end

この段階ではコントローラはまだ小さいので、さほど問題は感じない。だが、開発をすすめていくうちに、ユーザーによる決済の執行の過程で、エラーが起きた際に、特別な処理をしなくてはならない必要性が出てきたため、コントローラの中にrescue_fromブロックを用意し、DRIな実装を試みようとした。

class ChargesController < ApplicationController
  rescue_from do
    # ここでnew/createでの例外を拾う
  end

  def show
    authenticate_admin!
    ...
  end

  def new
    # 例外が起こる可能性がある処理
  end
    
  def create
    # 例外が起こる可能性のある処理
  end
end  

このようなコードを書くと、今度はshowアクションの中にある例外も、同じくrescue_fromの中で拾われてしまい、Adminスコープとして必要な処理とUserスコープとして必要な処理がコントローラの中に混在してくことになる。また、authenticate_admin!のような認証系の処理も、今例の短いコードであればよいが、コードの量が多くなればなるほど、ひと目ではどのアクションがどのユーザースコープに限定されたものであるのか理解しづらくなる。

こうしたコントローラの問題は、アプリケーションが肥大化した際にコントローラ内部のスコープの責務がすぐにはわかりづらくなり、スケールする際にバグを生みやすいという点である。このような責務の混在を解消するためのアプローチのひとつとして、コントローラを名前空間で分離し、責務の分割を図るという方法が有効である。

実装

Railsでは、ディレクトリの階層構造が名前空間を作るため、ディレクトリ構造は以下のようにUsersAdmins以下にそれぞれのChargesController.rbを作成する。

もしも、すでにDeviseでscoped_viewtrueにしてオーバライド用のコントローラを生成していれば、controllers以下にはスコープ名のディレクトリが存在しているはずなので、その中にコントローラを作ればよい。

- app
  + assets
  + channels
  - controllers
    - admins
      ...
      * charges_controller.rb
    - users
      ...
      * charges_controller.rb
    * application_controller.rb
  ...

コントローラへ名前空間を付与し、RESTfulに従いながらスコープに応じて必要なアクションを実装していく。

class Admins::ChargesController < ApplicationAontroller
  def show
    ...
  end
end
class Users::ChargesController < ApplicationController
  rescue_from do
    ... 
  end
  
  def create
    ...
  end
  
  def new
    ...
  end
end

上記のようにコントローラへ名前空間が付与されたため、ルーティングでは以下のコードのようにscope module: xxxなどを使う必要がある。

Rails.application.routes.draw do
  scope module: :users do
    resources :charges, only: %i(new create) 
  end
  
  scope module: :admins do
    resource :charges, only: %i(show) 
  end
end

これで、もとのChargesControllerは、Users::ChargesControllerAdmins::ChargesControllerに分割され、ひとつのコントローラ内の責務を適切に分割することができた。

まとめ

このテクニックは、実際に業務で遭遇した問題と、POSTDで寄稿された「DHHはどのようにコントローラを書くのか」という記事からインスピレーションを経た。このPOSTDの記事はいかにRESTfulのルールを維持しながら、Railsのコントローラを書くかというベストプラクティスのひとつであったが、その方策はDeviseを使う場合でも応用できる。

肥大化したRailsアプリケーションのリファクタリングのための最初の一歩は、(もしまだやっていなければ)きれいなコントローラを書くことだ。そのために、こうしたベストプラクティスをできるだけ多く取り入れて、気持ちの良い開発をしていければいいと思う。

その他

ひとつ思いつく別の解決策として、StandardErrorを継承したUserErrorAdminErrorなどのような独自例外を定義し、それぞれのスコープに関連するアクションからrescueブロックを用いて更に例外を投げ直すという手も考えられる。これであれば、コントローラの分割をする必要はない。だが、いずれにしてもアプリケーションが肥大化するにあたっては、コントローラの分割が最もよい手法であると思う。

【Quora】あなたが職場でやった悪いことを教えてください。

Read Josh Nymon's answer to What is the most immoral thing you have done in the workplace? on Quora

Josh Nymon

上司の机の上でセックスをしたことがある。当時僕はとある家族経営の店で働いていて、そこでは土曜日にひとりで商品を客に発送しないといけないことがたまにあった。その職場にはひどいコカイン中毒のジキルとハイドみたいな二重人格の上司がいて、そいつは「いい人」のときもあれば、僕達に平気で週50〜60時間オーバーの残業をさせる暴君にもなるやつだった。とはいえ、店を経営していた家族はけっこう気に入っていた。権威を振りかざすそいつみたいなタイプの上司がキライだっただけで。

そのはなしとは別に、僕は昔通っていた学校で一緒だった女の子と付き合っていた。彼女はだいたい車で8時間ぐらいかかる場所に住んでいて、ある週末、ぼくがひとりで働いているときに、たまたま彼女が僕の住んでいる街に来る予定が重なった。最後に彼女にあったのが数週間前だったというのもあって、ぼくは結構ムラムラしていた。恋する若人にとって三週間というのは永遠の別れと同じだ。丁度ぼくが仕事をきりあげかかっているときに、彼女は早めに着いたのでどこにいるか教えてほしいと連絡してきた。あまりにもムラムラしていて、正直数秒も待てる気がしなかったので、職場で会おうと伝えて、駐車場で彼女と落ち合い職場に招き入れた。お互いもう情動と性欲でいっぱいいっぱいになっていて、とりあえずどこかに窓のないスペースがないかどうか探し始めた。そのときたまたま見つかったのが上司の部屋で、当然わかるとおもうけれど、鍵もかかっていなかった。

次の月曜日、僕らがセックスをした机の上で昼飯をたべているのを見たときはほんとに笑えたよ。

(抄訳)