| @技術/プログラミング

lokka/lokka 、 Pull Request を出す度に Hound CI のチェックが走って bot にコードレビューでぼこぼこにされるので、この bot を黙らせるべくガチャガチャやってた。 Hound CI のチェックルールは Rubocop に準拠しているようで、 2011 年からある Rack アプリを Rubocop のチェックにかけるのは面白かった。

Lokka 、意外と Hacky なコードが多く、条件式内での代入とか、ヨーダ記法とか、後置の until とか、スコープが広い一文字変数とか、めっちゃ長いメソッドとか、 if 文のネスト、代入したものの使われてない変数なんかを修正した。 method_missing はカスタムフィールドを定義できるという Lokka の仕様上根絶できなかったけど、 .rubocop.yml に最低限の除外ルールを追加して Rubocop のチェックはパスするようにできた。

Lokka 、 ORM が ActiveRecord じゃないことが問題だと思ってたけど、真の問題は lib/lokka/helpers/{helers,render_helper}.rb にビジネスロジックが詰め込まれてることだと思った。しかもこのあたりのコードの可読性がよくなく、触るのが怖い感じの複雑なやつが多い。この辺のコードをもうちょいクラス化して分割し、ユニットテストも手厚くしていかないと ORM を変えても F/E を今風にしてもウェブアプリケーションとして生存していくことは厳しいと思う。

前に進んでいくためにも Rubocop のチェックを入れる&パスさせるのはプラスになると思う。 頑張ってメンテしていくぞ。

追記

この辺のコードをもうちょいクラス化して分割し

と書いたけど、 Rails と違って手軽にサクッと作れるのが Sinatra の良い所なわけではあって、仕事で作る Rails アプリのノリでクラスやファイルを分割したりするのは違うのかもしれないと思った。 Rails で作られたオープソースの CMS やブログツールに長生きし続けるものがないのも、 Rails の場合、個人が偶発的に始めてメンテ出来るようなものになりにくいからかも知れない。

とはいえヘルパーがビジネスロジックを所持しているのはテスタビリティやメンテナビリティが良くないので Lokka と心中する覚悟でやっていくぞ!!!、!

| @技術/プログラミング

Pinboard にブックマークしたらはてなブックマークに同期するやつを作った。

なんで Pinboard を使うのか、どうしてはてブに一本化しないのかというと、洋物のサービスを使うときに Pinboard の方が使いやすいから。 IFTTT に Pinboard 連携機能はあってもはてブ連携機能はないし、 Delibar や Reeder や ReadKit も Pinboard には対応しているけどはてブには対応してない。あと Pinboard は広告出ないしホッテントリ的なものもないので気がついたら Amazon で買い物してたとかホッテントリの海に溺れてた、ということも起こらない。とはいえはてブのコメントでキャッキャッウフフはしたい。なのでブックマークするときは両方にしたい。パソコンの Chrome からブックマークするときは Taberareloo でクロスポストできるのだけど、最近 iPhone からブックマークすることが増えて(iPhone 進化し過ぎて仕事するときしかパホコン使わなくなった) Pinboard とはてブにそれぞれブックマークするのがだるかった。そういうわけで Pinboard をメインにしつつはてブに同期を試みた。

はてなスタッフの aereal さんが作ってるはてブ API 用の gem (aereal/hatena-bookmark-restful: A client library for Hatena::Bookmark RESTful API)あって使わせてもらったのだけど、これはそのままだと使えない。タグが複数あるケースに対応してない& User-Agent を送らないので API 側から 401 Unauthorized が返ってくる。なので雑にモンキーパッチした。

はてなブックマークの API は tag を 10 個まで設定できるけど、以下のように Request Body が Encode されるのを期待しているっぽい。

"comment=&tags=ruby&tags=http&url=https%3A%2F%2Fgithub.com%2Flostisland%2Ffaraday"

この様な Request パラメーターを Ruby で表現すると以下のような Hash になると思う。

params = {
  comment: '',
  tags: ['ruby', 'http'],
  url: 'https://github.com/lostisland/faraday'
}

Hash は当然のことながら同じキーを複数持つことはできないから、 tags は配列として表現される。これをこのまま Faraday (Ruby の HTTP クライアント。上述の gem でも使われてる)に渡して Request Body を生成するとエラーになってしまうのだった。

最近の Faraday には encode option が追加されて FlatParamsEncoder というのを選べるようになってた。こいつを使うと配列を value に持つ Hash をシリアライズしたときに tags=foo&tags=bar みたいな形式にしてくれる。加えて User-Agent も載せるようにもした。こんな感じ。

class Hatena::Bookmark::Restful::V1
  def create_bookmark(bookmark_params)
    res = connection.post("/#{api_version}/my/bookmark") {|req|
      req.params = bookmark_params
    }
    attrs = JSON.parse(res.body)
    bookmark = Bookmark.new_from_response(attrs)
  end

  private

  def connection
    @connection ||= Faraday.new(url: 'http://api.b.hatena.ne.jp/') do |conn|
      conn.request :url_encoded
      conn.options.params_encoder = Faraday::FlatParamsEncoder
      conn.request     :oauth, {
        consumer_key:    @credentials.consumer_key,
        consumer_secret: @credentials.consumer_secret,
        token:           @credentials.access_token,
        token_secret:    @credentials.access_token_secret
      }
      conn.headers['User-Agent'] = 'Hatena::Bookmark::Restful Client'
      conn.adapter Faraday.default_adapter
    end
  end
end

なおはてブの API はリクエストパラメーターが不正なとき 400 Bad Request を返すのではなく 401 Unauthorized を返す。のみならず User-Agent なしのリクエストに対しても 401 を返す。一方で OAuth ヘッダーが不正なときは 400 を返す。原因の切り分けがむずかしくなるので、リクエストパラメータが不正なときは 400 を返して欲しいし認証できないときは 401 を返して欲しい。

追記 2017-05-22 18:34:38

作者の aereal さんに気づいてもらってパッチを取り込んでもらったんだけど、なんかやっぱりタグありの記事をブックマークしようとするとエラーになるっぽい。相変わらず 401 Unauthorized が返ってくる…。

| @技術/プログラミング

Archives ページは部分的に React.js を使ってる。これを全部 React にして SPA 化してみたい。そのためには Server Side Rendering が必要。

Ruby で Server Side Rendering するライブラリでは react-rails が有名だけど、 Rails 以外で Server Side Rendering したい。

有名なやつでは airbnb の hypernova というのがあった。これ自体は Node.js で、 hypernova-ruby というクライアントからサーバーを呼び出して Server Side Rendering するというもの。なのでサーバー側に一個プロセスが増える。開発環境でも Node.js でプロセス起動したりしないといけない。仕事で Microservice には食傷気味になってきてるので趣味では Majestic Monolith™ で行きたい。

名前似てるけど Hyper-Ract というやつもあった。これは過激で JavaScript とか HTML を排除して Ruby で全部書こうぜという Opal が React 対応したもの。良いんだけど F/E の進化早すぎ問題に対応できなくなる気がする。数年後には CoffeeScript みたいになりそうな予感がある。スタンダードでシンプルなものを使いたい。

もう一個、スター全然付いてないしメンテナンスもされてなさそうなんだけど、 V8 で React 読み込んで出力するだけという簡単なのもあった。react-ruby-v8 という直球な名前。こんなので十分かもしれない。

| @労働

会社を辞めた。3年半在籍してた。

ペパボに入る前は凄いブラック企業で働いてて、 Subversion やめて Git 使いたいと言ったら会社辞めろと言われたりしてた。そんなときに蜘蛛の糸のように目の前に垂らされたのが Dazaifu プロジェクトの求人で、藁にもすがる思いで応募し入社したのだった。この辺は過去のエントリに適当に書いてあるので読みたい人は読んで下さい。

ペパボは働きやすくて、毎日18時になったらみんなさっと帰るし、21時過ぎに会社出ると最終退出者であることもしばしばだった。家庭の事情にも理解があって、育児休業をさせてもらったり、ばあちゃんの具合が悪いときには会社休ませてもらったり早めに帰ったりしてたし、ばあちゃん死んだときにはお花とかも出してもらった。労働環境の他にも年末の社員旅行とかプレゼン大会とか社内の催し物があったりして良い雰囲気だった。課長が女性エンジニアにセキュリティルームでセクハラしてたり社長が気に食わない奴はいきなりクビにしたりしてたブラック企業から移ってきた身にはほとんど天国だった。

なにより自分にとってよかったのが、インターネットが会社になったみたいなところだった。会社に @shikakun がいて、あとから @antipop さん( Mr. CTO!)とか @udzura さんとか面白インターネットコンテンツな人も入ってきて、自分が @morygonzalez として存在することが是認される感じがとてもうれしかった。

とはいえペパボでもそれなりに厳しいことはあって、そういうのは一昨年の闇アドベントカレンダーに書いたのでこれも読みたい人は読んどいてください。

社内ではおおよそ一年おきに異動していて FANIC => MuuMuuDomain => minne と渡り歩いた。そう、僕はいま CM やったりしてる minne の中の人だったのです。

3年半の間に PHP を書くこともあったけど、自分の指向性とかを汲み取ってもらい、概ね Ruby を書かせてもらった。ウィンドウズを使えと強要されることはなかったし、 Ruby は書きたい放題だし、毎日会社行くのが楽しかった。

最後にいた minne は本当に良いチームで、みんなでリーンキャンバス描いたりエレベーターピッチを考えたりして、どうやったらサービスが圧倒的に成長できるのかを真剣に考えてた。

エンジニアはみんなできる人たちで、特に初期から minne を支えていた @mizoR さんが凄く、ちゃんとコンピューターサイエンスのバックグラウンドがあるため文系の自分にはない視点で問題にアプローチしていて非常に勉強になったし、また歩く UNIX の哲学みたいな存在で、小さく作ってこまめにリリースし検証することの大切さを教えてもらった。(@mizoR さん作の rake_notification は神 gem なのでオススメです)

新卒入社の @keokent もできる奴で、モバイル端末へのプッシュ通知をサクッと作るしサービス愛も厚いし、風紀の乱れにもうるさくて、Tシャツの裾は常にズボンにインするように指摘されてた。

去年の4月に入社してきた @amacou さんも凄くて、 Ruby/Rails も Objective-C も両方書けて、出張申請とか経費精算さえできればフルスタックおじさんという感じだった。

ムームードメイン時代に仲良くなりプラチナサーチャーで女性ファン急増中の @モノクロメガネ(いけすかないのでリンクはありません) さんに助っ人で来てもらうこともあって、絶対間に合わないだろみたいな無理めなスケジュールでタスクが降ってきたときにもみんなでホワイトボード囲んでワイワイ開発して余裕で終わらせたりして最高だった。モノクロさんは隙あらば Go で Ruby のコードを置き換えようとするところ以外はエンタープライズ力も高いしスクラムマスター業もこなすナイスガイだった。本当にいけすかない。

何をやらせてもスピーディーにこなす若手ネット芸人 @hisaichi5518 さんとも物理的に距離がある状態で仕事したけど、とにかく作り上げるという力はさすがだと思った。チーフエンジニアの @hsbt さんのオラオラと詰めてくる感じの Pull Request もサイコーだった。チームのエンジニアの間では「 @hsbt さんが通ったあとには草の根一本生えない」とよく言ったものだけど、こういう人がいないとフレームワークや言語のバージョンアップとかインフラ構成の大胆な変革はできないことがよくわかった。そういえば @udzura さんという人とも働いたけど、ギャグが寒いこと以外は問題ないです。

デザイナーやディレクター、サポートメンバーも良い人ばかりで、昨日は送別会を開いてもらったんだけど、こんなに良いチームを去るのは残念で仕方なかった。写真はトデガールズに対抗して森井ガールズが結集している様子です。

森井ガールズ

福岡でウェブサービスの開発やってみたいけどどこで働けばよいかわからないインターネットをこじらせたウェブプログラマーの人はペパボの門をたたいてみるとよいと思います。

で、誰?

無名のウェブプログラマーです、このような記事を書いてお目汚しをし誠に申し訳ございません。

なんで辞めんの?

「次に行く理由」があるだけで、「辞める理由」はないのです。

株式会社ドワンゴに転職します(4年3か月ぶり2度目) - Kwappa談話室

僕も同じ気持ちです。

次なにやんの?

Kaizen Platform という会社で働きます。無事試用期間を乗り切れるのでしょうか。ご期待下さい。

| @技術/プログラミング

Pull Request 、レビューしないといけないものが貯まってしまってつらいと感じることが多かった。そういうつらみを解消するための ruboty プラグイン作った。

ruboty-cron と組み合わせて

@ruboty [GitHub の Issue のラベル名]のPull Request

という感じで job 登録しておくと、毎時決まった時間になったら Bot が Pull Request のレビューを促す。

便利。

使い方

  1. ruboty の Gemfile に gem 'ruboty-check_pr_please', github: 'morygonzalez/ruboty-check_pr_please' と追加する
    • 定期的に動かすためには ruboty-cron もいる
  2. ruboty の .env に GITHUB_ACCESS_TOKENGITHUB_REPOSITORY='owner/repo' を書く
  3. ruboty をデプロイする

開発時に苦労した点

ruboty 、 rubory とタイポしてしまうこと多くてつらかった。

| @技術/プログラミング

Rails の E2E テスト、ずっと Capybara + Poltergeist (PhantomJS)でやろうとしてたけど結局うまくいかなかった。

つらいポイントいろいろあって、 SSL 通信が必要なアクションでリダイレクトが発生してうまく動かないとか、 RSpec の use_transactional_fixtures オプションが利用できなくなるため Database Cleaner とか使う必要あったりとか、Parallel Test するときだけ通らないテストケースあったりとか、高速なマシンを使うときだけ落ちる、 Jenkins でだけ落ちる、といった問題が出てきたりする。

この手の問題、 sleep を入れたりとか mutex 化したりとか場当たり的な対応でテスト通すようにできることはできるけど、 unit テストのようにずーっと通る状態を維持し続けることが難しい(上に書いたように特定の端末でだけ落ちたりする)。おまけに何をやっても RackTest を使う場合に比べてテストが遅くなる。 Pull Request ビルダーでテストが実行し終わるまでに 15 分とかかかったりする。良くない。

かといって JavaScript のテスト書かない訳にはいかない。 JavaScript いっぱい使っててテストがないのはやっぱり不安が大きい。結局、JavaScript のコードを DOM べったりな状態から切り離して、サーバーサイドの Rails アプリケーションの挙動は request spec なり controller spec なりでテストして、 JavaScript のテストは JavaScript で行うのが良いような気がした。どうしてもエンドツーエンドテストやりたかったら Selenium 使うしかない気がする。これはスピードはあきらめて、 Pull Request ビルダーとかでビルドするときはテストやらずに、 master ブランチの定期ビルドとかでぶっ壊れてないか試す、みたいな方法が良いと思う。

もうこれ以上、エクセルにスクリーンショット貼り続ける訳にはいかないんだ。

| @技術/プログラミング

Fukuoka.rbで Ruby で連続するダブルクオートの扱いについて @nagachika さんに聞いたら面白い話が聞けた。

Lokka のテストで FactoryGril でフィクスチャーデータ作ってるところに、以下のような文字列があった。

  factory :post do
    association :user
    sequence(:title){|n| Test Post #{n}” }
    body <p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>”
    type ‘Post’
    created_at create_time
    updated_at update_time
  end

この body の部分で、ダブルクオートが連続して書いてある場所があって、ここがらみでテストを流してたら失敗する現象に遭遇した。テストに失敗したときのメッセージは以下。

Failures:

  1) Post markup default should == “<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”
     Failure/Error: it { post.body.should == post.raw_body }
       expected: “<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”
            got: “<p>Welcome to Lokka!</p><p><a href=\”/admin/\”>Admin login</a> (user / password : test / test)</p>” (using ==)
     # ./spec/unit/post_spec.rb:56:in `block (4 levels) in <top (required)>

なんか expect の方で HTML 内のダブルクオートが省略されてる。ダブルクオートが連続してるのが怪しいなと思って、文字列内でダブルクオートが連続したら、一つ目の はエスケープ文字列みたいな扱いになるのかなと思った。

しかし実はそうではなくて、 @nagachika さんの説明によると、ダブルクオートが連続した場合、一つ目の " で文字列の終端と判定される。すぐ右隣の " は新しい文字列の開始と見なされ、結果としては文字列として連結されるらしい(C 言語由来の慣習とのこと)。

たとえば、次のような文字列は

<p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>”

“<p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>” という三つの文字列と見なされ、クオートとクオートの間に何も挟まらないので自動的に一つの文字列として連結され、以下のようになる。

<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”

これ、なぜいままで Lokka でこの状態で CI のテスト通ってたのかわからないけど、今日 wercker で CI の設定していてテストが通らなくてこの問題に気がついた。同じように手元でテスト実行しても落ちた。ひょっとすると Ruby 2.1.0 で落ちるのかも知れない。いずれにせよ "" は使わない方が良さそうなので修正して Pull Request 出そう。