特定のカラムの更新日を管理するためのGemを作りました

self_updated_atという、ActiveRecordで指定したカラムに対して更新が発生したときに更新日時を書き換えるgemを作りました。

利用方法

ActiveRecordのModelに以下のように設定します。 下記のように設定すると、titleとbodyの内容に変更があったときのみ、service_updated_atの時間が更新されるというような具合です。

class Post < ActiveRecord::Base
  self_updated_at :service_updated_at, columns: [:title, :body]
end

Gemを作った背景

業務やビジネス要件によって振る舞いを変更する「更新日」処理を簡単にするため

業務やビジネス要件によって振る舞いを変更したい「更新日」に対してActiveRecordが利用する updated_atは利用するべきではないと考えています。 なぜなら、「特定のカラムを更新したときにupdated_atは更新しない」を実現するのが現実的ではないと思っているからです。

例えば、updated_atを更新しないで特定のカラムを更新する方法として、update_columnというメソッドがActiveRecordにもありますが、確実に漏れると思っています。 誰かが運悪くupdateと書いてデータが更新されてしまった瞬間にupdated_atは更新されてしまい、今まで苦労は水泡に帰してしまいます。

そうならないために別のカラムを用意して、それを「サービスとしての更新日」として実装・利用するほうが良い思ったからです。*1

ただ、なんかスマートじゃないしカラム名だけ指定すればOKみたいに作れれば良いなぁ...というのが大きなモチベーションです。

どうしてupdated_atを直接改変しないのか

フレームワークで自動で更新されることがレールで敷かれており、業務やビジネス要件によって振る舞いを変更したい「サービスとしての更新日」に対して利用するべきではないと考えています。 それがフレームワークのデフォルトの振る舞いなので、そこを変に改変しないほうが良いと思っているからです。*2

TODO

  • ひとまずv0.0.1として出したけど、簡単なGemと言いつつもテストをまだ書けていない。さっさとテストを書く。
  • このブログを書いてて思ったのは、hooks_columnsという記法が微妙なので、columnsへの変更。
  • シンプルはシンプルだけど、READMEもちゃんと書く
  • 今はModelのカラムが更新されているかという条件しか指定出来ないので、さらにメソッド名かLambdaを渡せるオプションがあればうれしいですね
    • この当たりができればv1.0.0としてシュッとリリースしたい。

最後に

このGemを使うか使わないかは置いておいて、updated_atを「ビジネス・仕様上の更新日」として利用する場合は、ActiveRecordが更新するupdated_atとは別のカラムで管理したほうが幸せになれる。ということを強く言いたい。

あと、もし類似するライブラリがあったら教えてください... 簡単に探してみたけどなさそうだったのでサクッと作ってしまったので...

*1:このあたりの実装で、自分が痛い目を見たというのは内緒...

*2:このModelのupdated_atは「xxxx」カラムが更新されてもupdated_atは更新されないように注意してね...というのがあったりするのはまずいと思っている。

プロトタイプ用途でFirebaseを使ってよかったという話

最近、個人的な趣味と社の技術検証を兼ねてFirebaseを触っている。

一番のモチベーションとしてはFirebase Realtime Databaseの存在が大きかったのだけど、 フロントエンド・クライアントサイドエンジニアがプロトタイプ作るときに、 あると便利な機能が一通り揃っているという観点でも良いと思ったので、紹介したい。

知っている人からすれば2年遅れくらいだろうけど、サーバーサイドをメインにしている人間には新鮮だったので、 下記でそれぞれのサービスについて解説する。

Firebase Realtime Database / Firestore

firebase.google.com

firebase.google.com

今更解説する必要もないと思っているけど... チャットを5分で作れるだの、1分で作れるだのと言ったものはだいたいコレでできている。

Firebase Realtime Database はクラウドホスティングされるデータベースです。データは JSON として保存され、接続されているすべてのクライアントとリアルタイムで同期されます。iOSAndroidJavaScript SDK を使用してクロスプラットフォーム アプリを構築した場合、すべてのクライアントが、1 つの Firebase Realtime Database インスタンスを共有して、最新のデータによる更新を自動的に受信します。

とあるが、Websocketで通信して、内容を同期してくれるというシロモノである。 ローカルでも永続化されているので、オフラインでもちゃんとあとからオンラインになったときに同期してくれる。

Firebase RealTime Database / Firestoreの違いについて

  • Firebase Realtime Databaseの方が先発で、Firestoreが後発の発展系だと思ってもらって良い。

今はどっちにすればいいのか??

  • Firebase RealTime Databaseの方が情報量も多いし、関連ライブラリ等で面倒なく使えるのは多分こっちなので、プロトタイプ用途だけであればこっちでもエエんちゃうかと思っている。
  • プロダクションで...という場合はFirestoreの方がかゆいところに手が届く感じがして良さげ。*1

Firebase Hosting

firebase.google.com

Firebaseの静的サイトのホスティングサービスで、コマンドラインでのDeployがめっちゃ簡単。 コマンドラインツールをインストールして、firebase deployってやれば静的ファイルを配信してくれる。 既にプロジェクトを作っている場合、以下のコマンドでデプロイが完了する。

npm install -g firebase-tool
firebase login
firebase deploy #カレントディレクトリでコレを実行する

ここでは割愛するけど、CIサービスなんかでDeployするためのTokenを発行するコマンドもあって便利。 AmazonS3よりも手軽さで言うと勝っているという印象がある。 当然Firebase RealTime Databaseも感動したけど、コレがシンプルな機能だけど一番の感動だった。

SPAでのRewrite設定

フロントエンドルーターのあるアプリケーションでFirebase Hostingを利用する場合、rewrite設定をする必要がある。 他のフロントエンドルーターを利用する場合でも同じ設定をすればいいと思う。 VueRouterでの設定の解説ページを貼っておくので参考にして欲しい。

HTML5 History モード · vue-router

Firebase Storage

firebase.google.com

画像アップロードも面倒なので、サーバーサイド実装なしできると工数削られて本当に助かる。 とりあえず作るということを優先すれば本番でも投入できそう。(Pricingは見ていないが...) まぁプロトタイプ用途でココまで必要かと言われればそうでもないが...

おわりに

最近Railsでプロトタイプするのですらスピードが足りないと感じていて、 SPAでザクザク書いちゃった方が手慣れしていないエンジニアでも早いなぁと感じている。*2 ただコレに寄りかかったまま*3で本番投入すると、 特定のサービスに依存しすぎているのと、いざという時のデータの移行が大変なので、 parse.comのようにポシャる事を考えるとリスクヘッジは必要。

今後本番でも使いたいサービスなので、この辺についても書いていきたいです。

*1:Firestoreはまだベータです

*2:create-react-appやらvue-cli使えばとりあえず環境はできる

*3:特に懸念はFirebase RealTime DatabaseとFirestore

2017年の振り返り

会社的なアレ

色々社に動きがあり、関わる人間の数も増え、 一気に会社としての規模、大袈裟感は上がったように感じた。 その中で自分が納得感の行く成果が挙げられたかといえばそうではないと思っていて、 2018年は目に見える成果をどしどししていきたいという気持ちです。 というか技術的課題がヤバイと思ってて、 半年〜1年の間でコレをなんとかできないと会社としてもマズいと危機感を持っています。

エンジニアとして

わりと大きめなサイトをリリースできた

社の方針である程度しか口外出来ないのだけど、それなりにPVがある案件をリリースすることができた。 ほとんどトラフィックについては、 札束で解決したというのがほとんどな気がするけど、ある程度トラフィックへの対策はしてみたつもりなので、 それがうまくまわってよかった。 やってることは基本的に大したことないし、正直まだやりようはあるのだけど、自分の自信に繋がったと思っている。 来年はこの知見を元に改良版をどんどん作っていきたい。

Azure

社の都合でメインで利用するクラウドサービスをAzureになったので、色々実務で触っていった。 AWSに準ずる機能はまぁあるが、SDKのラインナップにRubyがなかったり、メンテナンスが積極的でなかったりと、 検索しても情報は少なくサポートがあったとはいえ、正直なところAWSに比べてまだまだ微妙なところが多いなと思った次第です。 まぁ文句だけだとアレなんでフォローしておくと、まだプレビュー版だけど、MySQLを動かせるようになったり、DockerでRuby動かせるようになったりで良くなっているとは思います。

Docker

ゆるふわにDockerを使い始めている。 K8sとかは使うまでも無い規模から投入しているので、シングルコンテナーでやっている。 理由としてはAzureにコンテナーをホスティングしてくれるサービスがあるためです。 k8sなどのオーケストレーションに関しては社に詳しい人がいたら任せたいっすねーくらい。

フロントエンド

社にフロントエンドエンジニアJOINしたので、 来年以降、自分はフロントエンド(JS)を書くことは少なくなるかなーと感じています。 別にフロントエンドが好きではないので、良いと思っているけど。 プロトタイプ目的だと全部JSで書いちゃうのがとってもラクなので、自分でもザクザク書いちゃうかも。

Firebase

完全に今更感あるのだけど、この年末年始に色々触ってみている。 日本でも使われる事例を見るようになって、知見が溜まって来ているので、 今更使い始めるのは、ある程度知の高速道路に乗れるという意味では良かった。 フロントエンドエンジニアがプロトタイプで使うのに便利なプロダクトが揃っており、今後も注目していきたい。 来年には何所かでプロダクションで使えればいいかな、とか考えています。

結婚しました

2/22に結婚しました。特に式とかはないです。 もう少しだけ新婚気分を味わいたいと思います。

4Kモニター買った

フィリップスの43インチモニターを買った。 自宅に導入したけど作業効率が上がってとても良かった。 後述するけどPS4でも快適に使えてQOLが上がった。

PS4

自分にとって4年ぶりくらいに据え置きゲームを買った。 モンハンに備えて買ったわけでだけど、これは良い買い物だった。 今はゆるゆるグランツーリスモをやっている。 今のところゲームに時間を丸々溶かすことはなくて、程よいバランスでストレス発散にゲームを使えているなと感じている。

来年の目標について

  • 前述しましたが、社の一員として、成果を上げること(メインは技術的負債の返済)
  • MHWに時間を吸われないように
  • 勉強会で積極的に登壇する

おわりに

それでは皆様残りわずかですが良いお年をお過ごしください。

VueかReactどっち勉強すればいいか問題

最近良く聞かれるので書いておく。

答える人のバックグラウンドについて

  • 最近はだいたいRails書いてるサーバーサイドエンジニア。
    • 最近はフロントエンドも書くことも少なくなったけど、Vue1.xのときにSPAっぽいものつくってたりした。
  • Reactは未経験だけど、Reduxとか、Fluxとかはなんとなーく考え方位はしっている。ほかのフレームワークはあまりしらない。
  • まぁサーバーサイドエンジニアから見た視点ということで、本職の人はちょっと大目に見て欲しい。

想定読者

  • このような質問をするのは、僕のようにどちらかと言えばフロントエンドのフレームワーク事情に詳しくないような人だと思っている*1ので、その人にあった回答をしておきます。

まとめ

  • ちょっと複雑なフォーム*2とか実装したいときはVue
    • フロントエンドを片手間でやってる人が欲しい機能の8割方これでカバーできる。
    • アホみたいにある周辺ツールもまずは無視してやってみてほしい。(Vueはまだ無視しやすい)
    • 直接HTMLにJS書いちゃうスタイルでもOK。
  • SPAをしっかり作り込みたいならReactとかAngularとか他のがいい
    • Vueでも出来なくないけど、VueよかSPAでの稼働実績も多いし、コミュニティもFacebookGoogleが関わっていて巨大なので安心。
    • そもそも片手間でSPA無理とか、そういうのはまた別の話。
  • TypeScript使いたいならVue以外が良い。
    • あまり相性が良くないと思っている。
    • 先述したしっかりSPA作り込みたい案件の場合、コレが痛いケースも。
  • そもそもどっち?なんて聞くくらいにフロントエンドにあまり積極的でない(悪い意味ではない)場合はVueだけでいい。そうでなかったらVueだけでなく、Reactとか周辺技術も頑張って勉強してください。
    • Vueを片手間レベルでもいいので使えるようになるとマジで楽になるし、コスパ最高と思っている
    • フレームワークに詳しいフロントエンドエンジニアはTPOに応じてReactとVueを使い分けれるようにしていると思ってる。

最後に

  • ちょっと複雑なフォームとかをシュッと書くときにReactを採用するメリットってあるのかな?(単純に知らないだけだと思うで、知見ある人教えてください。)
  • 本当にこれだけやれば片手間JS楽になるよーって記事は需要があるのかないのかわからない。*3

*1:詳しい人は両方知ってるし、そんな質問はしない

*2:例えばこんな中古車検索のフォームとか http://www.get-u.com/ucar/search/

*3: 記事にするほど難しくもないのでは…

RubyでInterface

そもそもRubyにInterfaceが必要とか、必要じゃないとかの議論は置いておいて、 無いのであれば、類似したことをやりたい場合はどうすればいいのかが気になって調べてた。

1. まずは簡単にやってみる

module TestInterface
  def method1
    raise NotImplementError
  end
end

class SomeClass
  include TestInterface
  def method1
    puts "hoge"
  end
end

SomeClass.new.method1

実装はココから拝借。

blog.wktk.co.jp

愚直にmoduleで書いたほうが良いんじゃないかなぁと思っている。 そのほうが明示的でわかりやすいというか… 業務でどうしても欲しい場合は、消去法でコレで良いんじゃないんですか。という感じ。

2. Gem使う

その名もズバリなInterFaceというGemがあるので、コレを使ってみることにする。

github.com

実装もここから読めるし、100行もないくらいの短いコードなので、興味がある人は読んでみて欲しい。 interface/interface.rb at master · djberg96/interface · GitHub

require 'rubygems'
require 'interface'

TestInterface = interface{
  required_methods :method1
}

class SomeClass
  def method1
    puts "hoge"
  end
  implements TestInterface
end

SomeClass.new.method1

Interfaceをそれっぽく、かつRubyっぽい感じ*1でかける。 ただ一番しっくりこないのは、implements*2を必ずメソッドの定義よりも下に書かないと動かないことだろう。 多分これは実装上の都合なだけな気がするので、もしかすると修正されて、メソッド定義よりも上に書いてみても大丈夫になるかもしれない。 業務で使うのはあまりオススメ出来ない…

まとめ

結局少し試してみたけど、これならやらんでもいいかな…となってしまった。

*1:ココでいうRubyっぽいというのは簡潔という意味で、そもそもInterface使おうとしている時点でRubyっぽくないのは間違いない。

*2:コード見れば解るけど、このGemのimplementsの実体はただのincludeのエイリアスだったりする。 https://github.com/djberg96/interface/blob/master/lib/interface.rb#L106