Write Code or Die

RubyやRailsまたはバスケットボールや筋トレについて書きます。

Railsでの部分的なjavascript_pack_tagとturbolinksの共存

最近作りたいアプリケーションがあり、チャット系のUIにしたく勉強がてらRails 5.1 + webpacker + Reactでやってみることにした

しかし、form_with経由のページ遷移した時にページにベタ書きしているjavascript_pack_tagが動かないという事案発生

こんなviewから

#app/view/root/index.html.slim
  .form
    = form_with url: that_path do |form|
      = form.hidden_field :id, value: @id
      = form.submit "submit"

こんなcontrollerを通りthat_pathに遷移する

#app/controller/that_controller.rb

  def create
    that = That.create!(user_id: current_user.id, that_id: params[:that_id])
    redirect_to that_path(id: that.id)
  end

thatはviewの中にjavascript_pack_tagがベタ書きされており

#app/view/that/show.html.slim

#that-container
=javascript_pack_tag('that_container')

このpackではDOMContentLoadedでReactがrenderされるようになっていた

//app/javascript/packs/that_container.js

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <ThatContainer />,
    document.getElementById('workout-container')
  )
})

ここで問題発生。form_withのボタンを押して遷移した時にReact DOMがrenderされない

調べた結果、turbolinksがうまく行ってない感じがした

試しにapp/view/layouts/application.html.erbjavascript_include_tagコメントアウトしてみる

//app/views/layouts/application.html.erb

<head>
  ~

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%#= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>

動いた

しかしcssフレームワーク使いたいのでjavascript_include_tagとは共存させたい

でも部分的にReact使いたいだけなので、出来ればturbolinksと共存させたい

こうして泥沼にハマりながら調べた結果

  • javascript_pack_tag<head>に記述する
  • javascript_pack_tagcontent_forで渡しつつ、'data-turbolinks-track':'reload'を指定する

ことで無事解決した

以下コード

//app/views/layouts/application.html.erb

<head>
  ~

  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= yield(:javascript_pack_tag) %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
//app/view/that/show.html.slim

= content_for(:javascript_pack_tag, javascript_pack_tag('that_container', 'data-turbolinks-track':'reload'))
#that-container

turbolinksのこともさらっとだが学ぶいい機会になった

続・体育館予約を自動化しようとした話

この記事はFablic Advent Calendar18日目の記事です。

体育館予約を自動化したい

こんにちは。Fablicのサーバーサイドエンジニア、Jamesです。

突然ですが、去年のアドベントカレンダーでこんな記事を書きました。

masacandy.hatenablog.com

要約すると

Selenium Web Driverで体育館予約システムに自動ログインできたけど体育館全然空いてないから予約できないやんけ!

と言う記事です。

去年は自動でログインした後に心が折れてやめてしまいました。

立場の変化

上記の記事を当時所属していたチームはそこまでしっかりとした活動もせず、たまに大会に出て試合をするスタイルでやっていました。 それなりに楽しくやっていたのですがこの一年で自分を取り巻くバスケ環境が紆余曲折あり、なぜか新チームを立ち上げることになりました。

更に気がつけばそのチームの代表になっていました。なんて恐ろしいことでしょう。

代表になったからには同じチームでやっている仲間達と楽しいバスケットボールライフを送りたいものです。幸いなことに「楽しい == 笑いながらやる」ではなく、「楽しい == 真剣にやる」メンバーが集まりました。我々は学生のように毎日練習することはできませんが、週1ぐらいの練習でできる限りのことをして都民大会で上位を狙うことを目標に活動することになりました。

となるとやはり立ちはだかる壁は、そうですね。

体育館予約を自動化したい

はい、我々は2017年になっても体育館の予約で消耗しています。

f:id:masacandy:20171218163048j:plain

↑Kobe Bryantファン必読の本でした(エルニドで撮影。エルニドは楽園でした。)

仕様書を確認する

前回心が折れたポイントは、

体育館の予約の仕方が直感的に理解しづらい

この一点に尽きます。

しかし、今回は前回とは違い責任が増えたので根気よく取り組むことにしました。 このようなシステムにありがちな 「ブラウザの戻るが使えない」 仕様のために何度もやり直しをするなどしながら調査を続けていると、普段世の中に出ているWebサービスがどれだけユーザーに寄り添っているのかと感激して涙が溢れてきました。

こうして調査を続けていると以下の仕様がわかってきました。

  • 予約は9-22時の間に行うことができる。
  • 予約は基本的に抽選である
  • 翌月の体育館予約の抽選は前月の1-5日に行われる
  • 結果が6日に出る
  • 結果を反映した後の9日から当該月のキャンセル分の予約が可能
  • キャンセルされた予約は早い者勝ち
  • 1つの予約をすると最初の画面からやり直さなければならないので、複数の予約を同時に行うことは不可能

なんという仕様でしょうか。 毎月9日の朝9時に翌月の予約の勝敗が全て決まってしまうではありませんか。

逆を言えば、 その時間にものすごいスピードで予約をしてしまえばだいたいよさそうです。

そして、キャンセル後は早い者勝ちなのでそれを漏れなく予約すればよさそうです。

抽選の内容に関してはこちらとしてはタッチできない部分なので諦めます。(根気よく抽選も申し込んではおりますが、これまで1日しか当選したことがありません、、、)

つまり、我々に必要なものは

体育館のキャンセル状況を確認して、空きが出ていたら自動で予約できる何か

ということがわかりました。

作りたいものが少し具体的になりました。

類似するシチュエーションを利用して仮で予約してみる

さて、作りたいものが具体的にはなりましたがやはり空いている体育館がありません。 どのような状態になれば予約完了に出来るかがわからないので開発が進めません。 去年の自分だったらここでまた心が折れて、この企画はまた来年に持ち越されていたでしょう。

しかし、2017年の僕は一味違いました。人は守りたいものができた時に強くなれるものです。

すぐに作りたいものを作ろうとするのではなく、可能な限り類似するシチュエーションを利用して他の施設を予約してみることにしました。 どういう意味かと申しますと、 まずは、「体育館」を予約することを諦めて、同じシステム上で施設の空きが多い「会議室」を予約出来るシステムを作りました 「体育館予約システム」のためにまずは「会議室予約システム」を作ることになるとは思いませんでした。 しかし、この作戦が功を奏しました。 「会議室予約システム」の完成後、体育館キャンセルをチラチラ見ていたら平日ですが体育館空きがある日も奇跡的に発見することができ、「体育館予約システム」に生まれ変わらせることができました。

コードは公開したくないので(ライバルを増やしたくないw)具体的な内容は伏せますが、フレームワークも使っていないRubyスクリプトです。

bundle exec ruby app.rb

この1行をターミナルで実行するだけでキャンセル状況を見て予約出来るプログラムがかけました。

マンバメンタリティー!

サーバー上で動かせるようにする

最後は書いたプログラムを定期的に実行させるだけです。 そのためにはサーバーが必要になります。

最初は「どこかのサーバーにcron登録するのかー、会社の開発機にでも置くかw」とか考えていたのですが、ものすごく便利なサービスがありました。

みなさんご存知Herokuです。

Heroku上でSelenium webdriver を利用するためにはChrome headlessモードを利用する必要があるので、Buildpackをインストールする必要があります。(Herokuの管理画面から簡単にできます。) そして、何より驚いたのはHerokuにはHeroku Schedulerというプラグインがあり、これを使うとポチポチ設定するだけで簡単に定期実行してくれます。 cron登録を代行してくれるだけかもしれないですが、とても楽でした。

参考 katsulog.tech

予約完了後の通知機能をつける

こうして一定の時間で予約をすることが可能になりました。 しかし、予約が成立しているか常に確認しに行くのは不便です。 そこでLINE Notifyを利用して予約が完了したらチームのLINEグループに通知が行われるようにしました。

LINE Notifyはトークンを渡してHTTPポストするだけなので簡単でした。

参考

qiita.com

身近な仲間を幸せにできること

結果

f:id:masacandy:20171218160035j:plain

見事我々のチームは安定したバスケットボールライフを送っております。 これまでよりチームプレイも増えてきて、とある市民大会では優勝することもできました。

f:id:masacandy:20171218163401j:plain

こんなに首から頭下げてると思いませんでした。

これまで仕事でたくさんのコードを書いてきましたが、1番価値のあるものになったと思いますw 合計で300行も満たないコードで幸せな気持ちにさせてくれるRubyにも改めて感激しました。

このように業務で得た知識が私生活をも充実させてくれるのはこの仕事のいいところであると思います。

しかし何より、仲間たちとバスケをする時間が増えたことがとても嬉しいです。

体育館予約には開発のエッセンスが詰まっていた

実は今回の一連で学んだことに 開発のエッセンスが詰まっているのではなのではないか と考えました。

  • 作りたいものの概要を理解する(顧客が本当に必要だったのは「自動予約システム」ではなく、「自動キャンセルされた体育館の予約システム」)
  • 制約を理解する(予約できる時間の把握等)
  • 機能を細分化し、似た事例を探す(Selenium Web Driverの使用事例を探す、Heroku Schedulerのようなやり方を探す)
  • すぐに作りたいものができない場合はそれに類似するシステムを作る(会議室予約システム)
  • 本番のシステムに適応させる
  • 更に便利になるような仕組みを考える(LINE Notify機能の追加)

特に 機能を細分化し、似た事例を探す というのは大切だと思います。

でも、結局のところ根性な気がします。

最後に

練習試合相手を絶賛募集中です! FacebookTwitterからお気軽に連絡ください!

クリスマスも近いのでウィッシュリスト載せておきます、、、

最後に何かの有名なセリフを載せておきます

The best teacher, failure is

Rubyのattr_readerについて

  • :attr_readerRubyのオブジェクト志向の本とか、海外のブログの例ではよく見かける
  • 影響されて何も考えずに仕事で使ったら割と突っ込まれた
  • インスタンス変数に@ つけなくていいから楽だし見やすいからいいのかと思った
  • 突っ込まれる理由としては隠蔽した方がいいということ
  • ではなぜ本とかブログでは使われているか
  • 個人的な結論としては 隠蔽した方がいい という原則に囚われているので使うべきという結論に達した
  • initializeで渡したものを利用してインスタンス変数にしてる時点で外側から見えて困ることはない
  • むしろそのオブジェクトを構成している要素の中身がわかるので、見えた方がいい場合もある
  • Duckの様に鳴くからDuckの例を使うとすれば、Duckの足の長さがわかった方がそのDuckがわかるのではないか?的な?(わかりにくいか)
  • もちろんむやみに :attr_accesor を使うべきではないが、:attr_readerには問題はないのではないか?
  • また、順番に依存した処理にその変数を使いたい時はゲッタで使うべきではなく、メソッドの返り値からスタートして、ローカル変数で渡すべき
  • 従ってある変数が多用されるからむやみにゲッタメソッドをつけるのではなく、initializeで外側から渡してくる変数などそのクラスの構成要素となり機能を表しているようなものに対しては積極的に使うべきだと考えた
  • とはいえこれは宗教戦争みたいなもんなのかな

結論がまとまったので忘れないうちに雑に書きましたが、気分が乗ったらちゃんと書きます。何か知見があればコメントをいただけると幸いです。

NBA バスケットボールコーチンブプレイブックが良すぎた件

http://amzn.to/2vLDqUw

自分が中高生だったら確実に出会いたかった本

発売日見たら2013年ってあったので、しょうがないかという気持ちになった。

スクリーンプレーやボールのもらい方の一つ一つが理論として記されていて、非常に勉強になった。

自分のようにコーチがいない環境でバスケットボールをしていた選手にとっては最高の教材なのではないだろうか?

しかし、あまりに文書になっているのでちょっと分かりづらいというのが正直なところである。

これをそのまま動画にするだけでもかなりチャンスありそうである。

ゴールドスタンダード・ラボ | バスケットボールコーチ・選手のための情報サイト

このブログにも死ぬほどありがたい情報がまとまっていた。

これをもっとプレイヤーに落としていくようなサービスを作りたいと思う

rails g とか rails c が動かない時

administrateを導入しようとして

github.com

bundle exec rails g administrate:install

とかやっても全然反応なくて

Rails5.1.1 に対応してないのかなてとか色々考えて

全然動かないからしょうがないから管理画面めんどくさいけど自作するかーとか覚悟を決めてて、

なんかのタイミングで bundle exec rails c したらこれも動かない

おかしいなーと思ってググったら Springが悪さしてるとかあって

まさかと思ってkill してもっかい bundle exec rails g administrate:install したら動いた、、、

activeadminの小ネタに関してブログ書いたら公式wikiに引用された話

ちょっと前の話

以前、会社の技術ブログでactiveadminのちょっとしたTipを書いた。

ActiveAdminでカスタムフィルターを実装する - inFablic

有名なgemって言っても所詮はライブラリだしDSLだしそんなに役にも立たないだろうけど一部の人の役に立てばいいかなーくらいに考えてたんだけど、Twitter見てたらある日突然外国人に感謝された。

f:id:masacandy:20170709165843p:plain

なんやねんこれーって思ってリンク先見て見たらactiveadminのwikiページで、なんと俺の書いたブログが引用されていた。

github.com

普段使ってるライブラリの作者とこう繋がれて、引用してくれてちょっと嬉しかった。

大したことしてないし、多分彼自身もブログの内容読めてないと思うけどw

わざわざ引用してくれた上に、日本語でメッセージくれてインターネッツに久々に感動しました。

brewでインストールしたmysql5.6が一生起動しなかった

mysql5.6を入れようとしたら、今はmysql@5.6 っていうのをインストールする必要があったんだけど、

mysql
mysql.server

どっちもやっても全然起動しなくて、よくあるPIDファイルだとか権限だとかだと思ったからめっちゃ調べていたのだけれど全然うまく行かなくて本当に途方にくれていたのだが

brew postinstall mysql@5.6

で解決した。

インストール時のメッセージはちゃんと読んでねって話か….