y-temp4/quambu

View on GitHub
mock/following_users_related_items.json

Summary

Maintainability
Test Coverage
[{
    "id": 420408,
    "uuid": "a90b3b09d008227d3d60",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "サヨナラBetter Specs!? 雑で気楽なRSpecのススメ",
    "created_at": "2016-09-05 07:52:30 +0900",
    "updated_at": "2016-09-05 07:57:08 +0900",
    "created_at_in_words": "3日",
    "updated_at_in_words": "3日",
    "tags": [{
        "name": "Ruby",
        "url_name": "ruby",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/0337fbcbff62fb8fa5d0b8be5c3b47d1115d91fc/medium.jpg?1418548649",
        "following": false,
        "versions": []
    }, {
        "name": "RSpec",
        "url_name": "rspec",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/aba2fc611bc7fe28b0c70347e68541ef7e186ac5/medium.jpg?1407701144",
        "following": false,
        "versions": []
    }],
    "stock_count": 43,
    "comment_count": 0,
    "url": "http://qiita.com/jnchito/items/a90b3b09d008227d3d60",
    "created_at_as_seconds": 1473029550,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## はじめに\n\nRSpecって結構「RSpecらしく書くこと」を求められたりします。\nたとえば、describeやcontextでしっかりグループを分けましょう、再利用するデータはletやsubjectに切り出しましょう、ひとつのexample(it)の中でテストするのはひとつの項目だけにしましょう、等々の方針です。\n\nよくあるのが「Better Specsを読んで、こんなふうに書くようにしましょう!」っていうパターンですね。\n\n[Better Specs](http://betterspecs.org/)<a href=\"http://b.hatena.ne.jp/entry/http://betterspecs.org\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://betterspecs.org\" class=\"bookmark-count\"></a>\n\n\nしかし僕の場合、最近は「そこまでがんばってキレイにしなくてもいいのでは?」と考えるようになってきています。\nその結果、テストコードがだんだん雑になってきています。\n\nというわけで、この記事では最近僕が実践している「雑なRSpec」の書き方を紹介します。\n\n### 備考\n\nこの記事は以前自分のブログに書いた内容を加筆・修正したものです。\n「雑なRSpec」以外の話題についても書いているので、よかったらこちらもどうぞ。\n\n[テストコードにまつわる5つのエトセトラ \\- give IT a try](http://blog.jnito.com/entry/2016/09/03/170806)<a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2016/09/03/170806\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2016/09/03/170806\" class=\"bookmark-count\"></a>\n\n\n\n## 「RSpecらしく」書くとこんな感じ\n\n百聞は一見にしかず、というわけで「RSpecらしく書いたRSpec」と「雑なRSpec」の具体例を挙げてみます。\nまずは「RSpecらしく書いたRSpec」のコード例です。\n\n```ruby\ndescribe Cloth do\n  describe '#price_with_tax' do\n    let(:cloth) { Cloth.new('RSpec Tシャツ', price) }\n    subject { cloth.half_price }\n    context '割り切れる場合' do\n      let(:price) { 1000 }\n      it { is_expected.to eq 500 }\n    end\n    context '割り切れる場合・その2' do\n      let(:price) { 2000 }\n      it { is_expected.to eq 1000 }\n    end\n    context '端数が出る場合' do\n      let(:price) { 999 }\n      it { is_expected.to eq 499 }\n    end\n  end\nend\n```\n\nRSpecらしく書いたコードは、\n\n- 条件をcontextに分けている\n- テスト内で使われる変数をletに切り出している\n- 遅延評価されるletの特性を活かして、context内で別々の`price`を設定している\n- テストするメソッドをsubjectに設定し、`it { is_expected.to ... }`の形で検証している\n- `it`の中でテストするのは1つだけ\n\nみたいな感じです。\nRSpecに詳しい人が見たら、「そうそう、RSpecで書くならこうだよね!」と思われるのではないでしょうか。\n\n## 「雑に書く」とこんな感じ\n\n一方、僕が最近よく書く「雑なRSpec」はこんなコードになります。\n\n```ruby\ndescribe Cloth do\n  describe '#half_price' do\n    example do\n      # 割り切れる場合\n      cloth = Cloth.new('RSpec Tシャツ', 1000)\n      expect(cloth.half_price).to eq 500\n\n      # 割り切れる場合\n      cloth.price = 2000\n      expect(cloth.half_price).to eq 1000\n\n      # 端数が出る場合\n      cloth.price = 999\n      expect(cloth.half_price).to eq 499\n    end\n  end\nend\n```\n\n雑なRSpecのポイントは、\n\n- describeやcontextのグループ分けは必要最小限にする\n- contextに相当するものはコメントで書く\n- ひとつのexampleの中で、まとめて複数のテストを書いてしまう\n- letやsubjectに切り出さず、ローカル変数にしたり、毎回同じようなコードをベタ書きしたりする\n- `it 'returns half price'`や`it '半額の値を返す'`のような説明文を省略して、`example`だけで済ませる\n\nみたいな感じです。\n\n## 雑なRSpecのメリット・デメリット\n\nメリット・デメリットはいろいろありますが、まずメリットを挙げると、\n\n- 思いついたテストパターンを上から下へさくっと書ける\n- どれをletやsubjectにすべきか、どうcontextを分割すべきか、といった「テストコードの設計」に頭を使わなくて済む\n- 「意図した通りにコードが動いていることを検証できる」という点では、RSpecらしいテストコードと変わりがない\n\nみたいになるかな、と思います。\n\nデメリットとしては、\n\n- 1つのexample内で複数のテストを実行しているので、途中で失敗するとその先のテストが成功するか失敗するかわからない\n- コードの重複が多いので、仕様変更が入ると同じような修正を繰り返すことになるかもしれない\n- `--format documentation`のオプションを付けて実行したときの出力結果が、文字通り「雑」になる\n- 「こんなRSpecは許せない!RSpecらしく書き直せ!:anger:」と怒り出す人が出てくるかも?\n\nみたいなところでしょうか。\n\n## 「RSpecらしいRSpec」を推奨しすぎると初心者を遠ざける?\n\n今回「雑なRSpec」の書き方を紹介した理由のひとつは、「RSpecが苦手」「RSpecがキライ」という人たちに、「いやいや、別にこういう書き方をしたっていいんだよ」という話をしたかったからです。\n\n巷にあふれる「よいRSpecの書き方」が全部「RSpecらしいRSpec」になると、初心者の人たちが「RSpec怖い」「RSpec難しい」「RSpecなんて書きたくない」と思ってしまうのではないか?とちょっと危惧しています。\n\n変にハードルを上げすぎて「こんなふうにRSpecを使いこなせないからテストを書きたくない」「RSpecらしく書くために何時間も格闘している」みたいなことが起きると、非常にもったいないです。\n形は何であれ、まずは「テストを書く」という習慣を身につけてもらう方が大事だと思います。\n\n実際こうやって比べてみると、「雑なRSpec」の方がまだ初心者の方にとってはわかりやすい(=理解すべきRSpecのDSLが少ない)はず、と考えているのですが、いかがでしょうか?\n\n## 重要:「RSpecらしいRSpec」を完全に放棄したわけではない\n\nとはいえ、僕も「RSpecらしいRSpec」を完全に放棄したわけではありません。\nテストコードの内容によってはRSpecらしく書いた方が、読み書きがしやすかったり、保守性が高かったりするケースもあります。\nそういう場合は「RSpecらしいRSpec」を書くようにしています。\n\nケースバイケースで「RSpecらしいRSpec」と「雑なRSpec」を使い分けている感じですね。\n\n「よくわかんないけど、これが巷で言う守破離の\"離\"ってやつ?」なんて自分では考えたりしています。(間違ってたらすいません)\n\n## カスタムマッチャよりも検証用の野良メソッド\n\nあと、少し話は変わりますが、最近はspec内で独自の検証メソッドを作ってそれを使うこともよくあります。\nたとえばこんな感じです。\n\n```ruby\ndescribe UsersController do\n  # 検証用メソッド\n  def assert_require_login\n    expect(response).to redirect_to root_path\n  end\n  # 検証用メソッド\n  def assert_ok\n    expect(response).to have_http_status 200\n  end\n  describe '#index' do\n    example do\n      # ログインしていない場合\n      get :index\n      assert_require_login\n      \n      # ログインしている場合\n      sign_in\n      assert_ok\n    end\n  end\n  describe '#show' do\n    let(:user) { create :user } \n    example do\n      # ログインしていない場合\n      get :show, params: { id: user.id }\n      assert_require_login\n      \n      # ログインしている場合\n      get :show, params: { id: user.id }\n      assert_ok\n    end\n  end\nend\n```\n\n実際はここまで単純なテストだと検証メソッドを作らずにベタ書きすると思いますが、あくまで利用イメージとして見てやってください。\n\n要するに「ややこしい検証が必要な箇所がいくつかある。でもそれを毎回ベタ書きするのは大変なので共通化したい。とはいえ、カスタムマッチャを用意するのは腰が重い」みたいなときに、テスト内で`assert_xxx`みたいな検証用メソッドを作ってそれを再利用する、というアプローチです。\n\n`assert_xxx`というメソッド名は全然RSpecらしくない、むしろMinitestやtest-unitのようなxUnit系のメソッド名ですが、明示的で分かりやすいので僕は`assert_xxx`みたいなメソッド名を付けています。\n\nカスタムマッチャを作らず、野良メソッド(その場限りの使い捨てメソッド)で済ませるというのがRSpecらしくないですし、「雑」ですね。\n\n### データのセットアップ等でも独自メソッドを使う\n\nまた、検証用のメソッドだけでなく、データのセットアップなどでもテスト内でメソッドを作ってそれを利用することがあります。\nイメージ的には次のような感じです。\n\n```ruby\ndescribe Blog do\n  let(:alice) { create :user, name: 'Alice' }\n  let(:bob) { create :user, name: 'Bob' }\n  let(:chris) { create :user, name: 'Chris' }\n  \n  def create_blogs(user)\n    # テスト用データをこしらえるためのややこしい処理\n  end\n  \n  before do\n    [alice, bob, chris].each do |user|\n      # テスト内で作ったメソッドを呼び出す\n      create_blogs(user)\n    end\n  end\n  example do\n    # 何かのテスト\n  end\nend\n```\n\n検証用メソッドもデータセットアップのメソッドも、複数のテストファイルで利用する場合はヘルパーマクロとしてSpec全体で使えるようにした方がいいですが、そうでなければファイル固有のメソッドとして定義して使えばいいと思います。\n\n## まとめ\n\nというわけで、この記事では最近僕が実践している「雑なRSpec」の書き方を紹介してみました。\n\n[Better Specs](http://betterspecs.org/)に載っているような書き方は全く知らないよりも知っている方がいいですし、実際にそう書けるに越したことはありません。\nですが、このルールを「いつも絶対守るべき!!」と思い込んでしまうとテストを書く効率を下げてしまいます。\n\nまた、他の人に対して「RSpecらしいRSpec」を強く推奨しすぎると、初心者のハードルを無駄に上げてしまうかもしれません。\n\n「ハンマーを持つ人にはすべてが釘に見える」ではありませんが、RSpecのDSLを覚えると「隅から隅まで徹底的にDSLを活用すべし」という気持ちが出てくるのは分かります。\n実際、昔の僕がそんな感じだったので(苦笑)。\n\nですが、ある程度DSLを使いこなせるようになったら、状況に応じて「あえて雑に書く」のもひとつの手です。\nある意味、すべてRSpecらしく書くことよりも上級者向けのテクニックかもしれませんが、もしあなたの周り「RSpecらしさの追求」に時間をかけすぎている人がいたら、「もっと気楽に書いていいんやで:kissing_closed_eyes:」とこの記事を見せてやってください。\n\n### Special thanks\n\n「雑なRSpec」というフレーズはこちらの記事を参考にさせてもらいました。\n\n[俺のRSpecがこんなに雑なわけがない \\- Qiita](http://qiita.com/kbaba1001/items/5333d325d0a76dd29581)<a href=\"http://b.hatena.ne.jp/entry/http://qiita.com/kbaba1001/items/5333d325d0a76dd29581\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://qiita.com/kbaba1001/items/5333d325d0a76dd29581\" class=\"bookmark-count\"></a>\n\n\nもともとは「Minitestっぽい書き方」と自分の中で読んでいたのですが、「\"雑なRSpec\"って響きがいいな」と思い、僕も「雑なRSpec」と呼ぶようになりました。\n\n上の記事は僕のアプローチと同じところもあれば、違うところもあります。\nですが、「Better Specs至上主義から離れて、もっと気楽に書こう」という基本方針は僕と同じですね。\n\n### あわせて読みたい\n\nそもそもRSpecを全く知らないので基礎の基礎から学びたい、という方はこちらの記事をご覧ください。\n\n[使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 \\- Qiita](http://qiita.com/jnchito/items/42193d066bd61c740612)<a href=\"http://b.hatena.ne.jp/entry/http://qiita.com/jnchito/items/42193d066bd61c740612\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://qiita.com/jnchito/items/42193d066bd61c740612\" class=\"bookmark-count\"></a>\n\n\nもっと本格的にRSpecの書き方を学びたい方は **「Everyday Rails - RSpecによるRailsテスト入門」** をどうぞ!(宣伝)\n\n[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)<a href=\"http://b.hatena.ne.jp/entry/https://leanpub.com/everydayrailsrspec-jp\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/https://leanpub.com/everydayrailsrspec-jp\" class=\"bookmark-count\"></a>\n[![hero.png](https://qiita-image-store.s3.amazonaws.com/0/7465/a6630664-cdda-7fa6-6673-d71c3ab50da2.png \"hero.png\")](https://leanpub.com/everydayrailsrspec-jp)\n\n",
    "body": "\n<h2>\n<span id=\"はじめに\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"><i class=\"fa fa-link\"></i></a>はじめに</h2>\n\n<p>RSpecって結構「RSpecらしく書くこと」を求められたりします。<br>\nたとえば、describeやcontextでしっかりグループを分けましょう、再利用するデータはletやsubjectに切り出しましょう、ひとつのexample(it)の中でテストするのはひとつの項目だけにしましょう、等々の方針です。</p>\n\n<p>よくあるのが「Better Specsを読んで、こんなふうに書くようにしましょう!」っていうパターンですね。</p>\n\n<p><a href=\"http://betterspecs.org/\" rel=\"nofollow\" target=\"_blank\">Better Specs</a><a href=\"http://b.hatena.ne.jp/entry/http://betterspecs.org\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://betterspecs.org\" class=\"bookmark-count\"></a></p>\n\n<p>しかし僕の場合、最近は「そこまでがんばってキレイにしなくてもいいのでは?」と考えるようになってきています。<br>\nその結果、テストコードがだんだん雑になってきています。</p>\n\n<p>というわけで、この記事では最近僕が実践している「雑なRSpec」の書き方を紹介します。</p>\n\n<h3>\n<span id=\"備考\" class=\"fragment\"></span><a href=\"#%E5%82%99%E8%80%83\"><i class=\"fa fa-link\"></i></a>備考</h3>\n\n<p>この記事は以前自分のブログに書いた内容を加筆・修正したものです。<br>\n「雑なRSpec」以外の話題についても書いているので、よかったらこちらもどうぞ。</p>\n\n<p><a href=\"http://blog.jnito.com/entry/2016/09/03/170806\" rel=\"nofollow\" target=\"_blank\">テストコードにまつわる5つのエトセトラ - give IT a try</a><a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2016/09/03/170806\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2016/09/03/170806\" class=\"bookmark-count\"></a></p>\n\n<h2>\n<span id=\"rspecらしく書くとこんな感じ\" class=\"fragment\"></span><a href=\"#rspec%E3%82%89%E3%81%97%E3%81%8F%E6%9B%B8%E3%81%8F%E3%81%A8%E3%81%93%E3%82%93%E3%81%AA%E6%84%9F%E3%81%98\"><i class=\"fa fa-link\"></i></a>「RSpecらしく」書くとこんな感じ</h2>\n\n<p>百聞は一見にしかず、というわけで「RSpecらしく書いたRSpec」と「雑なRSpec」の具体例を挙げてみます。<br>\nまずは「RSpecらしく書いたRSpec」のコード例です。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"n\">describe</span> <span class=\"no\">Cloth</span> <span class=\"k\">do</span>\n  <span class=\"n\">describe</span> <span class=\"s1\">'#price_with_tax'</span> <span class=\"k\">do</span>\n    <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:cloth</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"no\">Cloth</span><span class=\"o\">.</span><span class=\"n\">new</span><span class=\"p\">(</span><span class=\"s1\">'RSpec Tシャツ'</span><span class=\"p\">,</span> <span class=\"n\">price</span><span class=\"p\">)</span> <span class=\"p\">}</span>\n    <span class=\"n\">subject</span> <span class=\"p\">{</span> <span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">half_price</span> <span class=\"p\">}</span>\n    <span class=\"n\">context</span> <span class=\"s1\">'割り切れる場合'</span> <span class=\"k\">do</span>\n      <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:price</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"mi\">1000</span> <span class=\"p\">}</span>\n      <span class=\"n\">it</span> <span class=\"p\">{</span> <span class=\"n\">is_expected</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">500</span> <span class=\"p\">}</span>\n    <span class=\"k\">end</span>\n    <span class=\"n\">context</span> <span class=\"s1\">'割り切れる場合・その2'</span> <span class=\"k\">do</span>\n      <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:price</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"mi\">2000</span> <span class=\"p\">}</span>\n      <span class=\"n\">it</span> <span class=\"p\">{</span> <span class=\"n\">is_expected</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">1000</span> <span class=\"p\">}</span>\n    <span class=\"k\">end</span>\n    <span class=\"n\">context</span> <span class=\"s1\">'端数が出る場合'</span> <span class=\"k\">do</span>\n      <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:price</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"mi\">999</span> <span class=\"p\">}</span>\n      <span class=\"n\">it</span> <span class=\"p\">{</span> <span class=\"n\">is_expected</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">499</span> <span class=\"p\">}</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>RSpecらしく書いたコードは、</p>\n\n<ul>\n<li>条件をcontextに分けている</li>\n<li>テスト内で使われる変数をletに切り出している</li>\n<li>遅延評価されるletの特性を活かして、context内で別々の<code>price</code>を設定している</li>\n<li>テストするメソッドをsubjectに設定し、<code>it { is_expected.to ... }</code>の形で検証している</li>\n<li>\n<code>it</code>の中でテストするのは1つだけ</li>\n</ul>\n\n<p>みたいな感じです。<br>\nRSpecに詳しい人が見たら、「そうそう、RSpecで書くならこうだよね!」と思われるのではないでしょうか。</p>\n\n<h2>\n<span id=\"雑に書くとこんな感じ\" class=\"fragment\"></span><a href=\"#%E9%9B%91%E3%81%AB%E6%9B%B8%E3%81%8F%E3%81%A8%E3%81%93%E3%82%93%E3%81%AA%E6%84%9F%E3%81%98\"><i class=\"fa fa-link\"></i></a>「雑に書く」とこんな感じ</h2>\n\n<p>一方、僕が最近よく書く「雑なRSpec」はこんなコードになります。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"n\">describe</span> <span class=\"no\">Cloth</span> <span class=\"k\">do</span>\n  <span class=\"n\">describe</span> <span class=\"s1\">'#half_price'</span> <span class=\"k\">do</span>\n    <span class=\"n\">example</span> <span class=\"k\">do</span>\n      <span class=\"c1\"># 割り切れる場合</span>\n      <span class=\"n\">cloth</span> <span class=\"o\">=</span> <span class=\"no\">Cloth</span><span class=\"o\">.</span><span class=\"n\">new</span><span class=\"p\">(</span><span class=\"s1\">'RSpec Tシャツ'</span><span class=\"p\">,</span> <span class=\"mi\">1000</span><span class=\"p\">)</span>\n      <span class=\"n\">expect</span><span class=\"p\">(</span><span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">half_price</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">500</span>\n\n      <span class=\"c1\"># 割り切れる場合</span>\n      <span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">price</span> <span class=\"o\">=</span> <span class=\"mi\">2000</span>\n      <span class=\"n\">expect</span><span class=\"p\">(</span><span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">half_price</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">1000</span>\n\n      <span class=\"c1\"># 端数が出る場合</span>\n      <span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">price</span> <span class=\"o\">=</span> <span class=\"mi\">999</span>\n      <span class=\"n\">expect</span><span class=\"p\">(</span><span class=\"n\">cloth</span><span class=\"o\">.</span><span class=\"n\">half_price</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">eq</span> <span class=\"mi\">499</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>雑なRSpecのポイントは、</p>\n\n<ul>\n<li>describeやcontextのグループ分けは必要最小限にする</li>\n<li>contextに相当するものはコメントで書く</li>\n<li>ひとつのexampleの中で、まとめて複数のテストを書いてしまう</li>\n<li>letやsubjectに切り出さず、ローカル変数にしたり、毎回同じようなコードをベタ書きしたりする</li>\n<li>\n<code>it 'returns half price'</code>や<code>it '半額の値を返す'</code>のような説明文を省略して、<code>example</code>だけで済ませる</li>\n</ul>\n\n<p>みたいな感じです。</p>\n\n<h2>\n<span id=\"雑なrspecのメリットデメリット\" class=\"fragment\"></span><a href=\"#%E9%9B%91%E3%81%AArspec%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88\"><i class=\"fa fa-link\"></i></a>雑なRSpecのメリット・デメリット</h2>\n\n<p>メリット・デメリットはいろいろありますが、まずメリットを挙げると、</p>\n\n<ul>\n<li>思いついたテストパターンを上から下へさくっと書ける</li>\n<li>どれをletやsubjectにすべきか、どうcontextを分割すべきか、といった「テストコードの設計」に頭を使わなくて済む</li>\n<li>「意図した通りにコードが動いていることを検証できる」という点では、RSpecらしいテストコードと変わりがない</li>\n</ul>\n\n<p>みたいになるかな、と思います。</p>\n\n<p>デメリットとしては、</p>\n\n<ul>\n<li>1つのexample内で複数のテストを実行しているので、途中で失敗するとその先のテストが成功するか失敗するかわからない</li>\n<li>コードの重複が多いので、仕様変更が入ると同じような修正を繰り返すことになるかもしれない</li>\n<li>\n<code>--format documentation</code>のオプションを付けて実行したときの出力結果が、文字通り「雑」になる</li>\n<li>「こんなRSpecは許せない!RSpecらしく書き直せ!<img class=\"emoji\" title=\":anger:\" alt=\":anger:\" src=\"https://cdn.qiita.com/emoji/unicode/1f4a2.png\" height=\"20\" width=\"20\" align=\"absmiddle\">」と怒り出す人が出てくるかも?</li>\n</ul>\n\n<p>みたいなところでしょうか。</p>\n\n<h2>\n<span id=\"rspecらしいrspecを推奨しすぎると初心者を遠ざける\" class=\"fragment\"></span><a href=\"#rspec%E3%82%89%E3%81%97%E3%81%84rspec%E3%82%92%E6%8E%A8%E5%A5%A8%E3%81%97%E3%81%99%E3%81%8E%E3%82%8B%E3%81%A8%E5%88%9D%E5%BF%83%E8%80%85%E3%82%92%E9%81%A0%E3%81%96%E3%81%91%E3%82%8B\"><i class=\"fa fa-link\"></i></a>「RSpecらしいRSpec」を推奨しすぎると初心者を遠ざける?</h2>\n\n<p>今回「雑なRSpec」の書き方を紹介した理由のひとつは、「RSpecが苦手」「RSpecがキライ」という人たちに、「いやいや、別にこういう書き方をしたっていいんだよ」という話をしたかったからです。</p>\n\n<p>巷にあふれる「よいRSpecの書き方」が全部「RSpecらしいRSpec」になると、初心者の人たちが「RSpec怖い」「RSpec難しい」「RSpecなんて書きたくない」と思ってしまうのではないか?とちょっと危惧しています。</p>\n\n<p>変にハードルを上げすぎて「こんなふうにRSpecを使いこなせないからテストを書きたくない」「RSpecらしく書くために何時間も格闘している」みたいなことが起きると、非常にもったいないです。<br>\n形は何であれ、まずは「テストを書く」という習慣を身につけてもらう方が大事だと思います。</p>\n\n<p>実際こうやって比べてみると、「雑なRSpec」の方がまだ初心者の方にとってはわかりやすい(=理解すべきRSpecのDSLが少ない)はず、と考えているのですが、いかがでしょうか?</p>\n\n<h2>\n<span id=\"重要rspecらしいrspecを完全に放棄したわけではない\" class=\"fragment\"></span><a href=\"#%E9%87%8D%E8%A6%81rspec%E3%82%89%E3%81%97%E3%81%84rspec%E3%82%92%E5%AE%8C%E5%85%A8%E3%81%AB%E6%94%BE%E6%A3%84%E3%81%97%E3%81%9F%E3%82%8F%E3%81%91%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84\"><i class=\"fa fa-link\"></i></a>重要:「RSpecらしいRSpec」を完全に放棄したわけではない</h2>\n\n<p>とはいえ、僕も「RSpecらしいRSpec」を完全に放棄したわけではありません。<br>\nテストコードの内容によってはRSpecらしく書いた方が、読み書きがしやすかったり、保守性が高かったりするケースもあります。<br>\nそういう場合は「RSpecらしいRSpec」を書くようにしています。</p>\n\n<p>ケースバイケースで「RSpecらしいRSpec」と「雑なRSpec」を使い分けている感じですね。</p>\n\n<p>「よくわかんないけど、これが巷で言う守破離の\"離\"ってやつ?」なんて自分では考えたりしています。(間違ってたらすいません)</p>\n\n<h2>\n<span id=\"カスタムマッチャよりも検証用の野良メソッド\" class=\"fragment\"></span><a href=\"#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%9E%E3%83%83%E3%83%81%E3%83%A3%E3%82%88%E3%82%8A%E3%82%82%E6%A4%9C%E8%A8%BC%E7%94%A8%E3%81%AE%E9%87%8E%E8%89%AF%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89\"><i class=\"fa fa-link\"></i></a>カスタムマッチャよりも検証用の野良メソッド</h2>\n\n<p>あと、少し話は変わりますが、最近はspec内で独自の検証メソッドを作ってそれを使うこともよくあります。<br>\nたとえばこんな感じです。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"n\">describe</span> <span class=\"no\">UsersController</span> <span class=\"k\">do</span>\n  <span class=\"c1\"># 検証用メソッド</span>\n  <span class=\"k\">def</span> <span class=\"nf\">assert_require_login</span>\n    <span class=\"n\">expect</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">redirect_to</span> <span class=\"n\">root_path</span>\n  <span class=\"k\">end</span>\n  <span class=\"c1\"># 検証用メソッド</span>\n  <span class=\"k\">def</span> <span class=\"nf\">assert_ok</span>\n    <span class=\"n\">expect</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to</span> <span class=\"n\">have_http_status</span> <span class=\"mi\">200</span>\n  <span class=\"k\">end</span>\n  <span class=\"n\">describe</span> <span class=\"s1\">'#index'</span> <span class=\"k\">do</span>\n    <span class=\"n\">example</span> <span class=\"k\">do</span>\n      <span class=\"c1\"># ログインしていない場合</span>\n      <span class=\"n\">get</span> <span class=\"ss\">:index</span>\n      <span class=\"n\">assert_require_login</span>\n\n      <span class=\"c1\"># ログインしている場合</span>\n      <span class=\"n\">sign_in</span>\n      <span class=\"n\">assert_ok</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n  <span class=\"n\">describe</span> <span class=\"s1\">'#show'</span> <span class=\"k\">do</span>\n    <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:user</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"n\">create</span> <span class=\"ss\">:user</span> <span class=\"p\">}</span> \n    <span class=\"n\">example</span> <span class=\"k\">do</span>\n      <span class=\"c1\"># ログインしていない場合</span>\n      <span class=\"n\">get</span> <span class=\"ss\">:show</span><span class=\"p\">,</span> <span class=\"ss\">params</span><span class=\"p\">:</span> <span class=\"p\">{</span> <span class=\"nb\">id</span><span class=\"p\">:</span> <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">id</span> <span class=\"p\">}</span>\n      <span class=\"n\">assert_require_login</span>\n\n      <span class=\"c1\"># ログインしている場合</span>\n      <span class=\"n\">get</span> <span class=\"ss\">:show</span><span class=\"p\">,</span> <span class=\"ss\">params</span><span class=\"p\">:</span> <span class=\"p\">{</span> <span class=\"nb\">id</span><span class=\"p\">:</span> <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">id</span> <span class=\"p\">}</span>\n      <span class=\"n\">assert_ok</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>実際はここまで単純なテストだと検証メソッドを作らずにベタ書きすると思いますが、あくまで利用イメージとして見てやってください。</p>\n\n<p>要するに「ややこしい検証が必要な箇所がいくつかある。でもそれを毎回ベタ書きするのは大変なので共通化したい。とはいえ、カスタムマッチャを用意するのは腰が重い」みたいなときに、テスト内で<code>assert_xxx</code>みたいな検証用メソッドを作ってそれを再利用する、というアプローチです。</p>\n\n<p><code>assert_xxx</code>というメソッド名は全然RSpecらしくない、むしろMinitestやtest-unitのようなxUnit系のメソッド名ですが、明示的で分かりやすいので僕は<code>assert_xxx</code>みたいなメソッド名を付けています。</p>\n\n<p>カスタムマッチャを作らず、野良メソッド(その場限りの使い捨てメソッド)で済ませるというのがRSpecらしくないですし、「雑」ですね。</p>\n\n<h3>\n<span id=\"データのセットアップ等でも独自メソッドを使う\" class=\"fragment\"></span><a href=\"#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97%E7%AD%89%E3%81%A7%E3%82%82%E7%8B%AC%E8%87%AA%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%86\"><i class=\"fa fa-link\"></i></a>データのセットアップ等でも独自メソッドを使う</h3>\n\n<p>また、検証用のメソッドだけでなく、データのセットアップなどでもテスト内でメソッドを作ってそれを利用することがあります。<br>\nイメージ的には次のような感じです。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"n\">describe</span> <span class=\"no\">Blog</span> <span class=\"k\">do</span>\n  <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:alice</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"n\">create</span> <span class=\"ss\">:user</span><span class=\"p\">,</span> <span class=\"nb\">name</span><span class=\"p\">:</span> <span class=\"s1\">'Alice'</span> <span class=\"p\">}</span>\n  <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:bob</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"n\">create</span> <span class=\"ss\">:user</span><span class=\"p\">,</span> <span class=\"nb\">name</span><span class=\"p\">:</span> <span class=\"s1\">'Bob'</span> <span class=\"p\">}</span>\n  <span class=\"n\">let</span><span class=\"p\">(</span><span class=\"ss\">:chris</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"n\">create</span> <span class=\"ss\">:user</span><span class=\"p\">,</span> <span class=\"nb\">name</span><span class=\"p\">:</span> <span class=\"s1\">'Chris'</span> <span class=\"p\">}</span>\n\n  <span class=\"k\">def</span> <span class=\"nf\">create_blogs</span><span class=\"p\">(</span><span class=\"n\">user</span><span class=\"p\">)</span>\n    <span class=\"c1\"># テスト用データをこしらえるためのややこしい処理</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">before</span> <span class=\"k\">do</span>\n    <span class=\"o\">[</span><span class=\"n\">alice</span><span class=\"p\">,</span> <span class=\"n\">bob</span><span class=\"p\">,</span> <span class=\"n\">chris</span><span class=\"o\">].</span><span class=\"n\">each</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">user</span><span class=\"o\">|</span>\n      <span class=\"c1\"># テスト内で作ったメソッドを呼び出す</span>\n      <span class=\"n\">create_blogs</span><span class=\"p\">(</span><span class=\"n\">user</span><span class=\"p\">)</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n  <span class=\"n\">example</span> <span class=\"k\">do</span>\n    <span class=\"c1\"># 何かのテスト</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>検証用メソッドもデータセットアップのメソッドも、複数のテストファイルで利用する場合はヘルパーマクロとしてSpec全体で使えるようにした方がいいですが、そうでなければファイル固有のメソッドとして定義して使えばいいと思います。</p>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<p>というわけで、この記事では最近僕が実践している「雑なRSpec」の書き方を紹介してみました。</p>\n\n<p><a href=\"http://betterspecs.org/\" rel=\"nofollow\" target=\"_blank\">Better Specs</a>に載っているような書き方は全く知らないよりも知っている方がいいですし、実際にそう書けるに越したことはありません。<br>\nですが、このルールを「いつも絶対守るべき!!」と思い込んでしまうとテストを書く効率を下げてしまいます。</p>\n\n<p>また、他の人に対して「RSpecらしいRSpec」を強く推奨しすぎると、初心者のハードルを無駄に上げてしまうかもしれません。</p>\n\n<p>「ハンマーを持つ人にはすべてが釘に見える」ではありませんが、RSpecのDSLを覚えると「隅から隅まで徹底的にDSLを活用すべし」という気持ちが出てくるのは分かります。<br>\n実際、昔の僕がそんな感じだったので(苦笑)。</p>\n\n<p>ですが、ある程度DSLを使いこなせるようになったら、状況に応じて「あえて雑に書く」のもひとつの手です。<br>\nある意味、すべてRSpecらしく書くことよりも上級者向けのテクニックかもしれませんが、もしあなたの周り「RSpecらしさの追求」に時間をかけすぎている人がいたら、「もっと気楽に書いていいんやで<img class=\"emoji\" title=\":kissing_closed_eyes:\" alt=\":kissing_closed_eyes:\" src=\"https://cdn.qiita.com/emoji/unicode/1f61a.png\" height=\"20\" width=\"20\" align=\"absmiddle\">」とこの記事を見せてやってください。</p>\n\n<h3>\n<span id=\"special-thanks\" class=\"fragment\"></span><a href=\"#special-thanks\"><i class=\"fa fa-link\"></i></a>Special thanks</h3>\n\n<p>「雑なRSpec」というフレーズはこちらの記事を参考にさせてもらいました。</p>\n\n<p><a href=\"http://qiita.com/kbaba1001/items/5333d325d0a76dd29581\" id=\"reference-9cbaea431d766353affd\">俺のRSpecがこんなに雑なわけがない - Qiita</a><a href=\"http://b.hatena.ne.jp/entry/http://qiita.com/kbaba1001/items/5333d325d0a76dd29581\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://qiita.com/kbaba1001/items/5333d325d0a76dd29581\" class=\"bookmark-count\"></a></p>\n\n<p>もともとは「Minitestっぽい書き方」と自分の中で読んでいたのですが、「\"雑なRSpec\"って響きがいいな」と思い、僕も「雑なRSpec」と呼ぶようになりました。</p>\n\n<p>上の記事は僕のアプローチと同じところもあれば、違うところもあります。<br>\nですが、「Better Specs至上主義から離れて、もっと気楽に書こう」という基本方針は僕と同じですね。</p>\n\n<h3>\n<span id=\"あわせて読みたい\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>あわせて読みたい</h3>\n\n<p>そもそもRSpecを全く知らないので基礎の基礎から学びたい、という方はこちらの記事をご覧ください。</p>\n\n<p><a href=\"http://qiita.com/jnchito/items/42193d066bd61c740612\" id=\"reference-b281e2ff2ee25bce6e97\">使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 - Qiita</a><a href=\"http://b.hatena.ne.jp/entry/http://qiita.com/jnchito/items/42193d066bd61c740612\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://qiita.com/jnchito/items/42193d066bd61c740612\" class=\"bookmark-count\"></a></p>\n\n<p>もっと本格的にRSpecの書き方を学びたい方は <strong>「Everyday Rails - RSpecによるRailsテスト入門」</strong> をどうぞ!(宣伝)</p>\n\n<p><a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a><a href=\"http://b.hatena.ne.jp/entry/https://leanpub.com/everydayrailsrspec-jp\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/https://leanpub.com/everydayrailsrspec-jp\" class=\"bookmark-count\"></a><br>\n<a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/a6630664-cdda-7fa6-6673-d71c3ab50da2.png\" alt=\"hero.png\" title=\"hero.png\"></a></p>\n",
    "stock_users": [
        "k-ta-yamada",
        "rakuda_daraku",
        "key-amb",
        "blueberrystream",
        "Kenta-s",
        "UltraBirdTech",
        "mono0926",
        "fumiyasac@github",
        "okamu_",
        "kfsawada",
        "sfc",
        "hiromichinomata",
        "donghai821@github",
        "Yusuke-Shimizu",
        "knt45",
        "iwai0621",
        "teiet",
        "3to4",
        "ndxbn",
        "11Takanori",
        "canberra0709",
        "T-N0121",
        "tokomagu",
        "marumaru",
        "mtgzz@github",
        "jkr_2255",
        "Neos21",
        "myblue",
        "ohs30359-nobuhara",
        "QUANON",
        "kenixi",
        "tamoot@github",
        "tomcky",
        "yuku_t",
        "MasatoYoshioka@github",
        "gonjitti",
        "yuki3738",
        "sugichan_16",
        "keisukeohta",
        "sheep6box",
        "yshz",
        "shunt",
        "choryou"
    ]
}, {
    "id": 416362,
    "uuid": "939eac30439210fb6e4e",
    "user": {
        "id": 79241,
        "url_name": "306_san",
        "profile_image_url": "https://pbs.twimg.com/profile_images/714861996202672128/7IR2HoQl_normal.jpg",
        "following": true
    },
    "title": "Vagrant v1.8.5でvagrant upすると、エラー(Timed out while waiting for the machine to boot...)が出てきて進まない",
    "created_at": "2016-08-19 03:23:56 +0900",
    "updated_at": "2016-08-19 03:23:56 +0900",
    "created_at_in_words": "21日",
    "updated_at_in_words": "21日",
    "tags": [{
        "name": "vagrant",
        "url_name": "vagrant",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/894dfb593318a0ff9aa160c87fe75b920cafb662/medium.jpg?1398263728",
        "following": false,
        "versions": [
            "1.8.5"
        ]
    }, {
        "name": "VirtualBox",
        "url_name": "virtualbox",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/f548c52e6be38560564332b412bf06927f719d3c/medium.jpg?1398263985",
        "following": false,
        "versions": []
    }],
    "stock_count": 3,
    "comment_count": 1,
    "url": "http://qiita.com/306_san/items/939eac30439210fb6e4e",
    "created_at_as_seconds": 1471544636,
    "tweet": true,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "## 環境\n+ ホストOS: Xubuntu16.04.1(ただしOS関係なく起きる模様)\n+ ゲストOS: CentOS7\n+ Vagrant: 1.8.5(これが主な原因)\n+ VirtualBox: 5.1\n\n```bash\n$vagrant up\nほげほげ(省略)\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\n\nTimed out while waiting for the machine to boot. This means that\nVagrant was unable to communicate with the guest machine within\nthe configured (\"config.vm.boot_timeout\" value) time period.\n\nIf you look above, you should be able to see the error(s) that\nVagrant had when attempting to connect to the machine. These errors\nare usually good hints as to what may be wrong.\n\nIf you're using a custom box, make sure that networking is properly\nworking and you're able to connect to the machine. It is a common\nproblem that networking isn't setup properly in these boxes.\nVerify that authentication configurations are also setup properly,\nas well.\n\nIf the box appears to be booting properly, you may want to increase\nthe timeout (\"config.vm.boot_timeout\") value.\n```\n\nと出てきて何度もvagrant upしてもおないことが起きたのでメモ。\n\n# 原因\nvagrant **1.8.5** におけるバグ。\nhttps://github.com/mitchellh/vagrant/issues/7610\n要はvagrantで作った鍵の権限の設定ミスでログインできないっぽい。\n1.8.6では対策されそう。\n\n# 対策\n1.8.5におけるバグなので、1.8.4に下げればいいやと思ったが、1.8.4に下げるとVirtualBox5.1には対応していないので、Virtualboxもバージョン下げないといけない・・・。\nそれはめんどくさいので、Issueページに書いてある対策を実行した。\n\n### 方法1 public_key.rb書き換え\nWindowsの場合\n``C:\\HashiCorp\\vagrant\\embedded\\gems\\gems\\vagrant-1.8.5\\plugins\\guests\\linux\\cap\\public_key.rb``\nLinux/macOS(OS X)の場合\n``/opt/vagrant/embedded/gems/gems/vagrant-1.8.5/plugins/guests/linux/cap/public_key.rb``\nに、以下の+マークの行を追記する。\n\n```rb\n@@ -54,6 +54,7 @@\n             if test -f ~/.ssh/authorized_keys; then\n               grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp\n               mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys\n+              chmod 0600 ~/.ssh/authorized_keys\n             fi\n\n             rm -f '#{remote_path}'\n```\n\nこれで一旦VMを消して実行すれば大丈夫。\n\n### 方法2 ゲストOSの権限を変える\nもうこれについては記事が上がっているので省略。\n既存のVMはこれでやれば消さなくていいのでいいかも。\nhttp://qiita.com/ritukiii/items/9a51adc0334132bc8e15\n\n### 方法3 VagrantFileを書き換え\n```rb:VagrantFile\nconfig.ssh.insert_key = false\n```\nを追記。ただこれで直るかは試していません・・・。\n\n### まとめ\nVagrantめんどくさいなぁと思えるバグでした。バージョンには注意を払いつつ使いましょう。\n質問するときも環境開示する大切さがわかってもらえると嬉しいです。\n",
    "body": "\n<h2>\n<span id=\"環境\" class=\"fragment\"></span><a href=\"#%E7%92%B0%E5%A2%83\"><i class=\"fa fa-link\"></i></a>環境</h2>\n\n<ul>\n<li>ホストOS: Xubuntu16.04.1(ただしOS関係なく起きる模様)</li>\n<li>ゲストOS: CentOS7</li>\n<li>Vagrant: 1.8.5(これが主な原因)</li>\n<li>VirtualBox: 5.1</li>\n</ul>\n\n<div class=\"code-frame\" data-lang=\"bash\"><div class=\"highlight\"><pre>\n<span class=\"nv\">$vagrant</span> up\nほげほげ<span class=\"o\">(</span>省略<span class=\"o\">)</span>\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\ndefault: Warning: Authentication failure. Retrying...\n\nTimed out <span class=\"k\">while</span> waiting <span class=\"k\">for</span> the machine to boot. This means that\nVagrant was unable to communicate with the guest machine within\nthe configured <span class=\"o\">(</span><span class=\"s2\">\"config.vm.boot_timeout\"</span> value<span class=\"o\">)</span> <span class=\"nb\">time </span>period.\n\nIf you look above, you should be able to see the error<span class=\"o\">(</span>s<span class=\"o\">)</span> that\nVagrant had when attempting to connect to the machine. These errors\nare usually good hints as to what may be wrong.\n\nIf you<span class=\"s1\">'re using a custom box, make sure that networking is properly</span>\n<span class=\"s1\">working and you'</span>re able to connect to the machine. It is a common\nproblem that networking isn<span class=\"err\">'</span>t setup properly in these boxes.\nVerify that authentication configurations are also setup properly,\nas well.\n\nIf the box appears to be booting properly, you may want to increase\nthe timeout <span class=\"o\">(</span><span class=\"s2\">\"config.vm.boot_timeout\"</span><span class=\"o\">)</span> value.\n</pre></div></div>\n\n<p>と出てきて何度もvagrant upしてもおないことが起きたのでメモ。</p>\n\n<h1>\n<span id=\"原因\" class=\"fragment\"></span><a href=\"#%E5%8E%9F%E5%9B%A0\"><i class=\"fa fa-link\"></i></a>原因</h1>\n\n<p>vagrant <strong>1.8.5</strong> におけるバグ。<br>\n<a href=\"https://github.com/mitchellh/vagrant/issues/7610\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mitchellh/vagrant/issues/7610</a><br>\n要はvagrantで作った鍵の権限の設定ミスでログインできないっぽい。<br>\n1.8.6では対策されそう。</p>\n\n<h1>\n<span id=\"対策\" class=\"fragment\"></span><a href=\"#%E5%AF%BE%E7%AD%96\"><i class=\"fa fa-link\"></i></a>対策</h1>\n\n<p>1.8.5におけるバグなので、1.8.4に下げればいいやと思ったが、1.8.4に下げるとVirtualBox5.1には対応していないので、Virtualboxもバージョン下げないといけない・・・。<br>\nそれはめんどくさいので、Issueページに書いてある対策を実行した。</p>\n\n<h3>\n<span id=\"方法1-public_keyrb書き換え\" class=\"fragment\"></span><a href=\"#%E6%96%B9%E6%B3%951-public_keyrb%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88\"><i class=\"fa fa-link\"></i></a>方法1 public_key.rb書き換え</h3>\n\n<p>Windowsの場合<br>\n<code>C:\\HashiCorp\\vagrant\\embedded\\gems\\gems\\vagrant-1.8.5\\plugins\\guests\\linux\\cap\\public_key.rb</code><br>\nLinux/macOS(OS X)の場合<br>\n<code>/opt/vagrant/embedded/gems/gems/vagrant-1.8.5/plugins/guests/linux/cap/public_key.rb</code><br>\nに、以下の+マークの行を追記する。</p>\n\n<div class=\"code-frame\" data-lang=\"rb\"><div class=\"highlight\"><pre>\n<span class=\"err\">@@</span> <span class=\"o\">-</span><span class=\"mi\">54</span><span class=\"p\">,</span><span class=\"mi\">6</span> <span class=\"o\">+</span><span class=\"mi\">54</span><span class=\"p\">,</span><span class=\"mi\">7</span> <span class=\"err\">@@</span>\n             <span class=\"k\">if</span> <span class=\"nb\">test</span> <span class=\"o\">-</span><span class=\"n\">f</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span><span class=\"p\">;</span> <span class=\"k\">then</span>\n               <span class=\"n\">grep</span> <span class=\"o\">-</span><span class=\"n\">v</span> <span class=\"o\">-</span><span class=\"n\">x</span> <span class=\"o\">-</span><span class=\"n\">f</span> <span class=\"s1\">'#{remote_path}'</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span> <span class=\"o\">&gt;</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span><span class=\"o\">.</span><span class=\"n\">tmp</span>\n               <span class=\"n\">mv</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span><span class=\"o\">.</span><span class=\"n\">tmp</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span>\n<span class=\"o\">+</span>              <span class=\"n\">chmod</span> <span class=\"mo\">0600</span> <span class=\"o\">~</span><span class=\"sr\">/.ssh/</span><span class=\"n\">authorized_keys</span>\n             <span class=\"n\">fi</span>\n\n             <span class=\"n\">rm</span> <span class=\"o\">-</span><span class=\"n\">f</span> <span class=\"s1\">'#{remote_path}'</span>\n</pre></div></div>\n\n<p>これで一旦VMを消して実行すれば大丈夫。</p>\n\n<h3>\n<span id=\"方法2-ゲストosの権限を変える\" class=\"fragment\"></span><a href=\"#%E6%96%B9%E6%B3%95%EF%BC%92-%E3%82%B2%E3%82%B9%E3%83%88os%E3%81%AE%E6%A8%A9%E9%99%90%E3%82%92%E5%A4%89%E3%81%88%E3%82%8B\"><i class=\"fa fa-link\"></i></a>方法2 ゲストOSの権限を変える</h3>\n\n<p>もうこれについては記事が上がっているので省略。<br>\n既存のVMはこれでやれば消さなくていいのでいいかも。<br>\n<a href=\"http://qiita.com/ritukiii/items/9a51adc0334132bc8e15\" class=\"autolink\" id=\"reference-7fcb1b1ade801ab782bb\">http://qiita.com/ritukiii/items/9a51adc0334132bc8e15</a></p>\n\n<h3>\n<span id=\"方法3-vagrantfileを書き換え\" class=\"fragment\"></span><a href=\"#%E6%96%B9%E6%B3%953-vagrantfile%E3%82%92%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88\"><i class=\"fa fa-link\"></i></a>方法3 VagrantFileを書き換え</h3>\n\n<div class=\"code-frame\" data-lang=\"rb\">\n<div class=\"code-lang\"><span class=\"bold\">VagrantFile</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">ssh</span><span class=\"o\">.</span><span class=\"n\">insert_key</span> <span class=\"o\">=</span> <span class=\"kp\">false</span>\n</pre></div>\n</div>\n\n<p>を追記。ただこれで直るかは試していません・・・。</p>\n\n<h3>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h3>\n\n<p>Vagrantめんどくさいなぁと思えるバグでした。バージョンには注意を払いつつ使いましょう。<br>\n質問するときも環境開示する大切さがわかってもらえると嬉しいです。</p>\n",
    "stock_users": [
        "hirokaki",
        "shun-k1331",
        "tmpnam-com"
    ]
}, {
    "id": 416287,
    "uuid": "44f181fb49f4c7f43771",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "WebSocket と ActionCable",
    "created_at": "2016-08-18 19:55:13 +0900",
    "updated_at": "2016-08-18 22:37:37 +0900",
    "created_at_in_words": "21日",
    "updated_at_in_words": "21日",
    "tags": [{
        "name": "ActionCable",
        "url_name": "actioncable",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 70,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/44f181fb49f4c7f43771",
    "created_at_as_seconds": 1471517713,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": true,
    "raw_body": "## 序\n\n- Rails5 Meetup 発表資料\n\n-----\n\n## はじめに\n\n- 学生の頃に Socket.IO でゲームを作ってた\n- Rails は業務でコントローラに API 生やす程度\n- rspec が全然わからん\n\n---\n\n## 無茶振り\n\nyuku 「mizchi なら ActionCableでなんか作れるでしょ」\n\n---\n\n## なんか作った\n\n---\n\n## 今日の発表内容\n\n- WebSocket の現状\n- ActionCable\n\n既存機能のRails5の拡張については @takashi に任せる\n\n---\n\n# 1. WebSocket\n\n---\n\n## WebSocketとは\n\n- Webブラウザで扱えるTCP Socket抽象\n- HTTP1.1と比べて並列/高頻度イベントの効率が良い\n- プッシュ配信\n\n---\n\n## 今までWebSocket が使えなかった背景\n\n---\n\n![](https://i.gyazo.com/5413db627659f497e22b593b56586257.png)\n\n---\n\n## 昔話\n\n- 未対応ブラウザが多すぎて、フォールバック必要\n  - まともな Fallback は、ほぼ Socket.IO の特権\n- ロードバランサが辛い\n  - 二度目以降のリクエストを、必ず最初に接続した場所に戻す必要\n\n---\n\n## フォールバック先\n\n- Commet\n- XHL Long Polling\n\nいずれも Performance に難\n\n---\n\n## 今\n\n- フォールバック不要(IE>=11)\n- AWS ALB\n- Nginx のWebSocket サポート\n- Heroku の WebSocket サポート(※近くの Region がない)\n\n--- \n## 競合するスペック\n\n- WebRTC の P2P\n- HTTP/2のリクエスト多重化\n- ServiceWorker はバックグラウンドでもプッシュ通知を扱う\n\n=> 用途を選びましょう\n\n---\n\n## どこで使う?\n\n- チャットサーバー\n- MMORPGのようなゲーム\n- SNS でのリアルタイムイベント通知\n- 高頻度な時系列データのビジュアライズ\n\n---\n\n# 2 ActionCable\n\n---\n\n## ActionCable\n\n- 中身は FayeWebsocket の Rails用ラッパー といった出で立ち\n\nhttps://github.com/faye/faye-websocket-ruby\n\n\n----\n\n## ActionCable のメリット\n\n- シームレスなRailsインテグレーション\n- Rails::Engine をマウントする実装なので、WebSocketサーバーを切り離せる\n- 比較的枯れたRails運用ノウハウがそのまま使える\n\n----\n\n## ActionCable を使うには\n\n- Unicorn と EventMachine が相性悪いため、Puma必須\n- Rails 側で Channel を作成\n- JS 側 で 任意の Channel を 購読する\n\n----\n\n## Channel\n\n- 購読される単位\n  - チャットアプリならば1つのチャットルームに相当\n- `bloadcast` でChannel 購読者全員にプッシュ\n- `bloadcast_to` 特定のユーザーにプッシュ\n\n---\n\n\n## 実例:  JS から ActionCable への接続\n\n```js:application.js\n// sprockets\n//= aciton_cable\nvar cable = ActionCable.createConsumer(\"/cable\");\n```\n\nor \n\n```js:main.js\n// babel/browserify env\n// npm install actioncable\nimport ActionCable from \"actioncable\";\nconst cable = ActionCable.createConsumer(/\"cable\");\n```\n\n引数で任意のエンドポイントを取れる\n\n---\n\n## 実例: チャンネル名を指定して接続\n\n```ruby:app/channels/chat_room.rb\nclass ChatChannel < ApplicationCable::Channel\n  def subscribed\n    stream_from \"chat_#{params[:room_id]}\"\n  end\nend\n```\n\n```js:application.js\ncable.subscriptions.create({\n   channel: \"ChatChannel\",\n   room_id: \"my-chat-room\"\n}, {\n  connected: function() {\n    console.log(\"connected\");\n  }\n  // ...\n});\n```\n\n---\n\n## 実例: クライアントからイベントを投げる\n\n```js:main.js\nconst subscription = cable.subscriptions.create(\"ChatChannel\", {/* callback */});\n\nsubscription.perform(\"foo\", {data: 1})\n```\n\n\n```ruby:app/channels/chat_room.rb\nclass ChatChannel < ApplicationCable::Channel\n  //...\n  def foo(data)\n    puts data\n  end\nend\n```\n\n`perform(action, data)` を実行すると `ChatChannel[:action]` が呼ばれる。\n\n---\n\n## bloadcast と received\n\n```ruby:app/channels/chat_room.rb\nclass ChatChannel < ApplicationCable::Channel\n  //...\n  def foo(data)\n    ActionCable.server.broadcast \"ChatChannel\", data\n  end\nend\n```\n\n`ChatChannel` の購読者全員に data をプッシュする\n\n```js\ncable.subscriptions.create(\"ChatChannel\", {\n  // ...\n  received(data) {\n    console.log(data) // => {data: 1, action: \"foo\"}\n  }\n});\n```\n\n`bloadcast_to` でユーザー個別に送ることもできる\n\n---\n\n## デモ\n\n![](https://i.gyazo.com/f828c621f2568ede382fbd7057f6ade8.gif)\n\n\n----\n\n## **本当は**作りたかったもの\n\n- 編集が1s止まったら入力を送信(ここまで実装済み)\n- 3 way merge\n    - 他人のチャンク編集後、他人のチャンク編集前、自分の状態\n- 更新後のカーソル位置補正\n\n----\n\n## ActionCable の学習資料\n\n- [Action Cable Overview — Ruby on Rails Guides](http://edgeguides.rubyonrails.org/action_cable_overview.html \"Action Cable Overview — Ruby on Rails Guides\")\n- [rails/actioncable-examples: Action Cable Examples](https://github.com/rails/actioncable-examples \"rails/actioncable-examples: Action Cable Examples\")\n\n----\n\n## 閑話休題: Elixir/Phoenix\n\n- Phoenix.Channel は ActionCable 丸パクリ(作者はRailsコミッタの Chris McCord)\n- プロセスモデル/応答性の観点からはPhoenixの方が WebSocket に向いてそう\n\n[翻訳: 似て非なる Phoenix と Rails(原題『Phoenix is not Rails』) - Qiita](http://qiita.com/mserizawa/items/f32469bcac61aa6fa491 \"翻訳: 似て非なる Phoenix と Rails(原題『Phoenix is not Rails』) - Qiita\")\n\n----\n\n# 設計を考える\n\n-----\n\n## WebSocket アプリケーションの設計\n\n- 高頻度イベントにまつわる状態は Redis が向いてる\n- DBに 永続化するのは何らかのチェックポイントを通った時\n- 状態の差分を更新し続けるのは難易度が高いので、定期的に state を全部渡してsyncする、などの保証は欲しい\n\n---\n\n## チャットの場合\n\n- 最初のユーザーが部屋にログイン\n    - => 部屋名をキーにRedisのリスト要素を作成\n- 誰かが発言イベントを投げる\n    - => リスト要素を更新\n    - => 発言を bloadcast\n- 1分に1回\n    - => 全員に現在のリストを配信して補正\n- 5分に1回\n    - => リスト要素の中身を DB に保存\n- 最後のユーザーがログアウト\n    - => リスト要素の中身を DB に保存\n    - => Redisの要素を削除\n\n---\n\n## 設計意図\n\n- できるだけ DB を触らず Redis のインメモリデータだけ使う\n    - Redisは肥大化すると運用が辛いので、使い終わったらすぐ消す\n    - Redisの スナップショット を DB に保存する\n\n- ここまで来ると、あとは [オンラインゲームを支える技術](https://www.amazon.co.jp/dp/4774145807/ref=cm_sw_r_tw_dp_x_zxeTxbTC39VSA) などが参考になる\n\n---\n\n## 趣味でオンラインゲーム作ってた時\n\n- ダンジョン階層ごとにチャンネル分割\n- 12FPSでゲームステートを配信\n- クライアントは60FPSなので、時系列データから他のEntityの座標予測\n- モンスターを倒したら保存\n\n---\n\n## 最後に\n\n- まだ負荷計測に至ってないので、どこがボトルネックになるかわかってない\n- 高頻度イベントはWebのお約束が通じなかったりするんで、頑張りましょう\n\n---\n\n## 終わり\n",
    "body": "\n<h2>\n<span id=\"序\" class=\"fragment\"></span><a href=\"#%E5%BA%8F\"><i class=\"fa fa-link\"></i></a>序</h2>\n\n<ul>\n<li>Rails5 Meetup 発表資料</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"はじめに\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"><i class=\"fa fa-link\"></i></a>はじめに</h2>\n\n<ul>\n<li>学生の頃に Socket.IO でゲームを作ってた</li>\n<li>Rails は業務でコントローラに API 生やす程度</li>\n<li>rspec が全然わからん</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"無茶振り\" class=\"fragment\"></span><a href=\"#%E7%84%A1%E8%8C%B6%E6%8C%AF%E3%82%8A\"><i class=\"fa fa-link\"></i></a>無茶振り</h2>\n\n<p>yuku 「mizchi なら ActionCableでなんか作れるでしょ」</p>\n\n<hr>\n\n<h2>\n<span id=\"なんか作った\" class=\"fragment\"></span><a href=\"#%E3%81%AA%E3%82%93%E3%81%8B%E4%BD%9C%E3%81%A3%E3%81%9F\"><i class=\"fa fa-link\"></i></a>なんか作った</h2>\n\n<hr>\n\n<h2>\n<span id=\"今日の発表内容\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E6%97%A5%E3%81%AE%E7%99%BA%E8%A1%A8%E5%86%85%E5%AE%B9\"><i class=\"fa fa-link\"></i></a>今日の発表内容</h2>\n\n<ul>\n<li>WebSocket の現状</li>\n<li>ActionCable</li>\n</ul>\n\n<p>既存機能のRails5の拡張については <a href=\"/takashi\" class=\"user-mention js-hovercard\" title=\"takashi\" data-hovercard-target-type=\"user\" data-hovercard-target-name=\"takashi\">@takashi</a> に任せる</p>\n\n<hr>\n\n<h1>\n<span id=\"1-websocket\" class=\"fragment\"></span><a href=\"#1-websocket\"><i class=\"fa fa-link\"></i></a>1. WebSocket</h1>\n\n<hr>\n\n<h2>\n<span id=\"websocketとは\" class=\"fragment\"></span><a href=\"#websocket%E3%81%A8%E3%81%AF\"><i class=\"fa fa-link\"></i></a>WebSocketとは</h2>\n\n<ul>\n<li>Webブラウザで扱えるTCP Socket抽象</li>\n<li>HTTP1.1と比べて並列/高頻度イベントの効率が良い</li>\n<li>プッシュ配信</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"今までwebsocket-が使えなかった背景\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E3%81%BE%E3%81%A7websocket-%E3%81%8C%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E8%83%8C%E6%99%AF\"><i class=\"fa fa-link\"></i></a>今までWebSocket が使えなかった背景</h2>\n\n<hr>\n\n<p><a href=\"https://i.gyazo.com/5413db627659f497e22b593b56586257.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/5413db627659f497e22b593b56586257.png\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"昔話\" class=\"fragment\"></span><a href=\"#%E6%98%94%E8%A9%B1\"><i class=\"fa fa-link\"></i></a>昔話</h2>\n\n<ul>\n<li>未対応ブラウザが多すぎて、フォールバック必要\n\n<ul>\n<li>まともな Fallback は、ほぼ Socket.IO の特権</li>\n</ul>\n</li>\n<li>ロードバランサが辛い\n\n<ul>\n<li>二度目以降のリクエストを、必ず最初に接続した場所に戻す必要</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"フォールバック先\" class=\"fragment\"></span><a href=\"#%E3%83%95%E3%82%A9%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E5%85%88\"><i class=\"fa fa-link\"></i></a>フォールバック先</h2>\n\n<ul>\n<li>Commet</li>\n<li>XHL Long Polling</li>\n</ul>\n\n<p>いずれも Performance に難</p>\n\n<hr>\n\n<h2>\n<span id=\"今\" class=\"fragment\"></span><a href=\"#%E4%BB%8A\"><i class=\"fa fa-link\"></i></a>今</h2>\n\n<ul>\n<li>フォールバック不要(IE&gt;=11)</li>\n<li>AWS ALB</li>\n<li>Nginx のWebSocket サポート</li>\n<li>Heroku の WebSocket サポート(※近くの Region がない)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"競合するスペック\" class=\"fragment\"></span><a href=\"#%E7%AB%B6%E5%90%88%E3%81%99%E3%82%8B%E3%82%B9%E3%83%9A%E3%83%83%E3%82%AF\"><i class=\"fa fa-link\"></i></a>競合するスペック</h2>\n\n<ul>\n<li>WebRTC の P2P</li>\n<li>HTTP/2のリクエスト多重化</li>\n<li>ServiceWorker はバックグラウンドでもプッシュ通知を扱う</li>\n</ul>\n\n<p>=&gt; 用途を選びましょう</p>\n\n<hr>\n\n<h2>\n<span id=\"どこで使う\" class=\"fragment\"></span><a href=\"#%E3%81%A9%E3%81%93%E3%81%A7%E4%BD%BF%E3%81%86\"><i class=\"fa fa-link\"></i></a>どこで使う?</h2>\n\n<ul>\n<li>チャットサーバー</li>\n<li>MMORPGのようなゲーム</li>\n<li>SNS でのリアルタイムイベント通知</li>\n<li>高頻度な時系列データのビジュアライズ</li>\n</ul>\n\n<hr>\n\n<h1>\n<span id=\"2-actioncable\" class=\"fragment\"></span><a href=\"#2-actioncable\"><i class=\"fa fa-link\"></i></a>2 ActionCable</h1>\n\n<hr>\n\n<h2>\n<span id=\"actioncable\" class=\"fragment\"></span><a href=\"#actioncable\"><i class=\"fa fa-link\"></i></a>ActionCable</h2>\n\n<ul>\n<li>中身は FayeWebsocket の Rails用ラッパー といった出で立ち</li>\n</ul>\n\n<p><a href=\"https://github.com/faye/faye-websocket-ruby\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/faye/faye-websocket-ruby</a></p>\n\n<hr>\n\n<h2>\n<span id=\"actioncable-のメリット\" class=\"fragment\"></span><a href=\"#actioncable-%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88\"><i class=\"fa fa-link\"></i></a>ActionCable のメリット</h2>\n\n<ul>\n<li>シームレスなRailsインテグレーション</li>\n<li>Rails::Engine をマウントする実装なので、WebSocketサーバーを切り離せる</li>\n<li>比較的枯れたRails運用ノウハウがそのまま使える</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"actioncable-を使うには\" class=\"fragment\"></span><a href=\"#actioncable-%E3%82%92%E4%BD%BF%E3%81%86%E3%81%AB%E3%81%AF\"><i class=\"fa fa-link\"></i></a>ActionCable を使うには</h2>\n\n<ul>\n<li>Unicorn と EventMachine が相性悪いため、Puma必須</li>\n<li>Rails 側で Channel を作成</li>\n<li>JS 側 で 任意の Channel を 購読する</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"channel\" class=\"fragment\"></span><a href=\"#channel\"><i class=\"fa fa-link\"></i></a>Channel</h2>\n\n<ul>\n<li>購読される単位\n\n<ul>\n<li>チャットアプリならば1つのチャットルームに相当</li>\n</ul>\n</li>\n<li>\n<code>bloadcast</code> でChannel 購読者全員にプッシュ</li>\n<li>\n<code>bloadcast_to</code> 特定のユーザーにプッシュ</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"実例--js-から-actioncable-への接続\" class=\"fragment\"></span><a href=\"#%E5%AE%9F%E4%BE%8B--js-%E3%81%8B%E3%82%89-actioncable-%E3%81%B8%E3%81%AE%E6%8E%A5%E7%B6%9A\"><i class=\"fa fa-link\"></i></a>実例:  JS から ActionCable への接続</h2>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">application.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"c1\">// sprockets</span>\n<span class=\"c1\">//= aciton_cable</span>\n<span class=\"kd\">var</span> <span class=\"nx\">cable</span> <span class=\"o\">=</span> <span class=\"nx\">ActionCable</span><span class=\"p\">.</span><span class=\"nx\">createConsumer</span><span class=\"p\">(</span><span class=\"s2\">\"/cable\"</span><span class=\"p\">);</span>\n</pre></div>\n</div>\n\n<p>or </p>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">main.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"c1\">// babel/browserify env</span>\n<span class=\"c1\">// npm install actioncable</span>\n<span class=\"kr\">import</span> <span class=\"nx\">ActionCable</span> <span class=\"nx\">from</span> <span class=\"s2\">\"actioncable\"</span><span class=\"p\">;</span>\n<span class=\"kr\">const</span> <span class=\"nx\">cable</span> <span class=\"o\">=</span> <span class=\"nx\">ActionCable</span><span class=\"p\">.</span><span class=\"nx\">createConsumer</span><span class=\"p\">(</span><span class=\"err\">/\"cable\");</span>\n</pre></div>\n</div>\n\n<p>引数で任意のエンドポイントを取れる</p>\n\n<hr>\n\n<h2>\n<span id=\"実例-チャンネル名を指定して接続\" class=\"fragment\"></span><a href=\"#%E5%AE%9F%E4%BE%8B-%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E5%90%8D%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E6%8E%A5%E7%B6%9A\"><i class=\"fa fa-link\"></i></a>実例: チャンネル名を指定して接続</h2>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">app/channels/chat_room.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">ChatChannel</span> <span class=\"o\">&lt;</span> <span class=\"no\">ApplicationCable</span><span class=\"o\">::</span><span class=\"no\">Channel</span>\n  <span class=\"k\">def</span> <span class=\"nf\">subscribed</span>\n    <span class=\"n\">stream_from</span> <span class=\"s2\">\"chat_</span><span class=\"si\">#{</span><span class=\"n\">params</span><span class=\"o\">[</span><span class=\"ss\">:room_id</span><span class=\"o\">]</span><span class=\"si\">}</span><span class=\"s2\">\"</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div>\n</div>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">application.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"nx\">cable</span><span class=\"p\">.</span><span class=\"nx\">subscriptions</span><span class=\"p\">.</span><span class=\"nx\">create</span><span class=\"p\">({</span>\n   <span class=\"nx\">channel</span><span class=\"o\">:</span> <span class=\"s2\">\"ChatChannel\"</span><span class=\"p\">,</span>\n   <span class=\"nx\">room_id</span><span class=\"o\">:</span> <span class=\"s2\">\"my-chat-room\"</span>\n<span class=\"p\">},</span> <span class=\"p\">{</span>\n  <span class=\"nx\">connected</span><span class=\"o\">:</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"nx\">console</span><span class=\"p\">.</span><span class=\"nx\">log</span><span class=\"p\">(</span><span class=\"s2\">\"connected\"</span><span class=\"p\">);</span>\n  <span class=\"p\">}</span>\n  <span class=\"c1\">// ...</span>\n<span class=\"p\">});</span>\n</pre></div>\n</div>\n\n<hr>\n\n<h2>\n<span id=\"実例-クライアントからイベントを投げる\" class=\"fragment\"></span><a href=\"#%E5%AE%9F%E4%BE%8B-%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%8B%E3%82%89%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E6%8A%95%E3%81%92%E3%82%8B\"><i class=\"fa fa-link\"></i></a>実例: クライアントからイベントを投げる</h2>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">main.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"kr\">const</span> <span class=\"nx\">subscription</span> <span class=\"o\">=</span> <span class=\"nx\">cable</span><span class=\"p\">.</span><span class=\"nx\">subscriptions</span><span class=\"p\">.</span><span class=\"nx\">create</span><span class=\"p\">(</span><span class=\"s2\">\"ChatChannel\"</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"cm\">/* callback */</span><span class=\"p\">});</span>\n\n<span class=\"nx\">subscription</span><span class=\"p\">.</span><span class=\"nx\">perform</span><span class=\"p\">(</span><span class=\"s2\">\"foo\"</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"nx\">data</span><span class=\"o\">:</span> <span class=\"mi\">1</span><span class=\"p\">})</span>\n</pre></div>\n</div>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">app/channels/chat_room.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">ChatChannel</span> <span class=\"o\">&lt;</span> <span class=\"no\">ApplicationCable</span><span class=\"o\">::</span><span class=\"no\">Channel</span>\n  <span class=\"sr\">//</span><span class=\"o\">.</span><span class=\"n\">.</span><span class=\"o\">.</span>\n  <span class=\"k\">def</span> <span class=\"nf\">foo</span><span class=\"p\">(</span><span class=\"n\">data</span><span class=\"p\">)</span>\n    <span class=\"nb\">puts</span> <span class=\"n\">data</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div>\n</div>\n\n<p><code>perform(action, data)</code> を実行すると <code>ChatChannel[:action]</code> が呼ばれる。</p>\n\n<hr>\n\n<h2>\n<span id=\"bloadcast-と-received\" class=\"fragment\"></span><a href=\"#bloadcast-%E3%81%A8-received\"><i class=\"fa fa-link\"></i></a>bloadcast と received</h2>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">app/channels/chat_room.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">ChatChannel</span> <span class=\"o\">&lt;</span> <span class=\"no\">ApplicationCable</span><span class=\"o\">::</span><span class=\"no\">Channel</span>\n  <span class=\"sr\">//</span><span class=\"o\">.</span><span class=\"n\">.</span><span class=\"o\">.</span>\n  <span class=\"k\">def</span> <span class=\"nf\">foo</span><span class=\"p\">(</span><span class=\"n\">data</span><span class=\"p\">)</span>\n    <span class=\"no\">ActionCable</span><span class=\"o\">.</span><span class=\"n\">server</span><span class=\"o\">.</span><span class=\"n\">broadcast</span> <span class=\"s2\">\"ChatChannel\"</span><span class=\"p\">,</span> <span class=\"n\">data</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div>\n</div>\n\n<p><code>ChatChannel</code> の購読者全員に data をプッシュする</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"nx\">cable</span><span class=\"p\">.</span><span class=\"nx\">subscriptions</span><span class=\"p\">.</span><span class=\"nx\">create</span><span class=\"p\">(</span><span class=\"s2\">\"ChatChannel\"</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n  <span class=\"c1\">// ...</span>\n  <span class=\"nx\">received</span><span class=\"p\">(</span><span class=\"nx\">data</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"nx\">console</span><span class=\"p\">.</span><span class=\"nx\">log</span><span class=\"p\">(</span><span class=\"nx\">data</span><span class=\"p\">)</span> <span class=\"c1\">// =&gt; {data: 1, action: \"foo\"}</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">});</span>\n</pre></div></div>\n\n<p><code>bloadcast_to</code> でユーザー個別に送ることもできる</p>\n\n<hr>\n\n<h2>\n<span id=\"デモ\" class=\"fragment\"></span><a href=\"#%E3%83%87%E3%83%A2\"><i class=\"fa fa-link\"></i></a>デモ</h2>\n\n<p><a href=\"https://i.gyazo.com/f828c621f2568ede382fbd7057f6ade8.gif\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/f828c621f2568ede382fbd7057f6ade8.gif\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"本当は作りたかったもの\" class=\"fragment\"></span><a href=\"#%E6%9C%AC%E5%BD%93%E3%81%AF%E4%BD%9C%E3%82%8A%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE\"><i class=\"fa fa-link\"></i></a><strong>本当は</strong>作りたかったもの</h2>\n\n<ul>\n<li>編集が1s止まったら入力を送信(ここまで実装済み)</li>\n<li>3 way merge\n\n<ul>\n<li>他人のチャンク編集後、他人のチャンク編集前、自分の状態</li>\n</ul>\n</li>\n<li>更新後のカーソル位置補正</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"actioncable-の学習資料\" class=\"fragment\"></span><a href=\"#actioncable-%E3%81%AE%E5%AD%A6%E7%BF%92%E8%B3%87%E6%96%99\"><i class=\"fa fa-link\"></i></a>ActionCable の学習資料</h2>\n\n<ul>\n<li><a href=\"http://edgeguides.rubyonrails.org/action_cable_overview.html\" title=\"Action Cable Overview — Ruby on Rails Guides\" rel=\"nofollow\" target=\"_blank\">Action Cable Overview — Ruby on Rails Guides</a></li>\n<li><a href=\"https://github.com/rails/actioncable-examples\" title=\"rails/actioncable-examples: Action Cable Examples\" rel=\"nofollow\" target=\"_blank\">rails/actioncable-examples: Action Cable Examples</a></li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"閑話休題-elixirphoenix\" class=\"fragment\"></span><a href=\"#%E9%96%91%E8%A9%B1%E4%BC%91%E9%A1%8C-elixirphoenix\"><i class=\"fa fa-link\"></i></a>閑話休題: Elixir/Phoenix</h2>\n\n<ul>\n<li>Phoenix.Channel は ActionCable 丸パクリ(作者はRailsコミッタの Chris McCord)</li>\n<li>プロセスモデル/応答性の観点からはPhoenixの方が WebSocket に向いてそう</li>\n</ul>\n\n<p><a href=\"http://qiita.com/mserizawa/items/f32469bcac61aa6fa491\" title=\"翻訳: 似て非なる Phoenix と Rails(原題『Phoenix is not Rails』) - Qiita\" id=\"reference-69fcd77e95de30559fc8\">翻訳: 似て非なる Phoenix と Rails(原題『Phoenix is not Rails』) - Qiita</a></p>\n\n<hr>\n\n<h1>\n<span id=\"設計を考える\" class=\"fragment\"></span><a href=\"#%E8%A8%AD%E8%A8%88%E3%82%92%E8%80%83%E3%81%88%E3%82%8B\"><i class=\"fa fa-link\"></i></a>設計を考える</h1>\n\n<hr>\n\n<h2>\n<span id=\"websocket-アプリケーションの設計\" class=\"fragment\"></span><a href=\"#websocket-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%A8%AD%E8%A8%88\"><i class=\"fa fa-link\"></i></a>WebSocket アプリケーションの設計</h2>\n\n<ul>\n<li>高頻度イベントにまつわる状態は Redis が向いてる</li>\n<li>DBに 永続化するのは何らかのチェックポイントを通った時</li>\n<li>状態の差分を更新し続けるのは難易度が高いので、定期的に state を全部渡してsyncする、などの保証は欲しい</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"チャットの場合\" class=\"fragment\"></span><a href=\"#%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E3%81%AE%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>チャットの場合</h2>\n\n<ul>\n<li>最初のユーザーが部屋にログイン\n\n<ul>\n<li>=&gt; 部屋名をキーにRedisのリスト要素を作成</li>\n</ul>\n</li>\n<li>誰かが発言イベントを投げる\n\n<ul>\n<li>=&gt; リスト要素を更新</li>\n<li>=&gt; 発言を bloadcast</li>\n</ul>\n</li>\n<li>1分に1回\n\n<ul>\n<li>=&gt; 全員に現在のリストを配信して補正</li>\n</ul>\n</li>\n<li>5分に1回\n\n<ul>\n<li>=&gt; リスト要素の中身を DB に保存</li>\n</ul>\n</li>\n<li>最後のユーザーがログアウト\n\n<ul>\n<li>=&gt; リスト要素の中身を DB に保存</li>\n<li>=&gt; Redisの要素を削除</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"設計意図\" class=\"fragment\"></span><a href=\"#%E8%A8%AD%E8%A8%88%E6%84%8F%E5%9B%B3\"><i class=\"fa fa-link\"></i></a>設計意図</h2>\n\n<ul>\n<li>\n<p>できるだけ DB を触らず Redis のインメモリデータだけ使う</p>\n\n<ul>\n<li>Redisは肥大化すると運用が辛いので、使い終わったらすぐ消す</li>\n<li>Redisの スナップショット を DB に保存する</li>\n</ul>\n</li>\n<li><p>ここまで来ると、あとは <a href=\"https://www.amazon.co.jp/dp/4774145807/ref=cm_sw_r_tw_dp_x_zxeTxbTC39VSA\" rel=\"nofollow\" target=\"_blank\">オンラインゲームを支える技術</a> などが参考になる</p></li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"趣味でオンラインゲーム作ってた時\" class=\"fragment\"></span><a href=\"#%E8%B6%A3%E5%91%B3%E3%81%A7%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B2%E3%83%BC%E3%83%A0%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%9F%E6%99%82\"><i class=\"fa fa-link\"></i></a>趣味でオンラインゲーム作ってた時</h2>\n\n<ul>\n<li>ダンジョン階層ごとにチャンネル分割</li>\n<li>12FPSでゲームステートを配信</li>\n<li>クライアントは60FPSなので、時系列データから他のEntityの座標予測</li>\n<li>モンスターを倒したら保存</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"最後に\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB\"><i class=\"fa fa-link\"></i></a>最後に</h2>\n\n<ul>\n<li>まだ負荷計測に至ってないので、どこがボトルネックになるかわかってない</li>\n<li>高頻度イベントはWebのお約束が通じなかったりするんで、頑張りましょう</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"終わり\" class=\"fragment\"></span><a href=\"#%E7%B5%82%E3%82%8F%E3%82%8A\"><i class=\"fa fa-link\"></i></a>終わり</h2>\n",
    "stock_users": [
        "kysnm",
        "s_nakamura",
        "yasulab",
        "yukku0423@github",
        "webgyo",
        "coppieee",
        "kjunichi",
        "FiNGAHOLiC",
        "ykominami",
        "tunepolo",
        "akkun_choi",
        "star__hoshi",
        "mah_lab",
        "QUANON",
        "ozw_sei",
        "rabitarochan",
        "yoskhdia",
        "cyber_snufkin",
        "Arvelt",
        "yukihir0@github",
        "koki_cheese",
        "seyself",
        "kyuns",
        "damele0n",
        "hachi8833",
        "knt45",
        "tokibi",
        "selmertsx",
        "masayuki5160",
        "BlackPrincess",
        "m-nagae",
        "ryota-murakami",
        "t44cd",
        "tos-miyake",
        "tomookaku",
        "morisuke",
        "nightyknite",
        "suzumi",
        "YutakakINJO",
        "rooooomania",
        "HirokazuMiyaji",
        "ash1day",
        "nownabe",
        "yutuki",
        "itosho",
        "kuntao",
        "e-kichi",
        "e-taka",
        "YoshinoriSato",
        "ryooo",
        "kiwi_yuki",
        "mero",
        "k_yagisan9",
        "yano-yoshinori",
        "jTakasuRyuji",
        "koto_hajime",
        "MiyachiK",
        "17tea",
        "sugaf",
        "yishibashi",
        "mserizawa",
        "mnishiguchi",
        "orisano",
        "y-temp4",
        "bokuweb",
        "katsuaki1991",
        "shohohoh",
        "syauta1142",
        "tsunyan",
        "spank"
    ]
}, {
    "id": 415757,
    "uuid": "443944d5df4755a72f03",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "flowtype + eslint 環境で eslint の no-undef で declare された型の型アノテーションの検出を無視する",
    "created_at": "2016-08-16 22:08:33 +0900",
    "updated_at": "2016-08-16 22:10:36 +0900",
    "created_at_in_words": "23日",
    "updated_at_in_words": "23日",
    "tags": [{
        "name": "flowtype",
        "url_name": "flowtype",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/360610226709319baebd60679c05de4ae82726e3/medium.jpg?1457848947",
        "following": false,
        "versions": []
    }],
    "stock_count": 2,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/443944d5df4755a72f03",
    "created_at_as_seconds": 1471352913,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "## どんな状況か\n\ninclude してる型宣言ファイルでFooを宣言しているとする\n\n```\ndecrale type Foo = {};\n```\n\nこのとき 次のコードは eslint の no-undef で警告が出る\n\n```\nconst foo: Foo = {};\n```\n\n定義ファイルで declare された型はどこかからでもグローバルに参照可能だが、flow はそんなidentifier は知らないと警告してくる\n\n## 解決策1\n\n.eslintrc の globals に Foo を追加する。\nこれは動くけどインスタンス変数としてFooにアクセスしてしまうのを許容してしまう。ダメ。\n\n不満を持ってたら次の解決策を見つけた。\n\n## 解決策2\n\nhttps://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-define-flow-type を使う。\n\n```\n{\n  \"parser\": \"babel-eslint\",\n  \"plugins\": [\n    \"flowtype\"\n  ],\n  \"rules\": {\n    \"flowtype/define-flow-type\": 1,\n    \"flowtype/use-flow-type\": 1\n  }\n}\n```\n\nこれに辿り着くまで長かったので、一応メモしておく。ルール名がわかりにくいと思う。\n\n## 参考\n\n- https://github.com/eslint/eslint/issues/4741\n",
    "body": "\n<h2>\n<span id=\"どんな状況か\" class=\"fragment\"></span><a href=\"#%E3%81%A9%E3%82%93%E3%81%AA%E7%8A%B6%E6%B3%81%E3%81%8B\"><i class=\"fa fa-link\"></i></a>どんな状況か</h2>\n\n<p>include してる型宣言ファイルでFooを宣言しているとする</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\ndecrale type Foo = {};\n</pre></div></div>\n\n<p>このとき 次のコードは eslint の no-undef で警告が出る</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\nconst foo: Foo = {};\n</pre></div></div>\n\n<p>定義ファイルで declare された型はどこかからでもグローバルに参照可能だが、flow はそんなidentifier は知らないと警告してくる</p>\n\n<h2>\n<span id=\"解決策1\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%961\"><i class=\"fa fa-link\"></i></a>解決策1</h2>\n\n<p>.eslintrc の globals に Foo を追加する。<br>\nこれは動くけどインスタンス変数としてFooにアクセスしてしまうのを許容してしまう。ダメ。</p>\n\n<p>不満を持ってたら次の解決策を見つけた。</p>\n\n<h2>\n<span id=\"解決策2\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%962\"><i class=\"fa fa-link\"></i></a>解決策2</h2>\n\n<p><a href=\"https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-define-flow-type\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-define-flow-type</a> を使う。</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\n{\n  \"parser\": \"babel-eslint\",\n  \"plugins\": [\n    \"flowtype\"\n  ],\n  \"rules\": {\n    \"flowtype/define-flow-type\": 1,\n    \"flowtype/use-flow-type\": 1\n  }\n}\n</pre></div></div>\n\n<p>これに辿り着くまで長かったので、一応メモしておく。ルール名がわかりにくいと思う。</p>\n\n<h2>\n<span id=\"参考\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%83\"><i class=\"fa fa-link\"></i></a>参考</h2>\n\n<ul>\n<li><a href=\"https://github.com/eslint/eslint/issues/4741\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/eslint/eslint/issues/4741</a></li>\n</ul>\n",
    "stock_users": [
        "dorayakikun",
        "tgfjt"
    ]
}, {
    "id": 415581,
    "uuid": "9a426af4fe919cd9d9d5",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "react-dnd これだけ抑えておけばおk",
    "created_at": "2016-08-16 07:00:20 +0900",
    "updated_at": "2016-08-16 07:04:05 +0900",
    "created_at_in_words": "23日",
    "updated_at_in_words": "23日",
    "tags": [{
        "name": "React",
        "url_name": "react",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/cbf495549d731f9401c87e6cac0f5c88cd21b682/medium.jpg?1438055112",
        "following": true,
        "versions": []
    }, {
        "name": "react-dnd",
        "url_name": "react-dnd",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 53,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/9a426af4fe919cd9d9d5",
    "created_at_as_seconds": 1471298420,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": true,
    "raw_body": "巷で難しい難しいと言われていた react-dnd でドラッグアンドドロップを実装してみたところ、案外楽だったのでまとめる。\n\n## 用語整理\n\n- `DragSource` : マウスで掴んでドラッグする対象\n- `DropTarget` : ドロップ対象\n- `monitor` :  drop/hover時に今現在のイベント対象の状態を取り出せるもの\n\n\n## 使い方\n\n- `@DragDropContext` を ドラッグアンドドロップしたい領域のコンポーネントに注入する\n- `@DropTarget` を落としたい先のコンポーネントに注入する\n- `@DragSource` をドラッグさせたいコンポーネントに注入する\n\nたとえばドラッグアンドドロップでリストの要素を入れ替えたい場合、 リスト全体が `DragDropContext` で、 リスト要素が `DragSource` かつ `DropTarget` になる。\n\n\n## 実装例\n\n並び替えられるリスト要素の実装\n\n```js\n@DropTarget(\"item\", {\n  // drop 時のコールバック\n  drop(dropProps, monitor, dropComponent) {\n    const dragProps = monitor.getItem(); // DragSource の props が取り出せる\n    if (dropProps.id !== dragProps.id) {\n      dragProps.onDrop(dragProps.id, dropProps.id);\n    }\n  }\n}, connect => {\n  return {\n    connectDropTarget: connect.dropTarget()\n  };\n})\n@DragSource(\"item\", {\n  beginDrag(props) {\n    return props;\n  }\n}, (connect, monitor) => {\n  return {\n    connectDragSource: connect.dragSource(),\n    isDragging: monitor.isDragging()\n  };\n})\nclass DragItem extends Component {\n  render() {\n    return this.props.connectDragSource(this.props.connectDropTarget(\n      <div>\n        {this.props.children}\n      </div>\n    ));\n  }\n}\n```\n\n\nリストの実装\n\n```js\n\n@DragDropContext(ReactDnDHTML5Backend)\nexport default class SortableList extends Component {\n  constructor(props) {\n    super(props);\n    this.state = {\n      items: [\n        {id: 1, name: \"aaa\"},\n        {id: 2, name: \"bbb\"},\n        {id: 3, name: \"ccc\"}\n      ];\n    }\n  }\n  render() {\n    return (\n      <div>\n        {\n          this.state.items.map(item => {\n            return <DragItem\n              key={item.id}\n              id={item.id}\n              onDrop={\n                (toId, fromId) => {\n                  // ここで入れ替える処理をする\n                  const items = this.state.items.slice();\n                  const toIndex = items.findIndex(i => i.id === toId);\n                  const fromIndex = items.findIndex(i => i.id === fromId);\n                  const toItem = items[toIndex];\n                  const fromItem = items[fromIndex];\n                  const items[toIndex] = fromItem;\n                  const items[fromIndex] = toItem;\n                  this.setState({items})\n                }\n              }\n            >\n              {item.name}\n            </DragItem>\n              \n          })\n        }\n      </div>\n    );\n  }\n}\n```\n\nこの実装例だと onDrop コールバックを与えて、from, to を取得し、state.items の中身を入れ替える。flux だったら store に向かって投げるだろう。\n",
    "body": "<p>巷で難しい難しいと言われていた react-dnd でドラッグアンドドロップを実装してみたところ、案外楽だったのでまとめる。</p>\n\n<h2>\n<span id=\"用語整理\" class=\"fragment\"></span><a href=\"#%E7%94%A8%E8%AA%9E%E6%95%B4%E7%90%86\"><i class=\"fa fa-link\"></i></a>用語整理</h2>\n\n<ul>\n<li>\n<code>DragSource</code> : マウスで掴んでドラッグする対象</li>\n<li>\n<code>DropTarget</code> : ドロップ対象</li>\n<li>\n<code>monitor</code> :  drop/hover時に今現在のイベント対象の状態を取り出せるもの</li>\n</ul>\n\n<h2>\n<span id=\"使い方\" class=\"fragment\"></span><a href=\"#%E4%BD%BF%E3%81%84%E6%96%B9\"><i class=\"fa fa-link\"></i></a>使い方</h2>\n\n<ul>\n<li>\n<code>@DragDropContext</code> を ドラッグアンドドロップしたい領域のコンポーネントに注入する</li>\n<li>\n<code>@DropTarget</code> を落としたい先のコンポーネントに注入する</li>\n<li>\n<code>@DragSource</code> をドラッグさせたいコンポーネントに注入する</li>\n</ul>\n\n<p>たとえばドラッグアンドドロップでリストの要素を入れ替えたい場合、 リスト全体が <code>DragDropContext</code> で、 リスト要素が <code>DragSource</code> かつ <code>DropTarget</code> になる。</p>\n\n<h2>\n<span id=\"実装例\" class=\"fragment\"></span><a href=\"#%E5%AE%9F%E8%A3%85%E4%BE%8B\"><i class=\"fa fa-link\"></i></a>実装例</h2>\n\n<p>並び替えられるリスト要素の実装</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"err\">@</span><span class=\"nx\">DropTarget</span><span class=\"p\">(</span><span class=\"s2\">\"item\"</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n  <span class=\"c1\">// drop 時のコールバック</span>\n  <span class=\"nx\">drop</span><span class=\"p\">(</span><span class=\"nx\">dropProps</span><span class=\"p\">,</span> <span class=\"nx\">monitor</span><span class=\"p\">,</span> <span class=\"nx\">dropComponent</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"kr\">const</span> <span class=\"nx\">dragProps</span> <span class=\"o\">=</span> <span class=\"nx\">monitor</span><span class=\"p\">.</span><span class=\"nx\">getItem</span><span class=\"p\">();</span><span class=\"err\"> </span><span class=\"c1\">// DragSource の props が取り出せる</span>\n    <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"nx\">dropProps</span><span class=\"p\">.</span><span class=\"nx\">id</span> <span class=\"o\">!==</span> <span class=\"nx\">dragProps</span><span class=\"p\">.</span><span class=\"nx\">id</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"nx\">dragProps</span><span class=\"p\">.</span><span class=\"nx\">onDrop</span><span class=\"p\">(</span><span class=\"nx\">dragProps</span><span class=\"p\">.</span><span class=\"nx\">id</span><span class=\"p\">,</span> <span class=\"nx\">dropProps</span><span class=\"p\">.</span><span class=\"nx\">id</span><span class=\"p\">);</span>\n    <span class=\"p\">}</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">},</span> <span class=\"nx\">connect</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"k\">return</span> <span class=\"p\">{</span>\n    <span class=\"nx\">connectDropTarget</span><span class=\"o\">:</span> <span class=\"nx\">connect</span><span class=\"p\">.</span><span class=\"nx\">dropTarget</span><span class=\"p\">()</span>\n  <span class=\"p\">};</span>\n<span class=\"p\">})</span>\n<span class=\"err\">@</span><span class=\"nx\">DragSource</span><span class=\"p\">(</span><span class=\"s2\">\"item\"</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n  <span class=\"nx\">beginDrag</span><span class=\"p\">(</span><span class=\"nx\">props</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"nx\">props</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">},</span> <span class=\"p\">(</span><span class=\"nx\">connect</span><span class=\"p\">,</span> <span class=\"nx\">monitor</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"k\">return</span> <span class=\"p\">{</span>\n    <span class=\"nx\">connectDragSource</span><span class=\"o\">:</span> <span class=\"nx\">connect</span><span class=\"p\">.</span><span class=\"nx\">dragSource</span><span class=\"p\">(),</span>\n    <span class=\"nx\">isDragging</span><span class=\"o\">:</span> <span class=\"nx\">monitor</span><span class=\"p\">.</span><span class=\"nx\">isDragging</span><span class=\"p\">()</span>\n  <span class=\"p\">};</span>\n<span class=\"p\">})</span>\n<span class=\"kr\">class</span> <span class=\"nx\">DragItem</span> <span class=\"kr\">extends</span> <span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">connectDragSource</span><span class=\"p\">(</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">connectDropTarget</span><span class=\"p\">(</span>\n      <span class=\"o\">&lt;</span><span class=\"nx\">div</span><span class=\"o\">&gt;</span>\n        <span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">children</span><span class=\"p\">}</span>\n      <span class=\"o\">&lt;</span><span class=\"err\">/div&gt;</span>\n    <span class=\"p\">));</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p>リストの実装</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n\n<span class=\"err\">@</span><span class=\"nx\">DragDropContext</span><span class=\"p\">(</span><span class=\"nx\">ReactDnDHTML5Backend</span><span class=\"p\">)</span>\n<span class=\"kr\">export</span> <span class=\"k\">default</span> <span class=\"kr\">class</span> <span class=\"nx\">SortableList</span> <span class=\"kr\">extends</span> <span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">constructor</span><span class=\"p\">(</span><span class=\"nx\">props</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"kr\">super</span><span class=\"p\">(</span><span class=\"nx\">props</span><span class=\"p\">);</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n      <span class=\"nx\">items</span><span class=\"o\">:</span> <span class=\"p\">[</span>\n        <span class=\"p\">{</span><span class=\"nx\">id</span><span class=\"o\">:</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"nx\">name</span><span class=\"o\">:</span> <span class=\"s2\">\"aaa\"</span><span class=\"p\">},</span>\n        <span class=\"p\">{</span><span class=\"nx\">id</span><span class=\"o\">:</span> <span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"nx\">name</span><span class=\"o\">:</span> <span class=\"s2\">\"bbb\"</span><span class=\"p\">},</span>\n        <span class=\"p\">{</span><span class=\"nx\">id</span><span class=\"o\">:</span> <span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"nx\">name</span><span class=\"o\">:</span> <span class=\"s2\">\"ccc\"</span><span class=\"p\">}</span>\n      <span class=\"p\">];</span>\n    <span class=\"p\">}</span>\n  <span class=\"p\">}</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"p\">(</span>\n      <span class=\"o\">&lt;</span><span class=\"nx\">div</span><span class=\"o\">&gt;</span>\n        <span class=\"p\">{</span>\n          <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span><span class=\"p\">.</span><span class=\"nx\">items</span><span class=\"p\">.</span><span class=\"nx\">map</span><span class=\"p\">(</span><span class=\"nx\">item</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n            <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">DragItem</span>\n              <span class=\"nx\">key</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"nx\">item</span><span class=\"p\">.</span><span class=\"nx\">id</span><span class=\"p\">}</span>\n              <span class=\"nx\">id</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"nx\">item</span><span class=\"p\">.</span><span class=\"nx\">id</span><span class=\"p\">}</span>\n              <span class=\"nx\">onDrop</span><span class=\"o\">=</span><span class=\"p\">{</span>\n                <span class=\"p\">(</span><span class=\"nx\">toId</span><span class=\"p\">,</span> <span class=\"nx\">fromId</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n                  <span class=\"c1\">// ここで入れ替える処理をする</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">items</span> <span class=\"o\">=</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span><span class=\"p\">.</span><span class=\"nx\">items</span><span class=\"p\">.</span><span class=\"nx\">slice</span><span class=\"p\">();</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">toIndex</span> <span class=\"o\">=</span> <span class=\"nx\">items</span><span class=\"p\">.</span><span class=\"nx\">findIndex</span><span class=\"p\">(</span><span class=\"nx\">i</span> <span class=\"o\">=&gt;</span> <span class=\"nx\">i</span><span class=\"p\">.</span><span class=\"nx\">id</span> <span class=\"o\">===</span> <span class=\"nx\">toId</span><span class=\"p\">);</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">fromIndex</span> <span class=\"o\">=</span> <span class=\"nx\">items</span><span class=\"p\">.</span><span class=\"nx\">findIndex</span><span class=\"p\">(</span><span class=\"nx\">i</span> <span class=\"o\">=&gt;</span> <span class=\"nx\">i</span><span class=\"p\">.</span><span class=\"nx\">id</span> <span class=\"o\">===</span> <span class=\"nx\">fromId</span><span class=\"p\">);</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">toItem</span> <span class=\"o\">=</span> <span class=\"nx\">items</span><span class=\"p\">[</span><span class=\"nx\">toIndex</span><span class=\"p\">];</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">fromItem</span> <span class=\"o\">=</span> <span class=\"nx\">items</span><span class=\"p\">[</span><span class=\"nx\">fromIndex</span><span class=\"p\">];</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">items</span><span class=\"p\">[</span><span class=\"nx\">toIndex</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"nx\">fromItem</span><span class=\"p\">;</span>\n                  <span class=\"kr\">const</span> <span class=\"nx\">items</span><span class=\"p\">[</span><span class=\"nx\">fromIndex</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"nx\">toItem</span><span class=\"p\">;</span>\n                  <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">setState</span><span class=\"p\">({</span><span class=\"nx\">items</span><span class=\"p\">})</span>\n                <span class=\"p\">}</span>\n              <span class=\"p\">}</span>\n            <span class=\"o\">&gt;</span>\n              <span class=\"p\">{</span><span class=\"nx\">item</span><span class=\"p\">.</span><span class=\"nx\">name</span><span class=\"p\">}</span>\n            <span class=\"o\">&lt;</span><span class=\"err\">/DragItem&gt;</span>\n\n          <span class=\"p\">})</span>\n        <span class=\"p\">}</span>\n      <span class=\"o\">&lt;</span><span class=\"err\">/div&gt;</span>\n    <span class=\"p\">);</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p>この実装例だと onDrop コールバックを与えて、from, to を取得し、state.items の中身を入れ替える。flux だったら store に向かって投げるだろう。</p>\n",
    "stock_users": [
        "yuya_takeyama",
        "holyshared",
        "hanachin_",
        "yd_niku",
        "bluerabbit",
        "hatchinee",
        "hadzimme",
        "yancya",
        "SayKicho",
        "uehaj",
        "dorayakikun",
        "virifi",
        "damele0n",
        "ecaze",
        "busyoumono99",
        "urara",
        "SleepingBear",
        "chooser",
        "meganetaaan",
        "jfujima",
        "taka7beckham",
        "hkoba@github",
        "xylitol45@github",
        "shimpeiws",
        "silverskyvicto",
        "hkwid",
        "iiito",
        "tkdn",
        "ogch_v",
        "yl0g0ly",
        "harry0000",
        "nownabe",
        "tokimari",
        "minewebstaff",
        "seihmd",
        "xavier",
        "Serenade",
        "tsh71",
        "mismith",
        "cakecatz",
        "nabeliwo",
        "8845musign",
        "lyuich",
        "chuck0523",
        "snona",
        "shy_azusa",
        "y-temp4",
        "1-AizawaSatoshi",
        "LowSE01",
        "natsumi_m31",
        "suidobashi-papa",
        "rikudako",
        "w3cdp6084-dev"
    ]
}, {
    "id": 414849,
    "uuid": "357d0234d3548a9ccb09",
    "user": {
        "id": 42198,
        "url_name": "IKEA_dless",
        "profile_image_url": "https://avatars.githubusercontent.com/u/6467972?v=3",
        "following": true
    },
    "title": "【Rails】autoload_pathsの誤解",
    "created_at": "2016-08-13 02:49:20 +0900",
    "updated_at": "2016-08-13 02:54:09 +0900",
    "created_at_in_words": "27日",
    "updated_at_in_words": "27日",
    "tags": [{
        "name": "Ruby",
        "url_name": "ruby",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/0337fbcbff62fb8fa5d0b8be5c3b47d1115d91fc/medium.jpg?1418548649",
        "following": false,
        "versions": []
    }, {
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }],
    "stock_count": 9,
    "comment_count": 0,
    "url": "http://qiita.com/IKEA_dless/items/357d0234d3548a9ccb09",
    "created_at_as_seconds": 1471024160,
    "tweet": true,
    "gist_url": null,
    "private": false,
    "stocked": true,
    "raw_body": "\nrailsでservices層やform層を追加したとき、appディレクトリ以下ならautoload_pathsの記述は不要。\n\n```ruby:application.rb\nconfig.autoload_paths << \"#{Rails.root}/app/services\"\n```\n↑こんなやつ\n\n## rails5で確認してみる。\nrails newしただけの状態でautoload_pathsを出力する。\n\n```bash:console\n$ bundle exec rails r 'puts ActiveSupport::Dependencies.autoload_paths'\n/Users/ikea/rails5/sample/app/assets\n/Users/ikea/rails5/sample/app/channels\n/Users/ikea/rails5/sample/app/controllers\n/Users/ikea/rails5/sample/app/controllers/concerns\n/Users/ikea/rails5/sample/app/helpers\n/Users/ikea/rails5/sample/app/jobs\n/Users/ikea/rails5/sample/app/mailers\n/Users/ikea/rails5/sample/app/models\n/Users/ikea/rails5/sample/app/models/concerns\n/Users/ikea/rails5/sample/test/mailers/previews\n```\n\nこれのappディレクトリ配下にいろいろ追加してみる。\n\n```\napp\n├── assets\n├── channels\n├── controllers\n├── decorators\n├── forms\n├── helpers\n├── jobs\n├── mailers\n├── models\n├── services\n├── validators\n├── values\n└── views\n```\n\nこんな感じ。\n\n上記のディレクトリが追加された状態で、再度autoload_pathsを出力する。\n\n```bash:console\n$ bundle exec rails r 'puts ActiveSupport::Dependencies.autoload_paths'\n/Users/ikea/rails5/sample/app/assets\n/Users/ikea/rails5/sample/app/channels\n/Users/ikea/rails5/sample/app/controllers\n/Users/ikea/rails5/sample/app/controllers/concerns\n/Users/ikea/rails5/sample/app/decorators\n/Users/ikea/rails5/sample/app/forms\n/Users/ikea/rails5/sample/app/helpers\n/Users/ikea/rails5/sample/app/jobs\n/Users/ikea/rails5/sample/app/mailers\n/Users/ikea/rails5/sample/app/models\n/Users/ikea/rails5/sample/app/models/concerns\n/Users/ikea/rails5/sample/app/services\n/Users/ikea/rails5/sample/app/validators\n/Users/ikea/rails5/sample/app/values\n/Users/ikea/rails5/sample/test/mailers/previews\n```\n手動で読み込まなくてもautoload_pathsに追加され、辞書順に読み込まれている。\n\n## まとめ\napp以下に作成したディレクトリは自動で定数の読込が行われるため、autoload_pathsに手動で追加する必要はない。\nただし、ディレクトリ直下のlibとかを読み込ませたいときは、記述が必要。\nrails4系でも同じハズ。\n\n### 参考\n- [RailsGuides 定数の自動読み込みと再読み込み](http://railsguides.jp/autoloading_and_reloading_constants.html#autoload-paths)\n",
    "body": "<p>railsでservices層やform層を追加したとき、appディレクトリ以下ならautoload_pathsの記述は不要。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">application.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">autoload_paths</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">\"</span><span class=\"si\">#{</span><span class=\"no\">Rails</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"si\">}</span><span class=\"s2\">/app/services\"</span>\n</pre></div>\n</div>\n\n<p>↑こんなやつ</p>\n\n<h2>\n<span id=\"rails5で確認してみる\" class=\"fragment\"></span><a href=\"#rails5%E3%81%A7%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B\"><i class=\"fa fa-link\"></i></a>rails5で確認してみる。</h2>\n\n<p>rails newしただけの状態でautoload_pathsを出力する。</p>\n\n<div class=\"code-frame\" data-lang=\"bash\">\n<div class=\"code-lang\"><span class=\"bold\">console</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>rails r <span class=\"s1\">'puts ActiveSupport::Dependencies.autoload_paths'</span>\n/Users/ikea/rails5/sample/app/assets\n/Users/ikea/rails5/sample/app/channels\n/Users/ikea/rails5/sample/app/controllers\n/Users/ikea/rails5/sample/app/controllers/concerns\n/Users/ikea/rails5/sample/app/helpers\n/Users/ikea/rails5/sample/app/jobs\n/Users/ikea/rails5/sample/app/mailers\n/Users/ikea/rails5/sample/app/models\n/Users/ikea/rails5/sample/app/models/concerns\n/Users/ikea/rails5/sample/test/mailers/previews\n</pre></div>\n</div>\n\n<p>これのappディレクトリ配下にいろいろ追加してみる。</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\napp\n├── assets\n├── channels\n├── controllers\n├── decorators\n├── forms\n├── helpers\n├── jobs\n├── mailers\n├── models\n├── services\n├── validators\n├── values\n└── views\n</pre></div></div>\n\n<p>こんな感じ。</p>\n\n<p>上記のディレクトリが追加された状態で、再度autoload_pathsを出力する。</p>\n\n<div class=\"code-frame\" data-lang=\"bash\">\n<div class=\"code-lang\"><span class=\"bold\">console</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>rails r <span class=\"s1\">'puts ActiveSupport::Dependencies.autoload_paths'</span>\n/Users/ikea/rails5/sample/app/assets\n/Users/ikea/rails5/sample/app/channels\n/Users/ikea/rails5/sample/app/controllers\n/Users/ikea/rails5/sample/app/controllers/concerns\n/Users/ikea/rails5/sample/app/decorators\n/Users/ikea/rails5/sample/app/forms\n/Users/ikea/rails5/sample/app/helpers\n/Users/ikea/rails5/sample/app/jobs\n/Users/ikea/rails5/sample/app/mailers\n/Users/ikea/rails5/sample/app/models\n/Users/ikea/rails5/sample/app/models/concerns\n/Users/ikea/rails5/sample/app/services\n/Users/ikea/rails5/sample/app/validators\n/Users/ikea/rails5/sample/app/values\n/Users/ikea/rails5/sample/test/mailers/previews\n</pre></div>\n</div>\n\n<p>手動で読み込まなくてもautoload_pathsに追加され、辞書順に読み込まれている。</p>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<p>app以下に作成したディレクトリは自動で定数の読込が行われるため、autoload_pathsに手動で追加する必要はない。<br>\nただし、ディレクトリ直下のlibとかを読み込ませたいときは、記述が必要。<br>\nrails4系でも同じハズ。</p>\n\n<h3>\n<span id=\"参考\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%83\"><i class=\"fa fa-link\"></i></a>参考</h3>\n\n<ul>\n<li><a href=\"http://railsguides.jp/autoloading_and_reloading_constants.html#autoload-paths\" rel=\"nofollow\" target=\"_blank\">RailsGuides 定数の自動読み込みと再読み込み</a></li>\n</ul>\n",
    "stock_users": [
        "kadoppe",
        "tos-miyake",
        "Yarimizu14",
        "masoo",
        "soyanchu",
        "YumaInaura",
        "y-temp4",
        "MasaruTech",
        "jmtwgj"
    ]
}, {
    "id": 414633,
    "uuid": "84ec7c0e5631d647d67a",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "フロントエンドで ActionCable を npm 経由で使う",
    "created_at": "2016-08-12 08:21:28 +0900",
    "updated_at": "2016-08-12 08:23:50 +0900",
    "created_at_in_words": "27日",
    "updated_at_in_words": "27日",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }, {
        "name": "ActionCable",
        "url_name": "actioncable",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 15,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/84ec7c0e5631d647d67a",
    "created_at_as_seconds": 1470957688,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": true,
    "raw_body": "近年、我々がRails5をセットアップする際に最初にやることは sprockets を 無効化することだが、 rails5 の Channel は Sprockets ありきで書かれていたので、npm で完結して使えるか調べて、書きなおした。\n\n[actioncable](https://www.npmjs.com/package/actioncable \"actioncable\") を使う。\n\n```\nnpm install -S actioncable\n```\n\nどこ由来か怪しくて調べたけど、一応railsのリポジトリから publish されてる?\nhttps://github.com/rails/rails/blob/master/actioncable/package.json\n\nnpm に publish した eleencodes は https://github.com/eileencodes 見る限り rails コミッタなので、とりあえず信用できそう。同名の id を npm で騙ってないかぎり。\n\n\nhttp://qiita.com/jwako/items/96c01183c9c33ba1a48d を参考に babel/browserify 環境用に書き直す。\n\n```ruby\nimport ActionCable from \"actioncable\";\nconst cable = ActionCable.createConsumer();\nconst room = cable.subscriptions.create(\"RoomChannel\", {\n  connected() {\n    console.log(\"connected\");\n  },\n\n  disconnected() {\n    console.log(\"disconnected\");\n  },\n\n  received(data) {\n    console.log(\"received\",  data);\n  },\n\n  speak(mes = \"hello\") {\n    this.perform('speak', {mes});\n  }\n});\nexport default room;\n```\n\nだいたいこんな感じ\n\n## 注意点\n\n今はリリース直後だからブレ用がないけど、念のため Gemfile の方とバージョンを固定して合わせておかないと、非互換な変更あったときに死にそう。\n",
    "body": "<p>近年、我々がRails5をセットアップする際に最初にやることは sprockets を 無効化することだが、 rails5 の Channel は Sprockets ありきで書かれていたので、npm で完結して使えるか調べて、書きなおした。</p>\n\n<p><a href=\"https://www.npmjs.com/package/actioncable\" title=\"actioncable\" rel=\"nofollow\" target=\"_blank\">actioncable</a> を使う。</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\nnpm install -S actioncable\n</pre></div></div>\n\n<p>どこ由来か怪しくて調べたけど、一応railsのリポジトリから publish されてる?<br>\n<a href=\"https://github.com/rails/rails/blob/master/actioncable/package.json\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/rails/rails/blob/master/actioncable/package.json</a></p>\n\n<p>npm に publish した eleencodes は <a href=\"https://github.com/eileencodes\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/eileencodes</a> 見る限り rails コミッタなので、とりあえず信用できそう。同名の id を npm で騙ってないかぎり。</p>\n\n<p><a href=\"http://qiita.com/jwako/items/96c01183c9c33ba1a48d\" class=\"autolink\" id=\"reference-fef9c1d76c5fbeaca820\">http://qiita.com/jwako/items/96c01183c9c33ba1a48d</a> を参考に babel/browserify 環境用に書き直す。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"n\">import</span> <span class=\"no\">ActionCable</span> <span class=\"n\">from</span> <span class=\"s2\">\"actioncable\"</span><span class=\"p\">;</span>\n<span class=\"n\">const</span> <span class=\"n\">cable</span> <span class=\"o\">=</span> <span class=\"no\">ActionCable</span><span class=\"o\">.</span><span class=\"n\">createConsumer</span><span class=\"p\">();</span>\n<span class=\"n\">const</span> <span class=\"n\">room</span> <span class=\"o\">=</span> <span class=\"n\">cable</span><span class=\"o\">.</span><span class=\"n\">subscriptions</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"s2\">\"RoomChannel\"</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n  <span class=\"n\">connected</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"n\">console</span><span class=\"o\">.</span><span class=\"n\">log</span><span class=\"p\">(</span><span class=\"s2\">\"connected\"</span><span class=\"p\">);</span>\n  <span class=\"p\">},</span>\n\n  <span class=\"n\">disconnected</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"n\">console</span><span class=\"o\">.</span><span class=\"n\">log</span><span class=\"p\">(</span><span class=\"s2\">\"disconnected\"</span><span class=\"p\">);</span>\n  <span class=\"p\">},</span>\n\n  <span class=\"n\">received</span><span class=\"p\">(</span><span class=\"n\">data</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"n\">console</span><span class=\"o\">.</span><span class=\"n\">log</span><span class=\"p\">(</span><span class=\"s2\">\"received\"</span><span class=\"p\">,</span>  <span class=\"n\">data</span><span class=\"p\">);</span>\n  <span class=\"p\">},</span>\n\n  <span class=\"n\">speak</span><span class=\"p\">(</span><span class=\"n\">mes</span> <span class=\"o\">=</span> <span class=\"s2\">\"hello\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"n\">this</span><span class=\"o\">.</span><span class=\"n\">perform</span><span class=\"p\">(</span><span class=\"s1\">'speak'</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"n\">mes</span><span class=\"p\">});</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">});</span>\n<span class=\"n\">export</span> <span class=\"n\">default</span> <span class=\"n\">room</span><span class=\"p\">;</span>\n</pre></div></div>\n\n<p>だいたいこんな感じ</p>\n\n<h2>\n<span id=\"注意点\" class=\"fragment\"></span><a href=\"#%E6%B3%A8%E6%84%8F%E7%82%B9\"><i class=\"fa fa-link\"></i></a>注意点</h2>\n\n<p>今はリリース直後だからブレ用がないけど、念のため Gemfile の方とバージョンを固定して合わせておかないと、非互換な変更あったときに死にそう。</p>\n",
    "stock_users": [
        "oreshinya",
        "aki77",
        "holyshared",
        "henteko",
        "inuscript",
        "weakboson@github",
        "izumin5210",
        "heiz123@github",
        "mgi166",
        "tomlla",
        "kawasin73",
        "cakecatz",
        "ymiyamae",
        "y-temp4",
        "kkanazaw"
    ]
}, {
    "id": 414207,
    "uuid": "630b9f038c87298b5756",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "Arelでクエリを書くのはやめた方が良い5つの理由(Rails 5.0以前の場合)",
    "created_at": "2016-08-10 10:49:18 +0900",
    "updated_at": "2016-08-12 05:40:04 +0900",
    "created_at_in_words": "29日",
    "updated_at_in_words": "27日",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }, {
        "name": "Arel",
        "url_name": "arel",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 76,
    "comment_count": 2,
    "url": "http://qiita.com/jnchito/items/630b9f038c87298b5756",
    "created_at_as_seconds": 1470793758,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## はじめに:Arelって何?\n\nみなさん、Arel(アレル)ってご存知ですか?\nArelはActive Recordの内部で使用されるSQL生成ライブラリです。\n\nhttps://github.com/rails/arel\n\nRailsのクエリの書き方をググると、ときどきArelを使った実装例が見つかるので、見たことがある、もしくは何度か使ったことがある、という人もいると思います。\n\nArelをよく知らない人のために、Arelの利用例をちょっと見てみましょう。\nたとえば「コメント文中に、\"ruby\"が含まれるユーザープロフィールを検索したい」という場合、Rails標準のクエリインターフェースを使うと条件部分のSQLを文字列で書く必要があります。(PostgreSQL環境を想定)\n\n```ruby\nProfile.where(\n  \"profiles.comment ILIKE ?\", \"%ruby%\"\n).to_sql\n#=> SELECT \"profiles\".* \n#   FROM \"profiles\" \n#   WHERE (profiles.comment ILIKE '%ruby%')\n```\n\nしかし、Arelを使うと上のクエリが次のように書けます。\n\n```ruby\nProfile.where(\n  Profile.arel_table[:comment].matches(\"%ruby%\")\n).to_sql\n#=> SELECT \"profiles\".* \n#   FROM \"profiles\" \n#   WHERE (\"profiles\".\"comment\" ILIKE '%ruby%')\n```\n\nご覧のとおり、Arelを使うと文字列でSQLを書くことなく、Rubyのコードとしてクエリを書くことができます。\nなので、Arelは「SQLを書かなくて済む!」とか「Rubyのコードとして完結するので美しい!」といったメリットを強調されることが多いです。\n\nですが、個人的には「Arelは(やんごとなき理由がない限り)使わない方が良い」、と僕は考えています。\nその理由を以下に5つ挙げていきます。\n\n### 2016.8.12追記:Rails 5.1ではArelがパブリックAPIになるかも?\nRailsコミッタのSean Griffin([@sgrif](https://twitter.com/sgrif))さんいわく、ArelはRails 5.1でパブリックAPIになるそうです。\n\n情報源はこちらのポッドキャストで、5分~7分あたりでArel関連の話が展開されています。\n\n[74: A Dip in the Connection Pool \\| The Bike Shed](http://bikeshed.fm/74)\n\n英語が完全に聞き取れなかったので、本当にそうなのか質問してみたところ、\"Yes, it will be stable after 5.1\"との回答をもらいました。\n\n<blockquote class=\"twitter-tweet\" data-lang=\"en\"><p lang=\"en\" dir=\"ltr\"><a href=\"https://twitter.com/jnchito\">@jnchito</a> Yes, it will be stable after 5.1</p>&mdash; Sean Griffin (@sgrif) <a href=\"https://twitter.com/sgrif/status/763830491204026368\">August 11, 2016</a></blockquote>\n<script async src=\"//platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>\n\nというわけで、Rails 5.1以降はArelとの付き合い方が変わる可能性があります。\n以下の内容はRails 5.0までの情報として読んでください。\n\n## 理由その1: ArelはRailsのプライベートAPIだから\n\nArelはRailsのプライベートAPIという位置づけです。\nそのため、「サポートしないよ」「使うべきじゃないよ」というのがRails開発チームのスタンスです。\n\nhttps://github.com/rails/arel/issues/368#issuecomment-145351968\n\n> The use of Arel from Active Record is not supported. It is an internal, private API.\n>\n> Active RecordからArelを使うのはサポートされていません。Arelは内部用のプライベートAPIです。\n\nhttps://github.com/rails/rails/issues/16978#issuecomment-56211310\n> First thing, arel_table is private API of Rails and should not be used in applications.\n>\n> 始めに断っておきますが、arel_tableはRailsのプライベートAPIです。アプリケーション内で使うべきではありません。\n\nRailsの公式ドキュメントであるRailsガイドを見ても、Arelに関する説明は出てきません。\n\n- [Ruby on Rails Guides](http://guides.rubyonrails.org/)\n- [Ruby on Rails ガイド:体系的に Rails を学ぼう](http://railsguides.jp/)\n\nまた、ArelのREADMEには次のように書いてあります。\n\nhttps://github.com/rails/arel\n\n> It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.\n>\n> Arelはフレームワークのフレームワークとして使われることを想定しています。つまり、Arelを使えば自分独自のORM(O/Rマッピングツール)を構築できます。Arelを使うことでデータベースの互換性やクエリ生成ロジックに悩まされることなく、革新的なオブジェクトとコレクションのモデリングを構築できるのです。\n\nこうした公式発言を読むと、Railsアプリケーション内でカジュアルに利用するのはNGだ、と見なすことができます。\n\n## 理由その2: バージョンアップで動かなくなることがよくあるから\n\nこれは理由その1の「プライベートAPIだから」という理由とほぼイコールなのですが、Arelを使ってちょっと凝ったクエリを構築すると、Railsのバージョンアップ時にエラーが出て動かなくなる場合があります。\n僕は何度かこの問題に遭遇して、そのたびに解決に時間を食っています。\n\nもちろん、Railsをバージョンアップすると今まで動いていたコードが動かなくなる、というケースはときどきありますが、パブリックなAPIであれば通常、変更履歴やアップグレードガイドにAPIの変更点や移行方法が提示されます。\nまた、完全に廃止する前に警告のログを出してくれることもよくあります。\n\nしかし、ArelはプライベートAPIなので、そういったヒントになる情報が上がってきません。\nそれまで動いてたコードが突然予期せずエラーになります。\nなので、ググって同じようなエラーに遭遇している人を探したり、コードを追っかけて原因と対策を調査したりする必要があります。\nArelでエラーが起きると、パブリックAPIで仕様変更があったときに比べて、解決までの時間がかかります。\n\n## 理由その3: 絶対Arelじゃなきゃダメ!というケースは滅多にないから\n\n僕は基本的にActive Recordのクエリインターフェース(`where(name: 'jnchito')`や`joins(:users)`等)か、文字列SQL(`where(\"comment ILIKE ?\", comment)`等)でクエリを構築しています。\n\nまた複雑な集計クエリは、SELECT文を全体を文字列SQLで書いて実行することもあります。\n\n長年RailsでいろいろなWebアプリケーションを作ってきましたが、僕の経験上、「ここはArelを使わざるを得ない」というケースに遭遇したことは一度もありません。\n\n「ArelでSQLを構築した方がSQLの構文エラー(テーブルの別名の衝突等)が起きにくい」とか「RDBMSを変更してもそのまま動く」といったメリットは\"理論上\"想像はできますが、実際に問題が起きたことはありません。\n\nまた、SQLを使う場合でも`created_at`や`name`のような複数のテーブルに存在しそうなカラムは、`\"comments.created_at > ?\"`のように、必ず「テーブル名.カラム名」で書くようにすれば大半のケースでは名前の衝突は発生しないはずです。\n\n仮にその問題が起きるケースがあったとしても、YAGNI(You ain't gonna need it、「たぶんそれ、いらへんで」の略)の精神で、問題が起きたときに対処すればいいと考えています。\n\n## 理由その4: Arelは読み書きに時間がかかるから\n\nArelとSQLの得手不得手をパターン分けすると、次のようになると思います。\n\n1. SQLもArelも両方苦手\n2. SQLは得意、Arelは苦手\n3. SQLは苦手、Arelは得意\n4. SQLもArelも両方得意\n\nこの中でArelを使った方が圧倒的に読み書きが速くなるのは3の「SQLは苦手、Arelは得意」のパターンのみです。\nが!そんな人っていったいどれくらいいるんでしょうか・・・。\nArelで複雑なクエリを書こうと思ったら、SQLの知識が不可欠になると思うので、現実的にパターン3の人は滅多にいないと思います。\n\nまた、4の「SQLもArelも両方得意」という人でも、おそらく「SQLを考えてから、Arelでコードを書く」という手順を踏むと思います。\nであれば、「Arelで書かなければいけない特別な理由」がない限り、SQLで書いた方が速いはずです。\n\nコードを読む場合も同様で、SQLよりもArelを読む方が圧倒的に速い、というケースは滅多にないと思います。\n\n### 実際にSQLとArelのコードを比較してみる\n\n参考までに、ちょっと複雑なクエリをSQLとArelで書いてみましょう。\n\nたとえば「プロフィールに好きな食べ物と嫌いな食べ物を登録できるRailsアプリケーション」があったとします。\nモデルは次のようになっています。\n\n```ruby\n# プロフィール\nclass Profile < ActiveRecord::Base\n  has_many :food_likings\n  has_many :food_dislikings\nend\n\n# 食べ物\nclass Food < ActiveRecord::Base\nend\n\n# 好きな食べ物\nclass FoodLiking < ActiveRecord::Base\n  belongs_to :profile\n  belongs_to :food\nend\n\n# 嫌いな食べ物\nclass FoodDisliking < ActiveRecord::Base\n  belongs_to :profile\n  belongs_to :food\nend\n```\n\nER図で表すとこんな感じです。\n\n![Kobito.qLHZaw.png](https://qiita-image-store.s3.amazonaws.com/0/7465/074415a8-baa8-f6b5-bbef-c6d5c5439c93.png \"Kobito.qLHZaw.png\")\n\nここに次のようなデータが入っています。\n(Liking、Dislikingは関連するデータをカンマ区切りで表示しています)\n\n| Name  | Comment              | Liking              | Disliking   |\n|-------|----------------------|---------------------|-------------|\n| Alice | Hi.                  | tomato, tomato-soup | onion       |\n| Bob   | Hello.               | onion               | tomato      |\n| Chris | Tomato is beautiful. | tomato-soup         |             |\n| Dave  | Sleepy...            | onion               | tomato-soup |\n\n### 問題\n\nここから「コメント、好きな食べ物、嫌いな食べ物のいずれかに 'tomato' が含まれるプロフィール」を検索するにはどうすればいいでしょうか?\nなお、検索実行時は以下の条件も加えることにします。\n\n- 大文字小文字の違いは無視する\n- 食べ物は完全一致(tomatoは良いがtomato-soupはダメ)、コメントは部分一致で検索する\n\nつまり、ここではDaveだけが除外され、AliceとBobとChrisが抽出されればOK、ということになります。\n(ピンと来ない人はじっくり問題とデータを見てください)\n\n### SQLで抽出する場合\n\n僕であれば次のようなscopeを定義します。\n\n```ruby\nclass Profile < ActiveRecord::Base\n  scope :keyword_search, ->(keyword) do\n    where(<<-SQL, food_name: keyword.downcase, comment: \"%#{keyword}%\")\n-- 好きな食べ物にキーワードが一致する場合\n(\n  EXISTS (\n    SELECT *\n    FROM food_likings fl\n    INNER JOIN foods f\n    ON f.id = fl.food_id\n    WHERE\n    profiles.id = fl.profile_id\n    AND LOWER(f.name) = :food_name\n  )\n) OR\n-- 嫌いな食べ物にキーワードが一致する場合\n(\n  EXISTS (\n    SELECT *\n    FROM food_dislikings fd\n    INNER JOIN foods f\n    ON f.id = fd.food_id\n    WHERE\n    profiles.id = fd.profile_id\n    AND LOWER(f.name) = :food_name\n  )\n) OR\n-- プロフィールコメントにキーワードが含まれる場合\nprofiles.comment ILIKE :comment\nSQL\n  end\nend\n```\n\nSQLが苦手な人が見ると「???」かもしれませんが、SQLが得意な人であれば「ふむふむ・・・ああ、なるほど」となると思います。\n\n### Arelで抽出する場合\n\n一方、上と同じ条件をArelで構築してみました。\n\n```ruby\nclass Profile < ActiveRecord::Base\n  scope :keyword_search, -> (keyword) do\n    profiles = arel_table\n    foods = Food.arel_table\n    food_likings = FoodLiking.arel_table\n    food_dislikings = FoodDisliking.arel_table\n\n    lower_name = Arel::Nodes::NamedFunction.new(\"LOWER\", [foods[:name]])\n\n    food_likings_condition =\n        food_likings\n            .project(Arel.star)\n            .join(foods)\n            .on(foods[:id].eq(food_likings[:food_id]))\n            .where(profiles[:id].eq(food_likings[:profile_id]))\n            .where(lower_name.eq(keyword.downcase))\n            .exists\n\n    food_dislikings_condition =\n        food_dislikings\n            .project(Arel.star)\n            .join(foods)\n            .on(foods[:id].eq(food_dislikings[:food_id]))\n            .where(profiles[:id].eq(food_dislikings[:profile_id]))\n            .where(lower_name.eq(keyword.downcase))\n            .exists\n\n    comment_condition = profiles[:comment].matches(\"%#{keyword}%\")\n\n    where(food_likings_condition.or(food_dislikings_condition).or(comment_condition))\n  end\nend\n```\n\nいかがでしょう?\n「おお、やっぱりRubyで書くとわかりやすいですね!!」・・・となる人がどれくらいいるでしょうか??\n\nもちろん、わかりやすい/わかりにくい、は個人の主観によるので、「SQLは見たくない、Arelの方が落ち着く」という人もいるかもしれません。\nですが、僕は「素直にSQLで書けばいいものを、わざわざなぜ??」と思わざるを得ません。\n\nしかも、僕は普段Arelを使わない人なので、今回SQLと同等のArelの書き方を調べるのにかなりの時間を費やしてしまいました。\n「SQLを考える => Arelに翻訳する」という手間をわざわざかけなくても、「SQLでさっさと書いちゃえばいいじゃん」というのが、僕の感想です。\n\n## 理由その5: Arelを使っても生SQLは完全には排除できないから\n\nArelを使う理由が「生SQLを書きたくない」「RubyのコードにSQLを登場させたくない」という感情的・美意識的な理由なのであれば、それは捨てるべきじゃないかと思います。\n\nそもそもArelを使っても、完全に生SQLを排除することはできません。\n<s>たとえば、この記事を執筆している時点(2016年8月)ではArelはSQLのCASE WHENをサポートしていないようです。</s>\n(Arel 7.1からCASE WHENもサポートされているようです → [参考1](https://github.com/rails/arel/pull/400) / [参考2](https://github.com/rails/arel/blob/master/History.txt))\nなので、CASE WHENを使いたいときは生SQLが顔を出します。\n\n```ruby\n# https://github.com/rails/arel/blob/master/README.md\n\nphoto_clicks = Arel::Nodes::SqlLiteral.new(<<-SQL\n    CASE WHEN condition1 THEN calculation1\n    WHEN condition2 THEN calculation2\n    WHEN condition3 THEN calculation3\n    ELSE default_calculation END\nSQL\n)\n\nphotos.project(photo_clicks.as(\"photo_clicks\"))\n# => SELECT CASE WHEN condition1 THEN calculation1\n#    WHEN condition2 THEN calculation2\n#    WHEN condition3 THEN calculation3\n#    ELSE default_calculation END\n#    FROM \"photos\"\n```\n\nSQLは見た目によらず(?)非常に高度な機能を持っています。\nまた、Window関数やWITH句(Common Table Expression、共通テーブル式)など、RDBMSの方言として便利な機能も使えるようになっています。\n\nこのように、SQLは「SQLというひとつの言語」なので、それをRubyで完全にラップするというのは不可能だろうと僕は考えています。\n\nゆえに僕は「Active Recordのクエリインターフェースでカバーできる範囲はクエリインターフェースを使う」「クエリインターフェースの範疇を超えてしまう場合はクエリは生SQLを書く」というルールでクエリを書くようにしています。\n\n## まとめ:ArelはRailsの裏メニュー?\n\nというわけで、今回はArelでクエリを書くのをやめた方が良い5つの理由を書きました。\n最大の理由は最初の「プライベートAPIだから」で終わっていて、残りの4つは蛇足みたいなものです。\n\nArelはRailsの裏メニューだと僕は考えています。\n焼き肉屋さんでたとえると、馴染みの大将に「生レバーある?こっそり出してよ」と頼むようなものです。(こっちは法律違反ですが)\n\n使わずに済むなら使わない、使うなら自己責任で使う、あまりカジュアルにArelの使い方を紹介しない(=初心者をArelに誘導しない)、というのがArelとの望ましい付き合い方じゃないかな、と僕は思います。\n\nこれまで気軽にArelでクエリを書いていた人は、(特殊で例外的なケースを除いて)脱Arelを検討していきましょう。\n\n### 捕捉:Squeelはどうなの?\n\n生SQLは書きたくない、でもActive Recordのクエリインターフェースじゃ機能が足りない、という場合にときどき登場するのがSqueelというgemです。\nこのgemはActive Recordよりも高度なクエリインターフェースを提供してくれます。\n\nhttps://github.com/activerecord-hackery/squeel\n\nたとえば、冒頭で紹介したLIKEを使うSQLは次のように書くことができます。\n\n```ruby\nProfile.where { comment =~ '%ruby%' }.to_sql\n#=> SELECT \"profiles\".* \n#   FROM \"profiles\" \n#   WHERE \"profiles\".\"comment\" ILIKE '%ruby%'\n```\n\nただし、このgemは最近開発が停滞しており、GitHubを見ると最終のcommitが2015年2月になっています。(2016年8月10日時点)\n\n![Kobito.ALQ1e2.png](https://qiita-image-store.s3.amazonaws.com/0/7465/9c1b7e88-2b91-5c36-1002-0a09f77297cc.png \"Kobito.ALQ1e2.png\")\n\nRails 5の対応もあまり進展がないようです。\n\nhttps://github.com/activerecord-hackery/squeel/issues/412\n\nポピュラーなgemなのでそのうちRails 5対応がされるかもしれませんが、徐々に衰退しそうな気配があるので、新たに導入するのは避けた方がよい気がします。\n\n",
    "body": "\n<h2>\n<span id=\"はじめにarelって何\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%ABarel%E3%81%A3%E3%81%A6%E4%BD%95\"><i class=\"fa fa-link\"></i></a>はじめに:Arelって何?</h2>\n\n<p>みなさん、Arel(アレル)ってご存知ですか?<br>\nArelはActive Recordの内部で使用されるSQL生成ライブラリです。</p>\n\n<p><a href=\"https://github.com/rails/arel\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/rails/arel</a></p>\n\n<p>Railsのクエリの書き方をググると、ときどきArelを使った実装例が見つかるので、見たことがある、もしくは何度か使ったことがある、という人もいると思います。</p>\n\n<p>Arelをよく知らない人のために、Arelの利用例をちょっと見てみましょう。<br>\nたとえば「コメント文中に、\"ruby\"が含まれるユーザープロフィールを検索したい」という場合、Rails標準のクエリインターフェースを使うと条件部分のSQLを文字列で書く必要があります。(PostgreSQL環境を想定)</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"no\">Profile</span><span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span>\n  <span class=\"s2\">\"profiles.comment ILIKE ?\"</span><span class=\"p\">,</span> <span class=\"s2\">\"%ruby%\"</span>\n<span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to_sql</span>\n<span class=\"c1\">#=&gt; SELECT \"profiles\".* </span>\n<span class=\"c1\">#   FROM \"profiles\" </span>\n<span class=\"c1\">#   WHERE (profiles.comment ILIKE '%ruby%')</span>\n</pre></div></div>\n\n<p>しかし、Arelを使うと上のクエリが次のように書けます。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"no\">Profile</span><span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span>\n  <span class=\"no\">Profile</span><span class=\"o\">.</span><span class=\"n\">arel_table</span><span class=\"o\">[</span><span class=\"ss\">:comment</span><span class=\"o\">].</span><span class=\"n\">matches</span><span class=\"p\">(</span><span class=\"s2\">\"%ruby%\"</span><span class=\"p\">)</span>\n<span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">to_sql</span>\n<span class=\"c1\">#=&gt; SELECT \"profiles\".* </span>\n<span class=\"c1\">#   FROM \"profiles\" </span>\n<span class=\"c1\">#   WHERE (\"profiles\".\"comment\" ILIKE '%ruby%')</span>\n</pre></div></div>\n\n<p>ご覧のとおり、Arelを使うと文字列でSQLを書くことなく、Rubyのコードとしてクエリを書くことができます。<br>\nなので、Arelは「SQLを書かなくて済む!」とか「Rubyのコードとして完結するので美しい!」といったメリットを強調されることが多いです。</p>\n\n<p>ですが、個人的には「Arelは(やんごとなき理由がない限り)使わない方が良い」、と僕は考えています。<br>\nその理由を以下に5つ挙げていきます。</p>\n\n<h3>\n<span id=\"2016812追記rails-51ではarelがパブリックapiになるかも\" class=\"fragment\"></span><a href=\"#2016812%E8%BF%BD%E8%A8%98rails-51%E3%81%A7%E3%81%AFarel%E3%81%8C%E3%83%91%E3%83%96%E3%83%AA%E3%83%83%E3%82%AFapi%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%8B%E3%82%82\"><i class=\"fa fa-link\"></i></a>2016.8.12追記:Rails 5.1ではArelがパブリックAPIになるかも?</h3>\n\n<p>RailsコミッタのSean Griffin(<a href=\"https://twitter.com/sgrif\" rel=\"nofollow\" target=\"_blank\">@sgrif</a>)さんいわく、ArelはRails 5.1でパブリックAPIになるそうです。</p>\n\n<p>情報源はこちらのポッドキャストで、5分~7分あたりでArel関連の話が展開されています。</p>\n\n<p><a href=\"http://bikeshed.fm/74\" rel=\"nofollow\" target=\"_blank\">74: A Dip in the Connection Pool | The Bike Shed</a></p>\n\n<p>英語が完全に聞き取れなかったので、本当にそうなのか質問してみたところ、\"Yes, it will be stable after 5.1\"との回答をもらいました。</p>\n\n<blockquote class=\"twitter-tweet\" data-lang=\"en\">\n<p lang=\"en\"><a href=\"https://twitter.com/jnchito\" rel=\"nofollow\" target=\"_blank\">@jnchito</a> Yes, it will be stable after 5.1</p>— Sean Griffin (@sgrif) <a href=\"https://twitter.com/sgrif/status/763830491204026368\" rel=\"nofollow\" target=\"_blank\">August 11, 2016</a>\n</blockquote>\n\n\n\n<p>というわけで、Rails 5.1以降はArelとの付き合い方が変わる可能性があります。<br>\n以下の内容はRails 5.0までの情報として読んでください。</p>\n\n<h2>\n<span id=\"理由その1-arelはrailsのプライベートapiだから\" class=\"fragment\"></span><a href=\"#%E7%90%86%E7%94%B1%E3%81%9D%E3%81%AE1-arel%E3%81%AFrails%E3%81%AE%E3%83%97%E3%83%A9%E3%82%A4%E3%83%99%E3%83%BC%E3%83%88api%E3%81%A0%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>理由その1: ArelはRailsのプライベートAPIだから</h2>\n\n<p>ArelはRailsのプライベートAPIという位置づけです。<br>\nそのため、「サポートしないよ」「使うべきじゃないよ」というのがRails開発チームのスタンスです。</p>\n\n<p><a href=\"https://github.com/rails/arel/issues/368#issuecomment-145351968\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/rails/arel/issues/368#issuecomment-145351968</a></p>\n\n<blockquote>\n<p>The use of Arel from Active Record is not supported. It is an internal, private API.</p>\n\n<p>Active RecordからArelを使うのはサポートされていません。Arelは内部用のプライベートAPIです。</p>\n</blockquote>\n\n<p><a href=\"https://github.com/rails/rails/issues/16978#issuecomment-56211310\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/rails/rails/issues/16978#issuecomment-56211310</a></p>\n\n<blockquote>\n<p>First thing, arel_table is private API of Rails and should not be used in applications.</p>\n\n<p>始めに断っておきますが、arel_tableはRailsのプライベートAPIです。アプリケーション内で使うべきではありません。</p>\n</blockquote>\n\n<p>Railsの公式ドキュメントであるRailsガイドを見ても、Arelに関する説明は出てきません。</p>\n\n<ul>\n<li><a href=\"http://guides.rubyonrails.org/\" rel=\"nofollow\" target=\"_blank\">Ruby on Rails Guides</a></li>\n<li><a href=\"http://railsguides.jp/\" rel=\"nofollow\" target=\"_blank\">Ruby on Rails ガイド:体系的に Rails を学ぼう</a></li>\n</ul>\n\n<p>また、ArelのREADMEには次のように書いてあります。</p>\n\n<p><a href=\"https://github.com/rails/arel\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/rails/arel</a></p>\n\n<blockquote>\n<p>It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.</p>\n\n<p>Arelはフレームワークのフレームワークとして使われることを想定しています。つまり、Arelを使えば自分独自のORM(O/Rマッピングツール)を構築できます。Arelを使うことでデータベースの互換性やクエリ生成ロジックに悩まされることなく、革新的なオブジェクトとコレクションのモデリングを構築できるのです。</p>\n</blockquote>\n\n<p>こうした公式発言を読むと、Railsアプリケーション内でカジュアルに利用するのはNGだ、と見なすことができます。</p>\n\n<h2>\n<span id=\"理由その2-バージョンアップで動かなくなることがよくあるから\" class=\"fragment\"></span><a href=\"#%E7%90%86%E7%94%B1%E3%81%9D%E3%81%AE2-%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%83%E3%83%97%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%AA%E3%81%8F%E3%81%AA%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%82%88%E3%81%8F%E3%81%82%E3%82%8B%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>理由その2: バージョンアップで動かなくなることがよくあるから</h2>\n\n<p>これは理由その1の「プライベートAPIだから」という理由とほぼイコールなのですが、Arelを使ってちょっと凝ったクエリを構築すると、Railsのバージョンアップ時にエラーが出て動かなくなる場合があります。<br>\n僕は何度かこの問題に遭遇して、そのたびに解決に時間を食っています。</p>\n\n<p>もちろん、Railsをバージョンアップすると今まで動いていたコードが動かなくなる、というケースはときどきありますが、パブリックなAPIであれば通常、変更履歴やアップグレードガイドにAPIの変更点や移行方法が提示されます。<br>\nまた、完全に廃止する前に警告のログを出してくれることもよくあります。</p>\n\n<p>しかし、ArelはプライベートAPIなので、そういったヒントになる情報が上がってきません。<br>\nそれまで動いてたコードが突然予期せずエラーになります。<br>\nなので、ググって同じようなエラーに遭遇している人を探したり、コードを追っかけて原因と対策を調査したりする必要があります。<br>\nArelでエラーが起きると、パブリックAPIで仕様変更があったときに比べて、解決までの時間がかかります。</p>\n\n<h2>\n<span id=\"理由その3-絶対arelじゃなきゃダメというケースは滅多にないから\" class=\"fragment\"></span><a href=\"#%E7%90%86%E7%94%B1%E3%81%9D%E3%81%AE3-%E7%B5%B6%E5%AF%BEarel%E3%81%98%E3%82%83%E3%81%AA%E3%81%8D%E3%82%83%E3%83%80%E3%83%A1%E3%81%A8%E3%81%84%E3%81%86%E3%82%B1%E3%83%BC%E3%82%B9%E3%81%AF%E6%BB%85%E5%A4%9A%E3%81%AB%E3%81%AA%E3%81%84%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>理由その3: 絶対Arelじゃなきゃダメ!というケースは滅多にないから</h2>\n\n<p>僕は基本的にActive Recordのクエリインターフェース(<code>where(name: 'jnchito')</code>や<code>joins(:users)</code>等)か、文字列SQL(<code>where(\"comment ILIKE ?\", comment)</code>等)でクエリを構築しています。</p>\n\n<p>また複雑な集計クエリは、SELECT文を全体を文字列SQLで書いて実行することもあります。</p>\n\n<p>長年RailsでいろいろなWebアプリケーションを作ってきましたが、僕の経験上、「ここはArelを使わざるを得ない」というケースに遭遇したことは一度もありません。</p>\n\n<p>「ArelでSQLを構築した方がSQLの構文エラー(テーブルの別名の衝突等)が起きにくい」とか「RDBMSを変更してもそのまま動く」といったメリットは\"理論上\"想像はできますが、実際に問題が起きたことはありません。</p>\n\n<p>また、SQLを使う場合でも<code>created_at</code>や<code>name</code>のような複数のテーブルに存在しそうなカラムは、<code>\"comments.created_at &gt; ?\"</code>のように、必ず「テーブル名.カラム名」で書くようにすれば大半のケースでは名前の衝突は発生しないはずです。</p>\n\n<p>仮にその問題が起きるケースがあったとしても、YAGNI(You ain't gonna need it、「たぶんそれ、いらへんで」の略)の精神で、問題が起きたときに対処すればいいと考えています。</p>\n\n<h2>\n<span id=\"理由その4-arelは読み書きに時間がかかるから\" class=\"fragment\"></span><a href=\"#%E7%90%86%E7%94%B1%E3%81%9D%E3%81%AE4-arel%E3%81%AF%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%81%AB%E6%99%82%E9%96%93%E3%81%8C%E3%81%8B%E3%81%8B%E3%82%8B%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>理由その4: Arelは読み書きに時間がかかるから</h2>\n\n<p>ArelとSQLの得手不得手をパターン分けすると、次のようになると思います。</p>\n\n<ol>\n<li>SQLもArelも両方苦手</li>\n<li>SQLは得意、Arelは苦手</li>\n<li>SQLは苦手、Arelは得意</li>\n<li>SQLもArelも両方得意</li>\n</ol>\n\n<p>この中でArelを使った方が圧倒的に読み書きが速くなるのは3の「SQLは苦手、Arelは得意」のパターンのみです。<br>\nが!そんな人っていったいどれくらいいるんでしょうか・・・。<br>\nArelで複雑なクエリを書こうと思ったら、SQLの知識が不可欠になると思うので、現実的にパターン3の人は滅多にいないと思います。</p>\n\n<p>また、4の「SQLもArelも両方得意」という人でも、おそらく「SQLを考えてから、Arelでコードを書く」という手順を踏むと思います。<br>\nであれば、「Arelで書かなければいけない特別な理由」がない限り、SQLで書いた方が速いはずです。</p>\n\n<p>コードを読む場合も同様で、SQLよりもArelを読む方が圧倒的に速い、というケースは滅多にないと思います。</p>\n\n<h3>\n<span id=\"実際にsqlとarelのコードを比較してみる\" class=\"fragment\"></span><a href=\"#%E5%AE%9F%E9%9A%9B%E3%81%ABsql%E3%81%A8arel%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%AF%94%E8%BC%83%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B\"><i class=\"fa fa-link\"></i></a>実際にSQLとArelのコードを比較してみる</h3>\n\n<p>参考までに、ちょっと複雑なクエリをSQLとArelで書いてみましょう。</p>\n\n<p>たとえば「プロフィールに好きな食べ物と嫌いな食べ物を登録できるRailsアプリケーション」があったとします。<br>\nモデルは次のようになっています。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"c1\"># プロフィール</span>\n<span class=\"k\">class</span> <span class=\"nc\">Profile</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">has_many</span> <span class=\"ss\">:food_likings</span>\n  <span class=\"n\">has_many</span> <span class=\"ss\">:food_dislikings</span>\n<span class=\"k\">end</span>\n\n<span class=\"c1\"># 食べ物</span>\n<span class=\"k\">class</span> <span class=\"nc\">Food</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n<span class=\"k\">end</span>\n\n<span class=\"c1\"># 好きな食べ物</span>\n<span class=\"k\">class</span> <span class=\"nc\">FoodLiking</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">belongs_to</span> <span class=\"ss\">:profile</span>\n  <span class=\"n\">belongs_to</span> <span class=\"ss\">:food</span>\n<span class=\"k\">end</span>\n\n<span class=\"c1\"># 嫌いな食べ物</span>\n<span class=\"k\">class</span> <span class=\"nc\">FoodDisliking</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">belongs_to</span> <span class=\"ss\">:profile</span>\n  <span class=\"n\">belongs_to</span> <span class=\"ss\">:food</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>ER図で表すとこんな感じです。</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/074415a8-baa8-f6b5-bbef-c6d5c5439c93.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/074415a8-baa8-f6b5-bbef-c6d5c5439c93.png\" alt=\"Kobito.qLHZaw.png\" title=\"Kobito.qLHZaw.png\"></a></p>\n\n<p>ここに次のようなデータが入っています。<br>\n(Liking、Dislikingは関連するデータをカンマ区切りで表示しています)</p>\n\n<table>\n<thead>\n<tr>\n<th>Name</th>\n<th>Comment</th>\n<th>Liking</th>\n<th>Disliking</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Alice</td>\n<td>Hi.</td>\n<td>tomato, tomato-soup</td>\n<td>onion</td>\n</tr>\n<tr>\n<td>Bob</td>\n<td>Hello.</td>\n<td>onion</td>\n<td>tomato</td>\n</tr>\n<tr>\n<td>Chris</td>\n<td>Tomato is beautiful.</td>\n<td>tomato-soup</td>\n<td></td>\n</tr>\n<tr>\n<td>Dave</td>\n<td>Sleepy...</td>\n<td>onion</td>\n<td>tomato-soup</td>\n</tr>\n</tbody>\n</table>\n\n<h3>\n<span id=\"問題\" class=\"fragment\"></span><a href=\"#%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>問題</h3>\n\n<p>ここから「コメント、好きな食べ物、嫌いな食べ物のいずれかに 'tomato' が含まれるプロフィール」を検索するにはどうすればいいでしょうか?<br>\nなお、検索実行時は以下の条件も加えることにします。</p>\n\n<ul>\n<li>大文字小文字の違いは無視する</li>\n<li>食べ物は完全一致(tomatoは良いがtomato-soupはダメ)、コメントは部分一致で検索する</li>\n</ul>\n\n<p>つまり、ここではDaveだけが除外され、AliceとBobとChrisが抽出されればOK、ということになります。<br>\n(ピンと来ない人はじっくり問題とデータを見てください)</p>\n\n<h3>\n<span id=\"sqlで抽出する場合\" class=\"fragment\"></span><a href=\"#sql%E3%81%A7%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>SQLで抽出する場合</h3>\n\n<p>僕であれば次のようなscopeを定義します。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">Profile</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">scope</span> <span class=\"ss\">:keyword_search</span><span class=\"p\">,</span> <span class=\"o\">-&gt;</span><span class=\"p\">(</span><span class=\"n\">keyword</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"n\">where</span><span class=\"p\">(</span><span class=\"o\">&lt;&lt;-</span><span class=\"no\">SQL</span><span class=\"p\">,</span> <span class=\"ss\">food_name</span><span class=\"p\">:</span> <span class=\"n\">keyword</span><span class=\"o\">.</span><span class=\"n\">downcase</span><span class=\"p\">,</span> <span class=\"ss\">comment</span><span class=\"p\">:</span> <span class=\"s2\">\"%</span><span class=\"si\">#{</span><span class=\"n\">keyword</span><span class=\"si\">}</span><span class=\"s2\">%\"</span><span class=\"p\">)</span>\n<span class=\"sh\">-- 好きな食べ物にキーワードが一致する場合</span>\n<span class=\"sh\">(</span>\n<span class=\"sh\">  EXISTS (</span>\n<span class=\"sh\">    SELECT *</span>\n<span class=\"sh\">    FROM food_likings fl</span>\n<span class=\"sh\">    INNER JOIN foods f</span>\n<span class=\"sh\">    ON f.id = fl.food_id</span>\n<span class=\"sh\">    WHERE</span>\n<span class=\"sh\">    profiles.id = fl.profile_id</span>\n<span class=\"sh\">    AND LOWER(f.name) = :food_name</span>\n<span class=\"sh\">  )</span>\n<span class=\"sh\">) OR</span>\n<span class=\"sh\">-- 嫌いな食べ物にキーワードが一致する場合</span>\n<span class=\"sh\">(</span>\n<span class=\"sh\">  EXISTS (</span>\n<span class=\"sh\">    SELECT *</span>\n<span class=\"sh\">    FROM food_dislikings fd</span>\n<span class=\"sh\">    INNER JOIN foods f</span>\n<span class=\"sh\">    ON f.id = fd.food_id</span>\n<span class=\"sh\">    WHERE</span>\n<span class=\"sh\">    profiles.id = fd.profile_id</span>\n<span class=\"sh\">    AND LOWER(f.name) = :food_name</span>\n<span class=\"sh\">  )</span>\n<span class=\"sh\">) OR</span>\n<span class=\"sh\">-- プロフィールコメントにキーワードが含まれる場合</span>\n<span class=\"sh\">profiles.comment ILIKE :comment</span>\n<span class=\"no\">SQL</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>SQLが苦手な人が見ると「???」かもしれませんが、SQLが得意な人であれば「ふむふむ・・・ああ、なるほど」となると思います。</p>\n\n<h3>\n<span id=\"arelで抽出する場合\" class=\"fragment\"></span><a href=\"#arel%E3%81%A7%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>Arelで抽出する場合</h3>\n\n<p>一方、上と同じ条件をArelで構築してみました。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">Profile</span> <span class=\"o\">&lt;</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">scope</span> <span class=\"ss\">:keyword_search</span><span class=\"p\">,</span> <span class=\"o\">-&gt;</span> <span class=\"p\">(</span><span class=\"n\">keyword</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"n\">profiles</span> <span class=\"o\">=</span> <span class=\"n\">arel_table</span>\n    <span class=\"n\">foods</span> <span class=\"o\">=</span> <span class=\"no\">Food</span><span class=\"o\">.</span><span class=\"n\">arel_table</span>\n    <span class=\"n\">food_likings</span> <span class=\"o\">=</span> <span class=\"no\">FoodLiking</span><span class=\"o\">.</span><span class=\"n\">arel_table</span>\n    <span class=\"n\">food_dislikings</span> <span class=\"o\">=</span> <span class=\"no\">FoodDisliking</span><span class=\"o\">.</span><span class=\"n\">arel_table</span>\n\n    <span class=\"n\">lower_name</span> <span class=\"o\">=</span> <span class=\"no\">Arel</span><span class=\"o\">::</span><span class=\"no\">Nodes</span><span class=\"o\">::</span><span class=\"no\">NamedFunction</span><span class=\"o\">.</span><span class=\"n\">new</span><span class=\"p\">(</span><span class=\"s2\">\"LOWER\"</span><span class=\"p\">,</span> <span class=\"o\">[</span><span class=\"n\">foods</span><span class=\"o\">[</span><span class=\"ss\">:name</span><span class=\"o\">]]</span><span class=\"p\">)</span>\n\n    <span class=\"n\">food_likings_condition</span> <span class=\"o\">=</span>\n        <span class=\"n\">food_likings</span>\n            <span class=\"o\">.</span><span class=\"n\">project</span><span class=\"p\">(</span><span class=\"no\">Arel</span><span class=\"o\">.</span><span class=\"n\">star</span><span class=\"p\">)</span>\n            <span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">foods</span><span class=\"p\">)</span>\n            <span class=\"o\">.</span><span class=\"n\">on</span><span class=\"p\">(</span><span class=\"n\">foods</span><span class=\"o\">[</span><span class=\"ss\">:id</span><span class=\"o\">].</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">food_likings</span><span class=\"o\">[</span><span class=\"ss\">:food_id</span><span class=\"o\">]</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span><span class=\"n\">profiles</span><span class=\"o\">[</span><span class=\"ss\">:id</span><span class=\"o\">].</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">food_likings</span><span class=\"o\">[</span><span class=\"ss\">:profile_id</span><span class=\"o\">]</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span><span class=\"n\">lower_name</span><span class=\"o\">.</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">keyword</span><span class=\"o\">.</span><span class=\"n\">downcase</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">exists</span>\n\n    <span class=\"n\">food_dislikings_condition</span> <span class=\"o\">=</span>\n        <span class=\"n\">food_dislikings</span>\n            <span class=\"o\">.</span><span class=\"n\">project</span><span class=\"p\">(</span><span class=\"no\">Arel</span><span class=\"o\">.</span><span class=\"n\">star</span><span class=\"p\">)</span>\n            <span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">foods</span><span class=\"p\">)</span>\n            <span class=\"o\">.</span><span class=\"n\">on</span><span class=\"p\">(</span><span class=\"n\">foods</span><span class=\"o\">[</span><span class=\"ss\">:id</span><span class=\"o\">].</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">food_dislikings</span><span class=\"o\">[</span><span class=\"ss\">:food_id</span><span class=\"o\">]</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span><span class=\"n\">profiles</span><span class=\"o\">[</span><span class=\"ss\">:id</span><span class=\"o\">].</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">food_dislikings</span><span class=\"o\">[</span><span class=\"ss\">:profile_id</span><span class=\"o\">]</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">where</span><span class=\"p\">(</span><span class=\"n\">lower_name</span><span class=\"o\">.</span><span class=\"n\">eq</span><span class=\"p\">(</span><span class=\"n\">keyword</span><span class=\"o\">.</span><span class=\"n\">downcase</span><span class=\"p\">))</span>\n            <span class=\"o\">.</span><span class=\"n\">exists</span>\n\n    <span class=\"n\">comment_condition</span> <span class=\"o\">=</span> <span class=\"n\">profiles</span><span class=\"o\">[</span><span class=\"ss\">:comment</span><span class=\"o\">].</span><span class=\"n\">matches</span><span class=\"p\">(</span><span class=\"s2\">\"%</span><span class=\"si\">#{</span><span class=\"n\">keyword</span><span class=\"si\">}</span><span class=\"s2\">%\"</span><span class=\"p\">)</span>\n\n    <span class=\"n\">where</span><span class=\"p\">(</span><span class=\"n\">food_likings_condition</span><span class=\"o\">.</span><span class=\"n\">or</span><span class=\"p\">(</span><span class=\"n\">food_dislikings_condition</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">or</span><span class=\"p\">(</span><span class=\"n\">comment_condition</span><span class=\"p\">))</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>いかがでしょう?<br>\n「おお、やっぱりRubyで書くとわかりやすいですね!!」・・・となる人がどれくらいいるでしょうか??</p>\n\n<p>もちろん、わかりやすい/わかりにくい、は個人の主観によるので、「SQLは見たくない、Arelの方が落ち着く」という人もいるかもしれません。<br>\nですが、僕は「素直にSQLで書けばいいものを、わざわざなぜ??」と思わざるを得ません。</p>\n\n<p>しかも、僕は普段Arelを使わない人なので、今回SQLと同等のArelの書き方を調べるのにかなりの時間を費やしてしまいました。<br>\n「SQLを考える =&gt; Arelに翻訳する」という手間をわざわざかけなくても、「SQLでさっさと書いちゃえばいいじゃん」というのが、僕の感想です。</p>\n\n<h2>\n<span id=\"理由その5-arelを使っても生sqlは完全には排除できないから\" class=\"fragment\"></span><a href=\"#%E7%90%86%E7%94%B1%E3%81%9D%E3%81%AE5-arel%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%82%E7%94%9Fsql%E3%81%AF%E5%AE%8C%E5%85%A8%E3%81%AB%E3%81%AF%E6%8E%92%E9%99%A4%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>理由その5: Arelを使っても生SQLは完全には排除できないから</h2>\n\n<p>Arelを使う理由が「生SQLを書きたくない」「RubyのコードにSQLを登場させたくない」という感情的・美意識的な理由なのであれば、それは捨てるべきじゃないかと思います。</p>\n\n<p>そもそもArelを使っても、完全に生SQLを排除することはできません。<br>\n<s>たとえば、この記事を執筆している時点(2016年8月)ではArelはSQLのCASE WHENをサポートしていないようです。</s><br>\n(Arel 7.1からCASE WHENもサポートされているようです → <a href=\"https://github.com/rails/arel/pull/400\" rel=\"nofollow\" target=\"_blank\">参考1</a> / <a href=\"https://github.com/rails/arel/blob/master/History.txt\" rel=\"nofollow\" target=\"_blank\">参考2</a>)<br>\nなので、CASE WHENを使いたいときは生SQLが顔を出します。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"c1\"># https://github.com/rails/arel/blob/master/README.md</span>\n\n<span class=\"n\">photo_clicks</span> <span class=\"o\">=</span> <span class=\"no\">Arel</span><span class=\"o\">::</span><span class=\"no\">Nodes</span><span class=\"o\">::</span><span class=\"no\">SqlLiteral</span><span class=\"o\">.</span><span class=\"n\">new</span><span class=\"p\">(</span><span class=\"o\">&lt;&lt;-</span><span class=\"no\">SQL</span>\n<span class=\"sh\">    CASE WHEN condition1 THEN calculation1</span>\n<span class=\"sh\">    WHEN condition2 THEN calculation2</span>\n<span class=\"sh\">    WHEN condition3 THEN calculation3</span>\n<span class=\"sh\">    ELSE default_calculation END</span>\n<span class=\"no\">SQL</span>\n<span class=\"p\">)</span>\n\n<span class=\"n\">photos</span><span class=\"o\">.</span><span class=\"n\">project</span><span class=\"p\">(</span><span class=\"n\">photo_clicks</span><span class=\"o\">.</span><span class=\"n\">as</span><span class=\"p\">(</span><span class=\"s2\">\"photo_clicks\"</span><span class=\"p\">))</span>\n<span class=\"c1\"># =&gt; SELECT CASE WHEN condition1 THEN calculation1</span>\n<span class=\"c1\">#    WHEN condition2 THEN calculation2</span>\n<span class=\"c1\">#    WHEN condition3 THEN calculation3</span>\n<span class=\"c1\">#    ELSE default_calculation END</span>\n<span class=\"c1\">#    FROM \"photos\"</span>\n</pre></div></div>\n\n<p>SQLは見た目によらず(?)非常に高度な機能を持っています。<br>\nまた、Window関数やWITH句(Common Table Expression、共通テーブル式)など、RDBMSの方言として便利な機能も使えるようになっています。</p>\n\n<p>このように、SQLは「SQLというひとつの言語」なので、それをRubyで完全にラップするというのは不可能だろうと僕は考えています。</p>\n\n<p>ゆえに僕は「Active Recordのクエリインターフェースでカバーできる範囲はクエリインターフェースを使う」「クエリインターフェースの範疇を超えてしまう場合はクエリは生SQLを書く」というルールでクエリを書くようにしています。</p>\n\n<h2>\n<span id=\"まとめarelはrailsの裏メニュー\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81arel%E3%81%AFrails%E3%81%AE%E8%A3%8F%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC\"><i class=\"fa fa-link\"></i></a>まとめ:ArelはRailsの裏メニュー?</h2>\n\n<p>というわけで、今回はArelでクエリを書くのをやめた方が良い5つの理由を書きました。<br>\n最大の理由は最初の「プライベートAPIだから」で終わっていて、残りの4つは蛇足みたいなものです。</p>\n\n<p>ArelはRailsの裏メニューだと僕は考えています。<br>\n焼き肉屋さんでたとえると、馴染みの大将に「生レバーある?こっそり出してよ」と頼むようなものです。(こっちは法律違反ですが)</p>\n\n<p>使わずに済むなら使わない、使うなら自己責任で使う、あまりカジュアルにArelの使い方を紹介しない(=初心者をArelに誘導しない)、というのがArelとの望ましい付き合い方じゃないかな、と僕は思います。</p>\n\n<p>これまで気軽にArelでクエリを書いていた人は、(特殊で例外的なケースを除いて)脱Arelを検討していきましょう。</p>\n\n<h3>\n<span id=\"捕捉squeelはどうなの\" class=\"fragment\"></span><a href=\"#%E6%8D%95%E6%8D%89squeel%E3%81%AF%E3%81%A9%E3%81%86%E3%81%AA%E3%81%AE\"><i class=\"fa fa-link\"></i></a>捕捉:Squeelはどうなの?</h3>\n\n<p>生SQLは書きたくない、でもActive Recordのクエリインターフェースじゃ機能が足りない、という場合にときどき登場するのがSqueelというgemです。<br>\nこのgemはActive Recordよりも高度なクエリインターフェースを提供してくれます。</p>\n\n<p><a href=\"https://github.com/activerecord-hackery/squeel\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/activerecord-hackery/squeel</a></p>\n\n<p>たとえば、冒頭で紹介したLIKEを使うSQLは次のように書くことができます。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"no\">Profile</span><span class=\"o\">.</span><span class=\"n\">where</span> <span class=\"p\">{</span> <span class=\"n\">comment</span> <span class=\"o\">=~</span> <span class=\"s1\">'%ruby%'</span> <span class=\"p\">}</span><span class=\"o\">.</span><span class=\"n\">to_sql</span>\n<span class=\"c1\">#=&gt; SELECT \"profiles\".* </span>\n<span class=\"c1\">#   FROM \"profiles\" </span>\n<span class=\"c1\">#   WHERE \"profiles\".\"comment\" ILIKE '%ruby%'</span>\n</pre></div></div>\n\n<p>ただし、このgemは最近開発が停滞しており、GitHubを見ると最終のcommitが2015年2月になっています。(2016年8月10日時点)</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/9c1b7e88-2b91-5c36-1002-0a09f77297cc.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/9c1b7e88-2b91-5c36-1002-0a09f77297cc.png\" alt=\"Kobito.ALQ1e2.png\" title=\"Kobito.ALQ1e2.png\"></a></p>\n\n<p>Rails 5の対応もあまり進展がないようです。</p>\n\n<p><a href=\"https://github.com/activerecord-hackery/squeel/issues/412\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/activerecord-hackery/squeel/issues/412</a></p>\n\n<p>ポピュラーなgemなのでそのうちRails 5対応がされるかもしれませんが、徐々に衰退しそうな気配があるので、新たに導入するのは避けた方がよい気がします。</p>\n",
    "stock_users": [
        "kysnm",
        "aki77",
        "cutmail",
        "ysk_1031",
        "tyru",
        "akicho8",
        "sutetotanuki",
        "ykominami",
        "ryohashimoto",
        "m4i",
        "oginohiroaki@github",
        "mrtsh@github",
        "dayone",
        "kasei-san",
        "toyamarinyon",
        "star__hoshi",
        "Noboruhi",
        "tchikuba",
        "QUANON",
        "inuscript",
        "mnuma",
        "kanpou_",
        "ogomr",
        "pasela",
        "yancya",
        "makotot",
        "tomato360",
        "misogi@github",
        "cheezenaan",
        "JunKikuchi",
        "K_Matsumoto",
        "rentalname@github",
        "takaya1992",
        "curseoff",
        "Oakbow",
        "suemoc",
        "mono0926",
        "youcune",
        "hachi8833",
        "knt45",
        "ryz310@github",
        "tikkss@github",
        "ToruIwashita",
        "tos-miyake",
        "chansuke",
        "shshimamo",
        "taichi_a",
        "kohei_uetan",
        "Shunta_Suzuki",
        "ytkr0813",
        "yuki3738",
        "mmts1007",
        "Neos21",
        "kenixi",
        "hatappi",
        "masoo",
        "turner8585",
        "highwide",
        "dnby",
        "magaya0403",
        "jkr_2255",
        "oharato",
        "nwebcraft",
        "takada-s",
        "3sho7mi8",
        "mt2",
        "nobuy",
        "Kenta-s",
        "YudaiTsukamoto",
        "mnishiguchi",
        "snona",
        "0x4ba01071",
        "YumaInaura",
        "hiromichinomata",
        "jmtwgj",
        "asmsuechan"
    ]
}, {
    "id": 413558,
    "uuid": "17bc6c123207be7e6636",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "QiitaとMarkdownとコンテンツオーサリング",
    "created_at": "2016-08-07 14:52:16 +0900",
    "updated_at": "2016-08-10 13:54:47 +0900",
    "created_at_in_words": "約1ヶ月",
    "updated_at_in_words": "29日",
    "tags": [{
        "name": "Markdown",
        "url_name": "markdown",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/a8f214db2d0f7fbe9024d621e40103135e8f133b/medium.jpg?1458609899",
        "following": false,
        "versions": []
    }],
    "stock_count": 69,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/17bc6c123207be7e6636",
    "created_at_as_seconds": 1470549136,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "## はじめに\n\n[SIGPX: Special Interest Group on Programming Experience](http://sigpx.org/ \"SIGPX: Special Interest Group on Programming Experience\") 第二回 (2016年8月7日) での発表資料\n\n------\n\n## 今日話す内容\n\n- Qiitaでのコンテンツオーサリング\n- Qiita の Markdown について、泥臭い感じで(アカデミックな会なので)\n- Markdownという切り口で、標準化、そのレンダリング、オーサリング、ASTなどについて\n\n------\n\n# Markdown の仕様\n\n------\n\n## Markdown\n\n- HTMLに変換されるマークアップ言語の実装。またはその仕様。\n- Github の躍進とともにメジャーに\n- 同種のマークアップ言語として textile, はてな記法など\n\n----\n\n## Markdownの起源\n\n- オリジナル実装は John Gruber の `markdown.pl` というPerl スクリプト(2004)\n    - [Markdown - Wikipedia, the free encyclopedia](https://en.wikipedia.org/wiki/Markdown#History \"Markdown - Wikipedia, the free encyclopedia\")\n- 電子メールにおいてプレーンテキストを装飾する際の慣習から着想を得ている\n    - `It’s a plain text format for writing structured documents, based on formatting conventions from email and usenet. ` http://commonmark.org/\n\n\n-----\n\n![](https://i.gyazo.com/491c14e6d4a11d32f1e4d5fa3620683a.png)\n\n------\n\n## なぜ Markdown がエンジニアに好まれるか\n\n- **プレーンテキストである**\n    - Git等のバージョン管理システムとシナジー\n    - 特殊なWYSIWYG を要求された時代への反動\n- **セマンティクスと装飾が分離されている**\n    - テキストエディタでそれなりに読める\n    - HTMLの出力だけが決まっていて、装飾に関与しない\n\n-----\n\n## 複数の Markdown 実装\n\n- 各言語による実装\n    - Perl: `Text::Markdown` [Text::Markdown - search.cpan.org](http://search.cpan.org/~bobtfish/Text-Markdown-1.000031/lib/Text/Markdown.pm \"Text::Markdown - search.cpan.org\")\n    - Ruby: `RedCarpet` (厳密にはC実装のバインディング) [vmg/redcarpet: The safe Markdown parser, reloaded.](https://github.com/vmg/redcarpet \"vmg/redcarpet: The safe Markdown parser, reloaded.\")\n    - etc...\n- Markdown Extra: テーブル記法などをサポート(PHP)\n- Github の Github Flavored Markdown 通称 GFM  (ソース未公開)\n- CommonMark(commonmark.org) による標準化とそのサンプル実装 (2012~)\n\n------\n\n## Github Flavored Markdown\n\n- もっとも知られている実装\n- 仕様的には Markdown Extra をさらに拡張\n- GFM != Markdown、とはいえ**事実上の標準**\n- 拡張\n    - コードブロック記法とシンタックスハイライト\n    - テーブル記法\n    - 打ち消し線\n    - 絵文字\n    - URL の自動リンク\n- 非互換\n    - 「`_foo_` のようなアンダースコアによる強調」のオミット\n    - 「段落の末尾にスペースがあると改行」のオミット\n\n-----\n\n## Markdownの抱える問題\n\n- 仕様策定が決まる前に各言語で実装が乱立したので、実装にブレが多い\n    - [Babelmark 2 - Compare markdown implementations](http://johnmacfarlane.net/babelmark2/?normalize=1&text=*%3C*%3E*++%0A+*_*_ \"Babelmark 2 - Compare markdown implementations\")\n- 異なる実装で同じMarkdownをコンパイルした場合、同じものが出力されることは稀\n    - GFMのように勝手に一部の仕様を削除していたり…\n    - 構文が入れ子になった状態が未定義だったり…\n\n------\n\n## これはどうなる?\n\n```\n- a\n    - b\n   - c\n  - d\n-e\n```\n\n- 改行後の文頭スペースの数の比較で、ネストの深さを決定する実装(markdown.pl)\n- 2n で端数を切り捨てる仕様(CommonMark)\n- 4n で端数を切り捨てる実装(RedCarpet)\n- ハイフンの次のスペースを省略できるか\n\n------\n\n## CommonMark(2012~)\n\n- Stack Exchange(StackOverflow)、GitHub、Reddit などの代表者によって策定された Markdown の標準化グループ\n- [CommonMark Spec](http://spec.commonmark.org/0.25/ \"CommonMark Spec\")\n- 実行可能なテストコード [jgm/CommonMark: CommonMark spec, with reference implementations in C and JavaScript](https://github.com/jgm/CommonMark \"jgm/CommonMark: CommonMark spec, with reference implementations in C and JavaScript\")\n\n-----\n\n## CommonMarkの問題\n\n- 元々「Standard Markdown」だったが、John Gruber氏の反対でCommonMarkに改名\n    - それ以前の各種の先行実装が、CommonMarkに追従する動きがあまりない\n    - 乱立する仕様の One of them では?という批判も\n- 結局事実上の標準はGFMにあり、その拡張部分は未定義\n- **ユーザーが使いたいのはgfm** であり、**CommonMarkに準拠したからといって、ユーザーのニーズを満たせているわけではない**\n\n-----\n\n## 現実\n\n[CommonMark conformance · Issue #429 · vmg/redcarpet](https://github.com/vmg/redcarpet/issues/429 \"CommonMark conformance · Issue #429 · vmg/redcarpet\")\n\n```sh\ngit clone https://github.com/jgm/CommonMark.git\ncd CommonMark\n# install re2c like: brew install re2c\nmake\nmake test # Testing of reference implementation: 469 tests passed, 0 failed, 0 skipped.\ngem install redcarpet\nPROG=redcarpet make test # 213 tests passed, 256 failed, 0 skipped.\n```\n\n----\n\n# 213 tests passed, 256 failed !!!\n\n-----\n\n## Qiitaにおける Markdown の課題\n\n-----\n\n## QiitaのMarkdown実装\n\n- 数式サポート \n- ToCの生成\n- コードブロックでファイル名のサポート `js:foo.js`\n\n実装的には RedCarpet の Fork [increments/greenmat: A Markdown parser for Qiita, based on Redcarpet.](https://github.com/increments/greenmat \"increments/greenmat: A Markdown parser for Qiita, based on Redcarpet.\")\n\n-----\n\n## QiitaのMarkdownが抱える問題\n\n- CommonMark以前に追加された、非標準な独自拡張が存在\n- ベースとした RedCarpet が CommonMark準拠度が低い\n- RedCarpetはRubyとCの実装なので、JSによるクライアントプレビューが出来ず、サーバーに問い合わせる必要があり、プレビューの応答性が悪い\n- おそらく **プレビューと出力が違うような状態は許容されない**\n\n----\n\n# うまいことしたい…\n\n----\n\n## ここで解決したい問題\n\n- 同じMarkdownのソースから出力されるHTMLが同じ結果であることを保証したい\n\n\n----\n\n# 解決策を考える\n\n----\n\n## 考えられるアプローチ: 1\n\n何もしない。細かなズレはあるが、それらは所詮はエッジケース。\n体感として問題がないなら、一貫していることの方が大事である。\n\n> \"Sure it's broken, but at least it's consistently broken!\"\n\nhttps://github.com/php/php-src/commit/a0724d30817600540946b41e40f4cfc2a0c30f80#commitcomment-16174204\n\n----\n\n## 考えられるアプローチ: 2\n\n**サーバーサイドでJS実装のコンパイラを呼ぶ**\n\n単一実装でメンテナンス性は高い。RailsでからJSエンジン(V8)を呼ぶのがネック\n\n----\n\n## 考えられるアプローチ: 3\n\nクライアントのみでコンパイルをする\n\nSEOが懸念。\n\n----\n\n## 考えられるアプローチ: 4\n\nEmscripten で RedCarpetのCのコードをコンパイルする。\n\nあるいは WebAssembly を待つ。\n\n[kripken/emscripten: Emscripten: An LLVM-to-JavaScript Compiler](https://github.com/kripken/emscripten \"kripken/emscripten: Emscripten: An LLVM-to-JavaScript Compiler\")\n\n[WebAssembly/design: WebAssembly Design Documents](https://github.com/WebAssembly/design \"WebAssembly/design: WebAssembly Design Documents\") \n\n----\n\n## どの選択肢を取るか\n\nJSで実装が一番理想的だが、Rubyエンジニアが多い弊社の場合、まだしばらく現行のまま行きそう。\n\nいずれもパフォーマンスやSEOのトレードオフがある。\n\n---\n\n## Excuse\n\nQiitaの1開発者としてこの領域をリサーチした結果得た問題意識であり、開発チームとしての総意ではないが、いずれ後方互換性を捨ててコンパイラを変更する可能性がないわけではない、ぐらいの感じでお願いします\n\n---\n\n## Markdownの仕様まとめ\n\n- 標準化以前に実装が乱立\n- CommonMarkで標準化の試みだが追従されてるかは微妙\n- デファクトはGFMだがCommonMarkにGithubの拡張部分は含まれないよ!!!\n\n---\n\n## Markdown に関わるなら\n\n- 出来るだけ CommonMark に近い実装を選んだ上で、そのGFM拡張を入れるのオススメ。\n- パーサの互換がなくなるような拡張はできるだけ控えよう\n\n---\n\n## 次のテーマ\n\n---\n\n# Markdownのレンダリング\n\n---\n\n## 課題意識\n\n- Markdownが長くなると、展開するHTMLが大きくなりレンダリングが重い\n    - HTMLのパースとDOMの初期化が重い\n    - リアルタイムプレビューでは、ユーザーの打鍵ごとに更新が発生\n\n----\n\n## フロントエンドの用語整理\n\n- HTML: タグで記述するマークアップ言語\n- DOM: Document Object Model。HTMLによって生成されるブラウザの木構造のインスタンス。\n\n![](https://i.gyazo.com/09eb88314a25af3c3b82e39e74b13a1b.png)\n\n![](https://i.gyazo.com/bdf91873882848021800948d5a75f361.png)\n\n----\n\n## Kobito for Windows の場合\n\n![](http://kobito.qiita.com/images/logo.png)\n\n- Qiitaに投稿する機能を持つ、汎用のMarkdownエディタ/プレビューツール\n- Windows版はElectronで実装\n- デスクトップアプリで、ネットにつながらない状況を想定する必要\n    - => Qiitaとは独立したJSによる実装\n\n---\n\n## Kobito for Windows で実装したコンパイラ\n\n仮想DOM技術とMarkdown ASTを用いて、差分検出でプレビュー速度を高速化した\n\n※ Qiita はプレビュー速度より同じ出力結果を優先という判断で、実装されてない\n\n---\n\n## VirtualDOM\n\n- HTMLを木構造と捉えて、生成コストが重いDOM(Document Object Model)とは別に、仮想の木構造の前後状態の比較から、実際のHTMLに対する差分のdiff/patchを行う手法\n- もっとも有名な実装に [React](https://facebook.github.io/react/ \"A JavaScript library for building user interfaces | React\")\n- アルゴリズムの詳細: [Performance Calendar » React’s diff algorithm](http://calendar.perfplanet.com/2013/diff/ \"Performance Calendar » React’s diff algorithm\")\n\n![](http://calendar.perfplanet.com/wp-content/uploads/2013/12/vjeux/1.png)\n\n-----\n\n## Markdown AST\n\n- MarkdownをHTMLにコンパイルする過程で、一度AST(抽象構文木)を経過することで、任意のフォーマットに変形できる\n- [wooorm/remark: Markdown processor powered by plugins](https://github.com/wooorm/remark \"wooorm/remark: Markdown processor powered by plugins\") の 実装を使った\n    - さすがにこのASTは標準化されなさそう?\n\n------\n\n## Markdown => VirtualDOM のメリット\n\n- リアルタイムプレビューでは、全体における一度の差分は、ごく一部だと仮定できる\n- Markdown => Markdown AST => VirtualDOM という変換ステップを踏めば、ブラウザのDOM削除/生成ステップのほとんどを省ける\n- [mizchi/md2react: markdown to react element](https://github.com/mizchi/md2react \"mizchi/md2react: markdown to react element\")\n- https://github.com/mapbox/remark-react\n- Kobito for Windows で本番投入済み\n\n------\n\n## Markdown => AST => React => HTML => DOM\n\n```js\nvar md2react = require('md2react');\n\nvar md = '# Hello md2react';\nvar html = ReactDOM.renderToString(md2react(md));\n\n/*\n<div data-reactid=\".14qrwokr3sw\" data-react-checksum=\"20987480\"><h1 data-reactid=\".14qrwokr3sw.$_start_root_0_heading\"><span data-reactid=\".14qrwokr3sw.$_start_root_0_heading.0\">Hello md2react</span></h1></div>'\n//'<div data-reactid=\".58nba97pxc\" data-react-checksum=\"-55236619\"><h1 data-reactid=\".58nba97pxc.0\"><span data-reactid=\".58nba97pxc.0.0\">Hello</span></h1></div>'\n*/\n```\n\n------\n\n![](https://i.gyazo.com/6f3eb5a77d926ccb3ca651b98699dea1.gif)\n\n------\n\n## Markdown AST という概念の発展性\n\n- より高度なナビゲーションが可能に\n    - 高速なToCの生成\n    - タイトルジャンプ\n    - コンパイル前後のカーソルの位置対応\n- HTML以外の出力フォーマットに対応\n    - 仮想DOM\n    - スライド機能(これ)\n\n```markdown\n\n# Aaa\n\n---\n\n# Bbb\n\n```\n\n----\n\n## Qiita の Markdown 環境をどうするか\n\n- CommonMarkとぶつかるような独自実装は、あまり増やしたくない\n- どのプラットフォームでも同じように変換される、という安心感は大事にしたい。その為に実装を寄せたい。\n\n---\n\n## より具体的な(自分の)実務\n\n- スタンドアローンとして優秀なMarkdownエディタが多い中、お世辞にもQiitaのWebのエディタはそこまで使いやすいとは言えない。リファクタ中(苦戦中)\n- コンパイラの問題を解決して、クライアントで高速なプレビューや、リッチコンテンツの作成支援を行えるようにしたい\n\n----\n\n## 最後に\n\n----\n\n## 最後に\n\n- ただの Markdown という 1フォーマットでもこだわると無限にエッジケースが出てくる\n- 正しく仕様が決まると、エコシステムが回り、ツール同士の連携ができるようになり、結果としてユーザーが恩恵を受ける\n- Webの仕様は基本的に民主主義で、議論の過程が公開されているので、現状ダメなもののダメになってしまった理由も、優しく察していきましょう\n\n----\n\n## 終わり\n",
    "body": "\n<h2>\n<span id=\"はじめに\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"><i class=\"fa fa-link\"></i></a>はじめに</h2>\n\n<p><a href=\"http://sigpx.org/\" title=\"SIGPX: Special Interest Group on Programming Experience\" rel=\"nofollow\" target=\"_blank\">SIGPX: Special Interest Group on Programming Experience</a> 第二回 (2016年8月7日) での発表資料</p>\n\n<hr>\n\n<h2>\n<span id=\"今日話す内容\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E6%97%A5%E8%A9%B1%E3%81%99%E5%86%85%E5%AE%B9\"><i class=\"fa fa-link\"></i></a>今日話す内容</h2>\n\n<ul>\n<li>Qiitaでのコンテンツオーサリング</li>\n<li>Qiita の Markdown について、泥臭い感じで(アカデミックな会なので)</li>\n<li>Markdownという切り口で、標準化、そのレンダリング、オーサリング、ASTなどについて</li>\n</ul>\n\n<hr>\n\n<h1>\n<span id=\"markdown-の仕様\" class=\"fragment\"></span><a href=\"#markdown-%E3%81%AE%E4%BB%95%E6%A7%98\"><i class=\"fa fa-link\"></i></a>Markdown の仕様</h1>\n\n<hr>\n\n<h2>\n<span id=\"markdown\" class=\"fragment\"></span><a href=\"#markdown\"><i class=\"fa fa-link\"></i></a>Markdown</h2>\n\n<ul>\n<li>HTMLに変換されるマークアップ言語の実装。またはその仕様。</li>\n<li>Github の躍進とともにメジャーに</li>\n<li>同種のマークアップ言語として textile, はてな記法など</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"markdownの起源\" class=\"fragment\"></span><a href=\"#markdown%E3%81%AE%E8%B5%B7%E6%BA%90\"><i class=\"fa fa-link\"></i></a>Markdownの起源</h2>\n\n<ul>\n<li>オリジナル実装は John Gruber の <code>markdown.pl</code> というPerl スクリプト(2004)\n\n<ul>\n<li><a href=\"https://en.wikipedia.org/wiki/Markdown#History\" title=\"Markdown - Wikipedia, the free encyclopedia\" rel=\"nofollow\" target=\"_blank\">Markdown - Wikipedia, the free encyclopedia</a></li>\n</ul>\n</li>\n<li>電子メールにおいてプレーンテキストを装飾する際の慣習から着想を得ている\n\n<ul>\n<li>\n<code>It’s a plain text format for writing structured documents, based on formatting conventions from email and usenet.</code> <a href=\"http://commonmark.org/\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">http://commonmark.org/</a>\n</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<p><a href=\"https://i.gyazo.com/491c14e6d4a11d32f1e4d5fa3620683a.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/491c14e6d4a11d32f1e4d5fa3620683a.png\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"なぜ-markdown-がエンジニアに好まれるか\" class=\"fragment\"></span><a href=\"#%E3%81%AA%E3%81%9C-markdown-%E3%81%8C%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AB%E5%A5%BD%E3%81%BE%E3%82%8C%E3%82%8B%E3%81%8B\"><i class=\"fa fa-link\"></i></a>なぜ Markdown がエンジニアに好まれるか</h2>\n\n<ul>\n<li>\n<strong>プレーンテキストである</strong>\n\n<ul>\n<li>Git等のバージョン管理システムとシナジー</li>\n<li>特殊なWYSIWYG を要求された時代への反動</li>\n</ul>\n</li>\n<li>\n<strong>セマンティクスと装飾が分離されている</strong>\n\n<ul>\n<li>テキストエディタでそれなりに読める</li>\n<li>HTMLの出力だけが決まっていて、装飾に関与しない</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"複数の-markdown-実装\" class=\"fragment\"></span><a href=\"#%E8%A4%87%E6%95%B0%E3%81%AE-markdown-%E5%AE%9F%E8%A3%85\"><i class=\"fa fa-link\"></i></a>複数の Markdown 実装</h2>\n\n<ul>\n<li>各言語による実装\n\n<ul>\n<li>Perl: <code>Text::Markdown</code> <a href=\"http://search.cpan.org/%7Ebobtfish/Text-Markdown-1.000031/lib/Text/Markdown.pm\" title=\"Text::Markdown - search.cpan.org\" rel=\"nofollow\" target=\"_blank\">Text::Markdown - search.cpan.org</a>\n</li>\n<li>Ruby: <code>RedCarpet</code> (厳密にはC実装のバインディング) <a href=\"https://github.com/vmg/redcarpet\" title=\"vmg/redcarpet: The safe Markdown parser, reloaded.\" rel=\"nofollow\" target=\"_blank\">vmg/redcarpet: The safe Markdown parser, reloaded.</a>\n</li>\n<li>etc...</li>\n</ul>\n</li>\n<li>Markdown Extra: テーブル記法などをサポート(PHP)</li>\n<li>Github の Github Flavored Markdown 通称 GFM  (ソース未公開)</li>\n<li>CommonMark(commonmark.org) による標準化とそのサンプル実装 (2012~)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"github-flavored-markdown\" class=\"fragment\"></span><a href=\"#github-flavored-markdown\"><i class=\"fa fa-link\"></i></a>Github Flavored Markdown</h2>\n\n<ul>\n<li>もっとも知られている実装</li>\n<li>仕様的には Markdown Extra をさらに拡張</li>\n<li>GFM != Markdown、とはいえ<strong>事実上の標準</strong>\n</li>\n<li>拡張\n\n<ul>\n<li>コードブロック記法とシンタックスハイライト</li>\n<li>テーブル記法</li>\n<li>打ち消し線</li>\n<li>絵文字</li>\n<li>URL の自動リンク</li>\n</ul>\n</li>\n<li>非互換\n\n<ul>\n<li>「<code>_foo_</code> のようなアンダースコアによる強調」のオミット</li>\n<li>「段落の末尾にスペースがあると改行」のオミット</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"markdownの抱える問題\" class=\"fragment\"></span><a href=\"#markdown%E3%81%AE%E6%8A%B1%E3%81%88%E3%82%8B%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>Markdownの抱える問題</h2>\n\n<ul>\n<li>仕様策定が決まる前に各言語で実装が乱立したので、実装にブレが多い\n\n<ul>\n<li><a href=\"http://johnmacfarlane.net/babelmark2/?normalize=1&amp;text=*%3C*%3E*++%0A+*_*_\" title=\"Babelmark 2 - Compare markdown implementations\" rel=\"nofollow\" target=\"_blank\">Babelmark 2 - Compare markdown implementations</a></li>\n</ul>\n</li>\n<li>異なる実装で同じMarkdownをコンパイルした場合、同じものが出力されることは稀\n\n<ul>\n<li>GFMのように勝手に一部の仕様を削除していたり…</li>\n<li>構文が入れ子になった状態が未定義だったり…</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"これはどうなる\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%82%8C%E3%81%AF%E3%81%A9%E3%81%86%E3%81%AA%E3%82%8B\"><i class=\"fa fa-link\"></i></a>これはどうなる?</h2>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\n- a\n    - b\n   - c\n  - d\n-e\n</pre></div></div>\n\n<ul>\n<li>改行後の文頭スペースの数の比較で、ネストの深さを決定する実装(markdown.pl)</li>\n<li>2n で端数を切り捨てる仕様(CommonMark)</li>\n<li>4n で端数を切り捨てる実装(RedCarpet)</li>\n<li>ハイフンの次のスペースを省略できるか</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"commonmark2012\" class=\"fragment\"></span><a href=\"#commonmark2012\"><i class=\"fa fa-link\"></i></a>CommonMark(2012~)</h2>\n\n<ul>\n<li>Stack Exchange(StackOverflow)、GitHub、Reddit などの代表者によって策定された Markdown の標準化グループ</li>\n<li><a href=\"http://spec.commonmark.org/0.25/\" title=\"CommonMark Spec\" rel=\"nofollow\" target=\"_blank\">CommonMark Spec</a></li>\n<li>実行可能なテストコード <a href=\"https://github.com/jgm/CommonMark\" title=\"jgm/CommonMark: CommonMark spec, with reference implementations in C and JavaScript\" rel=\"nofollow\" target=\"_blank\">jgm/CommonMark: CommonMark spec, with reference implementations in C and JavaScript</a>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"commonmarkの問題\" class=\"fragment\"></span><a href=\"#commonmark%E3%81%AE%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>CommonMarkの問題</h2>\n\n<ul>\n<li>元々「Standard Markdown」だったが、John Gruber氏の反対でCommonMarkに改名\n\n<ul>\n<li>それ以前の各種の先行実装が、CommonMarkに追従する動きがあまりない</li>\n<li>乱立する仕様の One of them では?という批判も</li>\n</ul>\n</li>\n<li>結局事実上の標準はGFMにあり、その拡張部分は未定義</li>\n<li>\n<strong>ユーザーが使いたいのはgfm</strong> であり、<strong>CommonMarkに準拠したからといって、ユーザーのニーズを満たせているわけではない</strong>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"現実\" class=\"fragment\"></span><a href=\"#%E7%8F%BE%E5%AE%9F\"><i class=\"fa fa-link\"></i></a>現実</h2>\n\n<p><a href=\"https://github.com/vmg/redcarpet/issues/429\" title=\"CommonMark conformance · Issue #429 · vmg/redcarpet\" rel=\"nofollow\" target=\"_blank\">CommonMark conformance · Issue #429 · vmg/redcarpet</a></p>\n\n<div class=\"code-frame\" data-lang=\"sh\"><div class=\"highlight\"><pre>\ngit clone https://github.com/jgm/CommonMark.git\n<span class=\"nb\">cd </span>CommonMark\n<span class=\"c\"># install re2c like: brew install re2c</span>\nmake\nmake <span class=\"nb\">test</span> <span class=\"c\"># Testing of reference implementation: 469 tests passed, 0 failed, 0 skipped.</span>\ngem install redcarpet\n<span class=\"nv\">PROG</span><span class=\"o\">=</span>redcarpet make <span class=\"nb\">test</span> <span class=\"c\"># 213 tests passed, 256 failed, 0 skipped.</span>\n</pre></div></div>\n\n<hr>\n\n<h1>\n<span id=\"213-tests-passed-256-failed-\" class=\"fragment\"></span><a href=\"#213-tests-passed-256-failed-\"><i class=\"fa fa-link\"></i></a>213 tests passed, 256 failed !!!</h1>\n\n<hr>\n\n<h2>\n<span id=\"qiitaにおける-markdown-の課題\" class=\"fragment\"></span><a href=\"#qiita%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B-markdown-%E3%81%AE%E8%AA%B2%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>Qiitaにおける Markdown の課題</h2>\n\n<hr>\n\n<h2>\n<span id=\"qiitaのmarkdown実装\" class=\"fragment\"></span><a href=\"#qiita%E3%81%AEmarkdown%E5%AE%9F%E8%A3%85\"><i class=\"fa fa-link\"></i></a>QiitaのMarkdown実装</h2>\n\n<ul>\n<li>数式サポート </li>\n<li>ToCの生成</li>\n<li>コードブロックでファイル名のサポート <code>js:foo.js</code>\n</li>\n</ul>\n\n<p>実装的には RedCarpet の Fork <a href=\"https://github.com/increments/greenmat\" title=\"increments/greenmat: A Markdown parser for Qiita, based on Redcarpet.\" rel=\"nofollow\" target=\"_blank\">increments/greenmat: A Markdown parser for Qiita, based on Redcarpet.</a></p>\n\n<hr>\n\n<h2>\n<span id=\"qiitaのmarkdownが抱える問題\" class=\"fragment\"></span><a href=\"#qiita%E3%81%AEmarkdown%E3%81%8C%E6%8A%B1%E3%81%88%E3%82%8B%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>QiitaのMarkdownが抱える問題</h2>\n\n<ul>\n<li>CommonMark以前に追加された、非標準な独自拡張が存在</li>\n<li>ベースとした RedCarpet が CommonMark準拠度が低い</li>\n<li>RedCarpetはRubyとCの実装なので、JSによるクライアントプレビューが出来ず、サーバーに問い合わせる必要があり、プレビューの応答性が悪い</li>\n<li>おそらく <strong>プレビューと出力が違うような状態は許容されない</strong>\n</li>\n</ul>\n\n<hr>\n\n<h1>\n<span id=\"うまいことしたい\" class=\"fragment\"></span><a href=\"#%E3%81%86%E3%81%BE%E3%81%84%E3%81%93%E3%81%A8%E3%81%97%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>うまいことしたい…</h1>\n\n<hr>\n\n<h2>\n<span id=\"ここで解決したい問題\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%81%93%E3%81%A7%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%84%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>ここで解決したい問題</h2>\n\n<ul>\n<li>同じMarkdownのソースから出力されるHTMLが同じ結果であることを保証したい</li>\n</ul>\n\n<hr>\n\n<h1>\n<span id=\"解決策を考える\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%82%92%E8%80%83%E3%81%88%E3%82%8B\"><i class=\"fa fa-link\"></i></a>解決策を考える</h1>\n\n<hr>\n\n<h2>\n<span id=\"考えられるアプローチ-1\" class=\"fragment\"></span><a href=\"#%E8%80%83%E3%81%88%E3%82%89%E3%82%8C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81-1\"><i class=\"fa fa-link\"></i></a>考えられるアプローチ: 1</h2>\n\n<p>何もしない。細かなズレはあるが、それらは所詮はエッジケース。<br>\n体感として問題がないなら、一貫していることの方が大事である。</p>\n\n<blockquote>\n<p>\"Sure it's broken, but at least it's consistently broken!\"</p>\n</blockquote>\n\n<p><a href=\"https://github.com/php/php-src/commit/a0724d30817600540946b41e40f4cfc2a0c30f80#commitcomment-16174204\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/php/php-src/commit/a0724d30817600540946b41e40f4cfc2a0c30f80#commitcomment-16174204</a></p>\n\n<hr>\n\n<h2>\n<span id=\"考えられるアプローチ-2\" class=\"fragment\"></span><a href=\"#%E8%80%83%E3%81%88%E3%82%89%E3%82%8C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81-2\"><i class=\"fa fa-link\"></i></a>考えられるアプローチ: 2</h2>\n\n<p><strong>サーバーサイドでJS実装のコンパイラを呼ぶ</strong></p>\n\n<p>単一実装でメンテナンス性は高い。RailsでからJSエンジン(V8)を呼ぶのがネック</p>\n\n<hr>\n\n<h2>\n<span id=\"考えられるアプローチ-3\" class=\"fragment\"></span><a href=\"#%E8%80%83%E3%81%88%E3%82%89%E3%82%8C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81-3\"><i class=\"fa fa-link\"></i></a>考えられるアプローチ: 3</h2>\n\n<p>クライアントのみでコンパイルをする</p>\n\n<p>SEOが懸念。</p>\n\n<hr>\n\n<h2>\n<span id=\"考えられるアプローチ-4\" class=\"fragment\"></span><a href=\"#%E8%80%83%E3%81%88%E3%82%89%E3%82%8C%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81-4\"><i class=\"fa fa-link\"></i></a>考えられるアプローチ: 4</h2>\n\n<p>Emscripten で RedCarpetのCのコードをコンパイルする。</p>\n\n<p>あるいは WebAssembly を待つ。</p>\n\n<p><a href=\"https://github.com/kripken/emscripten\" title=\"kripken/emscripten: Emscripten: An LLVM-to-JavaScript Compiler\" rel=\"nofollow\" target=\"_blank\">kripken/emscripten: Emscripten: An LLVM-to-JavaScript Compiler</a></p>\n\n<p><a href=\"https://github.com/WebAssembly/design\" title=\"WebAssembly/design: WebAssembly Design Documents\" rel=\"nofollow\" target=\"_blank\">WebAssembly/design: WebAssembly Design Documents</a> </p>\n\n<hr>\n\n<h2>\n<span id=\"どの選択肢を取るか\" class=\"fragment\"></span><a href=\"#%E3%81%A9%E3%81%AE%E9%81%B8%E6%8A%9E%E8%82%A2%E3%82%92%E5%8F%96%E3%82%8B%E3%81%8B\"><i class=\"fa fa-link\"></i></a>どの選択肢を取るか</h2>\n\n<p>JSで実装が一番理想的だが、Rubyエンジニアが多い弊社の場合、まだしばらく現行のまま行きそう。</p>\n\n<p>いずれもパフォーマンスやSEOのトレードオフがある。</p>\n\n<hr>\n\n<h2>\n<span id=\"excuse\" class=\"fragment\"></span><a href=\"#excuse\"><i class=\"fa fa-link\"></i></a>Excuse</h2>\n\n<p>Qiitaの1開発者としてこの領域をリサーチした結果得た問題意識であり、開発チームとしての総意ではないが、いずれ後方互換性を捨ててコンパイラを変更する可能性がないわけではない、ぐらいの感じでお願いします</p>\n\n<hr>\n\n<h2>\n<span id=\"markdownの仕様まとめ\" class=\"fragment\"></span><a href=\"#markdown%E3%81%AE%E4%BB%95%E6%A7%98%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>Markdownの仕様まとめ</h2>\n\n<ul>\n<li>標準化以前に実装が乱立</li>\n<li>CommonMarkで標準化の試みだが追従されてるかは微妙</li>\n<li>デファクトはGFMだがCommonMarkにGithubの拡張部分は含まれないよ!!!</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"markdown-に関わるなら\" class=\"fragment\"></span><a href=\"#markdown-%E3%81%AB%E9%96%A2%E3%82%8F%E3%82%8B%E3%81%AA%E3%82%89\"><i class=\"fa fa-link\"></i></a>Markdown に関わるなら</h2>\n\n<ul>\n<li>出来るだけ CommonMark に近い実装を選んだ上で、そのGFM拡張を入れるのオススメ。</li>\n<li>パーサの互換がなくなるような拡張はできるだけ控えよう</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"次のテーマ\" class=\"fragment\"></span><a href=\"#%E6%AC%A1%E3%81%AE%E3%83%86%E3%83%BC%E3%83%9E\"><i class=\"fa fa-link\"></i></a>次のテーマ</h2>\n\n<hr>\n\n<h1>\n<span id=\"markdownのレンダリング\" class=\"fragment\"></span><a href=\"#markdown%E3%81%AE%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0\"><i class=\"fa fa-link\"></i></a>Markdownのレンダリング</h1>\n\n<hr>\n\n<h2>\n<span id=\"課題意識\" class=\"fragment\"></span><a href=\"#%E8%AA%B2%E9%A1%8C%E6%84%8F%E8%AD%98\"><i class=\"fa fa-link\"></i></a>課題意識</h2>\n\n<ul>\n<li>Markdownが長くなると、展開するHTMLが大きくなりレンダリングが重い\n\n<ul>\n<li>HTMLのパースとDOMの初期化が重い</li>\n<li>リアルタイムプレビューでは、ユーザーの打鍵ごとに更新が発生</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"フロントエンドの用語整理\" class=\"fragment\"></span><a href=\"#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AE%E7%94%A8%E8%AA%9E%E6%95%B4%E7%90%86\"><i class=\"fa fa-link\"></i></a>フロントエンドの用語整理</h2>\n\n<ul>\n<li>HTML: タグで記述するマークアップ言語</li>\n<li>DOM: Document Object Model。HTMLによって生成されるブラウザの木構造のインスタンス。</li>\n</ul>\n\n<p><a href=\"https://i.gyazo.com/09eb88314a25af3c3b82e39e74b13a1b.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/09eb88314a25af3c3b82e39e74b13a1b.png\" alt=\"\"></a></p>\n\n<p><a href=\"https://i.gyazo.com/bdf91873882848021800948d5a75f361.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/bdf91873882848021800948d5a75f361.png\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"kobito-for-windows-の場合\" class=\"fragment\"></span><a href=\"#kobito-for-windows-%E3%81%AE%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>Kobito for Windows の場合</h2>\n\n<p><a href=\"http://kobito.qiita.com/images/logo.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://kobito.qiita.com/images/logo.png\" alt=\"\"></a></p>\n\n<ul>\n<li>Qiitaに投稿する機能を持つ、汎用のMarkdownエディタ/プレビューツール</li>\n<li>Windows版はElectronで実装</li>\n<li>デスクトップアプリで、ネットにつながらない状況を想定する必要\n\n<ul>\n<li>=&gt; Qiitaとは独立したJSによる実装</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"kobito-for-windows-で実装したコンパイラ\" class=\"fragment\"></span><a href=\"#kobito-for-windows-%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9\"><i class=\"fa fa-link\"></i></a>Kobito for Windows で実装したコンパイラ</h2>\n\n<p>仮想DOM技術とMarkdown ASTを用いて、差分検出でプレビュー速度を高速化した</p>\n\n<p>※ Qiita はプレビュー速度より同じ出力結果を優先という判断で、実装されてない</p>\n\n<hr>\n\n<h2>\n<span id=\"virtualdom\" class=\"fragment\"></span><a href=\"#virtualdom\"><i class=\"fa fa-link\"></i></a>VirtualDOM</h2>\n\n<ul>\n<li>HTMLを木構造と捉えて、生成コストが重いDOM(Document Object Model)とは別に、仮想の木構造の前後状態の比較から、実際のHTMLに対する差分のdiff/patchを行う手法</li>\n<li>もっとも有名な実装に <a href=\"https://facebook.github.io/react/\" title=\"A JavaScript library for building user interfaces | React\" rel=\"nofollow\" target=\"_blank\">React</a>\n</li>\n<li>アルゴリズムの詳細: <a href=\"http://calendar.perfplanet.com/2013/diff/\" title=\"Performance Calendar » React’s diff algorithm\" rel=\"nofollow\" target=\"_blank\">Performance Calendar » React’s diff algorithm</a>\n</li>\n</ul>\n\n<p><a href=\"http://calendar.perfplanet.com/wp-content/uploads/2013/12/vjeux/1.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://calendar.perfplanet.com/wp-content/uploads/2013/12/vjeux/1.png\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"markdown-ast\" class=\"fragment\"></span><a href=\"#markdown-ast\"><i class=\"fa fa-link\"></i></a>Markdown AST</h2>\n\n<ul>\n<li>MarkdownをHTMLにコンパイルする過程で、一度AST(抽象構文木)を経過することで、任意のフォーマットに変形できる</li>\n<li>\n<a href=\"https://github.com/wooorm/remark\" title=\"wooorm/remark: Markdown processor powered by plugins\" rel=\"nofollow\" target=\"_blank\">wooorm/remark: Markdown processor powered by plugins</a> の 実装を使った\n\n<ul>\n<li>さすがにこのASTは標準化されなさそう?</li>\n</ul>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"markdown--virtualdom-のメリット\" class=\"fragment\"></span><a href=\"#markdown--virtualdom-%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88\"><i class=\"fa fa-link\"></i></a>Markdown =&gt; VirtualDOM のメリット</h2>\n\n<ul>\n<li>リアルタイムプレビューでは、全体における一度の差分は、ごく一部だと仮定できる</li>\n<li>Markdown =&gt; Markdown AST =&gt; VirtualDOM という変換ステップを踏めば、ブラウザのDOM削除/生成ステップのほとんどを省ける</li>\n<li><a href=\"https://github.com/mizchi/md2react\" title=\"mizchi/md2react: markdown to react element\" rel=\"nofollow\" target=\"_blank\">mizchi/md2react: markdown to react element</a></li>\n<li><a href=\"https://github.com/mapbox/remark-react\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mapbox/remark-react</a></li>\n<li>Kobito for Windows で本番投入済み</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"markdown--ast--react--html--dom\" class=\"fragment\"></span><a href=\"#markdown--ast--react--html--dom\"><i class=\"fa fa-link\"></i></a>Markdown =&gt; AST =&gt; React =&gt; HTML =&gt; DOM</h2>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kd\">var</span> <span class=\"nx\">md2react</span> <span class=\"o\">=</span> <span class=\"nx\">require</span><span class=\"p\">(</span><span class=\"s1\">'md2react'</span><span class=\"p\">);</span>\n\n<span class=\"kd\">var</span> <span class=\"nx\">md</span> <span class=\"o\">=</span> <span class=\"s1\">'# Hello md2react'</span><span class=\"p\">;</span>\n<span class=\"kd\">var</span> <span class=\"nx\">html</span> <span class=\"o\">=</span> <span class=\"nx\">ReactDOM</span><span class=\"p\">.</span><span class=\"nx\">renderToString</span><span class=\"p\">(</span><span class=\"nx\">md2react</span><span class=\"p\">(</span><span class=\"nx\">md</span><span class=\"p\">));</span>\n\n<span class=\"cm\">/*</span>\n<span class=\"cm\">&lt;div data-reactid=\".14qrwokr3sw\" data-react-checksum=\"20987480\"&gt;&lt;h1 data-reactid=\".14qrwokr3sw.$_start_root_0_heading\"&gt;&lt;span data-reactid=\".14qrwokr3sw.$_start_root_0_heading.0\"&gt;Hello md2react&lt;/span&gt;&lt;/h1&gt;&lt;/div&gt;'</span>\n<span class=\"cm\">//'&lt;div data-reactid=\".58nba97pxc\" data-react-checksum=\"-55236619\"&gt;&lt;h1 data-reactid=\".58nba97pxc.0\"&gt;&lt;span data-reactid=\".58nba97pxc.0.0\"&gt;Hello&lt;/span&gt;&lt;/h1&gt;&lt;/div&gt;'</span>\n<span class=\"cm\">*/</span>\n</pre></div></div>\n\n<hr>\n\n<p><a href=\"https://i.gyazo.com/6f3eb5a77d926ccb3ca651b98699dea1.gif\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/6f3eb5a77d926ccb3ca651b98699dea1.gif\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"markdown-ast-という概念の発展性\" class=\"fragment\"></span><a href=\"#markdown-ast-%E3%81%A8%E3%81%84%E3%81%86%E6%A6%82%E5%BF%B5%E3%81%AE%E7%99%BA%E5%B1%95%E6%80%A7\"><i class=\"fa fa-link\"></i></a>Markdown AST という概念の発展性</h2>\n\n<ul>\n<li>より高度なナビゲーションが可能に\n\n<ul>\n<li>高速なToCの生成</li>\n<li>タイトルジャンプ</li>\n<li>コンパイル前後のカーソルの位置対応</li>\n</ul>\n</li>\n<li>HTML以外の出力フォーマットに対応\n\n<ul>\n<li>仮想DOM</li>\n<li>スライド機能(これ)</li>\n</ul>\n</li>\n</ul>\n\n<div class=\"code-frame\" data-lang=\"markdown\"><div class=\"highlight\"><pre>\n\n# Aaa\n\n---\n\n# Bbb\n\n</pre></div></div>\n\n<hr>\n\n<h2>\n<span id=\"qiita-の-markdown-環境をどうするか\" class=\"fragment\"></span><a href=\"#qiita-%E3%81%AE-markdown-%E7%92%B0%E5%A2%83%E3%82%92%E3%81%A9%E3%81%86%E3%81%99%E3%82%8B%E3%81%8B\"><i class=\"fa fa-link\"></i></a>Qiita の Markdown 環境をどうするか</h2>\n\n<ul>\n<li>CommonMarkとぶつかるような独自実装は、あまり増やしたくない</li>\n<li>どのプラットフォームでも同じように変換される、という安心感は大事にしたい。その為に実装を寄せたい。</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"より具体的な自分の実務\" class=\"fragment\"></span><a href=\"#%E3%82%88%E3%82%8A%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AA%E8%87%AA%E5%88%86%E3%81%AE%E5%AE%9F%E5%8B%99\"><i class=\"fa fa-link\"></i></a>より具体的な(自分の)実務</h2>\n\n<ul>\n<li>スタンドアローンとして優秀なMarkdownエディタが多い中、お世辞にもQiitaのWebのエディタはそこまで使いやすいとは言えない。リファクタ中(苦戦中)</li>\n<li>コンパイラの問題を解決して、クライアントで高速なプレビューや、リッチコンテンツの作成支援を行えるようにしたい</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"最後に\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB\"><i class=\"fa fa-link\"></i></a>最後に</h2>\n\n<hr>\n\n<h2>\n<span id=\"最後に-1\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB-1\"><i class=\"fa fa-link\"></i></a>最後に</h2>\n\n<ul>\n<li>ただの Markdown という 1フォーマットでもこだわると無限にエッジケースが出てくる</li>\n<li>正しく仕様が決まると、エコシステムが回り、ツール同士の連携ができるようになり、結果としてユーザーが恩恵を受ける</li>\n<li>Webの仕様は基本的に民主主義で、議論の過程が公開されているので、現状ダメなもののダメになってしまった理由も、優しく察していきましょう</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"終わり\" class=\"fragment\"></span><a href=\"#%E7%B5%82%E3%82%8F%E3%82%8A\"><i class=\"fa fa-link\"></i></a>終わり</h2>\n",
    "stock_users": [
        "gfx",
        "fakestarbaby",
        "mosa_siru",
        "ysk_1031",
        "cognitom",
        "seizans",
        "tadsan",
        "labocho",
        "ykominami",
        "Noboruhi",
        "poad1010",
        "Real_analysis",
        "inuscript",
        "fukayatsu",
        "Quramy",
        "ooDEMi",
        "katsu33@github",
        "VienosNotes",
        "gaaamii",
        "b_wind",
        "mandel59",
        "rentalname@github",
        "makotoomori@github",
        "LightSpeedC",
        "yimajo",
        "armorik83",
        "droibit",
        "bonk",
        "rtoya",
        "mono0926",
        "todashuta",
        "mochizuki78",
        "wiger-tazz@github",
        "teracy",
        "mottox2",
        "tsuyoshi_cho",
        "Yu1Q89",
        "yasuhiroki",
        "doockie",
        "eedamame",
        "ndxbn",
        "x6dx6ex62",
        "s-hiiragi",
        "toshi0383",
        "hiro93n",
        "mochi_Flappe",
        "hennry",
        "Neos21",
        "tbpgr",
        "chocodoughnut",
        "maaa",
        "koher",
        "seihmd",
        "stak",
        "oharato",
        "matobaa",
        "kahuu",
        "17tea",
        "cither",
        "tanaka0325",
        "kplus",
        "musashi13z",
        "sndr",
        "Selene-Misso",
        "Anargts09",
        "Chitoge",
        "sylvan-yupa",
        "yklitter",
        "Anar_Gts"
    ]
}, {
    "id": 409947,
    "uuid": "ec04537b352123cab288",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "JavaScriptを実行するフィーチャスペックでActiveRecordへのモンキーパッチを無くす方法",
    "created_at": "2016-07-22 13:20:09 +0900",
    "updated_at": "2016-07-22 13:30:32 +0900",
    "created_at_in_words": "約2ヶ月",
    "updated_at_in_words": "約2ヶ月",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }, {
        "name": "RSpec",
        "url_name": "rspec",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/aba2fc611bc7fe28b0c70347e68541ef7e186ac5/medium.jpg?1407701144",
        "following": false,
        "versions": []
    }, {
        "name": "Capybara",
        "url_name": "capybara",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/b029a6363be2e1531fb2f58ac8ed30b98b2d8341/medium.jpg?1405648601",
        "following": false,
        "versions": []
    }],
    "stock_count": 12,
    "comment_count": 0,
    "url": "http://qiita.com/jnchito/items/ec04537b352123cab288",
    "created_at_as_seconds": 1469161209,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## Railsのフィーチャスペックでよく起きる問題\n\nRailsのフィーチャスペックで`js: true`にする(テスト実行時にJavaScriptも動かす)場合、フィーチャスペック内で作成したテストデータがブラウザ(PoltergeistやSelenium webdriverで起動するFirefox)内で参照できない、といった問題がよく発生する。\n\n以下は問題が発生するフィーチャスペックの例である。\n\n```ruby\nrequire 'rails_helper'\n\nfeature 'User management' do\n  scenario \"adds a new user\", js: true do\n    admin = create(:admin)\n    \n    visit root_path\n    click_link 'Log In'\n    fill_in 'Email', with: admin.email\n    fill_in 'Password', with: admin.password\n    # 上で作成したadminのデータがブラウザ側で参照できないのでログインに失敗する\n    click_button 'Log In'\n\n    # ...\n  end\nend\n```\n\nこの問題はテストを実行しているコードのDBコネクションと、フィーチャスペック用のWebサーバーが使うDBコネクションが別々であるため、一方のDBトランザクション内で作成されたデータは他方のDBコネクションから参照できないことに起因している。\n\n## 従来の古い解決策(モンキーパッチあり)\n\nこの問題を解決するために、特殊なモンキーパッチとDatabaseCleanerを使って、以下のような解決策がよく取られていた。\n\n```ruby:spec/support/shared_db_connection.rb\nclass ActiveRecord::Base\n  mattr_accessor :shared_connection\n  @@shared_connection = nil\n\n  def self.connection\n    @@shared_connection || retrieve_connection\n  end\nend\nActiveRecord::Base.shared_connection = ActiveRecord::Base.connection\n```\n\n```ruby:spec/rails_helper.rb\nDir[Rails.root.join(\"spec/support/**/*.rb\")].each { |f| require f }\n\nRSpec.configure do |config|\n  # ...\n  \n  config.use_transactional_fixtures = true\n\n  config.before(:suite) do\n    DatabaseCleaner.strategy = :transaction\n    DatabaseCleaner.clean_with :truncation\n  end\n  \n  config.around(:each) do |example|\n    DatabaseCleaner.cleaning do\n      example.run\n    end\n  end\n  \n  config.after(:each) do\n    DatabaseCleaner.clean\n  end\nend\n```\n\nこれは何をやっているかというと、テスト実行コードとフィーチャスペック用のWebサーバーでDBコネクションを共有して、どちらからでも同じデータの読み書きをできるようにしている。\n\n## 最近の解決策(モンキーパッチなし)\n\nしかし、ActiveRecordにモンキーパッチを当てるというのは黒魔術的であまり気持ちがいいものではない。\n\nそこで、最新のDatabaseCleanerのREADMEでは、上記のモンキーパッチを使わずにこの問題を解決する方法が載っている。\n\nhttps://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example\n\n```ruby:spec/support/shared_db_connection.rb\n# 不要になるのでファイルごと削除\n```\n\n```ruby:spec/rails_helper.rb\nRSpec.configure do |config|\n  config.use_transactional_fixtures = false\n\n  config.before(:suite) do\n    if config.use_transactional_fixtures?\n      raise(<<-MSG)\n        設定がおかしいので警告メッセージを表示 (省略)\n      MSG\n    end\n    DatabaseCleaner.clean_with(:truncation)\n  end  \n\n  config.before(:each) do\n    DatabaseCleaner.strategy = :transaction\n  end\n\n  config.before(:each, type: :feature) do\n    driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test\n\n    if !driver_shares_db_connection_with_specs\n      DatabaseCleaner.strategy = :truncation\n    end\n  end\n\n  config.before(:each) do\n    DatabaseCleaner.start\n  end\n\n  config.append_after(:each) do\n    DatabaseCleaner.clean\n  end\nend\n```\n\n上記の設定は簡単にいうと次のようになっている。\n\n- `js: true` の場合( `driver_shares_db_connection_with_specs` が `false` の場合)はトランザクションを使わずにデータを読み書きする。トランザクションを使わないので、どのDB接続からでも同じデータを参照できる。テストが終わったら全データをtruncateする。\n- それ以外の場合はDB接続は1つしかないので、トランザクション内でデータを読み書きする。\n\n## 動作確認した実行環境\n\n筆者は以下の環境で「モンキーパッチを使わない解決策」が有効に機能することを確認した。\n\n古いバージョンのgemでは確認していないので、この解決策を適用する場合はテスト関連のgemはなるべく最新にすることが望ましい。\n\n- Rails 5.0.0\n- rspec-rails 3.5.0\n- capybara 2.7.1\n- database_cleaner 1.5.3\n- selenium-webdriver 2.53.4\n- factory_girl_rails 4.7.0\n\n## サンプルコード\n\n[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)のサンプルアプリケーションをベースに上記の解決策を適用してみた。\n\nテストコードの内容は以下のGitHubリポジトリで確認できる。\n\nhttps://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0\n\n## この設定が解決するかもしれないトラブル\n\nRails 5にアップデートすると、リクエストスペックで次のようなエラーが発生してテストが落ちる場合がある。\n\n```\nPG::ConnectionBad: PQsocket() can't get socket descriptor: ROLLBACK TO SAVEPOINT active_record_1\n```\n\nテスト関連のgemを最新にし、モンキーパッチを使わない解決策を適用したところ、この問題が解消した。\n\n## その他:Everyday Railsの読者のみなさんへ\n\n[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)では「従来の古い解決策」が載っているので、適宜「モンキーパッチを使わない解決策」を使ってやってください。\n\n具体的なスケジュールは未定ですが、Everyday RailsもRails 5に対応する予定なので、そのときには最新の解決策が載っているはずです :bow: :bow: :bow:\n\n## あわせて読みたい\n\nEveryday RailsのサンプルアプリケーションをRails 5.0にアップグレードする方法を解説した記事です。\nこちらもあわせてどうぞ。\n\n[これでもう怖くない!?Rails 4\\.1からRails 5\\.0にアップグレードする手順を動画付きで解説します \\- Qiita](http://qiita.com/jnchito/items/fa680e104d4bf49ae06f)\n",
    "body": "\n<h2>\n<span id=\"railsのフィーチャスペックでよく起きる問題\" class=\"fragment\"></span><a href=\"#rails%E3%81%AE%E3%83%95%E3%82%A3%E3%83%BC%E3%83%81%E3%83%A3%E3%82%B9%E3%83%9A%E3%83%83%E3%82%AF%E3%81%A7%E3%82%88%E3%81%8F%E8%B5%B7%E3%81%8D%E3%82%8B%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>Railsのフィーチャスペックでよく起きる問題</h2>\n\n<p>Railsのフィーチャスペックで<code>js: true</code>にする(テスト実行時にJavaScriptも動かす)場合、フィーチャスペック内で作成したテストデータがブラウザ(PoltergeistやSelenium webdriverで起動するFirefox)内で参照できない、といった問題がよく発生する。</p>\n\n<p>以下は問題が発生するフィーチャスペックの例である。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"nb\">require</span> <span class=\"s1\">'rails_helper'</span>\n\n<span class=\"n\">feature</span> <span class=\"s1\">'User management'</span> <span class=\"k\">do</span>\n  <span class=\"n\">scenario</span> <span class=\"s2\">\"adds a new user\"</span><span class=\"p\">,</span> <span class=\"ss\">js</span><span class=\"p\">:</span> <span class=\"kp\">true</span> <span class=\"k\">do</span>\n    <span class=\"n\">admin</span> <span class=\"o\">=</span> <span class=\"n\">create</span><span class=\"p\">(</span><span class=\"ss\">:admin</span><span class=\"p\">)</span>\n\n    <span class=\"n\">visit</span> <span class=\"n\">root_path</span>\n    <span class=\"n\">click_link</span> <span class=\"s1\">'Log In'</span>\n    <span class=\"n\">fill_in</span> <span class=\"s1\">'Email'</span><span class=\"p\">,</span> <span class=\"ss\">with</span><span class=\"p\">:</span> <span class=\"n\">admin</span><span class=\"o\">.</span><span class=\"n\">email</span>\n    <span class=\"n\">fill_in</span> <span class=\"s1\">'Password'</span><span class=\"p\">,</span> <span class=\"ss\">with</span><span class=\"p\">:</span> <span class=\"n\">admin</span><span class=\"o\">.</span><span class=\"n\">password</span>\n    <span class=\"c1\"># 上で作成したadminのデータがブラウザ側で参照できないのでログインに失敗する</span>\n    <span class=\"n\">click_button</span> <span class=\"s1\">'Log In'</span>\n\n    <span class=\"c1\"># ...</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<p>この問題はテストを実行しているコードのDBコネクションと、フィーチャスペック用のWebサーバーが使うDBコネクションが別々であるため、一方のDBトランザクション内で作成されたデータは他方のDBコネクションから参照できないことに起因している。</p>\n\n<h2>\n<span id=\"従来の古い解決策モンキーパッチあり\" class=\"fragment\"></span><a href=\"#%E5%BE%93%E6%9D%A5%E3%81%AE%E5%8F%A4%E3%81%84%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%83%A2%E3%83%B3%E3%82%AD%E3%83%BC%E3%83%91%E3%83%83%E3%83%81%E3%81%82%E3%82%8A\"><i class=\"fa fa-link\"></i></a>従来の古い解決策(モンキーパッチあり)</h2>\n\n<p>この問題を解決するために、特殊なモンキーパッチとDatabaseCleanerを使って、以下のような解決策がよく取られていた。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">spec/support/shared_db_connection.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span>\n  <span class=\"n\">mattr_accessor</span> <span class=\"ss\">:shared_connection</span>\n  <span class=\"vc\">@@shared_connection</span> <span class=\"o\">=</span> <span class=\"kp\">nil</span>\n\n  <span class=\"k\">def</span> <span class=\"nc\">self</span><span class=\"o\">.</span><span class=\"nf\">connection</span>\n    <span class=\"vc\">@@shared_connection</span> <span class=\"o\">||</span> <span class=\"n\">retrieve_connection</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n<span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span><span class=\"o\">.</span><span class=\"n\">shared_connection</span> <span class=\"o\">=</span> <span class=\"no\">ActiveRecord</span><span class=\"o\">::</span><span class=\"no\">Base</span><span class=\"o\">.</span><span class=\"n\">connection</span>\n</pre></div>\n</div>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">spec/rails_helper.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"no\">Dir</span><span class=\"o\">[</span><span class=\"no\">Rails</span><span class=\"o\">.</span><span class=\"n\">root</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"s2\">\"spec/support/**/*.rb\"</span><span class=\"p\">)</span><span class=\"o\">].</span><span class=\"n\">each</span> <span class=\"p\">{</span> <span class=\"o\">|</span><span class=\"n\">f</span><span class=\"o\">|</span> <span class=\"nb\">require</span> <span class=\"n\">f</span> <span class=\"p\">}</span>\n\n<span class=\"no\">RSpec</span><span class=\"o\">.</span><span class=\"n\">configure</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">config</span><span class=\"o\">|</span>\n  <span class=\"c1\"># ...</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">use_transactional_fixtures</span> <span class=\"o\">=</span> <span class=\"kp\">true</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">before</span><span class=\"p\">(</span><span class=\"ss\">:suite</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">strategy</span> <span class=\"o\">=</span> <span class=\"ss\">:transaction</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">clean_with</span> <span class=\"ss\">:truncation</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">around</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">)</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">example</span><span class=\"o\">|</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">cleaning</span> <span class=\"k\">do</span>\n      <span class=\"n\">example</span><span class=\"o\">.</span><span class=\"n\">run</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">after</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">clean</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div>\n</div>\n\n<p>これは何をやっているかというと、テスト実行コードとフィーチャスペック用のWebサーバーでDBコネクションを共有して、どちらからでも同じデータの読み書きをできるようにしている。</p>\n\n<h2>\n<span id=\"最近の解決策モンキーパッチなし\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E8%BF%91%E3%81%AE%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%83%A2%E3%83%B3%E3%82%AD%E3%83%BC%E3%83%91%E3%83%83%E3%83%81%E3%81%AA%E3%81%97\"><i class=\"fa fa-link\"></i></a>最近の解決策(モンキーパッチなし)</h2>\n\n<p>しかし、ActiveRecordにモンキーパッチを当てるというのは黒魔術的であまり気持ちがいいものではない。</p>\n\n<p>そこで、最新のDatabaseCleanerのREADMEでは、上記のモンキーパッチを使わずにこの問題を解決する方法が載っている。</p>\n\n<p><a href=\"https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example</a></p>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">spec/support/shared_db_connection.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"c1\"># 不要になるのでファイルごと削除</span>\n</pre></div>\n</div>\n\n<div class=\"code-frame\" data-lang=\"ruby\">\n<div class=\"code-lang\"><span class=\"bold\">spec/rails_helper.rb</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"no\">RSpec</span><span class=\"o\">.</span><span class=\"n\">configure</span> <span class=\"k\">do</span> <span class=\"o\">|</span><span class=\"n\">config</span><span class=\"o\">|</span>\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">use_transactional_fixtures</span> <span class=\"o\">=</span> <span class=\"kp\">false</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">before</span><span class=\"p\">(</span><span class=\"ss\">:suite</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"k\">if</span> <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">use_transactional_fixtures?</span>\n      <span class=\"k\">raise</span><span class=\"p\">(</span><span class=\"o\">&lt;&lt;-</span><span class=\"no\">MSG</span><span class=\"p\">)</span>\n<span class=\"sh\">        設定がおかしいので警告メッセージを表示 (省略)</span>\n<span class=\"no\">      MSG</span>\n    <span class=\"k\">end</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">clean_with</span><span class=\"p\">(</span><span class=\"ss\">:truncation</span><span class=\"p\">)</span>\n  <span class=\"k\">end</span>  \n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">before</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">strategy</span> <span class=\"o\">=</span> <span class=\"ss\">:transaction</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">before</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">,</span> <span class=\"ss\">type</span><span class=\"p\">:</span> <span class=\"ss\">:feature</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"n\">driver_shares_db_connection_with_specs</span> <span class=\"o\">=</span> <span class=\"no\">Capybara</span><span class=\"o\">.</span><span class=\"n\">current_driver</span> <span class=\"o\">==</span> <span class=\"ss\">:rack_test</span>\n\n    <span class=\"k\">if</span> <span class=\"o\">!</span><span class=\"n\">driver_shares_db_connection_with_specs</span>\n      <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">strategy</span> <span class=\"o\">=</span> <span class=\"ss\">:truncation</span>\n    <span class=\"k\">end</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">before</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">start</span>\n  <span class=\"k\">end</span>\n\n  <span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">append_after</span><span class=\"p\">(</span><span class=\"ss\">:each</span><span class=\"p\">)</span> <span class=\"k\">do</span>\n    <span class=\"no\">DatabaseCleaner</span><span class=\"o\">.</span><span class=\"n\">clean</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div>\n</div>\n\n<p>上記の設定は簡単にいうと次のようになっている。</p>\n\n<ul>\n<li>\n<code>js: true</code> の場合( <code>driver_shares_db_connection_with_specs</code> が <code>false</code> の場合)はトランザクションを使わずにデータを読み書きする。トランザクションを使わないので、どのDB接続からでも同じデータを参照できる。テストが終わったら全データをtruncateする。</li>\n<li>それ以外の場合はDB接続は1つしかないので、トランザクション内でデータを読み書きする。</li>\n</ul>\n\n<h2>\n<span id=\"動作確認した実行環境\" class=\"fragment\"></span><a href=\"#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%9F%E5%AE%9F%E8%A1%8C%E7%92%B0%E5%A2%83\"><i class=\"fa fa-link\"></i></a>動作確認した実行環境</h2>\n\n<p>筆者は以下の環境で「モンキーパッチを使わない解決策」が有効に機能することを確認した。</p>\n\n<p>古いバージョンのgemでは確認していないので、この解決策を適用する場合はテスト関連のgemはなるべく最新にすることが望ましい。</p>\n\n<ul>\n<li>Rails 5.0.0</li>\n<li>rspec-rails 3.5.0</li>\n<li>capybara 2.7.1</li>\n<li>database_cleaner 1.5.3</li>\n<li>selenium-webdriver 2.53.4</li>\n<li>factory_girl_rails 4.7.0</li>\n</ul>\n\n<h2>\n<span id=\"サンプルコード\" class=\"fragment\"></span><a href=\"#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89\"><i class=\"fa fa-link\"></i></a>サンプルコード</h2>\n\n<p><a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a>のサンプルアプリケーションをベースに上記の解決策を適用してみた。</p>\n\n<p>テストコードの内容は以下のGitHubリポジトリで確認できる。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0</a></p>\n\n<h2>\n<span id=\"この設定が解決するかもしれないトラブル\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%8C%E8%A7%A3%E6%B1%BA%E3%81%99%E3%82%8B%E3%81%8B%E3%82%82%E3%81%97%E3%82%8C%E3%81%AA%E3%81%84%E3%83%88%E3%83%A9%E3%83%96%E3%83%AB\"><i class=\"fa fa-link\"></i></a>この設定が解決するかもしれないトラブル</h2>\n\n<p>Rails 5にアップデートすると、リクエストスペックで次のようなエラーが発生してテストが落ちる場合がある。</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\nPG::ConnectionBad: PQsocket() can't get socket descriptor: ROLLBACK TO SAVEPOINT active_record_1\n</pre></div></div>\n\n<p>テスト関連のgemを最新にし、モンキーパッチを使わない解決策を適用したところ、この問題が解消した。</p>\n\n<h2>\n<span id=\"その他everyday-railsの読者のみなさんへ\" class=\"fragment\"></span><a href=\"#%E3%81%9D%E3%81%AE%E4%BB%96everyday-rails%E3%81%AE%E8%AA%AD%E8%80%85%E3%81%AE%E3%81%BF%E3%81%AA%E3%81%95%E3%82%93%E3%81%B8\"><i class=\"fa fa-link\"></i></a>その他:Everyday Railsの読者のみなさんへ</h2>\n\n<p><a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a>では「従来の古い解決策」が載っているので、適宜「モンキーパッチを使わない解決策」を使ってやってください。</p>\n\n<p>具体的なスケジュールは未定ですが、Everyday RailsもRails 5に対応する予定なので、そのときには最新の解決策が載っているはずです <img class=\"emoji\" title=\":bow:\" alt=\":bow:\" src=\"https://cdn.qiita.com/emoji/unicode/1f647.png\" height=\"20\" width=\"20\" align=\"absmiddle\"> <img class=\"emoji\" title=\":bow:\" alt=\":bow:\" src=\"https://cdn.qiita.com/emoji/unicode/1f647.png\" height=\"20\" width=\"20\" align=\"absmiddle\"> <img class=\"emoji\" title=\":bow:\" alt=\":bow:\" src=\"https://cdn.qiita.com/emoji/unicode/1f647.png\" height=\"20\" width=\"20\" align=\"absmiddle\"></p>\n\n<h2>\n<span id=\"あわせて読みたい\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>あわせて読みたい</h2>\n\n<p>Everyday RailsのサンプルアプリケーションをRails 5.0にアップグレードする方法を解説した記事です。<br>\nこちらもあわせてどうぞ。</p>\n\n<p><a href=\"http://qiita.com/jnchito/items/fa680e104d4bf49ae06f\" id=\"reference-aaeae14603bfa3eb2551\">これでもう怖くない!?Rails 4.1からRails 5.0にアップグレードする手順を動画付きで解説します - Qiita</a></p>\n",
    "stock_users": [
        "tkawa",
        "ikedanoda",
        "K_Matsumoto",
        "ruzia",
        "chinmo@github",
        "DolphinJP",
        "Shunta_Suzuki",
        "mesiobass",
        "nwebcraft",
        "whitecrane",
        "myblue",
        "notti"
    ]
}, {
    "id": 407060,
    "uuid": "fa680e104d4bf49ae06f",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "これでもう怖くない!?Rails 4.1からRails 5.0にアップグレードする手順を動画付きで解説します",
    "created_at": "2016-07-11 05:19:57 +0900",
    "updated_at": "2016-07-22 13:23:40 +0900",
    "created_at_in_words": "約2ヶ月",
    "updated_at_in_words": "約2ヶ月",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }, {
        "name": "RSpec",
        "url_name": "rspec",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/aba2fc611bc7fe28b0c70347e68541ef7e186ac5/medium.jpg?1407701144",
        "following": false,
        "versions": []
    }, {
        "name": "RubyMine",
        "url_name": "rubymine",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/3f916260c0e6e906ecad85e4d0c4dad98b5d361d/medium.jpg?1416859796",
        "following": false,
        "versions": []
    }],
    "stock_count": 118,
    "comment_count": 0,
    "url": "http://qiita.com/jnchito/items/fa680e104d4bf49ae06f",
    "created_at_as_seconds": 1468181997,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## はじめに\n\nみなさんお待ちかねのRails 5.0が先日リリースされました。\nこれまでRailsを使って開発してきた方は、おそらく既存のRailsアプリケーションではRails 4系を使っているんじゃないかと思います。(Rails 3以前の方もいるかもしれませんが・・・)\n\nしかし、中には「Rails 5にアップグレードしたいけど、やり方がよくわからない・・・」と困っている方もいるんじゃないでしょうか?\nそこでこの記事ではRails 4.1で作ったサンプルアプリケーションをRails 5.0にアップグレードする手順を説明します。\n\n## (この記事よりも詳しい)解説動画はこちら!\n\nアップグレードの手順はYouTubeにアップした動画の中で詳しく説明しています。\nこの記事は動画の説明内容を簡単にピックアップするだけにとどめているので、Rails 5にアップグレードしようと考えている方は、必ず動画の方もチェックしてください!\n\n[![Screen Shot 2016-07-09 at 09.25.12.png](https://qiita-image-store.s3.amazonaws.com/0/7465/99df642a-3298-a23a-26a7-16f64d6e532e.png \"Screen Shot 2016-07-09 at 09.25.12.png\") これでもう怖くない!?Rails 4\\.1からRails 5\\.0にアップグレードする手順を動画付きで解説します \\- YouTube](https://www.youtube.com/watch?v=0FRNPY7WJ7w)\n\nなお、今回の動画は1時間ちょっとあるので、 **1.5倍~2.0倍ぐらいの再生速度** で再生することをオススメします。\n\n### RubyMineも活用しています\n\n動画の中ではRubyMineをメインのエディタ(IDE)として使用しています。\nRSpecの実行やdiffの確認など、RubyMineの便利機能がいろいろ見られるので、RubyMineに興味がある人もぜひ動画をチェックしてみてください。\n\n\n\n[![Screen Shot 2016-07-09 at 09.25.44.png](https://qiita-image-store.s3.amazonaws.com/0/7465/c99b3670-1761-4b23-9cd8-88996ce0c215.png \"Screen Shot 2016-07-09 at 09.25.44.png\")](https://www.youtube.com/watch?v=0FRNPY7WJ7w)\n\n\n## 今回使用するサンプルアプリケーション\n\nこの記事では電子書籍「[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)」で使われているサンプルアプリケーションをRails 5にアップグレードします。\n\n![Screen Shot 2016-07-09 at 09.29.25.png](https://qiita-image-store.s3.amazonaws.com/0/7465/76ff3cfe-9ede-c314-1013-ec8eb68400c3.png \"Screen Shot 2016-07-09 at 09.29.25.png\")\n\n\nこのアプリケーションのオリジナルの動作環境は **Ruby 2.1.1 + Rails 4.1.1** です。\n\nなお、Everyday Railsのサンプルアプリケーションは使用するものの、この記事自体は書籍「[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)」の内容そのものとは関係ありません。(あくまで個人の趣味で書いています)\n\n### サンプルアプリケーションのソースコード\n\nサンプルアプリケーションのオリジナルのソースコードはGitHubに公開されています。\nですので、誰でも参照することが可能です。\n\nhttps://github.com/everydayrails/rails-4-1-rspec-3-0\n\n実際にアップグレードをやってみたい方は自分のアカウントにフォークして試してみてください。\n\n## 作業のおおまかな流れ\n\nアップグレード作業をざっくり説明するとこんな感じです。\nなお、カッコ内の数字は動画の開始ポイントを示しています。\n\n1. 公式のアップグレードガイドを確認する(1:20)\n2. アップグレード用の作業ブランチを作る(3:25)\n3. 現状のテストが全部パスすることを確認する(3:50)\n4. Rubyのバージョンを最新版にアップグレードする(6:30)\n5. テスト関連のgemを最新版にアップグレードする(10:15)\n6. Rails 4.2.6にアップグレードする(13:50)\n7. Rails 5.0.0にアップグレードする(30:15)\n8. Rails 5 らしく書き直す(1:02:40)\n\nそれでは以下で詳しい手順を説明していきます。\n\n## 1. 公式のアップグレードガイドを確認する(1:20)\n\n\nアップグレードの手順はRailsガイドでも説明されています。\nまずは公式の情報に目を通して、何がどう変わるのか、どういった点に気をつければいいのかを確認しておきましょう。\n\n<s>なお、執筆時点(2016年7月9日)では、日本語訳が読めるのはRails 4.2までです。\nRails 5.0へのアップグレード手順は英語版を読む必要があります。</s>\n日本語訳もRails 5に対応しました。(2016/7/14)\n\n[Rails アップグレードガイド \\| Rails ガイド](http://railsguides.jp/upgrading_ruby_on_rails.html)\n\nまた、リリースノートの日本語訳もあります。\nこちらも一通りチェックしておくといいでしょう。\n\n[Ruby on Rails 4\\.2 リリースノート \\| Rails ガイド](http://railsguides.jp/4_2_release_notes.html)\n\n[Ruby on Rails 5\\.0 リリースノート \\| Rails ガイド](http://railsguides.jp/5_0_release_notes.html)\n\n## 2. アップグレード用の作業ブランチを作る(3:25)\n\n\nいきなりmasterブランチで作業せず、専用の作業ブランチを作ってから、最後にmasterにマージするようにしましょう。\n\n今回は rails-5-0 という作業ブランチを作りました。\n\n## 3. 現状のテストが全部パスすることを確認する(3:50)\n\n\nもし、最初からテストが壊れていると、テストが失敗してもアップグレードが原因かどうか区別が付きません。\nなので、現状のテストが全部パスすることを確認します。\n\n今回は selenium-webdriver のバージョンが古くてテストが途中で止まってしまったため、selenium-webdriver をアップデートする必要がありました。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/995b0eb6a9cb798454eda1cc713035de25ebe305)\n\n## 4. Rubyのバージョンを最新版にアップグレードする(6:30)\n\n\n**Rails 5はRuby 2.2.2以上でないと動きません。**\nなので、このタイミングで最新版のRubyにアップグレードしておきましょう。\n\n執筆時点(2016年7月9日)ではRuby 2.3.1が最新なので、このバージョンにアップデートします。\nRubyのバージョンを上げたら、テストが全部パスすることを確認します。\n\nなお、今回は bundle install 実行時に、json gemのインストールでエラーが発生しました。\nそこで、`bundle update json`を実行して json gemのバージョンを上げてこのエラーを解決しました。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/9f7600280b9ce859da4c77e86b83bfb95ecac59a)\n\n## 5. テスト関連のgemを最新版にアップグレードする(10:15)\n\nrspec-railsはバージョンが古いと最新のRailsでうまく動作しないことがあります。\nそこで、Railsのバージョンを上げる前に、rspec-railsを最新版(執筆時点の最新版は3.5.0)にアップグレードします。\n\n`bundle update rspec-rails`を実行した際に依存関係でエラーが出る場合は、エラーの原因になっているgemも一緒にアップデートします。\n今回は guard-rspec も一緒にアップデートする必要がありました。\n\nrspec-railsのバージョンを上げたら、テストが全部パスすることも確認しておきましょう。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/df4fc44cfd724bc391befa75e6c1bd3446e6b7d6)\n\n## 6. Rails 4.2.6にアップグレードする(13:50)\n\n\nいきなり4.1から5.0に上げるよりも、先にRails 4系の最終版であるRails 4.2にアップグレードしてから5.0にアップグレードする方が安全です。\n\nというわけで、まずGemfileで `gem 'rails', '4.2.6'` を指定し、`bundle update rails`を実行します。\n\ngemのインストールが終わったら、`bin/rake rails:update`を実行して設定ファイル等をアップデートします。\nこのときdiffを確認(d)しながら、上書き(Y)、またはスキップ(n)を選択します。\n\n`bin/rake rails:update`の実行が終わったら、gitのdiffを確認しながら必要に応じて上書きされたコードを元に戻していきます。\n\nここまで終わったらテストを実行して結果を確認します。\n\n今回はテスト実行時に turbolinks のバージョンが古いことに起因するエラーが発生していたので、`bundle update turbolinks` で turbolinks のバージョンも上げました。\n\nテストが全部パスしたら、念のためブラウザ上でも動作確認しておきます。\n\n特に問題が無さそうであれば、これでRails 4.2.6へのアップグレードは完了です。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/c48efc646cfda1f529a180a00b86df5e0db4c069)\n\n## 7. Rails 5.0.0にアップグレードする(30:15)\n\nさて、いよいよここからがRails 5.0へのアップグレード作業です。\n基本的な流れはRails 4.2.6へのアップグレードと同じです。\n\nまずGemfileで `gem 'rails', '5.0.0'` を指定し、`bundle update rails`を実行します。\n\nしかし依存関係でエラーが出るので、関連するgemも一緒に最新版にアップグレードします。\n\n今回は以下のgemも一緒にアップデートする必要がありました。\n\n- factory-girl\n- jquery-rails\n- sass-rails\n- coffee-rails\n- jbuilder\n- shoulda-matchers\n\n続いて、`rails app:update`を実行して設定ファイル等をアップデートします。\n(Rails 4系とは微妙にコマンドが変わっているので注意!)\n\n「diffを確認(d)しながら、上書き(Y)、またはスキップ(n)を選択」、「一通り終わったらgitのdiffを確認しながら必要に応じて上書きされたコードを元に戻す」という流れはRails 4.2.6のときと同じです。\n\nここまで終わったらテストを実行して結果を確認します。\n\n今回はテスト実行中に発生したエラーや警告に対して、以下のような対応をしました。\n\n- shoulda-matchers用の設定をrails_helper.rbに追加\n- コントローラのテストの仕様が変わったので、[rails-controller-testing](https://github.com/rails/rails-controller-testing) gemを追加\n- CapybaraとDatabaseCleanerを最新版にアップグレード\n- コントローラスペックのパラメータを`params:`で囲む\n  - 例:`get :index, letter: 'S'` => `get :index, params: { letter: 'S' }`\n\nテストを実行してすべてのエラーと警告が出なくなったら、ブラウザ上でも動作確認しておきます。\n\n特に問題が無さそうであれば、これでRails 5.0.0へのアップグレードは完了です。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/a83bb29db140f06cded3781247a83bc79d34abdd)\n\n## 8. Rails 5 らしく書き直す(1:02:40)\n\nアプリケーションの動きだけ見るとここで終わっても問題無さそうなのですが、せっかくなのでRails 5 らしいお作法に従ってコードを直しておきましょう。\n\n今回は config/application.rb に書いてあった generators の設定値を config/initializers/generators.rb に移動させました。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/a85038e4926cb87be3696af518b2576bdd020f55)\n\nそれから、app/models/application_record.rb を作成し、モデルの継承元を`ActiveRecord::Base`から`ApplicationRecord`に変更しました。\n\n[(diffを確認する)](https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/1f6e79c21782e472424593a11c0b182b198b2d43)\n\nここでも修正後にテストを実行してテストが全部パスすることを確認します。\n\n今回のサンプルアプリケーションではこの2点だけを修正しましたが、みなさんのアプリケーションでは他にも修正すべきポイントがあるかもしれません。\n詳しくはRailsガイドをチェックしてみてください。\n\n[A Guide for Upgrading Ruby on Rails — Ruby on Rails Guides](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html)\n\n本記事で説明するアップグレード手順はこれでおしまいです。\nお疲れ様でした!\n\n## Rails 5.0版のコードはこちら\n\n動画の中で説明したコードは僕のGitHubリポジトリにpushしてあります。\nよかったらこちらも参考にしてみてください。\n\nhttps://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0\n\n## 参考になりそうな他のブログ\n\n以下のブログにもRails 5へのアップグレード手順が詳しく解説されています。\n自分でトライしてみて解決できない問題が出てきた場合は、こちらのブログを読むと解決するかもしれません。\n\n[最速で Forkwell を Rails 5 にアップグレードしてみました \\- Grooves開発ブログ](http://tech.grooves.com/entry/2016/07/01/184458)\n\n## まとめ\n\nというわけで、この記事ではRails 4.1からRails 5.0にアップグレードする手順を説明しました。\n\n冒頭にも書いたように動画の方がより詳しく説明しているので、アップグレードにチャレンジする方はぜひ動画もチェックしてみてください。\n\n[![Screen Shot 2016-07-09 at 09.25.12.png](https://qiita-image-store.s3.amazonaws.com/0/7465/99df642a-3298-a23a-26a7-16f64d6e532e.png \"Screen Shot 2016-07-09 at 09.25.12.png\") これでもう怖くない!?Rails 4\\.1からRails 5\\.0にアップグレードする手順を動画付きで解説します \\- YouTube](https://www.youtube.com/watch?v=0FRNPY7WJ7w)\n\nまた、動画を見てもらうとよくわかると思いますが、テストを自動化しているかどうかでアップグレードの作業効率に雲泥の差が出てきます。\n「Railsのテストの書き方がわからない」「テストを書くのはどうしても苦手」という方は電子書籍「[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)」を読んで、テストの書き方をマスターしましょう!(宣伝)\n\n[Everyday Rails - RSpecによるRailsテスト入門![Screen Shot 2016-06-15 at 09.14.08.png](https://qiita-image-store.s3.amazonaws.com/0/7465/5802d72a-2002-f5ef-70a9-550dacc1185f.png \"Screen Shot 2016-06-15 at 09.14.08.png\")\n](https://leanpub.com/everydayrailsrspec-jp/) \n\n### あわせて読みたい\n\n過去に書いたRails 5関連のQiita記事です。\nよかったらこちらもどうぞ。\n\n[Rails 5\\.0で追加される主な新機能(Ruby on Rails公式ブログより) \\- Qiita](http://qiita.com/jnchito/items/6a93320334c48b967dfb)\n\n[Rails 5 \\+ ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より) \\- Qiita](http://qiita.com/jnchito/items/aec75fab42804287d71b)\n\nテストコード関連のgemを最新にしたタイミングで、こちらの対応もあわせて実施すると良いかもしれません。\n\n[JavaScriptを実行するフィーチャスペックでActiveRecordへのモンキーパッチを無くす方法 \\- Qiita](http://qiita.com/jnchito/items/ec04537b352123cab288)\n",
    "body": "\n<h2>\n<span id=\"はじめに\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"><i class=\"fa fa-link\"></i></a>はじめに</h2>\n\n<p>みなさんお待ちかねのRails 5.0が先日リリースされました。<br>\nこれまでRailsを使って開発してきた方は、おそらく既存のRailsアプリケーションではRails 4系を使っているんじゃないかと思います。(Rails 3以前の方もいるかもしれませんが・・・)</p>\n\n<p>しかし、中には「Rails 5にアップグレードしたいけど、やり方がよくわからない・・・」と困っている方もいるんじゃないでしょうか?<br>\nそこでこの記事ではRails 4.1で作ったサンプルアプリケーションをRails 5.0にアップグレードする手順を説明します。</p>\n\n<h2>\n<span id=\"この記事よりも詳しい解説動画はこちら\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%88%E3%82%8A%E3%82%82%E8%A9%B3%E3%81%97%E3%81%84%E8%A7%A3%E8%AA%AC%E5%8B%95%E7%94%BB%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89\"><i class=\"fa fa-link\"></i></a>(この記事よりも詳しい)解説動画はこちら!</h2>\n\n<p>アップグレードの手順はYouTubeにアップした動画の中で詳しく説明しています。<br>\nこの記事は動画の説明内容を簡単にピックアップするだけにとどめているので、Rails 5にアップグレードしようと考えている方は、必ず動画の方もチェックしてください!</p>\n\n<p><a href=\"https://www.youtube.com/watch?v=0FRNPY7WJ7w\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/99df642a-3298-a23a-26a7-16f64d6e532e.png\" alt=\"Screen Shot 2016-07-09 at 09.25.12.png\" title=\"Screen Shot 2016-07-09 at 09.25.12.png\"> これでもう怖くない!?Rails 4.1からRails 5.0にアップグレードする手順を動画付きで解説します - YouTube</a></p>\n\n<p>なお、今回の動画は1時間ちょっとあるので、 <strong>1.5倍~2.0倍ぐらいの再生速度</strong> で再生することをオススメします。</p>\n\n<h3>\n<span id=\"rubymineも活用しています\" class=\"fragment\"></span><a href=\"#rubymine%E3%82%82%E6%B4%BB%E7%94%A8%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99\"><i class=\"fa fa-link\"></i></a>RubyMineも活用しています</h3>\n\n<p>動画の中ではRubyMineをメインのエディタ(IDE)として使用しています。<br>\nRSpecの実行やdiffの確認など、RubyMineの便利機能がいろいろ見られるので、RubyMineに興味がある人もぜひ動画をチェックしてみてください。</p>\n\n<p><a href=\"https://www.youtube.com/watch?v=0FRNPY7WJ7w\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/c99b3670-1761-4b23-9cd8-88996ce0c215.png\" alt=\"Screen Shot 2016-07-09 at 09.25.44.png\" title=\"Screen Shot 2016-07-09 at 09.25.44.png\"></a></p>\n\n<h2>\n<span id=\"今回使用するサンプルアプリケーション\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E5%9B%9E%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3\"><i class=\"fa fa-link\"></i></a>今回使用するサンプルアプリケーション</h2>\n\n<p>この記事では電子書籍「<a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a>」で使われているサンプルアプリケーションをRails 5にアップグレードします。</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/76ff3cfe-9ede-c314-1013-ec8eb68400c3.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/76ff3cfe-9ede-c314-1013-ec8eb68400c3.png\" alt=\"Screen Shot 2016-07-09 at 09.29.25.png\" title=\"Screen Shot 2016-07-09 at 09.29.25.png\"></a></p>\n\n<p>このアプリケーションのオリジナルの動作環境は <strong>Ruby 2.1.1 + Rails 4.1.1</strong> です。</p>\n\n<p>なお、Everyday Railsのサンプルアプリケーションは使用するものの、この記事自体は書籍「<a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a>」の内容そのものとは関係ありません。(あくまで個人の趣味で書いています)</p>\n\n<h3>\n<span id=\"サンプルアプリケーションのソースコード\" class=\"fragment\"></span><a href=\"#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89\"><i class=\"fa fa-link\"></i></a>サンプルアプリケーションのソースコード</h3>\n\n<p>サンプルアプリケーションのオリジナルのソースコードはGitHubに公開されています。<br>\nですので、誰でも参照することが可能です。</p>\n\n<p><a href=\"https://github.com/everydayrails/rails-4-1-rspec-3-0\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/everydayrails/rails-4-1-rspec-3-0</a></p>\n\n<p>実際にアップグレードをやってみたい方は自分のアカウントにフォークして試してみてください。</p>\n\n<h2>\n<span id=\"作業のおおまかな流れ\" class=\"fragment\"></span><a href=\"#%E4%BD%9C%E6%A5%AD%E3%81%AE%E3%81%8A%E3%81%8A%E3%81%BE%E3%81%8B%E3%81%AA%E6%B5%81%E3%82%8C\"><i class=\"fa fa-link\"></i></a>作業のおおまかな流れ</h2>\n\n<p>アップグレード作業をざっくり説明するとこんな感じです。<br>\nなお、カッコ内の数字は動画の開始ポイントを示しています。</p>\n\n<ol>\n<li>公式のアップグレードガイドを確認する(1:20)</li>\n<li>アップグレード用の作業ブランチを作る(3:25)</li>\n<li>現状のテストが全部パスすることを確認する(3:50)</li>\n<li>Rubyのバージョンを最新版にアップグレードする(6:30)</li>\n<li>テスト関連のgemを最新版にアップグレードする(10:15)</li>\n<li>Rails 4.2.6にアップグレードする(13:50)</li>\n<li>Rails 5.0.0にアップグレードする(30:15)</li>\n<li>Rails 5 らしく書き直す(1:02:40)</li>\n</ol>\n\n<p>それでは以下で詳しい手順を説明していきます。</p>\n\n<h2>\n<span id=\"1-公式のアップグレードガイドを確認する120\" class=\"fragment\"></span><a href=\"#1-%E5%85%AC%E5%BC%8F%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%82%AC%E3%82%A4%E3%83%89%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B120\"><i class=\"fa fa-link\"></i></a>1. 公式のアップグレードガイドを確認する(1:20)</h2>\n\n<p>アップグレードの手順はRailsガイドでも説明されています。<br>\nまずは公式の情報に目を通して、何がどう変わるのか、どういった点に気をつければいいのかを確認しておきましょう。</p>\n\n<p><s>なお、執筆時点(2016年7月9日)では、日本語訳が読めるのはRails 4.2までです。<br>\nRails 5.0へのアップグレード手順は英語版を読む必要があります。</s><br>\n日本語訳もRails 5に対応しました。(2016/7/14)</p>\n\n<p><a href=\"http://railsguides.jp/upgrading_ruby_on_rails.html\" rel=\"nofollow\" target=\"_blank\">Rails アップグレードガイド | Rails ガイド</a></p>\n\n<p>また、リリースノートの日本語訳もあります。<br>\nこちらも一通りチェックしておくといいでしょう。</p>\n\n<p><a href=\"http://railsguides.jp/4_2_release_notes.html\" rel=\"nofollow\" target=\"_blank\">Ruby on Rails 4.2 リリースノート | Rails ガイド</a></p>\n\n<p><a href=\"http://railsguides.jp/5_0_release_notes.html\" rel=\"nofollow\" target=\"_blank\">Ruby on Rails 5.0 リリースノート | Rails ガイド</a></p>\n\n<h2>\n<span id=\"2-アップグレード用の作業ブランチを作る325\" class=\"fragment\"></span><a href=\"#2-%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E7%94%A8%E3%81%AE%E4%BD%9C%E6%A5%AD%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E4%BD%9C%E3%82%8B325\"><i class=\"fa fa-link\"></i></a>2. アップグレード用の作業ブランチを作る(3:25)</h2>\n\n<p>いきなりmasterブランチで作業せず、専用の作業ブランチを作ってから、最後にmasterにマージするようにしましょう。</p>\n\n<p>今回は rails-5-0 という作業ブランチを作りました。</p>\n\n<h2>\n<span id=\"3-現状のテストが全部パスすることを確認する350\" class=\"fragment\"></span><a href=\"#3-%E7%8F%BE%E7%8A%B6%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E5%85%A8%E9%83%A8%E3%83%91%E3%82%B9%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B350\"><i class=\"fa fa-link\"></i></a>3. 現状のテストが全部パスすることを確認する(3:50)</h2>\n\n<p>もし、最初からテストが壊れていると、テストが失敗してもアップグレードが原因かどうか区別が付きません。<br>\nなので、現状のテストが全部パスすることを確認します。</p>\n\n<p>今回は selenium-webdriver のバージョンが古くてテストが途中で止まってしまったため、selenium-webdriver をアップデートする必要がありました。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/995b0eb6a9cb798454eda1cc713035de25ebe305\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<h2>\n<span id=\"4-rubyのバージョンを最新版にアップグレードする630\" class=\"fragment\"></span><a href=\"#4-ruby%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E6%9C%80%E6%96%B0%E7%89%88%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B630\"><i class=\"fa fa-link\"></i></a>4. Rubyのバージョンを最新版にアップグレードする(6:30)</h2>\n\n<p><strong>Rails 5はRuby 2.2.2以上でないと動きません。</strong><br>\nなので、このタイミングで最新版のRubyにアップグレードしておきましょう。</p>\n\n<p>執筆時点(2016年7月9日)ではRuby 2.3.1が最新なので、このバージョンにアップデートします。<br>\nRubyのバージョンを上げたら、テストが全部パスすることを確認します。</p>\n\n<p>なお、今回は bundle install 実行時に、json gemのインストールでエラーが発生しました。<br>\nそこで、<code>bundle update json</code>を実行して json gemのバージョンを上げてこのエラーを解決しました。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/9f7600280b9ce859da4c77e86b83bfb95ecac59a\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<h2>\n<span id=\"5-テスト関連のgemを最新版にアップグレードする1015\" class=\"fragment\"></span><a href=\"#5-%E3%83%86%E3%82%B9%E3%83%88%E9%96%A2%E9%80%A3%E3%81%AEgem%E3%82%92%E6%9C%80%E6%96%B0%E7%89%88%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B1015\"><i class=\"fa fa-link\"></i></a>5. テスト関連のgemを最新版にアップグレードする(10:15)</h2>\n\n<p>rspec-railsはバージョンが古いと最新のRailsでうまく動作しないことがあります。<br>\nそこで、Railsのバージョンを上げる前に、rspec-railsを最新版(執筆時点の最新版は3.5.0)にアップグレードします。</p>\n\n<p><code>bundle update rspec-rails</code>を実行した際に依存関係でエラーが出る場合は、エラーの原因になっているgemも一緒にアップデートします。<br>\n今回は guard-rspec も一緒にアップデートする必要がありました。</p>\n\n<p>rspec-railsのバージョンを上げたら、テストが全部パスすることも確認しておきましょう。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/df4fc44cfd724bc391befa75e6c1bd3446e6b7d6\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<h2>\n<span id=\"6-rails-426にアップグレードする1350\" class=\"fragment\"></span><a href=\"#6-rails-426%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B1350\"><i class=\"fa fa-link\"></i></a>6. Rails 4.2.6にアップグレードする(13:50)</h2>\n\n<p>いきなり4.1から5.0に上げるよりも、先にRails 4系の最終版であるRails 4.2にアップグレードしてから5.0にアップグレードする方が安全です。</p>\n\n<p>というわけで、まずGemfileで <code>gem 'rails', '4.2.6'</code> を指定し、<code>bundle update rails</code>を実行します。</p>\n\n<p>gemのインストールが終わったら、<code>bin/rake rails:update</code>を実行して設定ファイル等をアップデートします。<br>\nこのときdiffを確認(d)しながら、上書き(Y)、またはスキップ(n)を選択します。</p>\n\n<p><code>bin/rake rails:update</code>の実行が終わったら、gitのdiffを確認しながら必要に応じて上書きされたコードを元に戻していきます。</p>\n\n<p>ここまで終わったらテストを実行して結果を確認します。</p>\n\n<p>今回はテスト実行時に turbolinks のバージョンが古いことに起因するエラーが発生していたので、<code>bundle update turbolinks</code> で turbolinks のバージョンも上げました。</p>\n\n<p>テストが全部パスしたら、念のためブラウザ上でも動作確認しておきます。</p>\n\n<p>特に問題が無さそうであれば、これでRails 4.2.6へのアップグレードは完了です。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/c48efc646cfda1f529a180a00b86df5e0db4c069\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<h2>\n<span id=\"7-rails-500にアップグレードする3015\" class=\"fragment\"></span><a href=\"#7-rails-500%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B3015\"><i class=\"fa fa-link\"></i></a>7. Rails 5.0.0にアップグレードする(30:15)</h2>\n\n<p>さて、いよいよここからがRails 5.0へのアップグレード作業です。<br>\n基本的な流れはRails 4.2.6へのアップグレードと同じです。</p>\n\n<p>まずGemfileで <code>gem 'rails', '5.0.0'</code> を指定し、<code>bundle update rails</code>を実行します。</p>\n\n<p>しかし依存関係でエラーが出るので、関連するgemも一緒に最新版にアップグレードします。</p>\n\n<p>今回は以下のgemも一緒にアップデートする必要がありました。</p>\n\n<ul>\n<li>factory-girl</li>\n<li>jquery-rails</li>\n<li>sass-rails</li>\n<li>coffee-rails</li>\n<li>jbuilder</li>\n<li>shoulda-matchers</li>\n</ul>\n\n<p>続いて、<code>rails app:update</code>を実行して設定ファイル等をアップデートします。<br>\n(Rails 4系とは微妙にコマンドが変わっているので注意!)</p>\n\n<p>「diffを確認(d)しながら、上書き(Y)、またはスキップ(n)を選択」、「一通り終わったらgitのdiffを確認しながら必要に応じて上書きされたコードを元に戻す」という流れはRails 4.2.6のときと同じです。</p>\n\n<p>ここまで終わったらテストを実行して結果を確認します。</p>\n\n<p>今回はテスト実行中に発生したエラーや警告に対して、以下のような対応をしました。</p>\n\n<ul>\n<li>shoulda-matchers用の設定をrails_helper.rbに追加</li>\n<li>コントローラのテストの仕様が変わったので、<a href=\"https://github.com/rails/rails-controller-testing\" rel=\"nofollow\" target=\"_blank\">rails-controller-testing</a> gemを追加</li>\n<li>CapybaraとDatabaseCleanerを最新版にアップグレード</li>\n<li>コントローラスペックのパラメータを<code>params:</code>で囲む\n\n<ul>\n<li>例:<code>get :index, letter: 'S'</code> =&gt; <code>get :index, params: { letter: 'S' }</code>\n</li>\n</ul>\n</li>\n</ul>\n\n<p>テストを実行してすべてのエラーと警告が出なくなったら、ブラウザ上でも動作確認しておきます。</p>\n\n<p>特に問題が無さそうであれば、これでRails 5.0.0へのアップグレードは完了です。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/a83bb29db140f06cded3781247a83bc79d34abdd\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<h2>\n<span id=\"8-rails-5-らしく書き直す10240\" class=\"fragment\"></span><a href=\"#8-rails-5-%E3%82%89%E3%81%97%E3%81%8F%E6%9B%B8%E3%81%8D%E7%9B%B4%E3%81%9910240\"><i class=\"fa fa-link\"></i></a>8. Rails 5 らしく書き直す(1:02:40)</h2>\n\n<p>アプリケーションの動きだけ見るとここで終わっても問題無さそうなのですが、せっかくなのでRails 5 らしいお作法に従ってコードを直しておきましょう。</p>\n\n<p>今回は config/application.rb に書いてあった generators の設定値を config/initializers/generators.rb に移動させました。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/a85038e4926cb87be3696af518b2576bdd020f55\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<p>それから、app/models/application_record.rb を作成し、モデルの継承元を<code>ActiveRecord::Base</code>から<code>ApplicationRecord</code>に変更しました。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/commit/1f6e79c21782e472424593a11c0b182b198b2d43\" rel=\"nofollow\" target=\"_blank\">(diffを確認する)</a></p>\n\n<p>ここでも修正後にテストを実行してテストが全部パスすることを確認します。</p>\n\n<p>今回のサンプルアプリケーションではこの2点だけを修正しましたが、みなさんのアプリケーションでは他にも修正すべきポイントがあるかもしれません。<br>\n詳しくはRailsガイドをチェックしてみてください。</p>\n\n<p><a href=\"http://guides.rubyonrails.org/upgrading_ruby_on_rails.html\" rel=\"nofollow\" target=\"_blank\">A Guide for Upgrading Ruby on Rails — Ruby on Rails Guides</a></p>\n\n<p>本記事で説明するアップグレード手順はこれでおしまいです。<br>\nお疲れ様でした!</p>\n\n<h2>\n<span id=\"rails-50版のコードはこちら\" class=\"fragment\"></span><a href=\"#rails-50%E7%89%88%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89\"><i class=\"fa fa-link\"></i></a>Rails 5.0版のコードはこちら</h2>\n\n<p>動画の中で説明したコードは僕のGitHubリポジトリにpushしてあります。<br>\nよかったらこちらも参考にしてみてください。</p>\n\n<p><a href=\"https://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/JunichiIto/rails-4-1-rspec-3-0/tree/rail-5-0</a></p>\n\n<h2>\n<span id=\"参考になりそうな他のブログ\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%9D%E3%81%86%E3%81%AA%E4%BB%96%E3%81%AE%E3%83%96%E3%83%AD%E3%82%B0\"><i class=\"fa fa-link\"></i></a>参考になりそうな他のブログ</h2>\n\n<p>以下のブログにもRails 5へのアップグレード手順が詳しく解説されています。<br>\n自分でトライしてみて解決できない問題が出てきた場合は、こちらのブログを読むと解決するかもしれません。</p>\n\n<p><a href=\"http://tech.grooves.com/entry/2016/07/01/184458\" rel=\"nofollow\" target=\"_blank\">最速で Forkwell を Rails 5 にアップグレードしてみました - Grooves開発ブログ</a></p>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<p>というわけで、この記事ではRails 4.1からRails 5.0にアップグレードする手順を説明しました。</p>\n\n<p>冒頭にも書いたように動画の方がより詳しく説明しているので、アップグレードにチャレンジする方はぜひ動画もチェックしてみてください。</p>\n\n<p><a href=\"https://www.youtube.com/watch?v=0FRNPY7WJ7w\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/99df642a-3298-a23a-26a7-16f64d6e532e.png\" alt=\"Screen Shot 2016-07-09 at 09.25.12.png\" title=\"Screen Shot 2016-07-09 at 09.25.12.png\"> これでもう怖くない!?Rails 4.1からRails 5.0にアップグレードする手順を動画付きで解説します - YouTube</a></p>\n\n<p>また、動画を見てもらうとよくわかると思いますが、テストを自動化しているかどうかでアップグレードの作業効率に雲泥の差が出てきます。<br>\n「Railsのテストの書き方がわからない」「テストを書くのはどうしても苦手」という方は電子書籍「<a href=\"https://leanpub.com/everydayrailsrspec-jp\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門</a>」を読んで、テストの書き方をマスターしましょう!(宣伝)</p>\n\n<p><a href=\"https://leanpub.com/everydayrailsrspec-jp/\" rel=\"nofollow\" target=\"_blank\">Everyday Rails - RSpecによるRailsテスト入門<img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/5802d72a-2002-f5ef-70a9-550dacc1185f.png\" alt=\"Screen Shot 2016-06-15 at 09.14.08.png\" title=\"Screen Shot 2016-06-15 at 09.14.08.png\"><br>\n</a> </p>\n\n<h3>\n<span id=\"あわせて読みたい\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>あわせて読みたい</h3>\n\n<p>過去に書いたRails 5関連のQiita記事です。<br>\nよかったらこちらもどうぞ。</p>\n\n<p><a href=\"http://qiita.com/jnchito/items/6a93320334c48b967dfb\" id=\"reference-45baa6089a13a6b2cc12\">Rails 5.0で追加される主な新機能(Ruby on Rails公式ブログより) - Qiita</a></p>\n\n<p><a href=\"http://qiita.com/jnchito/items/aec75fab42804287d71b\" id=\"reference-e7ad6d203fc8f5adfb97\">Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より) - Qiita</a></p>\n\n<p>テストコード関連のgemを最新にしたタイミングで、こちらの対応もあわせて実施すると良いかもしれません。</p>\n\n<p><a href=\"http://qiita.com/jnchito/items/ec04537b352123cab288\" id=\"reference-79a002686ed45d25cd7e\">JavaScriptを実行するフィーチャスペックでActiveRecordへのモンキーパッチを無くす方法 - Qiita</a></p>\n",
    "stock_users": [
        "safu0826",
        "Kuchitama",
        "mat_aki",
        "fakestarbaby",
        "ppworks",
        "hfujimoto@github",
        "yasulab",
        "watson1978",
        "gungle",
        "ta1kt0me@github",
        "ykominami",
        "ryohashimoto",
        "tochi",
        "Y_Fujikawa",
        "noppo",
        "ryam",
        "yusabana",
        "SongOfYste",
        "blp1526",
        "mnuma",
        "kakipo",
        "sue738",
        "mrsy",
        "akm",
        "patorash",
        "md1961@github",
        "tomato360",
        "nkmr_jp",
        "K_Matsumoto",
        "fukuiretu",
        "photoxp",
        "rentalname@github",
        "ozaki_shigenobu",
        "fumiyasac@github",
        "suzukaze",
        "masahirok_jp",
        "kskomori",
        "HaraShun",
        "t-saito@github",
        "k_boss2930",
        "mono0926",
        "mokoaki",
        "acairojuni",
        "mrpero",
        "ryz310@github",
        "idahobean",
        "masawada",
        "perlunit",
        "radioboo",
        "ohbarye",
        "kamatama_41",
        "tos-miyake",
        "tomomomo1217",
        "HeRo",
        "syokenz",
        "shshimamo",
        "rakuda_daraku",
        "Shunta_Suzuki",
        "kera",
        "ABCanG1015",
        "aritaku",
        "kinushu",
        "shunyam0071",
        "shun-k1331",
        "okusawa",
        "takanodann",
        "___uhu",
        "Ysaton",
        "Neos21",
        "dounokouno",
        "tokomagu",
        "YOSUKE8080",
        "n-fukidome",
        "cigalecigales",
        "tatsu",
        "kou3",
        "yukitoto",
        "tanaka4410",
        "BathingMiloaHolic5150",
        "ucmsky",
        "SatoshiMorita",
        "yamotech",
        "tagackt",
        "jkr_2255",
        "T-N0121",
        "nwebcraft",
        "Sohma",
        "Masato-I",
        "tadauki",
        "17tea",
        "HayneRyo",
        "ymiyamae",
        "yh_cake",
        "306_san",
        "suga_de_suga",
        "togana",
        "mnishiguchi",
        "snona",
        "nishimaki",
        "saino-katsutoshi",
        "0x4ba01071",
        "MikazukiFuyuno",
        "YuitoSato",
        "arakawashintaro",
        "huddle",
        "myblue",
        "meriy100",
        "ryskito",
        "hiromichinomata",
        "acro5piano",
        "Esfahan",
        "yoshiko8080",
        "Ryota-C",
        "pinhead",
        "souichirou",
        "udetamago",
        "tanakahironobu",
        "moheji_dri"
    ]
}, {
    "id": 406818,
    "uuid": "d48eb7d4692fc76de6c2",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "draft-jsでシンタックスハイライターを作る",
    "created_at": "2016-07-10 02:14:10 +0900",
    "updated_at": "2016-07-10 02:16:53 +0900",
    "created_at_in_words": "2ヶ月",
    "updated_at_in_words": "2ヶ月",
    "tags": [{
        "name": "Draft.js",
        "url_name": "draft.js",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 16,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/d48eb7d4692fc76de6c2",
    "created_at_as_seconds": 1468084450,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## tl;dr\n\nstrategy で 正規表現でヒットさせて、ヒットした部分を component で置き換える。正規表現と色の組み合わせは一般化できるので、そこまでやってみた。\n\n## 最小コード\n\n@mizchi みたいにユーザー名に色を付ける\n\n```js:decorators/username-decorator.js\nimport {CompositeDecorator} from \"draft-js\";\nconst USERNAME_REGEX = /\\@[\\w]+/g;\n\nexport default new CompositeDecorator([\n  {\n    strategy(contentBlock, callback) {\n      const text = contentBlock.getText();\n      let matchArr, start;\n      while ((matchArr = USERNAME_REGEX.exec(text)) !== null) {\n        start = matchArr.index;\n        callback(start, start + matchArr[0].length);\n      }\n    },\n    component(props) {\n      return <span {...props} style={styles.handle}>{props.children}</span>;\n    },\n  },\n]);\n\nconst styles = {\n  handle: {\n    color: \"rgba(98, 177, 254, 1.0)\",\n    direction: \"ltr\",\n    unicodeBidi: \"bidi-override\",\n  }\n};\n```\n\n\n使う\n\n```js:components/my-editor.js\nimport {Component} from \"react\";\nimport {Editor, EditorState} from \"draft-js\";\nimport usernameDecorator from \"../decorators/tweet-decorator\";\n\nexport default class MyEditor extends Component {\n  constructor() {\n    super();\n    this.state = {\n      editorState: EditorState.createEmpty(usernameDecorator),\n    };\n  }\n\n  onChange(editorState) {\n    this.setState({editorState});\n  }\n\n  render() {\n    return (\n      <Editor\n        editorState={this.state.editorState}\n        onChange={this.onChange.bind(this)}\n        ref=\"editor\"\n      />\n    );\n  }\n}\n```\n\n動きました\n\n![](https://i.gyazo.com/90fbe257f94fd5ce40ade284c5fa1c95.png)\n\n\n## 一般化する\n\n任意の正規表現でヒットしたものをハイライトするのを一般化するとこうなる。\n\n```js\nimport {CompositeDecorator} from \"draft-js\";\n\nconst HIGHLIGHT_RULES = [\n  {\n    regex: /\\@[\\w]+/g,\n    color: \"rgb(98, 177, 254)\"\n  },\n  {\n    regex: /\\#[\\w]+/g,\n    color: \"rgb(95, 184, 138)\"\n  }\n];\n\nexport default new CompositeDecorator(HIGHLIGHT_RULES.map(rule => (\n  {\n    strategy(contentBlock, callback) {\n      const text = contentBlock.getText();\n      let matchArr, start;\n      while ((matchArr = rule.regex.exec(text)) !== null) {\n        start = matchArr.index;\n        callback(start, start + matchArr[0].length);\n      }\n    },\n    component(props) {\n      return <span {...props} style={{\n        color: rule.color,\n        direction: \"ltr\",\n        unicodeBidi: \"bidi-override\",\n      }}>{props.children}</span>;\n    },\n  }\n)));\n```\n\nまだ使い込んでないんで色々と引数を足したくなるだろうが、そのうちライブラリに切り出す。\n\n特定の構文下でルールを変える、といった拡張が考えられる。\n\n## 参考\n\n- [Draft.js | Rich Text Editor Framework for React](https://facebook.github.io/draft-js/docs/advanced-topics-decorators.html \"Draft.js | Rich Text Editor Framework for React\")\n- [draft-js/tweet.html at master · facebook/draft-js](https://github.com/facebook/draft-js/blob/master/examples/tweet/tweet.html \"draft-js/tweet.html at master · facebook/draft-js\")\n",
    "body": "\n<h2>\n<span id=\"tldr\" class=\"fragment\"></span><a href=\"#tldr\"><i class=\"fa fa-link\"></i></a>tl;dr</h2>\n\n<p>strategy で 正規表現でヒットさせて、ヒットした部分を component で置き換える。正規表現と色の組み合わせは一般化できるので、そこまでやってみた。</p>\n\n<h2>\n<span id=\"最小コード\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%B0%8F%E3%82%B3%E3%83%BC%E3%83%89\"><i class=\"fa fa-link\"></i></a>最小コード</h2>\n\n<p><a href=\"/mizchi\" class=\"user-mention js-hovercard\" title=\"mizchi\" data-hovercard-target-type=\"user\" data-hovercard-target-name=\"mizchi\">@mizchi</a> みたいにユーザー名に色を付ける</p>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">decorators/username-decorator.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">CompositeDecorator</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"draft-js\"</span><span class=\"p\">;</span>\n<span class=\"kr\">const</span> <span class=\"nx\">USERNAME_REGEX</span> <span class=\"o\">=</span> <span class=\"sr\">/\\@[\\w]+/g</span><span class=\"p\">;</span>\n\n<span class=\"kr\">export</span> <span class=\"k\">default</span> <span class=\"k\">new</span> <span class=\"nx\">CompositeDecorator</span><span class=\"p\">([</span>\n  <span class=\"p\">{</span>\n    <span class=\"nx\">strategy</span><span class=\"p\">(</span><span class=\"nx\">contentBlock</span><span class=\"p\">,</span> <span class=\"nx\">callback</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"kr\">const</span> <span class=\"nx\">text</span> <span class=\"o\">=</span> <span class=\"nx\">contentBlock</span><span class=\"p\">.</span><span class=\"nx\">getText</span><span class=\"p\">();</span>\n      <span class=\"kd\">let</span> <span class=\"nx\">matchArr</span><span class=\"p\">,</span> <span class=\"nx\">start</span><span class=\"p\">;</span>\n      <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"nx\">matchArr</span> <span class=\"o\">=</span> <span class=\"nx\">USERNAME_REGEX</span><span class=\"p\">.</span><span class=\"nx\">exec</span><span class=\"p\">(</span><span class=\"nx\">text</span><span class=\"p\">))</span> <span class=\"o\">!==</span> <span class=\"kc\">null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n        <span class=\"nx\">start</span> <span class=\"o\">=</span> <span class=\"nx\">matchArr</span><span class=\"p\">.</span><span class=\"nx\">index</span><span class=\"p\">;</span>\n        <span class=\"nx\">callback</span><span class=\"p\">(</span><span class=\"nx\">start</span><span class=\"p\">,</span> <span class=\"nx\">start</span> <span class=\"o\">+</span> <span class=\"nx\">matchArr</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">].</span><span class=\"nx\">length</span><span class=\"p\">);</span>\n      <span class=\"p\">}</span>\n    <span class=\"p\">},</span>\n    <span class=\"nx\">component</span><span class=\"p\">(</span><span class=\"nx\">props</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">span</span> <span class=\"p\">{...</span><span class=\"nx\">props</span><span class=\"p\">}</span> <span class=\"nx\">style</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"nx\">styles</span><span class=\"p\">.</span><span class=\"nx\">handle</span><span class=\"p\">}</span><span class=\"o\">&gt;</span><span class=\"p\">{</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">children</span><span class=\"p\">}</span><span class=\"o\">&lt;</span><span class=\"err\">/span&gt;;</span>\n    <span class=\"p\">},</span>\n  <span class=\"p\">},</span>\n<span class=\"p\">]);</span>\n\n<span class=\"kr\">const</span> <span class=\"nx\">styles</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n  <span class=\"nx\">handle</span><span class=\"o\">:</span> <span class=\"p\">{</span>\n    <span class=\"nx\">color</span><span class=\"o\">:</span> <span class=\"s2\">\"rgba(98, 177, 254, 1.0)\"</span><span class=\"p\">,</span>\n    <span class=\"nx\">direction</span><span class=\"o\">:</span> <span class=\"s2\">\"ltr\"</span><span class=\"p\">,</span>\n    <span class=\"nx\">unicodeBidi</span><span class=\"o\">:</span> <span class=\"s2\">\"bidi-override\"</span><span class=\"p\">,</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">};</span>\n</pre></div>\n</div>\n\n<p>使う</p>\n\n<div class=\"code-frame\" data-lang=\"js\">\n<div class=\"code-lang\"><span class=\"bold\">components/my-editor.js</span></div>\n<div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">Component</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">Editor</span><span class=\"p\">,</span> <span class=\"nx\">EditorState</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"draft-js\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"nx\">usernameDecorator</span> <span class=\"nx\">from</span> <span class=\"s2\">\"../decorators/tweet-decorator\"</span><span class=\"p\">;</span>\n\n<span class=\"kr\">export</span> <span class=\"k\">default</span> <span class=\"kr\">class</span> <span class=\"nx\">MyEditor</span> <span class=\"kr\">extends</span> <span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">constructor</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"kr\">super</span><span class=\"p\">();</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n      <span class=\"nx\">editorState</span><span class=\"o\">:</span> <span class=\"nx\">EditorState</span><span class=\"p\">.</span><span class=\"nx\">createEmpty</span><span class=\"p\">(</span><span class=\"nx\">usernameDecorator</span><span class=\"p\">),</span>\n    <span class=\"p\">};</span>\n  <span class=\"p\">}</span>\n\n  <span class=\"nx\">onChange</span><span class=\"p\">(</span><span class=\"nx\">editorState</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">setState</span><span class=\"p\">({</span><span class=\"nx\">editorState</span><span class=\"p\">});</span>\n  <span class=\"p\">}</span>\n\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"p\">(</span>\n      <span class=\"o\">&lt;</span><span class=\"nx\">Editor</span>\n        <span class=\"nx\">editorState</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span><span class=\"p\">.</span><span class=\"nx\">editorState</span><span class=\"p\">}</span>\n        <span class=\"nx\">onChange</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">onChange</span><span class=\"p\">.</span><span class=\"nx\">bind</span><span class=\"p\">(</span><span class=\"k\">this</span><span class=\"p\">)}</span>\n        <span class=\"nx\">ref</span><span class=\"o\">=</span><span class=\"s2\">\"editor\"</span>\n      <span class=\"o\">/&gt;</span>\n    <span class=\"p\">);</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</pre></div>\n</div>\n\n<p>動きました</p>\n\n<p><a href=\"https://i.gyazo.com/90fbe257f94fd5ce40ade284c5fa1c95.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/90fbe257f94fd5ce40ade284c5fa1c95.png\" alt=\"\"></a></p>\n\n<h2>\n<span id=\"一般化する\" class=\"fragment\"></span><a href=\"#%E4%B8%80%E8%88%AC%E5%8C%96%E3%81%99%E3%82%8B\"><i class=\"fa fa-link\"></i></a>一般化する</h2>\n\n<p>任意の正規表現でヒットしたものをハイライトするのを一般化するとこうなる。</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">CompositeDecorator</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"draft-js\"</span><span class=\"p\">;</span>\n\n<span class=\"kr\">const</span> <span class=\"nx\">HIGHLIGHT_RULES</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n  <span class=\"p\">{</span>\n    <span class=\"nx\">regex</span><span class=\"o\">:</span> <span class=\"sr\">/\\@[\\w]+/g</span><span class=\"p\">,</span>\n    <span class=\"nx\">color</span><span class=\"o\">:</span> <span class=\"s2\">\"rgb(98, 177, 254)\"</span>\n  <span class=\"p\">},</span>\n  <span class=\"p\">{</span>\n    <span class=\"nx\">regex</span><span class=\"o\">:</span> <span class=\"sr\">/\\#[\\w]+/g</span><span class=\"p\">,</span>\n    <span class=\"nx\">color</span><span class=\"o\">:</span> <span class=\"s2\">\"rgb(95, 184, 138)\"</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">];</span>\n\n<span class=\"kr\">export</span> <span class=\"k\">default</span> <span class=\"k\">new</span> <span class=\"nx\">CompositeDecorator</span><span class=\"p\">(</span><span class=\"nx\">HIGHLIGHT_RULES</span><span class=\"p\">.</span><span class=\"nx\">map</span><span class=\"p\">(</span><span class=\"nx\">rule</span> <span class=\"o\">=&gt;</span> <span class=\"p\">(</span>\n  <span class=\"p\">{</span>\n    <span class=\"nx\">strategy</span><span class=\"p\">(</span><span class=\"nx\">contentBlock</span><span class=\"p\">,</span> <span class=\"nx\">callback</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"kr\">const</span> <span class=\"nx\">text</span> <span class=\"o\">=</span> <span class=\"nx\">contentBlock</span><span class=\"p\">.</span><span class=\"nx\">getText</span><span class=\"p\">();</span>\n      <span class=\"kd\">let</span> <span class=\"nx\">matchArr</span><span class=\"p\">,</span> <span class=\"nx\">start</span><span class=\"p\">;</span>\n      <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"nx\">matchArr</span> <span class=\"o\">=</span> <span class=\"nx\">rule</span><span class=\"p\">.</span><span class=\"nx\">regex</span><span class=\"p\">.</span><span class=\"nx\">exec</span><span class=\"p\">(</span><span class=\"nx\">text</span><span class=\"p\">))</span> <span class=\"o\">!==</span> <span class=\"kc\">null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n        <span class=\"nx\">start</span> <span class=\"o\">=</span> <span class=\"nx\">matchArr</span><span class=\"p\">.</span><span class=\"nx\">index</span><span class=\"p\">;</span>\n        <span class=\"nx\">callback</span><span class=\"p\">(</span><span class=\"nx\">start</span><span class=\"p\">,</span> <span class=\"nx\">start</span> <span class=\"o\">+</span> <span class=\"nx\">matchArr</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">].</span><span class=\"nx\">length</span><span class=\"p\">);</span>\n      <span class=\"p\">}</span>\n    <span class=\"p\">},</span>\n    <span class=\"nx\">component</span><span class=\"p\">(</span><span class=\"nx\">props</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">span</span> <span class=\"p\">{...</span><span class=\"nx\">props</span><span class=\"p\">}</span> <span class=\"nx\">style</span><span class=\"o\">=</span><span class=\"p\">{{</span>\n        <span class=\"nx\">color</span><span class=\"o\">:</span> <span class=\"nx\">rule</span><span class=\"p\">.</span><span class=\"nx\">color</span><span class=\"p\">,</span>\n        <span class=\"nx\">direction</span><span class=\"o\">:</span> <span class=\"s2\">\"ltr\"</span><span class=\"p\">,</span>\n        <span class=\"nx\">unicodeBidi</span><span class=\"o\">:</span> <span class=\"s2\">\"bidi-override\"</span><span class=\"p\">,</span>\n      <span class=\"p\">}}</span><span class=\"o\">&gt;</span><span class=\"p\">{</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">children</span><span class=\"p\">}</span><span class=\"o\">&lt;</span><span class=\"err\">/span&gt;;</span>\n    <span class=\"p\">},</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">)));</span>\n</pre></div></div>\n\n<p>まだ使い込んでないんで色々と引数を足したくなるだろうが、そのうちライブラリに切り出す。</p>\n\n<p>特定の構文下でルールを変える、といった拡張が考えられる。</p>\n\n<h2>\n<span id=\"参考\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%83\"><i class=\"fa fa-link\"></i></a>参考</h2>\n\n<ul>\n<li><a href=\"https://facebook.github.io/draft-js/docs/advanced-topics-decorators.html\" title=\"Draft.js | Rich Text Editor Framework for React\" rel=\"nofollow\" target=\"_blank\">Draft.js | Rich Text Editor Framework for React</a></li>\n<li><a href=\"https://github.com/facebook/draft-js/blob/master/examples/tweet/tweet.html\" title=\"draft-js/tweet.html at master · facebook/draft-js\" rel=\"nofollow\" target=\"_blank\">draft-js/tweet.html at master · facebook/draft-js</a></li>\n</ul>\n",
    "stock_users": [
        "mizchi",
        "chaba418",
        "shiwano",
        "oreshinya",
        "doockie",
        "cutmail",
        "erukiti",
        "hkoba@github",
        "dorayakikun",
        "kiyomaru_nns",
        "ndxbn",
        "zansin_",
        "hige-tahara",
        "moaible",
        "gaaamii",
        "all__user"
    ]
}, {
    "id": 406120,
    "uuid": "063e332cbe3023f52f93",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "あなたがマスターしたのはいくつ? Railsを習得するために必要な技術要素の一覧",
    "created_at": "2016-07-07 08:42:49 +0900",
    "updated_at": "2016-07-27 07:02:18 +0900",
    "created_at_in_words": "2ヶ月",
    "updated_at_in_words": "約1ヶ月",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }],
    "stock_count": 740,
    "comment_count": 0,
    "url": "http://qiita.com/jnchito/items/063e332cbe3023f52f93",
    "created_at_as_seconds": 1467848569,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## これはなんですか?\n\nこれは「[This is Why Learning Rails is Hard](https://www.codefellows.org/blog/this-is-why-learning-rails-is-hard)(Railsの習得が大変な理由はこれだ)」という海外記事に載っているマインドマップを日本語化&リスト化したものです。\n\n元記事には「Railsを習得するために必要な技術要素の一覧」を表す、以下のようなマインドマップが載っています。\n\n![Rails_Competencies.jpg](https://qiita-image-store.s3.amazonaws.com/0/7465/cbc50a23-db31-8cde-21eb-b5c20365bdca.jpeg \"Rails_Competencies.jpg\")\n\n長年Railsの開発に携わってきた人間として、このマインドマップは「うん、たしかに!」と非常に納得できる内容です。\nただし、サイズの大きな画像なので一覧性に欠けるのと、英語なので日本人にとってはぱっと頭に入りづらい点があるのも否めません。\n\nそこでいつでもぱっと確認できるように、このマインドマップ内容を日本語化&リスト化することにしました。\n\nこのリストを読んで、自分がすでに身につけている技術要素は何か、また、これから習得が必要な技術要素にはどんなものがあるのか、各自で確認してみてください。\n\n## 本記事のライセンスについて\n\n本記事は[クリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 ライセンス](https://creativecommons.org/licenses/by-nc-sa/3.0/deed.ja)で提供されています。\n\n![by-nc-sa.png](https://qiita-image-store.s3.amazonaws.com/0/7465/d36e6add-14f3-3381-8759-c61901a93f80.png \"by-nc-sa.png\")\n\nそれでは以下が「Railsを習得するために必要な技術要素の一覧」です。\n\n\n## Ruby言語\n\n- オブジェクト指向プログラミング\n    - クラス&モジュール\n    - 属性&メソッド\n    - 継承&ミックスイン\n- 関数型プログラミング\n    - ブロック\n    - Proc&ラムダ\n- フロー制御\n    - 条件分岐\n    - 繰り返し\n- REPL(対話型評価環境)&デバッグ\n- メタプログラミング\n\n## Ruby gems\n\n- RVM&rbenv\n- Gem\n    - ファイル構成\n    - オープンソースのエチケット\n    - Bundler&依存関係の管理\n\n## Railsフレームワーク\n\n- MVC\n    - Assetパイプライン&プリプロセッサ\n    - UJS(jquery-ujs)\n    - パフォーマンス\n        - 監視\n        - キャッシュ&ページネーション\n        - バックグラウンドタスク\n- REST\n    - ルーティング\n    - API\n        - JSON&XML\n        - ハイパーメディア\n    - ネストしたリソース&ネームスペース\n- ORM(オブジェクト・リレーショナル・マッピング)\n    - リレーショナルなスキーマ\n        - マイグレーション\n        - 正規化\n        - 外部キー\n        - インデックス\n    - ドキュメント・ストア(ドキュメントデータベース)\n    - 関連\n        - 依存関係\n        - イーガーローディング(Eager loading)\n    - バリデーション&コールバック\n- ユーザー管理\n    - 認可\n    - 認証\n    - マルチテナント機能\n- 規約&ベストプラクティス\n    - I18n(国際化)&タイムゾーン\n    - ActiveSupport\n    - セキュリティ&デフォルト設定\n- CLI(キャラクタユーザインタフェース)\n    - Railsコンソール\n    - Railsジェネレータ\n    - Rakeタスク\n- ActionMailer\n\n## Git\n\n- バージョン管理システム(VCS)\n    - 分散VCS\n    - ブランチ\n    - マージ\n    - 差分(Diff)\n- Github\n    - フォーク\n    - プルリクエスト\n- 設定\n    - リモート(Remotes)\n    - デフォルトの挙動\n\n## IDE(統合開発環境)/テキストエディタ\n\n- シンタックスハイライト\n- ショートカットキー\n- スコープの認識(入力補完等)\n- 拡張性\n- 高度な編集機能\n    - 複数行編集\n    - クリップボード履歴\n    - 迅速なファイルの切替え\n\n## ソフトウェア工学\n\n- アプリケーションアーキテクチャ\n    - モジュール化&SOA\n    - ドメインロジックのカプセル化\n- アジャイルプロセス\n    - プロジェクトのオーナーシップ&ユーザーストーリー\n    - 繰り返しサイクル\n        - テストコード&ペアプログラミング\n        - CI(継続的インテグレーション)&コードレビュー\n        - QA(品質保証)&デバッグ\n    - 頻繁なコミュニケーション\n- プロジェクト管理\n    - 計画&見積り\n    - ツール&ドキュメント\n    - ライセンス\n\n## オペレーティングシステム\n\n- ファイル管理\n- 管理者権限\n- パッケージ管理\n- ローカルwebサーバー\n\n## コマンドライン\n\n- ナビゲーション(移動)\n- セキュア接続&キー管理\n- ファイルの中身の操作\n\n## WWW\n\n- HTTP\n    - リクエスト\n        - メソッド\n        - ステート\n            - パラメータ\n            - クッキー\n        - スキーム\n    - レスポンス\n        - ステータスコード\n        - メディアタイプ\n- HTML\n    - DOM\n    - セマンティックなマークアップ\n    - HAML\n    - HTML5の機能&互換性\n- CSS\n    - Sass&Less\n    - CSSフレームワーク\n    - ブラウザのクセ\n    - セレクタ&詳細度\n- JavaScript\n    - ライブラリ\n        - jQuery\n        - クライアントサイドMVCフレームワーク\n    - プロトタイプ&オブジェクト\n    - 無名関数\n    - AJAX\n    - CoffeeScript\n\n## デプロイ\n\n- 自動化\n    - 継続的デプロイ\n    - 継続的インテグレーション\n    - サーバー監視&ロギング\n- 設定管理\n- リリース管理\n- セキュリティ&データ完全性\n- プラットフォーム\n    - 専用環境\n    - バーチャル環境\n    - 抽象化された環境\n\n## テスト\n\n- 種類\n    - 単体テスト\n    - 機能テスト\n    - インテグレーションテスト\n    - パフォーマンステスト\n    - 受入れテスト\n    - 回帰テスト\n- ツール\n    - RSpec&Minitest\n    - ファクトリ&フィクスチャ\n    - モック&スタブ\n- TATFT (Test all the fucking time、常時テスト)\n    - TDD&BDD\n    - 外から中へのリファクタリング\n\n## SQL&データモデリング\n\n- SELECT文\n    - GROUP\n    - ORDER\n    - LIMIT\n- INSERT文&UPDATE文\n- トランザクション\n- JOIN\n- パフォーマンス\n    - スロークエリの記録\n    - クエリプラン\n    - N+1問題の回避\n\n## 独自に追記:英語\n\n元記事には(当然)載っていませんが、日本人プログラマにとっては英語も必要な技術力の一つと言えそうです。\n\n- 基礎 \n\t- 語彙\n\t- 文法\n- 読む\n- 書く\n- 聞く\n- 話す\n\n## まとめ\n\nいかがだったでしょうか?\nひとことで「Railsを習得する」と言っても、そこには非常に幅広い技術要素があることがわかりますね。\n\nチームで開発しているRailsエンジニアの方は、職場内でこのリストやマインドマップをスキルチェックシートとして使うと良いかもしれません、\n\n一朝一夕にRailsの達人になることは不可能ですが、「千里の道も一歩から」の精神でコツコツがんばっていきましょう!\n\n### あわせて読みたい\n\n「コツコツがんばれ、って言われても一体何から始めれば?」という方は、僕が以前書いたこちらのブログが参考になるかもしれません。\n\n[新人プログラマ向け・スキル向上のための具体的なアプローチと考え方 - give IT a try](http://blog.jnito.com/entry/2016/03/22/083055)\n",
    "body": "\n<h2>\n<span id=\"これはなんですか\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%82%8C%E3%81%AF%E3%81%AA%E3%82%93%E3%81%A7%E3%81%99%E3%81%8B\"><i class=\"fa fa-link\"></i></a>これはなんですか?</h2>\n\n<p>これは「<a href=\"https://www.codefellows.org/blog/this-is-why-learning-rails-is-hard\" rel=\"nofollow\" target=\"_blank\">This is Why Learning Rails is Hard</a>(Railsの習得が大変な理由はこれだ)」という海外記事に載っているマインドマップを日本語化&リスト化したものです。</p>\n\n<p>元記事には「Railsを習得するために必要な技術要素の一覧」を表す、以下のようなマインドマップが載っています。</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/cbc50a23-db31-8cde-21eb-b5c20365bdca.jpeg\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/cbc50a23-db31-8cde-21eb-b5c20365bdca.jpeg\" alt=\"Rails_Competencies.jpg\" title=\"Rails_Competencies.jpg\"></a></p>\n\n<p>長年Railsの開発に携わってきた人間として、このマインドマップは「うん、たしかに!」と非常に納得できる内容です。<br>\nただし、サイズの大きな画像なので一覧性に欠けるのと、英語なので日本人にとってはぱっと頭に入りづらい点があるのも否めません。</p>\n\n<p>そこでいつでもぱっと確認できるように、このマインドマップ内容を日本語化&リスト化することにしました。</p>\n\n<p>このリストを読んで、自分がすでに身につけている技術要素は何か、また、これから習得が必要な技術要素にはどんなものがあるのか、各自で確認してみてください。</p>\n\n<h2>\n<span id=\"本記事のライセンスについて\" class=\"fragment\"></span><a href=\"#%E6%9C%AC%E8%A8%98%E4%BA%8B%E3%81%AE%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6\"><i class=\"fa fa-link\"></i></a>本記事のライセンスについて</h2>\n\n<p>本記事は<a href=\"https://creativecommons.org/licenses/by-nc-sa/3.0/deed.ja\" rel=\"nofollow\" target=\"_blank\">クリエイティブ・コモンズ 表示 - 非営利 - 継承 3.0 ライセンス</a>で提供されています。</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/d36e6add-14f3-3381-8759-c61901a93f80.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/d36e6add-14f3-3381-8759-c61901a93f80.png\" alt=\"by-nc-sa.png\" title=\"by-nc-sa.png\"></a></p>\n\n<p>それでは以下が「Railsを習得するために必要な技術要素の一覧」です。</p>\n\n<h2>\n<span id=\"ruby言語\" class=\"fragment\"></span><a href=\"#ruby%E8%A8%80%E8%AA%9E\"><i class=\"fa fa-link\"></i></a>Ruby言語</h2>\n\n<ul>\n<li>オブジェクト指向プログラミング\n\n<ul>\n<li>クラス&モジュール</li>\n<li>属性&メソッド</li>\n<li>継承&ミックスイン</li>\n</ul>\n</li>\n<li>関数型プログラミング\n\n<ul>\n<li>ブロック</li>\n<li>Proc&ラムダ</li>\n</ul>\n</li>\n<li>フロー制御\n\n<ul>\n<li>条件分岐</li>\n<li>繰り返し</li>\n</ul>\n</li>\n<li>REPL(対話型評価環境)&デバッグ</li>\n<li>メタプログラミング</li>\n</ul>\n\n<h2>\n<span id=\"ruby-gems\" class=\"fragment\"></span><a href=\"#ruby-gems\"><i class=\"fa fa-link\"></i></a>Ruby gems</h2>\n\n<ul>\n<li>RVM&rbenv</li>\n<li>Gem\n\n<ul>\n<li>ファイル構成</li>\n<li>オープンソースのエチケット</li>\n<li>Bundler&依存関係の管理</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"railsフレームワーク\" class=\"fragment\"></span><a href=\"#rails%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF\"><i class=\"fa fa-link\"></i></a>Railsフレームワーク</h2>\n\n<ul>\n<li>MVC\n\n<ul>\n<li>Assetパイプライン&プリプロセッサ</li>\n<li>UJS(jquery-ujs)</li>\n<li>パフォーマンス\n\n<ul>\n<li>監視</li>\n<li>キャッシュ&ページネーション</li>\n<li>バックグラウンドタスク</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>REST\n\n<ul>\n<li>ルーティング</li>\n<li>API\n\n<ul>\n<li>JSON&XML</li>\n<li>ハイパーメディア</li>\n</ul>\n</li>\n<li>ネストしたリソース&ネームスペース</li>\n</ul>\n</li>\n<li>ORM(オブジェクト・リレーショナル・マッピング)\n\n<ul>\n<li>リレーショナルなスキーマ\n\n<ul>\n<li>マイグレーション</li>\n<li>正規化</li>\n<li>外部キー</li>\n<li>インデックス</li>\n</ul>\n</li>\n<li>ドキュメント・ストア(ドキュメントデータベース)</li>\n<li>関連\n\n<ul>\n<li>依存関係</li>\n<li>イーガーローディング(Eager loading)</li>\n</ul>\n</li>\n<li>バリデーション&コールバック</li>\n</ul>\n</li>\n<li>ユーザー管理\n\n<ul>\n<li>認可</li>\n<li>認証</li>\n<li>マルチテナント機能</li>\n</ul>\n</li>\n<li>規約&ベストプラクティス\n\n<ul>\n<li>I18n(国際化)&タイムゾーン</li>\n<li>ActiveSupport</li>\n<li>セキュリティ&デフォルト設定</li>\n</ul>\n</li>\n<li>CLI(キャラクタユーザインタフェース)\n\n<ul>\n<li>Railsコンソール</li>\n<li>Railsジェネレータ</li>\n<li>Rakeタスク</li>\n</ul>\n</li>\n<li>ActionMailer</li>\n</ul>\n\n<h2>\n<span id=\"git\" class=\"fragment\"></span><a href=\"#git\"><i class=\"fa fa-link\"></i></a>Git</h2>\n\n<ul>\n<li>バージョン管理システム(VCS)\n\n<ul>\n<li>分散VCS</li>\n<li>ブランチ</li>\n<li>マージ</li>\n<li>差分(Diff)</li>\n</ul>\n</li>\n<li>Github\n\n<ul>\n<li>フォーク</li>\n<li>プルリクエスト</li>\n</ul>\n</li>\n<li>設定\n\n<ul>\n<li>リモート(Remotes)</li>\n<li>デフォルトの挙動</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"ide統合開発環境テキストエディタ\" class=\"fragment\"></span><a href=\"#ide%E7%B5%B1%E5%90%88%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF\"><i class=\"fa fa-link\"></i></a>IDE(統合開発環境)/テキストエディタ</h2>\n\n<ul>\n<li>シンタックスハイライト</li>\n<li>ショートカットキー</li>\n<li>スコープの認識(入力補完等)</li>\n<li>拡張性</li>\n<li>高度な編集機能\n\n<ul>\n<li>複数行編集</li>\n<li>クリップボード履歴</li>\n<li>迅速なファイルの切替え</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"ソフトウェア工学\" class=\"fragment\"></span><a href=\"#%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E5%B7%A5%E5%AD%A6\"><i class=\"fa fa-link\"></i></a>ソフトウェア工学</h2>\n\n<ul>\n<li>アプリケーションアーキテクチャ\n\n<ul>\n<li>モジュール化&SOA</li>\n<li>ドメインロジックのカプセル化</li>\n</ul>\n</li>\n<li>アジャイルプロセス\n\n<ul>\n<li>プロジェクトのオーナーシップ&ユーザーストーリー</li>\n<li>繰り返しサイクル\n\n<ul>\n<li>テストコード&ペアプログラミング</li>\n<li>CI(継続的インテグレーション)&コードレビュー</li>\n<li>QA(品質保証)&デバッグ</li>\n</ul>\n</li>\n<li>頻繁なコミュニケーション</li>\n</ul>\n</li>\n<li>プロジェクト管理\n\n<ul>\n<li>計画&見積り</li>\n<li>ツール&ドキュメント</li>\n<li>ライセンス</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"オペレーティングシステム\" class=\"fragment\"></span><a href=\"#%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0\"><i class=\"fa fa-link\"></i></a>オペレーティングシステム</h2>\n\n<ul>\n<li>ファイル管理</li>\n<li>管理者権限</li>\n<li>パッケージ管理</li>\n<li>ローカルwebサーバー</li>\n</ul>\n\n<h2>\n<span id=\"コマンドライン\" class=\"fragment\"></span><a href=\"#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3\"><i class=\"fa fa-link\"></i></a>コマンドライン</h2>\n\n<ul>\n<li>ナビゲーション(移動)</li>\n<li>セキュア接続&キー管理</li>\n<li>ファイルの中身の操作</li>\n</ul>\n\n<h2>\n<span id=\"www\" class=\"fragment\"></span><a href=\"#www\"><i class=\"fa fa-link\"></i></a>WWW</h2>\n\n<ul>\n<li>HTTP\n\n<ul>\n<li>リクエスト\n\n<ul>\n<li>メソッド</li>\n<li>ステート\n\n<ul>\n<li>パラメータ</li>\n<li>クッキー</li>\n</ul>\n</li>\n<li>スキーム</li>\n</ul>\n</li>\n<li>レスポンス\n\n<ul>\n<li>ステータスコード</li>\n<li>メディアタイプ</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>HTML\n\n<ul>\n<li>DOM</li>\n<li>セマンティックなマークアップ</li>\n<li>HAML</li>\n<li>HTML5の機能&互換性</li>\n</ul>\n</li>\n<li>CSS\n\n<ul>\n<li>Sass&Less</li>\n<li>CSSフレームワーク</li>\n<li>ブラウザのクセ</li>\n<li>セレクタ&詳細度</li>\n</ul>\n</li>\n<li>JavaScript\n\n<ul>\n<li>ライブラリ\n\n<ul>\n<li>jQuery</li>\n<li>クライアントサイドMVCフレームワーク</li>\n</ul>\n</li>\n<li>プロトタイプ&オブジェクト</li>\n<li>無名関数</li>\n<li>AJAX</li>\n<li>CoffeeScript</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"デプロイ\" class=\"fragment\"></span><a href=\"#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4\"><i class=\"fa fa-link\"></i></a>デプロイ</h2>\n\n<ul>\n<li>自動化\n\n<ul>\n<li>継続的デプロイ</li>\n<li>継続的インテグレーション</li>\n<li>サーバー監視&ロギング</li>\n</ul>\n</li>\n<li>設定管理</li>\n<li>リリース管理</li>\n<li>セキュリティ&データ完全性</li>\n<li>プラットフォーム\n\n<ul>\n<li>専用環境</li>\n<li>バーチャル環境</li>\n<li>抽象化された環境</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"テスト\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88\"><i class=\"fa fa-link\"></i></a>テスト</h2>\n\n<ul>\n<li>種類\n\n<ul>\n<li>単体テスト</li>\n<li>機能テスト</li>\n<li>インテグレーションテスト</li>\n<li>パフォーマンステスト</li>\n<li>受入れテスト</li>\n<li>回帰テスト</li>\n</ul>\n</li>\n<li>ツール\n\n<ul>\n<li>RSpec&Minitest</li>\n<li>ファクトリ&フィクスチャ</li>\n<li>モック&スタブ</li>\n</ul>\n</li>\n<li>TATFT (Test all the fucking time、常時テスト)\n\n<ul>\n<li>TDD&BDD</li>\n<li>外から中へのリファクタリング</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"sqlデータモデリング\" class=\"fragment\"></span><a href=\"#sql%E3%83%87%E3%83%BC%E3%82%BF%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0\"><i class=\"fa fa-link\"></i></a>SQL&データモデリング</h2>\n\n<ul>\n<li>SELECT文\n\n<ul>\n<li>GROUP</li>\n<li>ORDER</li>\n<li>LIMIT</li>\n</ul>\n</li>\n<li>INSERT文&UPDATE文</li>\n<li>トランザクション</li>\n<li>JOIN</li>\n<li>パフォーマンス\n\n<ul>\n<li>スロークエリの記録</li>\n<li>クエリプラン</li>\n<li>N+1問題の回避</li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"独自に追記英語\" class=\"fragment\"></span><a href=\"#%E7%8B%AC%E8%87%AA%E3%81%AB%E8%BF%BD%E8%A8%98%E8%8B%B1%E8%AA%9E\"><i class=\"fa fa-link\"></i></a>独自に追記:英語</h2>\n\n<p>元記事には(当然)載っていませんが、日本人プログラマにとっては英語も必要な技術力の一つと言えそうです。</p>\n\n<ul>\n<li>基礎 \n\n<ul>\n<li>語彙</li>\n<li>文法</li>\n</ul>\n</li>\n<li>読む</li>\n<li>書く</li>\n<li>聞く</li>\n<li>話す</li>\n</ul>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<p>いかがだったでしょうか?<br>\nひとことで「Railsを習得する」と言っても、そこには非常に幅広い技術要素があることがわかりますね。</p>\n\n<p>チームで開発しているRailsエンジニアの方は、職場内でこのリストやマインドマップをスキルチェックシートとして使うと良いかもしれません、</p>\n\n<p>一朝一夕にRailsの達人になることは不可能ですが、「千里の道も一歩から」の精神でコツコツがんばっていきましょう!</p>\n\n<h3>\n<span id=\"あわせて読みたい\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>あわせて読みたい</h3>\n\n<p>「コツコツがんばれ、って言われても一体何から始めれば?」という方は、僕が以前書いたこちらのブログが参考になるかもしれません。</p>\n\n<p><a href=\"http://blog.jnito.com/entry/2016/03/22/083055\" rel=\"nofollow\" target=\"_blank\">新人プログラマ向け・スキル向上のための具体的なアプローチと考え方 - give IT a try</a></p>\n",
    "stock_users": [
        "kysnm",
        "aki77",
        "mat_aki",
        "cutmail",
        "tsumuchan",
        "okadabasso",
        "gabu",
        "nbeat",
        "mizchi",
        "jwako",
        "yahihi",
        "yasulab",
        "intemous9",
        "kkawauchi",
        "Vermee81",
        "junkjunctions",
        "cantk@github",
        "externvoid@github",
        "kzmcond",
        "yuya_presto",
        "hondam",
        "ykominami",
        "tatsuoSakurai",
        "ryohashimoto",
        "phyllite",
        "takasianpride",
        "k2tanaka",
        "oginohiroaki@github",
        "Yas_098",
        "Y_Fujikawa",
        "usutani",
        "fukamiiiiinmin",
        "star__hoshi",
        "mm36",
        "raindogs",
        "moto",
        "noriaki",
        "tektoh",
        "takayukishmz@github",
        "giwa",
        "wiro34",
        "QUANON",
        "hitomi_",
        "takatama",
        "object-kazuAtGitHub",
        "ganta",
        "mnuma",
        "zaoriku0",
        "geso_mori",
        "enish",
        "tomiacannondale",
        "nakaji",
        "kakipo",
        "one_pattern",
        "hogenishi",
        "snaka",
        "yuki777",
        "ogomr",
        "yamukotonaku",
        "shunsugai@github",
        "shoichiaizawa@github",
        "okemori",
        "twingo-b@github",
        "zeroyonichihachi",
        "hadzimme",
        "motchang",
        "Takayasu_Beharu",
        "Chrowa3",
        "hiroki23@github",
        "wgkoro@github",
        "katsu33@github",
        "hamasyou",
        "piinattu",
        "snowsunny",
        "Shuya",
        "siwamot",
        "md1961@github",
        "NaohiroKashimoto",
        "saba1024",
        "orange-lion",
        "bon10",
        "Zambiker",
        "gaaamii",
        "makotot",
        "gungnir_odin",
        "tomato360",
        "atticatticattic",
        "builtinnya@github",
        "Mt_blue81",
        "mitukiii",
        "torus108",
        "hirokaki",
        "kichiGeorge",
        "bells17",
        "ina_ryu",
        "kotarokimura",
        "ligerbolt",
        "takemon62act",
        "Silbercat",
        "rentalname@github",
        "tomcha_",
        "fkshom",
        "ds_age",
        "st5757",
        "Tsutomu-KKE@github",
        "takarake",
        "ozaki_shigenobu",
        "skinoshita",
        "nntsugu@github",
        "barasixi",
        "atsaki",
        "itkrt2y",
        "gajumaru4444",
        "mekemeke9",
        "Tamadon",
        "_Yasuun_",
        "kibitan",
        "ledsun",
        "geek_duck",
        "hidenori_tsuji",
        "Nebutan",
        "hiralin@github",
        "surume",
        "ariiyu",
        "fumiyasac@github",
        "suzukaze",
        "arfyasu",
        "nish",
        "masahirok_jp",
        "kskomori",
        "nobeans",
        "yu01",
        "nijojin",
        "noliaki@github",
        "majingai",
        "IzumiSy",
        "atijust",
        "k-ta-yamada",
        "mmaruyam@github",
        "riocampos",
        "tomo_will",
        "amyroi",
        "hachi8833",
        "knt45",
        "acairojuni",
        "kuroda@github",
        "mtgzz@github",
        "ryz310@github",
        "hiloki@github",
        "sugitk",
        "matsuhisa_h",
        "teracy",
        "noir_neo",
        "upsilon2@github",
        "wanpa",
        "kgsi",
        "tokibi",
        "taka7beckham",
        "yutakau@github",
        "tamano",
        "nao_ina",
        "danimal141",
        "idahobean",
        "shun666",
        "baboocon",
        "hackyGQ",
        "shimoju",
        "yamarag",
        "perlunit",
        "kyaukyuai",
        "ntakahashi",
        "Ago2_0727",
        "celeryn",
        "1990kawa",
        "hachinobu",
        "kasaharu",
        "chatii0079",
        "DolphinJP",
        "m-nagae",
        "ndxbn",
        "doorfkin",
        "nagasu0324",
        "Siu747",
        "t44cd",
        "ktana_",
        "sudahiroshi",
        "yashiro1234",
        "ihsiek",
        "CleySense",
        "tknakatsu",
        "tos-miyake",
        "rgb",
        "miwanobe",
        "toshiyuki",
        "hiroyukim",
        "disxme",
        "delta93815",
        "ymtk0815",
        "kano-e",
        "itochu0523",
        "osumium",
        "syokenz",
        "robotypetwo",
        "nicchi__1985",
        "niwacchi",
        "ass_out",
        "amaron518",
        "shogito",
        "_yama3_",
        "kodamax",
        "yukika",
        "HamaTech",
        "microgravity",
        "tkwn1",
        "rakuda_daraku",
        "daiiz",
        "murata0705",
        "Shunta_Suzuki",
        "ats777",
        "Tanemura",
        "snowpoll",
        "kkyouhei",
        "aokabin",
        "mktakuya",
        "peace_journey",
        "YutakakINJO",
        "guretaro",
        "ABCanG1015",
        "kkakizaki",
        "rockkhuya",
        "moya_k",
        "aritaku",
        "gaooh",
        "haraki",
        "color_box",
        "justin999",
        "intermezzo-fr",
        "n-satoru",
        "kinushu",
        "zug",
        "mmts1007",
        "Hi_Noguchi",
        "TanakanoAnchan",
        "kaizumt",
        "nagatashinya",
        "HexB",
        "Hassan",
        "aminamid",
        "dica33",
        "junymn",
        "atshow2010",
        "tanshio",
        "kush2",
        "finalblow",
        "takemizu",
        "k-masaki",
        "yujis",
        "take__8810",
        "maru",
        "takayukii",
        "nana-i",
        "yakiimo23",
        "imagepit",
        "shunyam0071",
        "shun-k1331",
        "elzup",
        "suitedJK",
        "tmizo",
        "misuken",
        "Matsushin",
        "samuraikun",
        "nobuta_pawaa",
        "little",
        "kumaetsu",
        "misfrog",
        "sylph01",
        "akira_21",
        "Reyurnible",
        "okawaryota",
        "NiraCha",
        "kainabels",
        "___uhu",
        "yostos",
        "msmt_k",
        "Neos21",
        "muraoka17",
        "walrein",
        "dirablue",
        "munepi",
        "yaha",
        "isdnok",
        "kyrieleison",
        "kota2_0",
        "tokomagu",
        "r-ngtm",
        "suuuuzuuuu",
        "hidex7777",
        "ryobb",
        "mizukmb",
        "masu77",
        "T-Kage",
        "furutone",
        "gonzui",
        "yuukis",
        "takayuki-ochiai",
        "sasaron397",
        "kenunado",
        "rot",
        "moyaminC",
        "tuiterukun",
        "n-fukidome",
        "yuppy1221",
        "LordOfNightmare",
        "UedaTakeyuki",
        "toshi0607",
        "Yukishigure01",
        "opponitur",
        "hiraishi",
        "oubakiou",
        "shunhikita",
        "tkimura",
        "Hoouseei",
        "kei-p",
        "zegu",
        "saruh",
        "Kaki_Shoichi",
        "rifuch",
        "ryo620",
        "makopy_inside",
        "gotoysk",
        "shinsukeoshiro",
        "ko-aoki",
        "dnby",
        "tamurayoshiya",
        "banbara23",
        "kou3",
        "aild_arch_bfmv",
        "k-ishimitsu",
        "inodev",
        "n-imai",
        "takuwan0405",
        "odawara",
        "kazukousen",
        "wnoooo",
        "murat-to",
        "yukitoto",
        "hara_p",
        "mori_koma",
        "aTomoyoshiToyoda",
        "yutak_a",
        "hashimoto1009",
        "tanaka4410",
        "kamonas",
        "Moby-Dick",
        "3upjp",
        "usiusi360",
        "test2test",
        "tkcfjips",
        "aabbcc",
        "usizou",
        "uriborn",
        "tsh23nkzw",
        "Hirano0319",
        "kfujii",
        "amasik",
        "startselect",
        "testmonstar01",
        "umiboz",
        "fishell",
        "ken0nek",
        "routerman",
        "ogatakoki",
        "Shinonome",
        "ynakamori",
        "sugichan_16",
        "ligtnrcat",
        "u_nation",
        "mizunos",
        "yamotech",
        "tagackt",
        "naichilab",
        "negabaro",
        "turu50s",
        "th3m1s",
        "tasukujp",
        "naknaknakjp",
        "kazuyuki_koma",
        "KoyaFukushi",
        "T-N0121",
        "issy",
        "lonelydarklark",
        "nozaking16",
        "falconedge",
        "kawasaki",
        "kyumon_shota",
        "masanori_",
        "nwebcraft",
        "helloinfoloth",
        "wint",
        "tomioage",
        "tjinjin",
        "o_msyk",
        "rllllho",
        "takehito-koshimizu",
        "schinen",
        "w-matsumoto",
        "Sohma",
        "whitecrane",
        "zkohi",
        "yatsu",
        "keyama4",
        "tamanegikenshi_",
        "supermanner",
        "noi000",
        "Rp7rf",
        "rrr",
        "takenta",
        "555ryusuke",
        "Fendo181",
        "shortaizumi",
        "kanohisa",
        "Masato-I",
        "nwhiro700",
        "daichi_ito",
        "taKassi",
        "hayato-k",
        "yukitaya",
        "hirokyun",
        "yo-itz",
        "chanibarin",
        "mismith",
        "Kazuki_Ito",
        "whiteleaves",
        "tadauki",
        "kosukes",
        "cakecatz",
        "KazukiSaima",
        "a_araki",
        "sujii",
        "haricotando",
        "mizuno_",
        "aikomnw",
        "aichan",
        "wataru23",
        "syng",
        "donadona2225",
        "hanai_hiroyuki",
        "ShuntaShirai",
        "nao_u",
        "shunsuke227ono",
        "chrischris0801",
        "o-hayato",
        "Tackeyyyyyyyy",
        "s-take",
        "MiyachiK",
        "nobuy",
        "loonip",
        "17tea",
        "taka159",
        "owls",
        "KoheiKameda",
        "fiona_snow",
        "arith-kawai",
        "haptaro",
        "YujiMiyashita",
        "iKenji",
        "furu8ma",
        "guccish",
        "Takamichi-tsutsumi",
        "DaisukeKgr",
        "atozzz",
        "seniyatta",
        "oweee",
        "saltharu",
        "foursue",
        "yoshifuji",
        "wasabinp",
        "Gupi",
        "chic_n_",
        "scarletrunner7000",
        "mserizawa",
        "tatsuki-m",
        "banz-ghb",
        "taku_ssd",
        "oshirucordex",
        "y-fukuda",
        "i-ishida",
        "albatross901",
        "Tocyuki",
        "itooww",
        "KeiKawano",
        "Dafemon",
        "secure0318",
        "varsaista",
        "asayamakk",
        "masaponjp",
        "ochiait",
        "ShinMatsudaira",
        "alumys",
        "koutt6",
        "pio2",
        "sika_boze",
        "tmorikawam",
        "k4h4shi",
        "seiyaan",
        "mnishiguchi",
        "kotonari",
        "YoshiISHIGAMI",
        "Dooor",
        "takasaka",
        "kinmugi",
        "iamdaisuke",
        "lasershow",
        "chano2",
        "snona",
        "shingen",
        "FlechaMaker",
        "pokohide",
        "seeeeo",
        "yutaszk",
        "kazurasaka",
        "KohtaMiyairi",
        "yukisaibai",
        "ksugimori",
        "saino-katsutoshi",
        "10kmr",
        "twwt",
        "0x4ba01071",
        "kakudou3",
        "sshu",
        "sengg_m",
        "peg_73_",
        "cawa",
        "kazuki_ste6a",
        "bbrubymm",
        "ebiryu65",
        "h-ishibashi",
        "nakajou4",
        "abcmark2010",
        "21-Hidetaka-Ko",
        "nilinomiya1222",
        "yokkie8383",
        "3kuni",
        "indigolain",
        "doki_k",
        "naoki_koreeda",
        "depero",
        "jagio",
        "msasaki",
        "debatable13",
        "masashi-t-h",
        "kokeiro001",
        "kei2ro",
        "takkuya11",
        "tsintermax",
        "maehara08",
        "gainings",
        "Dvolition",
        "arakawashintaro",
        "waspman",
        "t15i",
        "yoshi522",
        "hc0208",
        "nakki",
        "chocoken517",
        "huddle",
        "casualplay",
        "21c",
        "hinakade",
        "3naU",
        "myblue",
        "yasuhiro-okada-aktsk",
        "wiell",
        "choco_latie",
        "maeda_02",
        "katsuaki1991",
        "stmaruta",
        "Ryosuke2325",
        "qiita3665",
        "ma5aki-Y",
        "HideakiSaito",
        "meriy100",
        "1-AizawaSatoshi",
        "maromaro0013",
        "RSATAKE",
        "yuuuuuki",
        "playnext-shimpeiminato",
        "myrio3",
        "nuxaxtu",
        "soudai_s",
        "ratovia",
        "rennton1009",
        "sigk2",
        "takaakitanaka",
        "ISLA",
        "msykmrt",
        "kuwahata_takayuki",
        "kentarohorie",
        "headsofpara",
        "D4prog",
        "stomk",
        "hiromichinomata",
        "k_miyasue",
        "Terunaga",
        "tosaka07",
        "ybiquitous",
        "tatsuroro",
        "100010",
        "peta727",
        "Esfahan",
        "BLUEZERO",
        "r1ck666",
        "ryosukemaehira",
        "RumikoNishikawa",
        "kkanazaw",
        "keroyon",
        "ttwo32",
        "OkiSuguru",
        "ka2jun8",
        "TakaakiOno",
        "sakura_anpan",
        "IFK7",
        "youpy03",
        "mocchi7188",
        "masarusan24",
        "sleepyfaun",
        "ryutoyasugi",
        "moricode",
        "K_K_",
        "MikamiHiroaki",
        "yklitter",
        "himu62",
        "kaorina",
        "Fluff",
        "omelet",
        "musix55",
        "bu-son",
        "mottie0911",
        "kamadash",
        "akihakirise",
        "fushimi",
        "uraben",
        "jianghan0",
        "hoyamaman",
        "taiki_one",
        "SatomiO",
        "HironobuOhta",
        "tosakaya",
        "venqu",
        "masui_nao",
        "katolisa",
        "San_",
        "yunayuna",
        "silossowski",
        "grublomam07",
        "FujikuraYohei",
        "hagash",
        "TokyoYoshida",
        "ohs30359-nobuhara",
        "akamaru7610",
        "tachapu",
        "pinhead",
        "i77828",
        "takugi",
        "RyoNkmr",
        "KXqu05",
        "nekononeko_53",
        "tanakeiQ",
        "ShingoYokoyama",
        "hskwakr",
        "tackeyy",
        "ken-sendai",
        "tsuzuki557",
        "SmokeCheese",
        "tw_takamura",
        "Kirifuda1102",
        "recotic",
        "keigo0331",
        "TYY",
        "KotaFukada",
        "StudioPlumCreek",
        "yuuuking",
        "Yorinton",
        "siro_qii",
        "imagine0311",
        "T-Toshiya",
        "namahage1935",
        "masahito_y",
        "suica1011",
        "h15",
        "To_BB",
        "d3etbkucsasgy",
        "megalith",
        "kerri_net",
        "Kaiki",
        "weeeb",
        "tinkanketsu",
        "szken3is",
        "kiyo_",
        "kekekekekevin",
        "onionmk2",
        "mako1770",
        "shota_hayashi",
        "grsipen0529",
        "digitan21",
        "yawainu",
        "jun-oku",
        "teiet",
        "okuchan",
        "alpha-cyril",
        "taiki6173",
        "yoshitaku_jp",
        "enunu",
        "KazuyukiYokoyama",
        "aaaaa",
        "lkeli",
        "choyasu",
        "shoheiz",
        "hdykokd",
        "pokotyan",
        "jintaka1989",
        "myuasa",
        "Shiraberu",
        "coyupola",
        "kanaria007",
        "huwahuwa_games",
        "shuheiokuda0704",
        "shocknin",
        "akakotori",
        "bin_300K",
        "abeshi",
        "zumin",
        "gatetool000",
        "aknomaz",
        "Takapy_I",
        "murasaki",
        "muramegu",
        "moheji_dri",
        "kenfkd244",
        "lulu-ulul"
    ]
}, {
    "id": 404366,
    "uuid": "28c36e52346f315d0860",
    "user": {
        "id": 79241,
        "url_name": "306_san",
        "profile_image_url": "https://pbs.twimg.com/profile_images/714861996202672128/7IR2HoQl_normal.jpg",
        "following": true
    },
    "title": "22番ポートが使えなくても、SSHでGitしたい!",
    "created_at": "2016-07-01 09:50:09 +0900",
    "updated_at": "2016-07-01 09:50:09 +0900",
    "created_at_in_words": "2ヶ月",
    "updated_at_in_words": "2ヶ月",
    "tags": [{
        "name": "Git",
        "url_name": "git",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5d9ff7508a0c2d5bfa9536b6a0fe1864c11cee89/medium.jpg?1387912380",
        "following": false,
        "versions": []
    }, {
        "name": "SSH",
        "url_name": "ssh",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/b5cadc3eff868b31ec9023e1643a94c66870e60a/medium.jpg?1387904189",
        "following": false,
        "versions": []
    }],
    "stock_count": 23,
    "comment_count": 1,
    "url": "http://qiita.com/306_san/items/28c36e52346f315d0860",
    "created_at_as_seconds": 1467334209,
    "tweet": false,
    "gist_url": "https://gist.github.com/af4bb608407b04259d5c4d72f0507680",
    "private": false,
    "stocked": false,
    "raw_body": "SSHでGit使ってると、ファイアーウォールやプロキシ環境でいらいらしますよね。\nでも主要なサイト(GitHub,GitLab,Bitbucket)なら実は443ポートでSSHできちゃったりするんです。\nでも、調べても正しい情報がなかなか出てこなかったりするので自分へのメモ用に残して残しておくことにしました。\n\n使い方は簡単!以下のコンフィグを自分仕様に合わせて書くだけ!\n(hoge.proxy.jpをプロキシサーバーのURLにして1080のとこはポート番号を入れてください)\n\n## Windowsの場合\n### Git Bash使っている場合(CMDは多分ダメ)\nconnectが入っているのでこれを使います。他の環境は調査してませんが似たような感じでいけると思います。\n\n```bash:~/.ssh/config\nHost github.com\n    User git\n    Hostname ssh.github.com\n    IdentityFile ~/.ssh/id_rsa\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n\nHost bitbucket.org\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.bitbucket.org\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n\nHost gitlab.com\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.gitlab.com\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n```\n\n## macOS(OS X)/Linuxの場合\nncコマンドを使ってやります。ない場合はインストールしてください(方法は省略)\n\n```bash:~/.ssh/config\nHost github.com\n    User git\n    Hostname ssh.github.com\n    IdentityFile ~/.ssh/id_rsa\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n\nHost bitbucket.org\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.bitbucket.org\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n\nHost gitlab.com\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.gitlab.com\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n```\n## 最後に\n筆者はプロキシ使う環境と使わない環境の2つの環境でGit使っているので環境のたびにコメントをつけたり外したりするのがめんどくさいです。なにかいい方法はないでしょうか?(ホスト名proxy.github.comみたいな感じでリポジトリ切り替えとかすればいい?)\n",
    "body": "<p>SSHでGit使ってると、ファイアーウォールやプロキシ環境でいらいらしますよね。<br>\nでも主要なサイト(GitHub,GitLab,Bitbucket)なら実は443ポートでSSHできちゃったりするんです。<br>\nでも、調べても正しい情報がなかなか出てこなかったりするので自分へのメモ用に残して残しておくことにしました。</p>\n\n<p>使い方は簡単!以下のコンフィグを自分仕様に合わせて書くだけ!<br>\n(hoge.proxy.jpをプロキシサーバーのURLにして1080のとこはポート番号を入れてください)</p>\n\n<h2>\n<span id=\"windowsの場合\" class=\"fragment\"></span><a href=\"#windows%E3%81%AE%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>Windowsの場合</h2>\n\n<h3>\n<span id=\"git-bash使っている場合cmdは多分ダメ\" class=\"fragment\"></span><a href=\"#git-bash%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E5%A0%B4%E5%90%88cmd%E3%81%AF%E5%A4%9A%E5%88%86%E3%83%80%E3%83%A1\"><i class=\"fa fa-link\"></i></a>Git Bash使っている場合(CMDは多分ダメ)</h3>\n\n<p>connectが入っているのでこれを使います。他の環境は調査してませんが似たような感じでいけると思います。</p>\n\n<div class=\"code-frame\" data-lang=\"bash\">\n<div class=\"code-lang\"><span class=\"bold\">~/.ssh/config</span></div>\n<div class=\"highlight\"><pre>\nHost github.com\n    User git\n    Hostname ssh.github.com\n    IdentityFile ~/.ssh/id_rsa\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n\nHost bitbucket.org\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.bitbucket.org\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n\nHost gitlab.com\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.gitlab.com\n    Port 443\n    ProxyCommand connect.exe -H hoge.proxy.jp:1080 %h %p\n</pre></div>\n</div>\n\n<h2>\n<span id=\"macosos-xlinuxの場合\" class=\"fragment\"></span><a href=\"#macosos-xlinux%E3%81%AE%E5%A0%B4%E5%90%88\"><i class=\"fa fa-link\"></i></a>macOS(OS X)/Linuxの場合</h2>\n\n<p>ncコマンドを使ってやります。ない場合はインストールしてください(方法は省略)</p>\n\n<div class=\"code-frame\" data-lang=\"bash\">\n<div class=\"code-lang\"><span class=\"bold\">~/.ssh/config</span></div>\n<div class=\"highlight\"><pre>\nHost github.com\n    User git\n    Hostname ssh.github.com\n    IdentityFile ~/.ssh/id_rsa\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n\nHost bitbucket.org\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.bitbucket.org\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n\nHost gitlab.com\n    User git\n    IdentityFile ~/.ssh/id_rsa\n    HostName altssh.gitlab.com\n    Port 443\n    ProxyCommand nc -X connect -x hoge.proxy.jp:1080 %h %p\n</pre></div>\n</div>\n\n<h2>\n<span id=\"最後に\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB\"><i class=\"fa fa-link\"></i></a>最後に</h2>\n\n<p>筆者はプロキシ使う環境と使わない環境の2つの環境でGit使っているので環境のたびにコメントをつけたり外したりするのがめんどくさいです。なにかいい方法はないでしょうか?(ホスト名proxy.github.comみたいな感じでリポジトリ切り替えとかすればいい?)</p>\n",
    "stock_users": [
        "hogehiga",
        "hazy_moon",
        "Noboruhi",
        "poad1010",
        "ngyuki",
        "takuan_osho",
        "katochar",
        "tsuyoshi_cho",
        "suzuki_yo",
        "ndxbn",
        "takenoko_str",
        "gentahgr",
        "hamu502",
        "walrein",
        "Hidden4682",
        "yunano",
        "ikmski",
        "takeshou",
        "Ouvill",
        "zaemon",
        "aquariusbirth",
        "crosspointst",
        "bluebird445"
    ]
}, {
    "id": 403288,
    "uuid": "056325421b7e36f02335",
    "user": {
        "id": 7465,
        "url_name": "jnchito",
        "profile_image_url": "https://secure.gravatar.com/avatar/48a913a2e3bb5e68aae6f73079648e84",
        "following": true
    },
    "title": "プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意(解説動画付き)",
    "created_at": "2016-06-27 05:06:24 +0900",
    "updated_at": "2016-06-27 20:39:49 +0900",
    "created_at_in_words": "2ヶ月",
    "updated_at_in_words": "2ヶ月",
    "tags": [{
        "name": "Rails",
        "url_name": "rails",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/5310a6d3a8555d87a7060deec2c9e128bf3b3372/medium.jpg?1364838150",
        "following": true,
        "versions": []
    }, {
        "name": "debug",
        "url_name": "debug",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 617,
    "comment_count": 5,
    "url": "http://qiita.com/jnchito/items/056325421b7e36f02335",
    "created_at_as_seconds": 1466971584,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## はじめに\n\n先日、スタック・オーバーフローを見ているとこんな質問が載っていました。\n\n[Ruby On Railsで質問に対してのBA機能 - スタック・オーバーフロー](http://ja.stackoverflow.com/questions/26959/ruby-on-rails%e3%81%a7%e8%b3%aa%e5%95%8f%e3%81%ab%e5%af%be%e3%81%97%e3%81%a6%e3%81%aeba%e6%a9%9f%e8%83%bd)\n\n「BA機能」というのはどうやらベストアンサー機能の略らしいです。(BAって略し方は一般的なの??)\n\nそれはさておき、僕が気になったのは質問の最後の部分です。\n\n> ```\nProcessing by BestAnswersController#best as HTML\nParameters\n{\"authenticity_token\"=>\"DtGJ+4qzzG2PqEJpa7GH9Fb8pQhGDX0cg+w+qhf0tP/9HIIVYabiJeW0rEiL7iydpa5PpjrdR1V1LeGzfOeJjw==\", \"comment\"=>\"43\", \"note_id\"=>\"36\"}\nNote Load (0.2ms)  SELECT  \"notes\".* FROM \"notes\" WHERE \"notes\".\"id\" = ? LIMIT 1  [[\"id\", 36]]\nComment Load (0.3ms)  SELECT  \"comments\".* FROM \"comments\" WHERE \"comments\".\"id\" = ? LIMIT 1  [[\"id\", 43]]\nCompleted 500 Internal Server Error in 34ms (ActiveRecord: 1.1ms)\n>\n>ActiveRecord::AssociationTypeMismatch (Comment(#70182053951140) expected, got Fixnum(#15377440)):\napp/controllers/best_answers_controller.rb:5:in `best'\n```\n\n> ここで、エラーがでるのですが、どうすればいいでしょうか?\n\n質問は「ここで、エラーがでるのですが、どうすればいいでしょうか?」で終わっています。\n\nこの質問に限らず、スタック・オーバーフローのようなプログラマ向けのQ&Aサイトを見ていてよく思うのは、「エラーが出るとそこで止まって先に進めなくなる人が多いんだなあ」ということです。\n\n僕に言わせれば、「えっ、原因はそこに書いてあるとおりやん?」と思うのですが、プログラミング初心者の方は「エラーが出た!どうしよう、直し方がわからない・・・」と右往左往してしまう人が多いかと思います。\n\nそこでこの記事ではプログラミング初心者のために、エラーが出たら何を確認してどう解決すべきか、というポイントをあれこれ書いていきます。\n\n## 解説動画も作りました!(というか、むしろ動画がメインです)\n\nこの記事はYouTubeにアップした解説動画との連動記事です。\nというよりむしろ、 **動画の方がメイン** で、僕が伝えたい内容を詳しく説明しています。\nこちらの記事は動画の中でしゃべっている話を箇条書きしたような内容になっています。\n\n内容をしっかり理解するためにも、ぜひ動画と合わせて本文を読んでみてください。\n(ただし、動画の長さが1時間ちょっとあるので、1.5倍ぐらいの再生速度で見ることをオススメします)\n\n[![Screen Shot 2016-06-25 at 10.26.25.png](https://qiita-image-store.s3.amazonaws.com/0/7465/59a61c02-a06e-8a44-49b9-d46497f2e872.png \"Screen Shot 2016-06-25 at 10.26.25.png\")プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube](https://www.youtube.com/watch?v=5fyrGslhUcY)\n\n\n## この記事で使用するサンプルアプリケーション\n\n今回使用するサンプルアプリケーション(Railsアプリ)は、先ほどのスタック・オーバーフローに載っていたコードをほぼそのまま僕が書き写して作ったものです。\nエラーも同じように発生させています。\n\nソースコード(エラーが発生するコード)はGitHubに置いてあります。\n\nhttps://github.com/JunichiIto/backtrace-sandbox\n\nサンプルアプリを作っていると、設計や実装に関しても「うーん、これはちょっと・・・」と思うところがいくつかあったのですが、そこを指摘しだすとテーマが際限なく広がってしまいます。\nなので、今回はあくまでエラーの修正だけにフォーカスします。\n\nちなみにエラーが発生するのは以下のように、BA(ベストアンサー)を決定しようとしたときです。\n\n![TTPiOgFgor.gif](https://qiita-image-store.s3.amazonaws.com/0/7465/fbf23323-2480-6085-2b9a-acbcee7f2b39.gif \"TTPiOgFgor.gif\")\n\n\nそれでは以下が本編です。(ぜひ動画と合わせてどうぞ!)\n\n## エラーが起きた箇所を特定しよう\n\nまずは「どこでエラーが起きたのか」を特定します。\n特にスタックトレース(バックトレース)を読み解くスキルは非常に重要です!\n\n- 開発環境であれば画面にエラーの発生箇所を表示される。\n- 本番環境であればログを読む。ログにもエラーの発生箇所が表示される。\n- エラー画面やログを見て、エラーが起きたファイルと行番号を確認する。\n- 実際にエラーが起きたのは自分が書いたコードではなく、gemやフレームワークのコードであることも多い。Railsのエラー画面であれば Full trace リンクをクリックすると、gemやフレームワークのスタックトレース(バックトレース)が表示される。\n- スタックトレースは下から上にメソッドが呼び出された順番が表示される。スタックトレースを読むことで、エラーが発生するまでにどのファイルのどの行が呼ばれてきたのかを確認できる。\n- Railsの場合、[better_errors](https://github.com/charliesome/better_errors) gemを使うと便利で高機能なエラー画面が表示される。\n\n### ちなみに:今回エラーが発生したのはここです\n\nこのサンプルアプリでエラーが発生するのは、 `bestanswer = note.build_best_answer(comment: params[:comment])` の行です。\n\n```ruby\nclass BestAnswersController < ApplicationController\n  def best\n    note = Note.find(params[:note_id])\n    # この行でエラーが発生!\n    bestanswer = note.build_best_answer(comment: params[:comment])\n    bestanswer.save\n    redirect_to note, notice: 'hogehoge'\n  end\nend\n```\n\n## エラーメッセージとエラータイプを「ちゃんと」読もう\n\n英語が苦手だからといってないがしろにしていませんか?\nここは絶対に逃げちゃダメです!\n\n- エラーメッセージを読むとエラーの原因がわかることが多い。\n- エラータイプ(例外クラス)も確認する。必要であればAPIドキュメントを開き、どういうときに発生するエラーなのかを確認する。\n- **英語から逃げるな!!辞書を引いてでも読め!!**\n- 技術系の英語はよく使われる単語が決まっているので、そのうち自然と覚えられるはず。\n\n### ちなみに:今回のエラーメッセージとエラータイプはこちら\n\n今回はこんなエラーが発生していました。\n\n- エラーメッセージ:Comment(#70227754972740) expected, got String(#70227753082960)\n- エラータイプ:ActiveRecord::AssociationTypeMismatch\n\nエラーメッセージを直訳すると、「Commentを期待していたが、Stringが渡ってきた」の意味になります。\n\nまた、 `ActiveRecord::AssociationTypeMismatch` は、[APIドキュメント](http://api.rubyonrails.org/classes/ActiveRecord/AssociationTypeMismatch.html)によると、「関連にアサインされたオブジェクトが不正な型を持っていたときにraiseされる」と(英語で)説明されています。\n\n## 開発環境でエラーを再現させよう\n\nエラーが再現しないのに、当てずっぽうで修正してはいけません。\n\n- 本番環境でエラーが起きている場合は、まず自分の開発環境で同じエラーを再現させる。\n- 再現手順がぱっとわからない場合は、ログからユーザーの操作手順を読み解く。\n- ログを読むとユーザーがどの画面を開いて、どのリンクやボタンをクリックしたのか、といったことが(だいたい)わかる。\n- HTTPステータスコードの意味もちゃんと理解しておく。(200 = OK、500 = システムエラー、等)\n\n## 変数の中身やメソッドの戻り値、実行された条件分岐等を確認しよう\n\nプログラムの実行順序やデータの中身を正確に把握しましょう。\n\n- puts を使って、コンソールに情報を表示させる。\n- Rails.logger を使って、ログに情報を表示させる。\n- デバッガ([byebug](https://github.com/deivid-rodriguez/byebug))を使って、対話的にデバッグする。\n- [RubyMine](https://www.jetbrains.com/ruby/)を使って、対話的にデバッグする。RubyMineならIDE内でブレークポイントを付けるだけなので、コードをいじらずに済む。\n- better_errorsを使って、ブラウザ内で変数の中身やメソッドの戻り値を確認する。\n\n## テストコードを使ってエラーを再現させよう\n\nテストコードは実はデバッグするときにも活躍します。\n\n- テストコード内でエラーを再現できると、毎回ブラウザを操作する必要がなくなる。\n- エラー再現の手順が面倒な場合は、先にテストコードを書いた方がトータルの工数が抑えられる。\n- テストコードがあれば、コードを修正してからデバッガを起動するまでの時間も短い。\n- 工数面のメリットだけでなく、テストコードがあれば今後同じエラーの再発を防ぐこともできる。\n- ただし、デバッグの工数を抑えるためには、テストコードをそれなりのスピードで書ける必要がある。\n\n## エラーを再現させるためだけのRailsアプリを作ろう\n\nもっと気軽に `rails new` してもいいんです!\n\n- 元のアプリケーションが大規模で複雑だったりすると、予想外の原因でエラーが起きている可能性もある。\n- なので、ある程度原因の予想を立てて、そのエラーを再現させるためだけのシンプルなRailsアプリを作る。\n- そのアプリ内でもエラーが再現すれば予想は正しいので、そのアプリ内でデバッグする。\n- エラーが再現しなければ予想が外れているので、別の原因を探る。\n\n## エラーを修正して動作確認しよう\n\nさあ、これだけ情報が揃えば簡単にエラーを直せるはず!?\n\n- ここまでに得た情報を元にコードを修正する。\n- 画面を操作したり、テストコードを実行したりして、エラーが出なくなったことを確認する。\n\n### ちなみに:今回のエラーの原因と解決策はこちら\n\nこの記事で紹介しているサンプルアプリケーションでエラーが発生していた原因は、本来Commentクラスのオブジェクトを渡すべきところを、String型のIDを渡していたのが原因です。\n\nなので、IDではなく、Commentオブジェクトを渡せば問題が解決します。\n\n```diff\n class BestAnswersController < ApplicationController\n   def best\n     note = Note.find(params[:note_id])\n-    bestanswer = note.build_best_answer(comment: params[:comment])\n+    comment = note.comments.find(params[:comment])\n+    bestanswer = note.build_best_answer(comment: comment)\n     bestanswer.save\n     redirect_to note, notice: 'hogehoge'\n   end\n end\n```\n\nご覧のとおり、ちゃんとベストアンサーが選択できるようになりました!\n\n![5VgwOEdA9B.gif](https://qiita-image-store.s3.amazonaws.com/0/7465/c035088f-07bf-4b1e-cd13-004286047392.gif \"5VgwOEdA9B.gif\")\n\n\n## 公式ドキュメントやGitHubのissueを読もう\n\n正確な情報を把握するため、ドキュメントやissueもちゃんと読みましょう。\n\n- 必ずしも自分が書いたコードに原因があるとは限らない。gemやフレームワークのバグを踏んでいる可能性もある。\n- GitHubのissueを検索すると、そのエラーが実は修正済みの不具合に起因するものだった、と判明したりすることがある。\n- もしくは単純に自分がgemやフレームワークの使い方を知らなかったり、大きな勘違いをしているだけ、ということもある。\n- GitHubのREADMEやWikiページ、公式ドキュメント([Railsガイド](http://railsguides.jp/)等)を読んで、必要な情報が載っていないかチェックする。\n- やはりここでも英語力が必要。 **英語から逃げるな!!辞書を引いてでも読め!!** (2回目)\n\n## gemやフレームワークのコードを追いかけよう\n\n熟練したプログラマだと、「何かあったらコードを読む」を習慣づけている人も多いです。\n\n- エラーが簡単に解決しない場合は、gemやフレームワークのコードを追いかけることで解決する場合がある。\n- デバッガ(byebugやRubyMine)やbetter_errorsを使って、どこでどんなコードが呼ばれているのかを深掘りしていく。\n- gemやフレームワークのコードを読むのは初心者の人には敷居が高いかもしれないが、こうしたスキルものちのち必要になってくるはず。早い内から慣れておく方が良い。\n\n## エラータイプ(例外クラス)やエラーメッセージでググろう(ただし過信は禁物)\n\n一番簡単、だけど一番危険:skull:な方法です。\n\n- エラータイプやエラーメッセージでググると、ネット上であっさり解決方法が見つかったりすることも多い。\n- ただし、情報が古かったり、適切でない解決策が載っていたりすることも多々あるので、くれぐれも過信は禁物。 **公式ではない情報は疑ってかかること!!**\n- 意味もわからず盲目的にソースコードをコピペしたりするのも危険。\n- 「コピペしようとしているそのコード、自分で説明できますか?」を自問自答する。説明できないならまず、コードの意味を理解することから始めること。\n- [Stack Overflow](http://stackoverflow.com/)の情報はベストアンサーよりも投票数に着目する。([参考](http://blog.jnito.com/entry/2015/10/04/073825#オーディエンスQAを見る人の心構え))\n\n## やみくもにデバッグせず、仮説と検証を繰り返そう\n\nコードを書くときだけでなく、デバッグするときも論理的に取り組みましょう。\n\n- コードを触る前にまず、どこにエラーの原因があるのか、当たりを付ける。\n- 「これがエラーの原因である」と仮説を立てたら、その仮説が正しいかどうかを検証できるような変更を加える。(仮説と検証)\n- もし仮説が外れていたら、別の仮説を立て、その仮説を検証する。\n- このように、エラーが解決するまで仮説と検証を繰り返していく。\n\n## 二分探索法でエラーの原因を徐々に絞り込もう\n\n大きな的(まと)から小さな的へ、原因を段階的に絞り込んで特定するアプローチです。\n\n- 問題の原因がハッキリしない場合は[二分探索法](https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2)的なアプローチを取る。\n- まず、問題の原因をAとBという大きな二つのグループに分ける。\n- AとBを調べて、Aの中でエラーが出たら、Aのどこかにエラーの原因がある。\n- さらにAを、CとDというグループに分ける。\n- Cではエラーが出ず、Dでエラーが出たら、Dのどこかにエラーの原因がある。\n- さらにDの中で・・・という手順を繰り返していくと、エラーの原因が特定できる。\n\n## どうしても解決しなければ誰かに質問しよう\n\nここまで説明してきたテクニックやアプローチをもってしても解決しないなら、誰かに聞きましょう。\n初心者に限らず、上級者でもダメなときはダメです。\n\n- 頑張ってもダメなときは誰かに質問する。\n- 職場であれば同僚のプログラマに質問する。\n- 誰かに説明している途中に「・・・あ、ごめん。原因がわかった:sweat_smile:」と自分で解決できることもよくある。([テディベア効果](http://plaza.rakuten.co.jp/sebook/diary/200703230000/))\n- 身近に質問できる人がいなければ、ネットのQ&Aサイトで質問する。([参考](http://blog.mb.cloud.nifty.com/?p=3164))\n- ネットで質問する場合は、回答者が答えやすくなるように説明する。([参考](http://blog.jnito.com/entry/2015/10/04/073825#上手に質問するコツ))\n\n## 時間を決めてデバッグしよう/気軽に質問できる雰囲気を作ろう\n\nゲームと一緒で、デバッグも熱中するとあっという間に時間が過ぎてしまいます・・・。\n\n- 延々とデバッグに時間を浪費しない。2時間も3時間も浪費してしまうのは時間のムダ。(特に仕事でコードを書く場合)\n- たとえば「30分やってもダメなら諦めて誰かに聞く」と自分で時間を決めてデバッグする。\n- チームで開発する場合は気軽に質問できる雰囲気を作る。(みんなで話し合って意識あわせをする)\n- 答える方も快く答えてあげる。「えっ、そんなことも知らないの?(苦笑)」とイヤミを言ったりしない。\n- トータルで見て生産性が上がるオプションを選ぶ。(何時間も一人で悩むのと、誰かに聞いてぱっと解決するのではどちらが生産性が高いか?)\n\n## ハマったときは一度コードから離れてみよう\n\n「一晩寝かせてみたら一発でわかった!」なんてこともよくあります。\n\n- 何時間やっても解決しない(つまりハマってしまった)、というときは一度コードから離れてみる。\n- 外を散歩したり、お風呂に入ったり、ぐっすり眠ったりすると、全然違う視点やアイデアが浮かんで、さくっと解決してしまうことがある。\n- とはいえ、身近に質問できる人がいるなら、さっさと質問してしまった方がたぶん早い。\n\n## Qiitaやブログで知見を共有しよう\n\n世の中、意外と同じ問題で困っている人は多いものです。\n\n- 問題が解決したら、Qiitaやブログで原因と解決策を書く。\n- 知見をネット上で共有することで、他のエンジニアの生産性向上に寄与できる。\n- 場合によっては他のエンジニアから、さらに良い解決策を教えてもらえることがある。\n- 自分の書いた記事が話題になれば、知名度が上がり、転職時に有利になったりする(かもしれない)。\n- 良い技術記事の書き方はこちらのリンクを参考にする。\n  - [個人的な良い記事のガイドライン = 2ヶ月前の自分が泣いて喜ぶような記事を書く - Qiita](http://qiita.com/jnchito/items/5c3eb3640ad57b3edc6c)\n  - [これであなたのQiita記事もランキング入り!?@jnchitoによる編集リクエスト解説(解説動画付き) - Qiita](http://qiita.com/jnchito/items/a939906de64bf34700a9)\n\n## 解説動画の紹介(再掲)\n\nいかがだったでしょうか?\nここで再度、解説動画のリンクを載せておきます。\n(冒頭で述べたとおり、 **動画がメインでこの記事がオマケ** です!)\n\n[![Screen Shot 2016-06-25 at 10.26.25.png](https://qiita-image-store.s3.amazonaws.com/0/7465/59a61c02-a06e-8a44-49b9-d46497f2e872.png \"Screen Shot 2016-06-25 at 10.26.25.png\")プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube](https://www.youtube.com/watch?v=5fyrGslhUcY)\n\n\nスクリーンキャストで実際の画面やコードを見ながら説明を聞くと、さらに理解度がアップするはずです。\n\n## まとめ\n\nというわけで、この記事では「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意を説明しました。\n\nエラーが発生した箇所を特定する、スタックトレースを読む、エラーメッセージとエラータイプをちゃんと確認する、デバッガを使って対話的にデバッグする、といった基本テクニックが身に付いていれば、きっと「エラーが出ました。どうすればいいですか?」から卒業できるはずです。\n\nそれ以外にも素早く問題解決に至るためのテクニックやアプローチをいろいろと紹介してみました。\n独学でプログラミングをやっていたりすると、なかなかこういった泥臭い(?)テクニックを学ぶ機会はないかもしれません。\n\nこの記事を読んで、みなさんが「エラーが出ました。どうすればいいですか?」から卒業できれば幸いです。\n\n### あわせて読みたい\n\n自分のブログにこの記事を書いた動機をもう少し詳しく書きました。\nよかったらこちらもどうぞ。\n\n[「エラーが出ました。どうすればいいですか?」から卒業するための記事をQiitaに書きました - give IT a try](http://blog.jnito.com/entry/2016/06/27/050818) <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2016/06/27/050818\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2016/06/27/050818\" class=\"bookmark-count\"></a>\n\nこの記事にも登場するプログラマ向けQ&Aサイト、スタック・オーバーフローの使い方を詳しく解説したブログ記事です。\n\n[今夜わかる「スタック・オーバーフロー」の世界 - give IT a try](http://blog.jnito.com/entry/2015/10/04/073825) <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2015/10/04/073825\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2015/10/04/073825\" class=\"bookmark-count\"></a>\n\n「**英語から逃げるな!!辞書を引いてでも読め!!**って言われてもどうすれば・・・??」と思っている方は、こちらのブログ記事にヒントが載っているかもしれません。\n\n[英語力をアップさせる知見がいっぱい!「Rubyistのための英語勉強会」を開催しました - give IT a try](http://blog.jnito.com/entry/2015/09/02/120620) <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2015/09/02/120620\" target=\"_blank\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2015/09/02/120620\" class=\"bookmark-count\"></a>\n",
    "body": "\n<h2>\n<span id=\"はじめに\" class=\"fragment\"></span><a href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"><i class=\"fa fa-link\"></i></a>はじめに</h2>\n\n<p>先日、スタック・オーバーフローを見ているとこんな質問が載っていました。</p>\n\n<p><a href=\"http://ja.stackoverflow.com/questions/26959/ruby-on-rails%e3%81%a7%e8%b3%aa%e5%95%8f%e3%81%ab%e5%af%be%e3%81%97%e3%81%a6%e3%81%aeba%e6%a9%9f%e8%83%bd\" rel=\"nofollow\" target=\"_blank\">Ruby On Railsで質問に対してのBA機能 - スタック・オーバーフロー</a></p>\n\n<p>「BA機能」というのはどうやらベストアンサー機能の略らしいです。(BAって略し方は一般的なの??)</p>\n\n<p>それはさておき、僕が気になったのは質問の最後の部分です。</p>\n\n<blockquote>\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\nProcessing by BestAnswersController#best as HTML\nParameters\n{\"authenticity_token\"=&gt;\"DtGJ+4qzzG2PqEJpa7GH9Fb8pQhGDX0cg+w+qhf0tP/9HIIVYabiJeW0rEiL7iydpa5PpjrdR1V1LeGzfOeJjw==\", \"comment\"=&gt;\"43\", \"note_id\"=&gt;\"36\"}\nNote Load (0.2ms)  SELECT  \"notes\".* FROM \"notes\" WHERE \"notes\".\"id\" = ? LIMIT 1  [[\"id\", 36]]\nComment Load (0.3ms)  SELECT  \"comments\".* FROM \"comments\" WHERE \"comments\".\"id\" = ? LIMIT 1  [[\"id\", 43]]\nCompleted 500 Internal Server Error in 34ms (ActiveRecord: 1.1ms)\n\nActiveRecord::AssociationTypeMismatch (Comment(#70182053951140) expected, got Fixnum(#15377440)):\napp/controllers/best_answers_controller.rb:5:in `best'\n</pre></div></div>\n\n<p>ここで、エラーがでるのですが、どうすればいいでしょうか?</p>\n</blockquote>\n\n<p>質問は「ここで、エラーがでるのですが、どうすればいいでしょうか?」で終わっています。</p>\n\n<p>この質問に限らず、スタック・オーバーフローのようなプログラマ向けのQ&amp;Aサイトを見ていてよく思うのは、「エラーが出るとそこで止まって先に進めなくなる人が多いんだなあ」ということです。</p>\n\n<p>僕に言わせれば、「えっ、原因はそこに書いてあるとおりやん?」と思うのですが、プログラミング初心者の方は「エラーが出た!どうしよう、直し方がわからない・・・」と右往左往してしまう人が多いかと思います。</p>\n\n<p>そこでこの記事ではプログラミング初心者のために、エラーが出たら何を確認してどう解決すべきか、というポイントをあれこれ書いていきます。</p>\n\n<h2>\n<span id=\"解説動画も作りましたというかむしろ動画がメインです\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E8%AA%AC%E5%8B%95%E7%94%BB%E3%82%82%E4%BD%9C%E3%82%8A%E3%81%BE%E3%81%97%E3%81%9F%E3%81%A8%E3%81%84%E3%81%86%E3%81%8B%E3%82%80%E3%81%97%E3%82%8D%E5%8B%95%E7%94%BB%E3%81%8C%E3%83%A1%E3%82%A4%E3%83%B3%E3%81%A7%E3%81%99\"><i class=\"fa fa-link\"></i></a>解説動画も作りました!(というか、むしろ動画がメインです)</h2>\n\n<p>この記事はYouTubeにアップした解説動画との連動記事です。<br>\nというよりむしろ、 <strong>動画の方がメイン</strong> で、僕が伝えたい内容を詳しく説明しています。<br>\nこちらの記事は動画の中でしゃべっている話を箇条書きしたような内容になっています。</p>\n\n<p>内容をしっかり理解するためにも、ぜひ動画と合わせて本文を読んでみてください。<br>\n(ただし、動画の長さが1時間ちょっとあるので、1.5倍ぐらいの再生速度で見ることをオススメします)</p>\n\n<p><a href=\"https://www.youtube.com/watch?v=5fyrGslhUcY\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/59a61c02-a06e-8a44-49b9-d46497f2e872.png\" alt=\"Screen Shot 2016-06-25 at 10.26.25.png\" title=\"Screen Shot 2016-06-25 at 10.26.25.png\">プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube</a></p>\n\n<h2>\n<span id=\"この記事で使用するサンプルアプリケーション\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3\"><i class=\"fa fa-link\"></i></a>この記事で使用するサンプルアプリケーション</h2>\n\n<p>今回使用するサンプルアプリケーション(Railsアプリ)は、先ほどのスタック・オーバーフローに載っていたコードをほぼそのまま僕が書き写して作ったものです。<br>\nエラーも同じように発生させています。</p>\n\n<p>ソースコード(エラーが発生するコード)はGitHubに置いてあります。</p>\n\n<p><a href=\"https://github.com/JunichiIto/backtrace-sandbox\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/JunichiIto/backtrace-sandbox</a></p>\n\n<p>サンプルアプリを作っていると、設計や実装に関しても「うーん、これはちょっと・・・」と思うところがいくつかあったのですが、そこを指摘しだすとテーマが際限なく広がってしまいます。<br>\nなので、今回はあくまでエラーの修正だけにフォーカスします。</p>\n\n<p>ちなみにエラーが発生するのは以下のように、BA(ベストアンサー)を決定しようとしたときです。</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/fbf23323-2480-6085-2b9a-acbcee7f2b39.gif\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/fbf23323-2480-6085-2b9a-acbcee7f2b39.gif\" alt=\"TTPiOgFgor.gif\" title=\"TTPiOgFgor.gif\"></a></p>\n\n<p>それでは以下が本編です。(ぜひ動画と合わせてどうぞ!)</p>\n\n<h2>\n<span id=\"エラーが起きた箇所を特定しよう\" class=\"fragment\"></span><a href=\"#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E8%B5%B7%E3%81%8D%E3%81%9F%E7%AE%87%E6%89%80%E3%82%92%E7%89%B9%E5%AE%9A%E3%81%97%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>エラーが起きた箇所を特定しよう</h2>\n\n<p>まずは「どこでエラーが起きたのか」を特定します。<br>\n特にスタックトレース(バックトレース)を読み解くスキルは非常に重要です!</p>\n\n<ul>\n<li>開発環境であれば画面にエラーの発生箇所を表示される。</li>\n<li>本番環境であればログを読む。ログにもエラーの発生箇所が表示される。</li>\n<li>エラー画面やログを見て、エラーが起きたファイルと行番号を確認する。</li>\n<li>実際にエラーが起きたのは自分が書いたコードではなく、gemやフレームワークのコードであることも多い。Railsのエラー画面であれば Full trace リンクをクリックすると、gemやフレームワークのスタックトレース(バックトレース)が表示される。</li>\n<li>スタックトレースは下から上にメソッドが呼び出された順番が表示される。スタックトレースを読むことで、エラーが発生するまでにどのファイルのどの行が呼ばれてきたのかを確認できる。</li>\n<li>Railsの場合、<a href=\"https://github.com/charliesome/better_errors\" rel=\"nofollow\" target=\"_blank\">better_errors</a> gemを使うと便利で高機能なエラー画面が表示される。</li>\n</ul>\n\n<h3>\n<span id=\"ちなみに今回エラーが発生したのはここです\" class=\"fragment\"></span><a href=\"#%E3%81%A1%E3%81%AA%E3%81%BF%E3%81%AB%E4%BB%8A%E5%9B%9E%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E7%99%BA%E7%94%9F%E3%81%97%E3%81%9F%E3%81%AE%E3%81%AF%E3%81%93%E3%81%93%E3%81%A7%E3%81%99\"><i class=\"fa fa-link\"></i></a>ちなみに:今回エラーが発生したのはここです</h3>\n\n<p>このサンプルアプリでエラーが発生するのは、 <code>bestanswer = note.build_best_answer(comment: params[:comment])</code> の行です。</p>\n\n<div class=\"code-frame\" data-lang=\"ruby\"><div class=\"highlight\"><pre>\n<span class=\"k\">class</span> <span class=\"nc\">BestAnswersController</span> <span class=\"o\">&lt;</span> <span class=\"no\">ApplicationController</span>\n  <span class=\"k\">def</span> <span class=\"nf\">best</span>\n    <span class=\"n\">note</span> <span class=\"o\">=</span> <span class=\"no\">Note</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"o\">[</span><span class=\"ss\">:note_id</span><span class=\"o\">]</span><span class=\"p\">)</span>\n    <span class=\"c1\"># この行でエラーが発生!</span>\n    <span class=\"n\">bestanswer</span> <span class=\"o\">=</span> <span class=\"n\">note</span><span class=\"o\">.</span><span class=\"n\">build_best_answer</span><span class=\"p\">(</span><span class=\"ss\">comment</span><span class=\"p\">:</span> <span class=\"n\">params</span><span class=\"o\">[</span><span class=\"ss\">:comment</span><span class=\"o\">]</span><span class=\"p\">)</span>\n    <span class=\"n\">bestanswer</span><span class=\"o\">.</span><span class=\"n\">save</span>\n    <span class=\"n\">redirect_to</span> <span class=\"n\">note</span><span class=\"p\">,</span> <span class=\"ss\">notice</span><span class=\"p\">:</span> <span class=\"s1\">'hogehoge'</span>\n  <span class=\"k\">end</span>\n<span class=\"k\">end</span>\n</pre></div></div>\n\n<h2>\n<span id=\"エラーメッセージとエラータイプをちゃんと読もう\" class=\"fragment\"></span><a href=\"#%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%A8%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E3%82%92%E3%81%A1%E3%82%83%E3%82%93%E3%81%A8%E8%AA%AD%E3%82%82%E3%81%86\"><i class=\"fa fa-link\"></i></a>エラーメッセージとエラータイプを「ちゃんと」読もう</h2>\n\n<p>英語が苦手だからといってないがしろにしていませんか?<br>\nここは絶対に逃げちゃダメです!</p>\n\n<ul>\n<li>エラーメッセージを読むとエラーの原因がわかることが多い。</li>\n<li>エラータイプ(例外クラス)も確認する。必要であればAPIドキュメントを開き、どういうときに発生するエラーなのかを確認する。</li>\n<li><strong>英語から逃げるな!!辞書を引いてでも読め!!</strong></li>\n<li>技術系の英語はよく使われる単語が決まっているので、そのうち自然と覚えられるはず。</li>\n</ul>\n\n<h3>\n<span id=\"ちなみに今回のエラーメッセージとエラータイプはこちら\" class=\"fragment\"></span><a href=\"#%E3%81%A1%E3%81%AA%E3%81%BF%E3%81%AB%E4%BB%8A%E5%9B%9E%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%A8%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89\"><i class=\"fa fa-link\"></i></a>ちなみに:今回のエラーメッセージとエラータイプはこちら</h3>\n\n<p>今回はこんなエラーが発生していました。</p>\n\n<ul>\n<li>エラーメッセージ:Comment(#70227754972740) expected, got String(#70227753082960)</li>\n<li>エラータイプ:ActiveRecord::AssociationTypeMismatch</li>\n</ul>\n\n<p>エラーメッセージを直訳すると、「Commentを期待していたが、Stringが渡ってきた」の意味になります。</p>\n\n<p>また、 <code>ActiveRecord::AssociationTypeMismatch</code> は、<a href=\"http://api.rubyonrails.org/classes/ActiveRecord/AssociationTypeMismatch.html\" rel=\"nofollow\" target=\"_blank\">APIドキュメント</a>によると、「関連にアサインされたオブジェクトが不正な型を持っていたときにraiseされる」と(英語で)説明されています。</p>\n\n<h2>\n<span id=\"開発環境でエラーを再現させよう\" class=\"fragment\"></span><a href=\"#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E5%86%8D%E7%8F%BE%E3%81%95%E3%81%9B%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>開発環境でエラーを再現させよう</h2>\n\n<p>エラーが再現しないのに、当てずっぽうで修正してはいけません。</p>\n\n<ul>\n<li>本番環境でエラーが起きている場合は、まず自分の開発環境で同じエラーを再現させる。</li>\n<li>再現手順がぱっとわからない場合は、ログからユーザーの操作手順を読み解く。</li>\n<li>ログを読むとユーザーがどの画面を開いて、どのリンクやボタンをクリックしたのか、といったことが(だいたい)わかる。</li>\n<li>HTTPステータスコードの意味もちゃんと理解しておく。(200 = OK、500 = システムエラー、等)</li>\n</ul>\n\n<h2>\n<span id=\"変数の中身やメソッドの戻り値実行された条件分岐等を確認しよう\" class=\"fragment\"></span><a href=\"#%E5%A4%89%E6%95%B0%E3%81%AE%E4%B8%AD%E8%BA%AB%E3%82%84%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E6%88%BB%E3%82%8A%E5%80%A4%E5%AE%9F%E8%A1%8C%E3%81%95%E3%82%8C%E3%81%9F%E6%9D%A1%E4%BB%B6%E5%88%86%E5%B2%90%E7%AD%89%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%97%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>変数の中身やメソッドの戻り値、実行された条件分岐等を確認しよう</h2>\n\n<p>プログラムの実行順序やデータの中身を正確に把握しましょう。</p>\n\n<ul>\n<li>puts を使って、コンソールに情報を表示させる。</li>\n<li>Rails.logger を使って、ログに情報を表示させる。</li>\n<li>デバッガ(<a href=\"https://github.com/deivid-rodriguez/byebug\" rel=\"nofollow\" target=\"_blank\">byebug</a>)を使って、対話的にデバッグする。</li>\n<li>\n<a href=\"https://www.jetbrains.com/ruby/\" rel=\"nofollow\" target=\"_blank\">RubyMine</a>を使って、対話的にデバッグする。RubyMineならIDE内でブレークポイントを付けるだけなので、コードをいじらずに済む。</li>\n<li>better_errorsを使って、ブラウザ内で変数の中身やメソッドの戻り値を確認する。</li>\n</ul>\n\n<h2>\n<span id=\"テストコードを使ってエラーを再現させよう\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E5%86%8D%E7%8F%BE%E3%81%95%E3%81%9B%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>テストコードを使ってエラーを再現させよう</h2>\n\n<p>テストコードは実はデバッグするときにも活躍します。</p>\n\n<ul>\n<li>テストコード内でエラーを再現できると、毎回ブラウザを操作する必要がなくなる。</li>\n<li>エラー再現の手順が面倒な場合は、先にテストコードを書いた方がトータルの工数が抑えられる。</li>\n<li>テストコードがあれば、コードを修正してからデバッガを起動するまでの時間も短い。</li>\n<li>工数面のメリットだけでなく、テストコードがあれば今後同じエラーの再発を防ぐこともできる。</li>\n<li>ただし、デバッグの工数を抑えるためには、テストコードをそれなりのスピードで書ける必要がある。</li>\n</ul>\n\n<h2>\n<span id=\"エラーを再現させるためだけのrailsアプリを作ろう\" class=\"fragment\"></span><a href=\"#%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E5%86%8D%E7%8F%BE%E3%81%95%E3%81%9B%E3%82%8B%E3%81%9F%E3%82%81%E3%81%A0%E3%81%91%E3%81%AErails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86\"><i class=\"fa fa-link\"></i></a>エラーを再現させるためだけのRailsアプリを作ろう</h2>\n\n<p>もっと気軽に <code>rails new</code> してもいいんです!</p>\n\n<ul>\n<li>元のアプリケーションが大規模で複雑だったりすると、予想外の原因でエラーが起きている可能性もある。</li>\n<li>なので、ある程度原因の予想を立てて、そのエラーを再現させるためだけのシンプルなRailsアプリを作る。</li>\n<li>そのアプリ内でもエラーが再現すれば予想は正しいので、そのアプリ内でデバッグする。</li>\n<li>エラーが再現しなければ予想が外れているので、別の原因を探る。</li>\n</ul>\n\n<h2>\n<span id=\"エラーを修正して動作確認しよう\" class=\"fragment\"></span><a href=\"#%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E4%BF%AE%E6%AD%A3%E3%81%97%E3%81%A6%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D%E3%81%97%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>エラーを修正して動作確認しよう</h2>\n\n<p>さあ、これだけ情報が揃えば簡単にエラーを直せるはず!?</p>\n\n<ul>\n<li>ここまでに得た情報を元にコードを修正する。</li>\n<li>画面を操作したり、テストコードを実行したりして、エラーが出なくなったことを確認する。</li>\n</ul>\n\n<h3>\n<span id=\"ちなみに今回のエラーの原因と解決策はこちら\" class=\"fragment\"></span><a href=\"#%E3%81%A1%E3%81%AA%E3%81%BF%E3%81%AB%E4%BB%8A%E5%9B%9E%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E5%8E%9F%E5%9B%A0%E3%81%A8%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89\"><i class=\"fa fa-link\"></i></a>ちなみに:今回のエラーの原因と解決策はこちら</h3>\n\n<p>この記事で紹介しているサンプルアプリケーションでエラーが発生していた原因は、本来Commentクラスのオブジェクトを渡すべきところを、String型のIDを渡していたのが原因です。</p>\n\n<p>なので、IDではなく、Commentオブジェクトを渡せば問題が解決します。</p>\n\n<div class=\"code-frame\" data-lang=\"diff\"><div class=\"highlight\"><pre>\n class BestAnswersController &lt; ApplicationController\n   def best\n     note = Note.find(params[:note_id])\n<span class=\"gd\">-    bestanswer = note.build_best_answer(comment: params[:comment])</span>\n<span class=\"gi\">+    comment = note.comments.find(params[:comment])</span>\n<span class=\"gi\">+    bestanswer = note.build_best_answer(comment: comment)</span>\n     bestanswer.save\n     redirect_to note, notice: 'hogehoge'\n   end\n end\n</pre></div></div>\n\n<p>ご覧のとおり、ちゃんとベストアンサーが選択できるようになりました!</p>\n\n<p><a href=\"https://qiita-image-store.s3.amazonaws.com/0/7465/c035088f-07bf-4b1e-cd13-004286047392.gif\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/c035088f-07bf-4b1e-cd13-004286047392.gif\" alt=\"5VgwOEdA9B.gif\" title=\"5VgwOEdA9B.gif\"></a></p>\n\n<h2>\n<span id=\"公式ドキュメントやgithubのissueを読もう\" class=\"fragment\"></span><a href=\"#%E5%85%AC%E5%BC%8F%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%82%84github%E3%81%AEissue%E3%82%92%E8%AA%AD%E3%82%82%E3%81%86\"><i class=\"fa fa-link\"></i></a>公式ドキュメントやGitHubのissueを読もう</h2>\n\n<p>正確な情報を把握するため、ドキュメントやissueもちゃんと読みましょう。</p>\n\n<ul>\n<li>必ずしも自分が書いたコードに原因があるとは限らない。gemやフレームワークのバグを踏んでいる可能性もある。</li>\n<li>GitHubのissueを検索すると、そのエラーが実は修正済みの不具合に起因するものだった、と判明したりすることがある。</li>\n<li>もしくは単純に自分がgemやフレームワークの使い方を知らなかったり、大きな勘違いをしているだけ、ということもある。</li>\n<li>GitHubのREADMEやWikiページ、公式ドキュメント(<a href=\"http://railsguides.jp/\" rel=\"nofollow\" target=\"_blank\">Railsガイド</a>等)を読んで、必要な情報が載っていないかチェックする。</li>\n<li>やはりここでも英語力が必要。 <strong>英語から逃げるな!!辞書を引いてでも読め!!</strong> (2回目)</li>\n</ul>\n\n<h2>\n<span id=\"gemやフレームワークのコードを追いかけよう\" class=\"fragment\"></span><a href=\"#gem%E3%82%84%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E3%81%84%E3%81%8B%E3%81%91%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>gemやフレームワークのコードを追いかけよう</h2>\n\n<p>熟練したプログラマだと、「何かあったらコードを読む」を習慣づけている人も多いです。</p>\n\n<ul>\n<li>エラーが簡単に解決しない場合は、gemやフレームワークのコードを追いかけることで解決する場合がある。</li>\n<li>デバッガ(byebugやRubyMine)やbetter_errorsを使って、どこでどんなコードが呼ばれているのかを深掘りしていく。</li>\n<li>gemやフレームワークのコードを読むのは初心者の人には敷居が高いかもしれないが、こうしたスキルものちのち必要になってくるはず。早い内から慣れておく方が良い。</li>\n</ul>\n\n<h2>\n<span id=\"エラータイプ例外クラスやエラーメッセージでググろうただし過信は禁物\" class=\"fragment\"></span><a href=\"#%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E4%BE%8B%E5%A4%96%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%84%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%A7%E3%82%B0%E3%82%B0%E3%82%8D%E3%81%86%E3%81%9F%E3%81%A0%E3%81%97%E9%81%8E%E4%BF%A1%E3%81%AF%E7%A6%81%E7%89%A9\"><i class=\"fa fa-link\"></i></a>エラータイプ(例外クラス)やエラーメッセージでググろう(ただし過信は禁物)</h2>\n\n<p>一番簡単、だけど一番危険<img class=\"emoji\" title=\":skull:\" alt=\":skull:\" src=\"https://cdn.qiita.com/emoji/unicode/1f480.png\" height=\"20\" width=\"20\" align=\"absmiddle\">な方法です。</p>\n\n<ul>\n<li>エラータイプやエラーメッセージでググると、ネット上であっさり解決方法が見つかったりすることも多い。</li>\n<li>ただし、情報が古かったり、適切でない解決策が載っていたりすることも多々あるので、くれぐれも過信は禁物。 <strong>公式ではない情報は疑ってかかること!!</strong>\n</li>\n<li>意味もわからず盲目的にソースコードをコピペしたりするのも危険。</li>\n<li>「コピペしようとしているそのコード、自分で説明できますか?」を自問自答する。説明できないならまず、コードの意味を理解することから始めること。</li>\n<li>\n<a href=\"http://stackoverflow.com/\" rel=\"nofollow\" target=\"_blank\">Stack Overflow</a>の情報はベストアンサーよりも投票数に着目する。(<a href=\"http://blog.jnito.com/entry/2015/10/04/073825#%E3%82%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%82%A8%E3%83%B3%E3%82%B9QA%E3%82%92%E8%A6%8B%E3%82%8B%E4%BA%BA%E3%81%AE%E5%BF%83%E6%A7%8B%E3%81%88\" rel=\"nofollow\" target=\"_blank\">参考</a>)</li>\n</ul>\n\n<h2>\n<span id=\"やみくもにデバッグせず仮説と検証を繰り返そう\" class=\"fragment\"></span><a href=\"#%E3%82%84%E3%81%BF%E3%81%8F%E3%82%82%E3%81%AB%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%9B%E3%81%9A%E4%BB%AE%E8%AA%AC%E3%81%A8%E6%A4%9C%E8%A8%BC%E3%82%92%E7%B9%B0%E3%82%8A%E8%BF%94%E3%81%9D%E3%81%86\"><i class=\"fa fa-link\"></i></a>やみくもにデバッグせず、仮説と検証を繰り返そう</h2>\n\n<p>コードを書くときだけでなく、デバッグするときも論理的に取り組みましょう。</p>\n\n<ul>\n<li>コードを触る前にまず、どこにエラーの原因があるのか、当たりを付ける。</li>\n<li>「これがエラーの原因である」と仮説を立てたら、その仮説が正しいかどうかを検証できるような変更を加える。(仮説と検証)</li>\n<li>もし仮説が外れていたら、別の仮説を立て、その仮説を検証する。</li>\n<li>このように、エラーが解決するまで仮説と検証を繰り返していく。</li>\n</ul>\n\n<h2>\n<span id=\"二分探索法でエラーの原因を徐々に絞り込もう\" class=\"fragment\"></span><a href=\"#%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2%E6%B3%95%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E5%8E%9F%E5%9B%A0%E3%82%92%E5%BE%90%E3%80%85%E3%81%AB%E7%B5%9E%E3%82%8A%E8%BE%BC%E3%82%82%E3%81%86\"><i class=\"fa fa-link\"></i></a>二分探索法でエラーの原因を徐々に絞り込もう</h2>\n\n<p>大きな的(まと)から小さな的へ、原因を段階的に絞り込んで特定するアプローチです。</p>\n\n<ul>\n<li>問題の原因がハッキリしない場合は<a href=\"https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2\" rel=\"nofollow\" target=\"_blank\">二分探索法</a>的なアプローチを取る。</li>\n<li>まず、問題の原因をAとBという大きな二つのグループに分ける。</li>\n<li>AとBを調べて、Aの中でエラーが出たら、Aのどこかにエラーの原因がある。</li>\n<li>さらにAを、CとDというグループに分ける。</li>\n<li>Cではエラーが出ず、Dでエラーが出たら、Dのどこかにエラーの原因がある。</li>\n<li>さらにDの中で・・・という手順を繰り返していくと、エラーの原因が特定できる。</li>\n</ul>\n\n<h2>\n<span id=\"どうしても解決しなければ誰かに質問しよう\" class=\"fragment\"></span><a href=\"#%E3%81%A9%E3%81%86%E3%81%97%E3%81%A6%E3%82%82%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%AA%E3%81%91%E3%82%8C%E3%81%B0%E8%AA%B0%E3%81%8B%E3%81%AB%E8%B3%AA%E5%95%8F%E3%81%97%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>どうしても解決しなければ誰かに質問しよう</h2>\n\n<p>ここまで説明してきたテクニックやアプローチをもってしても解決しないなら、誰かに聞きましょう。<br>\n初心者に限らず、上級者でもダメなときはダメです。</p>\n\n<ul>\n<li>頑張ってもダメなときは誰かに質問する。</li>\n<li>職場であれば同僚のプログラマに質問する。</li>\n<li>誰かに説明している途中に「・・・あ、ごめん。原因がわかった<img class=\"emoji\" title=\":sweat_smile:\" alt=\":sweat_smile:\" src=\"https://cdn.qiita.com/emoji/unicode/1f605.png\" height=\"20\" width=\"20\" align=\"absmiddle\">」と自分で解決できることもよくある。(<a href=\"http://plaza.rakuten.co.jp/sebook/diary/200703230000/\" rel=\"nofollow\" target=\"_blank\">テディベア効果</a>)</li>\n<li>身近に質問できる人がいなければ、ネットのQ&amp;Aサイトで質問する。(<a href=\"http://blog.mb.cloud.nifty.com/?p=3164\" rel=\"nofollow\" target=\"_blank\">参考</a>)</li>\n<li>ネットで質問する場合は、回答者が答えやすくなるように説明する。(<a href=\"http://blog.jnito.com/entry/2015/10/04/073825#%E4%B8%8A%E6%89%8B%E3%81%AB%E8%B3%AA%E5%95%8F%E3%81%99%E3%82%8B%E3%82%B3%E3%83%84\" rel=\"nofollow\" target=\"_blank\">参考</a>)</li>\n</ul>\n\n<h2>\n<span id=\"時間を決めてデバッグしよう気軽に質問できる雰囲気を作ろう\" class=\"fragment\"></span><a href=\"#%E6%99%82%E9%96%93%E3%82%92%E6%B1%BA%E3%82%81%E3%81%A6%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%97%E3%82%88%E3%81%86%E6%B0%97%E8%BB%BD%E3%81%AB%E8%B3%AA%E5%95%8F%E3%81%A7%E3%81%8D%E3%82%8B%E9%9B%B0%E5%9B%B2%E6%B0%97%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86\"><i class=\"fa fa-link\"></i></a>時間を決めてデバッグしよう/気軽に質問できる雰囲気を作ろう</h2>\n\n<p>ゲームと一緒で、デバッグも熱中するとあっという間に時間が過ぎてしまいます・・・。</p>\n\n<ul>\n<li>延々とデバッグに時間を浪費しない。2時間も3時間も浪費してしまうのは時間のムダ。(特に仕事でコードを書く場合)</li>\n<li>たとえば「30分やってもダメなら諦めて誰かに聞く」と自分で時間を決めてデバッグする。</li>\n<li>チームで開発する場合は気軽に質問できる雰囲気を作る。(みんなで話し合って意識あわせをする)</li>\n<li>答える方も快く答えてあげる。「えっ、そんなことも知らないの?(苦笑)」とイヤミを言ったりしない。</li>\n<li>トータルで見て生産性が上がるオプションを選ぶ。(何時間も一人で悩むのと、誰かに聞いてぱっと解決するのではどちらが生産性が高いか?)</li>\n</ul>\n\n<h2>\n<span id=\"ハマったときは一度コードから離れてみよう\" class=\"fragment\"></span><a href=\"#%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%8D%E3%81%AF%E4%B8%80%E5%BA%A6%E3%82%B3%E3%83%BC%E3%83%89%E3%81%8B%E3%82%89%E9%9B%A2%E3%82%8C%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>ハマったときは一度コードから離れてみよう</h2>\n\n<p>「一晩寝かせてみたら一発でわかった!」なんてこともよくあります。</p>\n\n<ul>\n<li>何時間やっても解決しない(つまりハマってしまった)、というときは一度コードから離れてみる。</li>\n<li>外を散歩したり、お風呂に入ったり、ぐっすり眠ったりすると、全然違う視点やアイデアが浮かんで、さくっと解決してしまうことがある。</li>\n<li>とはいえ、身近に質問できる人がいるなら、さっさと質問してしまった方がたぶん早い。</li>\n</ul>\n\n<h2>\n<span id=\"qiitaやブログで知見を共有しよう\" class=\"fragment\"></span><a href=\"#qiita%E3%82%84%E3%83%96%E3%83%AD%E3%82%B0%E3%81%A7%E7%9F%A5%E8%A6%8B%E3%82%92%E5%85%B1%E6%9C%89%E3%81%97%E3%82%88%E3%81%86\"><i class=\"fa fa-link\"></i></a>Qiitaやブログで知見を共有しよう</h2>\n\n<p>世の中、意外と同じ問題で困っている人は多いものです。</p>\n\n<ul>\n<li>問題が解決したら、Qiitaやブログで原因と解決策を書く。</li>\n<li>知見をネット上で共有することで、他のエンジニアの生産性向上に寄与できる。</li>\n<li>場合によっては他のエンジニアから、さらに良い解決策を教えてもらえることがある。</li>\n<li>自分の書いた記事が話題になれば、知名度が上がり、転職時に有利になったりする(かもしれない)。</li>\n<li>良い技術記事の書き方はこちらのリンクを参考にする。\n\n<ul>\n<li><a href=\"http://qiita.com/jnchito/items/5c3eb3640ad57b3edc6c\" id=\"reference-521d5b4aa6b9381d4751\">個人的な良い記事のガイドライン = 2ヶ月前の自分が泣いて喜ぶような記事を書く - Qiita</a></li>\n<li><a href=\"http://qiita.com/jnchito/items/a939906de64bf34700a9\" id=\"reference-8229b62a2465898335f8\">これであなたのQiita記事もランキング入り!?@jnchitoによる編集リクエスト解説(解説動画付き) - Qiita</a></li>\n</ul>\n</li>\n</ul>\n\n<h2>\n<span id=\"解説動画の紹介再掲\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E8%AA%AC%E5%8B%95%E7%94%BB%E3%81%AE%E7%B4%B9%E4%BB%8B%E5%86%8D%E6%8E%B2\"><i class=\"fa fa-link\"></i></a>解説動画の紹介(再掲)</h2>\n\n<p>いかがだったでしょうか?<br>\nここで再度、解説動画のリンクを載せておきます。<br>\n(冒頭で述べたとおり、 <strong>動画がメインでこの記事がオマケ</strong> です!)</p>\n\n<p><a href=\"https://www.youtube.com/watch?v=5fyrGslhUcY\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://qiita-image-store.s3.amazonaws.com/0/7465/59a61c02-a06e-8a44-49b9-d46497f2e872.png\" alt=\"Screen Shot 2016-06-25 at 10.26.25.png\" title=\"Screen Shot 2016-06-25 at 10.26.25.png\">プログラミング初心者歓迎!「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意 - YouTube</a></p>\n\n<p>スクリーンキャストで実際の画面やコードを見ながら説明を聞くと、さらに理解度がアップするはずです。</p>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<p>というわけで、この記事では「エラーが出ました。どうすればいいですか?」から卒業するための基本と極意を説明しました。</p>\n\n<p>エラーが発生した箇所を特定する、スタックトレースを読む、エラーメッセージとエラータイプをちゃんと確認する、デバッガを使って対話的にデバッグする、といった基本テクニックが身に付いていれば、きっと「エラーが出ました。どうすればいいですか?」から卒業できるはずです。</p>\n\n<p>それ以外にも素早く問題解決に至るためのテクニックやアプローチをいろいろと紹介してみました。<br>\n独学でプログラミングをやっていたりすると、なかなかこういった泥臭い(?)テクニックを学ぶ機会はないかもしれません。</p>\n\n<p>この記事を読んで、みなさんが「エラーが出ました。どうすればいいですか?」から卒業できれば幸いです。</p>\n\n<h3>\n<span id=\"あわせて読みたい\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>あわせて読みたい</h3>\n\n<p>自分のブログにこの記事を書いた動機をもう少し詳しく書きました。<br>\nよかったらこちらもどうぞ。</p>\n\n<p><a href=\"http://blog.jnito.com/entry/2016/06/27/050818\" rel=\"nofollow\" target=\"_blank\">「エラーが出ました。どうすればいいですか?」から卒業するための記事をQiitaに書きました - give IT a try</a> <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2016/06/27/050818\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2016/06/27/050818\" class=\"bookmark-count\"></a></p>\n\n<p>この記事にも登場するプログラマ向けQ&amp;Aサイト、スタック・オーバーフローの使い方を詳しく解説したブログ記事です。</p>\n\n<p><a href=\"http://blog.jnito.com/entry/2015/10/04/073825\" rel=\"nofollow\" target=\"_blank\">今夜わかる「スタック・オーバーフロー」の世界 - give IT a try</a> <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2015/10/04/073825\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2015/10/04/073825\" class=\"bookmark-count\"></a></p>\n\n<p>「<strong>英語から逃げるな!!辞書を引いてでも読め!!</strong>って言われてもどうすれば・・・??」と思っている方は、こちらのブログ記事にヒントが載っているかもしれません。</p>\n\n<p><a href=\"http://blog.jnito.com/entry/2015/09/02/120620\" rel=\"nofollow\" target=\"_blank\">英語力をアップさせる知見がいっぱい!「Rubyistのための英語勉強会」を開催しました - give IT a try</a> <a href=\"http://b.hatena.ne.jp/entry/http://blog.jnito.com/entry/2015/09/02/120620\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://b.st-hatena.com/entry/image/http://blog.jnito.com/entry/2015/09/02/120620\" class=\"bookmark-count\"></a></p>\n",
    "stock_users": [
        "samurai20000@github",
        "Kuchitama",
        "machida",
        "is8r",
        "aki77",
        "yamitake@github",
        "Layzie",
        "esehara@github",
        "issei-m",
        "pugiemonn",
        "mizchi",
        "hogehiga",
        "kazoo0217",
        "hazy_moon",
        "wate",
        "hiracchi",
        "plastocyanin",
        "tanvoid",
        "erukiti",
        "mori_a1",
        "kuboaki",
        "hnakamur",
        "makoto-ogata@github",
        "exfreeter",
        "ispern",
        "tadsan",
        "ykominami",
        "ramne96@github",
        "tatsuoSakurai",
        "ryohashimoto",
        "zakuni@github",
        "foldrr",
        "oginohiroaki@github",
        "iishun",
        "nashirox",
        "usutani",
        "fukamiiiiinmin",
        "mahopapa",
        "mm36",
        "applesnow",
        "Noboruhi",
        "dohq",
        "moto",
        "murayama",
        "minodisk",
        "uchiemon57",
        "zakirosh",
        "keima_12",
        "h14i",
        "geso_mori",
        "nakaji",
        "teruhito",
        "hogenishi",
        "yoskhdia",
        "people-of-the-s",
        "yancya",
        "wingswinging",
        "tak01cuebs",
        "n_slender",
        "wbrabit",
        "kou1tw",
        "snowsunny",
        "orange-lion",
        "gaaamii",
        "tomato360",
        "usakoyama",
        "dorayakikun",
        "neverkrukru",
        "funnything",
        "monoqlo",
        "bells17",
        "stiq",
        "ligerbolt",
        "Silbercat",
        "rentalname@github",
        "tomcha_",
        "ques0942",
        "takaya1992",
        "tanamako@github",
        "quenhulu",
        "keiiii2011",
        "kawacho",
        "bigplants@github",
        "MasatoYoshioka@github",
        "atsaki",
        "keisei_1092",
        "s_s_k",
        "mekemeke9",
        "Tamadon",
        "c-chonan@github",
        "fumiyasac@github",
        "arfyasu",
        "karumado",
        "minoringo",
        "ecaze",
        "koudaiii",
        "rtoya",
        "satty3104",
        "BJCRobot",
        "ryojiyama@github",
        "k2works",
        "myy_",
        "ko8@github",
        "mokoaki",
        "hiloki@github",
        "mizzz",
        "disc_7",
        "kanehama",
        "klee_arc",
        "blue-emc2-github",
        "taka7beckham",
        "shiranuik@github",
        "ysaito8015@github",
        "monmonpower",
        "ec1513079",
        "kakky0312",
        "tsuyoshi_cho",
        "t39@github",
        "sukobuto",
        "mfks17",
        "amanoiverse",
        "tommy6073",
        "nobokoba",
        "kojisato118",
        "hiroyuki_hon",
        "Luecy1",
        "shsh0310",
        "fsouju",
        "kazica",
        "ytcy",
        "kenichi_cc",
        "otolab",
        "lionfish7699",
        "cwan",
        "kazucha",
        "snowmi",
        "hase5021",
        "eedamame",
        "m-nagae",
        "ic_lifewood",
        "ndxbn",
        "doorfkin",
        "yoshiokaCB",
        "ryota-murakami",
        "bagpack",
        "hika09",
        "lw-iharada",
        "comefigo",
        "osd",
        "toshiyuki",
        "RyogaBarbie",
        "PruneMazui",
        "yuchans87",
        "sinofseven",
        "chopwave",
        "SatoTakeshiX",
        "kkogorou",
        "ayaniimi213",
        "saku09",
        "tomcky",
        "ymtk0815",
        "soujyan",
        "ytabuchi",
        "obanaopon",
        "Rela",
        "davaa",
        "amaron518",
        "klaNath",
        "maxmellon",
        "AnzNetJp",
        "_yama3_",
        "kazzy",
        "keik",
        "imachan0322",
        "minmaxsum",
        "4696",
        "mzgk",
        "Shunta_Suzuki",
        "Tanemura",
        "yshz",
        "kkyouhei",
        "ma_me",
        "mktakuya",
        "ichiro_vin",
        "xkumiyu",
        "ABCanG1015",
        "ktyubeshi",
        "aritaku",
        "kenji0x02",
        "ryosukes",
        "intermezzo-fr",
        "shintarow",
        "rooooomania",
        "kota22",
        "nikuniku_P",
        "tamura_CD",
        "Hassan",
        "b9o2n8xq",
        "junymn",
        "kush2",
        "Kazunori-Kimura",
        "electron",
        "yutarouk",
        "shibacho",
        "tmizo",
        "nbjiao",
        "ikeponmaru",
        "keisukeohta",
        "mekashin",
        "arc279",
        "ban05",
        "hiropon4000",
        "4cres",
        "s-mori",
        "akira_21",
        "SeiichiroTada",
        "EIN_K",
        "___uhu",
        "msmt_k",
        "Neos21",
        "misosouplover51",
        "niwatori720",
        "alucky4416",
        "dora56",
        "panchan9",
        "natsu_nacho",
        "YOSUKE8080",
        "heliac2000",
        "side",
        "marco",
        "hidetoshi",
        "takayuki-ochiai",
        "meguroman",
        "dkimura",
        "zom",
        "ox3Bcpz4dO",
        "LordOfNightmare",
        "Kasse",
        "Yukishigure01",
        "takino",
        "IceCandy",
        "yamaken",
        "kyuhutech",
        "igiy",
        "SatoShin55251",
        "xxxDATxxx",
        "tdsuke",
        "kei-p",
        "abetar0",
        "itosho",
        "zegu",
        "mofmof",
        "hagi017",
        "okashoi",
        "ahiruson",
        "kroton",
        "k_anz__",
        "j_nakayama",
        "sakamotoj",
        "h-imai",
        "gotoysk",
        "kozy",
        "_dorayaki_",
        "63shocker",
        "hogemmhoge",
        "garam",
        "dnby",
        "hiramal",
        "mktkmr4",
        "watouch",
        "soyanchu",
        "wnoooo",
        "starlod",
        "bloom",
        "simpleSan",
        "sorano",
        "takahiro_kuribayashi",
        "uma6661",
        "yutasuzuki",
        "hashimoto1009",
        "YasuharuIida",
        "usiusi360",
        "sentaro",
        "test2test",
        "28peso",
        "zacky1972",
        "usizou",
        "kiyodori",
        "su1009",
        "KonishiYudai",
        "Guwanya",
        "testmonstar01",
        "_ruka_",
        "anmorenight",
        "dsanno",
        "syouit523",
        "5843435",
        "Shinonome",
        "Sugle",
        "supai0",
        "TSKGunGun",
        "SatoshiMorita",
        "quaynst",
        "mizunos",
        "shinnosuke-takagi",
        "yamotech",
        "yhyhyh",
        "tagackt",
        "ita_boardman",
        "cooltiger",
        "kamomafu",
        "tomlla",
        "semipure",
        "tatsu983",
        "jkr_2255",
        "kanase",
        "alancodvo",
        "object1985",
        "Nagissa",
        "tasukujp",
        "kazuyuki_koma",
        "suzutsuki0220",
        "akmterk",
        "T-N0121",
        "nozaking16",
        "sasho",
        "yusuke28",
        "nggg",
        "falconedge",
        "kfsawada",
        "nagaryu000",
        "nwebcraft",
        "helloinfoloth",
        "tomioage",
        "soutag14",
        "Sohma",
        "oomori-you",
        "whitecrane",
        "noi000",
        "raccy",
        "shoheihei",
        "yoshio_0221",
        "derorian3",
        "nwhiro700",
        "divider_kuro",
        "araki-ka",
        "yamataku0613",
        "butaosuinu",
        "taKassi",
        "Den_H_Shige",
        "yukitaya",
        "Clpsplug",
        "Sef_Sonue",
        "morimoto13",
        "Gideon_rose",
        "riosce",
        "masaibar",
        "ShoichiKuraoka",
        "whiteleaves",
        "makipe",
        "hhma0001",
        "konnosena",
        "aichan",
        "m2mtu",
        "migrant46",
        "akawaguc",
        "daikiyanai",
        "17tea",
        "taka159",
        "gimupop",
        "mase",
        "kakikohime",
        "coiwai",
        "aristotll",
        "haptaro",
        "y0w0y",
        "osamunix",
        "thori0908",
        "RyutaYoshi",
        "g_ryotaro",
        "yang36",
        "maeshun",
        "atozzz",
        "fuka",
        "m_maru",
        "871p",
        "yh_cake",
        "sainome_7",
        "Gupi",
        "godan09",
        "oshirucordex",
        "halueda",
        "saneppie",
        "a-okuyama53453",
        "Tocyuki",
        "daikingca2",
        "happou31",
        "KeiKawano",
        "heppoko_dev",
        "mohira",
        "kurosawa_kuro",
        "Teraguchi_Norikazu",
        "Rinaba",
        "motoda",
        "komu",
        "myfunnykingyo",
        "yyxml1988",
        "oattsu_",
        "immmmmmmm",
        "upaver20",
        "Natsumi_Shimizu",
        "takizawakei",
        "abebe_shu",
        "matsujn",
        "lasershow",
        "musashi13z",
        "atle-tomoya",
        "kaz1408174",
        "Peeeeepei",
        "mefiras",
        "snona",
        "51koichi",
        "holywise",
        "tyk",
        "ksugimori",
        "aks",
        "shogiai",
        "sai_3110",
        "nappyon",
        "twipg",
        "yok",
        "Takao_h",
        "shy_azusa",
        "sukechansan",
        "Yousuke123",
        "bbrubymm",
        "t_s_ink",
        "koki_ide",
        "kiyohiko",
        "gonji567",
        "hiromi_iwai",
        "nocoto",
        "j-un",
        "m_seiya",
        "jagio",
        "yakRx",
        "shimabkra",
        "dawn_628",
        "jwmx",
        "shima11",
        "fox-ninja",
        "yumetodo",
        "t15i",
        "m_hatanaka",
        "hibirock",
        "nakki",
        "RS_Dessin",
        "huddle",
        "hinakade",
        "motte_gibson",
        "myblue",
        "watakushi",
        "Eugene_L",
        "choco_latie",
        "pandasan65",
        "maru_katy",
        "kunimi53chi",
        "kuroneko",
        "johnmanjiro",
        "matsuyoshi",
        "sega_yuu",
        "1-AizawaSatoshi",
        "RSATAKE",
        "playnext-shimpeiminato",
        "minase12",
        "udonta",
        "milky-1234",
        "UltraBirdTech",
        "yacco",
        "Hidenao",
        "capelitoGDK",
        "kiku38",
        "mtzack",
        "matcha_yama",
        "naname",
        "gucchi43",
        "ikiyoyuki",
        "tf-oikawa",
        "mashiroyuya",
        "k-kurihara",
        "manabedaiki",
        "iws",
        "tattsu_saka",
        "trileg",
        "kamaboko4u",
        "Toumei0091",
        "yusukexyusuke",
        "Not1But4",
        "Iyutaka",
        "hellohello722",
        "ayusak",
        "tukushiari",
        "Shuheiktgw",
        "emowl",
        "yuichiro-yoshida",
        "3to4",
        "sslkudo",
        "nanko-kt",
        "htkys12",
        "sir0water",
        "keroyon",
        "osdmss",
        "Akagi",
        "sakura_anpan",
        "hasi6363",
        "zono2016",
        "dasisyouyu",
        "chojuku",
        "K_K_",
        "hommatkms",
        "YusakuS16",
        "nabenabetwi",
        "musix55",
        "minoriinoue",
        "MakiKamimura",
        "yagi21",
        "masterjp",
        "SgnTkfm",
        "3at_nishioka",
        "SatomiO",
        "zaruofiron",
        "venqu",
        "hibriiiiidge",
        "kryoi",
        "umskn",
        "kittaka",
        "hepppoo",
        "ttnzw",
        "hakozaru",
        "tuttyjupi",
        "bunkabu",
        "shirogani",
        "Mic-U",
        "emono",
        "hirotsuru",
        "shakkee",
        "karlos753",
        "nakanov",
        "Muraka",
        "k_nakayama",
        "Heiichi",
        "hiruko_jp",
        "tsuzuki557",
        "okukazu",
        "SmokeCheese",
        "kizi519",
        "sdsdsd",
        "tw_takamura",
        "Kirifuda1102",
        "tyoshioka",
        "recotic",
        "tsako",
        "kk_take",
        "hamadasa",
        "siro_qii",
        "yonahaty",
        "Yoshiki_Henmi",
        "k_mail",
        "bero012",
        "d3etbkucsasgy",
        "yoshi9n4",
        "MoriNaga_C",
        "takhippo8",
        "synia",
        "sparta-hachiman",
        "taku_0401",
        "Mr_Legacy",
        "Y-MITSUBOSHI",
        "osawa-ri",
        "satowatarufootprint",
        "kawako",
        "pair2",
        "cocomil11",
        "rei_leilei",
        "akehata",
        "popmac",
        "c_krystalia",
        "igama",
        "takuzoo3868",
        "nmaru53",
        "hkude",
        "haruto",
        "strongjaian",
        "guai3",
        "aaaaa",
        "dokusenjohn",
        "kinchiki",
        "_t74",
        "rakudaride",
        "pokotyan",
        "y_takita",
        "bake0937",
        "shigemit",
        "prin",
        "k-harada413",
        "mii7",
        "rinanada",
        "ajira",
        "cheatingcheetah",
        "akakotori",
        "abeshi",
        "muura",
        "hironon626",
        "marshmallow0319",
        "kenfkd244",
        "bluebird445",
        "kiku"
    ]
}, {
    "id": 403111,
    "uuid": "81ab83f0243b4b09f6fc",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "フロントエンドエンジニア(mizchi)が暇な時にやること",
    "created_at": "2016-06-26 13:20:22 +0900",
    "updated_at": "2016-06-26 13:20:22 +0900",
    "created_at_in_words": "2ヶ月",
    "updated_at_in_words": "2ヶ月",
    "tags": [{
        "name": "frontend",
        "url_name": "frontend",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 254,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/81ab83f0243b4b09f6fc",
    "created_at_as_seconds": 1466914822,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## 暇というか日常的にやってること\n\nhttps://news.ycombinator.com/ と http://www.echojs.com/ と http://b.hatena.ne.jp/efcl/ をフィードリーダーに突っ込んでいて、面白そうなのをメモっておく\n\n## 暇なとき\n\n- 日頃メモってたライブラリの試し切りをする\n    - 面白かったら紹介記事を書く\n- 多少やる気リソースが多めだと新しい言語(最近はRustかElixir)の勉強を進める\n- http://codepen.io/ で面白い動きするやつのコードを探してコード読む\n\nとくにCodePenがオススメで、割とゲラゲラ笑いながら読めるやつが多いので楽しい。CodePenのテクニックはそのまま自分の業務に持ち込むと悪目立ちするので控えているが、Webでもこういう演出ができる、と頭の片隅にいれておくことで、いずれ何かに役立ったりする。たとえば昨日読んだ奴は CSS の:before の box-shadow でドット絵書いてた。\n\n\n\n中の人っぽい意見なんだけど、Qiitaのサービス内でもっと能動的にクリエイティブな暇つぶしをできるような、たとえば暇つぶしでフィードリーダーを開くような、そんな仕組みを提供したいと思いつつも、今のところそういう仕組みやアフォーダンスがないので、どういうのがいいかなーと考えてはいる。\n\n\nあと、これは単に愚痴なんですが、僕が去年1年スプラトゥーンしてる間に自分がキューに詰んでたやつ、誰かが多少調べてくれると期待していたんだけど、一切そんなことはなく自分で調べるしかねえ!となっている。\n",
    "body": "\n<h2>\n<span id=\"暇というか日常的にやってること\" class=\"fragment\"></span><a href=\"#%E6%9A%87%E3%81%A8%E3%81%84%E3%81%86%E3%81%8B%E6%97%A5%E5%B8%B8%E7%9A%84%E3%81%AB%E3%82%84%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%93%E3%81%A8\"><i class=\"fa fa-link\"></i></a>暇というか日常的にやってること</h2>\n\n<p><a href=\"https://news.ycombinator.com/\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://news.ycombinator.com/</a> と <a href=\"http://www.echojs.com/\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">http://www.echojs.com/</a> と <a href=\"http://b.hatena.ne.jp/efcl/\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">http://b.hatena.ne.jp/efcl/</a> をフィードリーダーに突っ込んでいて、面白そうなのをメモっておく</p>\n\n<h2>\n<span id=\"暇なとき\" class=\"fragment\"></span><a href=\"#%E6%9A%87%E3%81%AA%E3%81%A8%E3%81%8D\"><i class=\"fa fa-link\"></i></a>暇なとき</h2>\n\n<ul>\n<li>日頃メモってたライブラリの試し切りをする\n\n<ul>\n<li>面白かったら紹介記事を書く</li>\n</ul>\n</li>\n<li>多少やる気リソースが多めだと新しい言語(最近はRustかElixir)の勉強を進める</li>\n<li>\n<a href=\"http://codepen.io/\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">http://codepen.io/</a> で面白い動きするやつのコードを探してコード読む</li>\n</ul>\n\n<p>とくにCodePenがオススメで、割とゲラゲラ笑いながら読めるやつが多いので楽しい。CodePenのテクニックはそのまま自分の業務に持ち込むと悪目立ちするので控えているが、Webでもこういう演出ができる、と頭の片隅にいれておくことで、いずれ何かに役立ったりする。たとえば昨日読んだ奴は CSS の:before の box-shadow でドット絵書いてた。</p>\n\n<p>中の人っぽい意見なんだけど、Qiitaのサービス内でもっと能動的にクリエイティブな暇つぶしをできるような、たとえば暇つぶしでフィードリーダーを開くような、そんな仕組みを提供したいと思いつつも、今のところそういう仕組みやアフォーダンスがないので、どういうのがいいかなーと考えてはいる。</p>\n\n<p>あと、これは単に愚痴なんですが、僕が去年1年スプラトゥーンしてる間に自分がキューに詰んでたやつ、誰かが多少調べてくれると期待していたんだけど、一切そんなことはなく自分で調べるしかねえ!となっている。</p>\n",
    "stock_users": [
        "htomine",
        "gogyo",
        "aki77",
        "nyamogera",
        "aki_55p",
        "tyru",
        "syoichi",
        "hironow",
        "yukku0423@github",
        "toshi_miura",
        "webgyo",
        "atsumo",
        "erukiti",
        "moonblogger",
        "NeXTSTEP2OSX",
        "ta1kt0me@github",
        "esak@github",
        "maechabin",
        "ongaeshi",
        "futoase",
        "tadsan",
        "FiNGAHOLiC",
        "ishideo",
        "ukoasis",
        "oginohiroaki@github",
        "ryurock",
        "dempasong",
        "DotEarl",
        "umeume66",
        "cu39",
        "skkzsh",
        "mayutan",
        "tchikuba",
        "pika_shi",
        "inuscript",
        "mnuma",
        "ooyabuh",
        "hogenishi",
        "futabooo",
        "ooDEMi",
        "yancya",
        "motchang",
        "miwara",
        "angedessin",
        "muto-y@github",
        "lenomick",
        "Narikazu3",
        "gaaamii",
        "tomato360",
        "kichikuchi",
        "dorayakikun",
        "builtinnya@github",
        "funnything",
        "cheezenaan",
        "hirokaki",
        "repon",
        "MasatoYoshioka@github",
        "na-o-ys",
        "akameco",
        "Tamadon",
        "P_tan",
        "iwata-n@github",
        "yasaichi",
        "suzukaze",
        "armorik83",
        "bonk",
        "luccafort",
        "ecaze",
        "zakuroishikuro",
        "IzumiSy",
        "atijust",
        "Nawada",
        "imudak",
        "mazeltov7",
        "meganetaaan",
        "pixyzehn",
        "hiro14aki",
        "miu",
        "yoshito-maeoka@github",
        "shiranuik@github",
        "t_tomizuka",
        "wara_be",
        "kimama1997",
        "mottox2",
        "kmkm",
        "mpyw",
        "mfks17",
        "naoto_n",
        "rei-m",
        "shsh0310",
        "Fly_high_747",
        "nowri",
        "waytoa",
        "harenchi8",
        "arukmn",
        "nolick1219",
        "hase5021",
        "emahiro",
        "DolphinJP",
        "m-nagae",
        "chohey",
        "marsa746079",
        "ryota-murakami",
        "shimpeiws",
        "kakakakakku",
        "yuzamme",
        "tos-miyake",
        "fatbros",
        "kegamin",
        "mamorunner",
        "tabachain",
        "p1ch_jp",
        "soujyan",
        "obanaopon",
        "yura",
        "maxmellon",
        "_yama3_",
        "ishi",
        "ashinya",
        "howdy39",
        "color_box",
        "diwao",
        "lo_0l",
        "shinfkd",
        "intermezzo-fr",
        "Hassan",
        "11Takanori",
        "kekekenta",
        "This420",
        "ga9ji",
        "shun-k1331",
        "ymorired",
        "tmizo",
        "hiro93n",
        "keisukeohta",
        "mochi_Flappe",
        "ctenti",
        "mettoboshi",
        "Neos21",
        "tbpgr",
        "niwatori720",
        "hami",
        "r-ngtm",
        "matsuoshi",
        "takayuki-ochiai",
        "minewebstaff",
        "mt-takao",
        "Kasse",
        "koher",
        "opponitur",
        "shigemaruu",
        "dondoko-susumu",
        "taro0628",
        "j_nakayama",
        "nterakado",
        "seihmd",
        "hogemmhoge",
        "wordijp",
        "k-ishimitsu",
        "nodoca",
        "sorano",
        "_-_-_",
        "koukun",
        "KazuyaHara",
        "test2test",
        "vladinomo",
        "imfiare",
        "iskz",
        "m3y",
        "takutakuma",
        "wrbss",
        "pakkun",
        "miura-d",
        "purple_jwl",
        "quaynst",
        "naru0504",
        "shiraa",
        "satoysan",
        "kanase",
        "c0de4",
        "matobaa",
        "lhside",
        "khsk",
        "chaba418",
        "5st7",
        "zkohi",
        "saka1_p",
        "sakuto",
        "minamooon",
        "wwweric",
        "jTakasuRyuji",
        "tadauki",
        "tomonoza",
        "guda2",
        "takaken",
        "fkm_y",
        "MiyachiK",
        "17tea",
        "m-miyake",
        "yokota",
        "RyutaYoshi",
        "cither",
        "kazuakiokamoto",
        "trkbt10",
        "off_ants",
        "atosimitu",
        "godan09",
        "chuck0523",
        "RyoheiKishi",
        "rukurx",
        "secure0318",
        "l08084",
        "sinagaki58",
        "aki323buri2",
        "musashi13z",
        "snona",
        "nyoronyororo",
        "yutaszk",
        "ys-works",
        "HirokiOmote",
        "uyaponz",
        "wabeshew",
        "koki_ide",
        "ryochang",
        "jwmx",
        "y4shiro",
        "ponpoko04",
        "nejimakidori",
        "myblue",
        "yasuhiro-okada-aktsk",
        "sonoshou",
        "choco_latie",
        "hanaland",
        "kuroneko",
        "hrloca",
        "8Uchi29",
        "akioshino",
        "bashio",
        "Not1But4",
        "mimimiyoyoyo",
        "ik66",
        "ttwo32",
        "t-tsuchida",
        "SatomiO",
        "venqu",
        "meiko",
        "defunty",
        "yamaguchij",
        "domxdom",
        "mag6_",
        "Suzunari",
        "mediba-kanamori",
        "ain0204",
        "knaito-coosy"
    ]
}, {
    "id": 402735,
    "uuid": "3b49882c7481cd11bb10",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "react-dispatcher-decorator で PubSub な Flux",
    "created_at": "2016-06-24 17:51:10 +0900",
    "updated_at": "2016-06-24 18:06:51 +0900",
    "created_at_in_words": "3ヶ月",
    "updated_at_in_words": "3ヶ月",
    "tags": [{
        "name": "flux",
        "url_name": "flux",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/9bc0948492733596c2ee8c078c546a6f0a1ff1f4/medium.jpg?1422718920",
        "following": false,
        "versions": []
    }, {
        "name": "react.js",
        "url_name": "react.js",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/e6867d326364bb2498f72f152c92408bf457de8c/medium.jpg?1426679594",
        "following": true,
        "versions": []
    }],
    "stock_count": 23,
    "comment_count": 3,
    "url": "http://qiita.com/mizchi/items/3b49882c7481cd11bb10",
    "created_at_as_seconds": 1466758270,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\nreact-dispatcher-decorator というのを作った\n\n```\nnpm install react-dispatcher-decorator --save\n```\n\n## コンセプト\n\n昔作ったArdaのPubSub部分をDecoratorで表現した。https://github.com/mizchi/arda\n\n- [EventEmitterバケツリレースタイル/フレームワークなしで小さくFluxする - Qiita](http://qiita.com/mizchi/items/6a3500e598ec36746509 \"EventEmitterバケツリレースタイル/フレームワークなしで小さくFluxする - Qiita\")\n- [React の Context を使って Flux を実装する - Qiita](http://qiita.com/mizchi/items/ef3fe8a82ce0fb49b52d \"React の Context を使って Flux を実装する - Qiita\")\n\nこの2つを使ってバケツリレーを隠蔽することで、デコレータでPub側とSub側を自然に繋げられるようにした。\n\n[hokaccha/react-micro-container: Micro framework for React](https://github.com/hokaccha/react-micro-container \"hokaccha/react-micro-container: Micro framework for React\") も多少参考にした。\n\n## Example\n\n```js\nimport React from \"react\";\nimport {dispatcher, subscriber} from \"react-dispatcher-decorator\";\n\n// This component subscribes events from child components by context\n@subscriber((self, subscribe) => {\n  subscribe('foo', (prop) => {\n    console.log('foo received with', prop);\n  });\n})\nclass App extends React.Component {\n  render() {\n    return <Child/>\n  }\n}\n\n// This component has this.context.dispatch and it will dispatch to parent subscriber\n@dispatcher\nclass Child extends React.Component {\n  render() {\n    return <button onClick={() => this.context.dispatch('foo', 1)}>hello</button>\n  }\n}\n\nimport ReactDOM from \"react-dom\";\nconst el = document.querySelector(\".main\");\nReactDOM.render(<App/>, el);\n```\n\nPub側で`@subscriber`デコレータを宣言しながらエミッターを初期化する。意図的に動的には生やしたりはできないようにしている。\n\n`@dipatcher` が付与されたComponentの `this.context.dispatch` は自分の`@subscribe`デコレータを持つ親(何層上でもおk)の`subscribe`を発火させる。\n\n\n## with PromisedReducer\n\nこれも自作のライブラリだけど、\n\nhttps://github.com/mizchi/promised-reducer\n\nを使うと、こんな感じになる。\n\n\n```js\nimport React from \"react\";\nimport {dispatcher, subscriber} from \"react-dispatcher-decorator\";\nimport PromisedReducer from \"promised-ruducer\";\n\n@subscriber((self, subscribe) => {\n  const reducer = new PromisedReducer({count: 0});\n  reducer.on(\":update\", state => self.setState(state)); \n  subscribe('increment', (prop) => {\n    reducer.update(state => ({count: state.count + 1}));\n  });\n})\nclass App extends React.Component {\n  render() {\n    return <Child count={this.state.count}/>\n  }\n}\n\n// This component has this.context.dispatch and it will dispatch to parent subscriber\n@dispatcher\nclass Child extends React.Component {\n  render() {\n    return <button onClick={() => this.context.dispatch('increment')}> {this.props.count} </button>\n  }\n}\n\n// 略\n```\n\nPromisedReducer はこれまた自分が作った https://github.com/mizchi/flumpt の非同期更新のバッチ処理を最適化する部分を抜き出したもので、要はこの例はFlumptを小さいパーツに分解してデコレータ構文に糖衣して再構築するとこうなる、というもの。\n\nflumpt はSSR対応してなかったんだけど、仕組み上だるかったので、SSRしたいならこういうアプローチでいけるはず。\n",
    "body": "<p>react-dispatcher-decorator というのを作った</p>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\nnpm install react-dispatcher-decorator --save\n</pre></div></div>\n\n<h2>\n<span id=\"コンセプト\" class=\"fragment\"></span><a href=\"#%E3%82%B3%E3%83%B3%E3%82%BB%E3%83%97%E3%83%88\"><i class=\"fa fa-link\"></i></a>コンセプト</h2>\n\n<p>昔作ったArdaのPubSub部分をDecoratorで表現した。<a href=\"https://github.com/mizchi/arda\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mizchi/arda</a></p>\n\n<ul>\n<li><a href=\"http://qiita.com/mizchi/items/6a3500e598ec36746509\" title=\"EventEmitterバケツリレースタイル/フレームワークなしで小さくFluxする - Qiita\" id=\"reference-11871a1a33c77287b95c\">EventEmitterバケツリレースタイル/フレームワークなしで小さくFluxする - Qiita</a></li>\n<li><a href=\"http://qiita.com/mizchi/items/ef3fe8a82ce0fb49b52d\" title=\"React の Context を使って Flux を実装する - Qiita\" id=\"reference-24896f1d7a3c40fb53ab\">React の Context を使って Flux を実装する - Qiita</a></li>\n</ul>\n\n<p>この2つを使ってバケツリレーを隠蔽することで、デコレータでPub側とSub側を自然に繋げられるようにした。</p>\n\n<p><a href=\"https://github.com/hokaccha/react-micro-container\" title=\"hokaccha/react-micro-container: Micro framework for React\" rel=\"nofollow\" target=\"_blank\">hokaccha/react-micro-container: Micro framework for React</a> も多少参考にした。</p>\n\n<h2>\n<span id=\"example\" class=\"fragment\"></span><a href=\"#example\"><i class=\"fa fa-link\"></i></a>Example</h2>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"nx\">React</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">dispatcher</span><span class=\"p\">,</span> <span class=\"nx\">subscriber</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react-dispatcher-decorator\"</span><span class=\"p\">;</span>\n\n<span class=\"c1\">// This component subscribes events from child components by context</span>\n<span class=\"err\">@</span><span class=\"nx\">subscriber</span><span class=\"p\">((</span><span class=\"nx\">self</span><span class=\"p\">,</span> <span class=\"nx\">subscribe</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"nx\">subscribe</span><span class=\"p\">(</span><span class=\"s1\">'foo'</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"nx\">prop</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"nx\">console</span><span class=\"p\">.</span><span class=\"nx\">log</span><span class=\"p\">(</span><span class=\"s1\">'foo received with'</span><span class=\"p\">,</span> <span class=\"nx\">prop</span><span class=\"p\">);</span>\n  <span class=\"p\">});</span>\n<span class=\"p\">})</span>\n<span class=\"kr\">class</span> <span class=\"nx\">App</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">Child</span><span class=\"o\">/&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"c1\">// This component has this.context.dispatch and it will dispatch to parent subscriber</span>\n<span class=\"err\">@</span><span class=\"nx\">dispatcher</span>\n<span class=\"kr\">class</span> <span class=\"nx\">Child</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">button</span> <span class=\"nx\">onClick</span><span class=\"o\">=</span><span class=\"p\">{()</span> <span class=\"o\">=&gt;</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">context</span><span class=\"p\">.</span><span class=\"nx\">dispatch</span><span class=\"p\">(</span><span class=\"s1\">'foo'</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">)}</span><span class=\"o\">&gt;</span><span class=\"nx\">hello</span><span class=\"o\">&lt;</span><span class=\"err\">/button&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"kr\">import</span> <span class=\"nx\">ReactDOM</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react-dom\"</span><span class=\"p\">;</span>\n<span class=\"kr\">const</span> <span class=\"nx\">el</span> <span class=\"o\">=</span> <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">querySelector</span><span class=\"p\">(</span><span class=\"s2\">\".main\"</span><span class=\"p\">);</span>\n<span class=\"nx\">ReactDOM</span><span class=\"p\">.</span><span class=\"nx\">render</span><span class=\"p\">(</span><span class=\"o\">&lt;</span><span class=\"nx\">App</span><span class=\"o\">/&gt;</span><span class=\"p\">,</span> <span class=\"nx\">el</span><span class=\"p\">);</span>\n</pre></div></div>\n\n<p>Pub側で<code>@subscriber</code>デコレータを宣言しながらエミッターを初期化する。意図的に動的には生やしたりはできないようにしている。</p>\n\n<p><code>@dipatcher</code> が付与されたComponentの <code>this.context.dispatch</code> は自分の<code>@subscribe</code>デコレータを持つ親(何層上でもおk)の<code>subscribe</code>を発火させる。</p>\n\n<h2>\n<span id=\"with-promisedreducer\" class=\"fragment\"></span><a href=\"#with-promisedreducer\"><i class=\"fa fa-link\"></i></a>with PromisedReducer</h2>\n\n<p>これも自作のライブラリだけど、</p>\n\n<p><a href=\"https://github.com/mizchi/promised-reducer\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mizchi/promised-reducer</a></p>\n\n<p>を使うと、こんな感じになる。</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"nx\">React</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">dispatcher</span><span class=\"p\">,</span> <span class=\"nx\">subscriber</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react-dispatcher-decorator\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"nx\">PromisedReducer</span> <span class=\"nx\">from</span> <span class=\"s2\">\"promised-ruducer\"</span><span class=\"p\">;</span>\n\n<span class=\"err\">@</span><span class=\"nx\">subscriber</span><span class=\"p\">((</span><span class=\"nx\">self</span><span class=\"p\">,</span> <span class=\"nx\">subscribe</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"kr\">const</span> <span class=\"nx\">reducer</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">PromisedReducer</span><span class=\"p\">({</span><span class=\"nx\">count</span><span class=\"o\">:</span> <span class=\"mi\">0</span><span class=\"p\">});</span>\n  <span class=\"nx\">reducer</span><span class=\"p\">.</span><span class=\"nx\">on</span><span class=\"p\">(</span><span class=\"s2\">\":update\"</span><span class=\"p\">,</span> <span class=\"nx\">state</span> <span class=\"o\">=&gt;</span> <span class=\"nx\">self</span><span class=\"p\">.</span><span class=\"nx\">setState</span><span class=\"p\">(</span><span class=\"nx\">state</span><span class=\"p\">));</span> \n  <span class=\"nx\">subscribe</span><span class=\"p\">(</span><span class=\"s1\">'increment'</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"nx\">prop</span><span class=\"p\">)</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"nx\">reducer</span><span class=\"p\">.</span><span class=\"nx\">update</span><span class=\"p\">(</span><span class=\"nx\">state</span> <span class=\"o\">=&gt;</span> <span class=\"p\">({</span><span class=\"nx\">count</span><span class=\"o\">:</span> <span class=\"nx\">state</span><span class=\"p\">.</span><span class=\"nx\">count</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">}));</span>\n  <span class=\"p\">});</span>\n<span class=\"p\">})</span>\n<span class=\"kr\">class</span> <span class=\"nx\">App</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">Child</span> <span class=\"nx\">count</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">state</span><span class=\"p\">.</span><span class=\"nx\">count</span><span class=\"p\">}</span><span class=\"o\">/&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"c1\">// This component has this.context.dispatch and it will dispatch to parent subscriber</span>\n<span class=\"err\">@</span><span class=\"nx\">dispatcher</span>\n<span class=\"kr\">class</span> <span class=\"nx\">Child</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">button</span> <span class=\"nx\">onClick</span><span class=\"o\">=</span><span class=\"p\">{()</span> <span class=\"o\">=&gt;</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">context</span><span class=\"p\">.</span><span class=\"nx\">dispatch</span><span class=\"p\">(</span><span class=\"s1\">'increment'</span><span class=\"p\">)}</span><span class=\"o\">&gt;</span> <span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">count</span><span class=\"p\">}</span> <span class=\"o\">&lt;</span><span class=\"err\">/button&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"c1\">// 略</span>\n</pre></div></div>\n\n<p>PromisedReducer はこれまた自分が作った <a href=\"https://github.com/mizchi/flumpt\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mizchi/flumpt</a> の非同期更新のバッチ処理を最適化する部分を抜き出したもので、要はこの例はFlumptを小さいパーツに分解してデコレータ構文に糖衣して再構築するとこうなる、というもの。</p>\n\n<p>flumpt はSSR対応してなかったんだけど、仕組み上だるかったので、SSRしたいならこういうアプローチでいけるはず。</p>\n",
    "stock_users": [
        "Nkzn",
        "seizans",
        "FiNGAHOLiC",
        "sotayamashita",
        "takashi",
        "nana4gonta",
        "ishiduca",
        "sora0077@github",
        "yancya",
        "izumin5210",
        "meganetaaan",
        "kazufusa",
        "ichi944",
        "tos-miyake",
        "cotto89",
        "hkusu",
        "takayuki-ochiai",
        "minewebstaff",
        "moaible",
        "take-sei",
        "kuy",
        "rchaser53",
        "Tanoemon"
    ]
}, {
    "id": 402592,
    "uuid": "2b6de4e5a853f7a578a6",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "MobXでReactのステート管理をする",
    "created_at": "2016-06-24 01:56:15 +0900",
    "updated_at": "2016-06-24 02:02:32 +0900",
    "created_at_in_words": "3ヶ月",
    "updated_at_in_words": "3ヶ月",
    "tags": [{
        "name": "react.js",
        "url_name": "react.js",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/e6867d326364bb2498f72f152c92408bf457de8c/medium.jpg?1426679594",
        "following": true,
        "versions": []
    }, {
        "name": "mobx",
        "url_name": "mobx",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 23,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/2b6de4e5a853f7a578a6",
    "created_at_as_seconds": 1466700975,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\nMobXは`Simple, scalable state management` を謳うステート管理マネージャ。\n\n最近Hacker NewsやEchoJSで、にわかにMobXがフィーチャーされている。\n\n[3 Reasons why I stopped using React.setState — Medium](https://medium.com/@mweststrate/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e#.94ggj6xa7 \"3 Reasons why I stopped using React.setState — Medium\")\n\nこの記事を読んで、そんなにいいもんかなと思い、試してみることにした。\n\n[mobxjs/mobx: Simple, scalable state management.](https://github.com/mobxjs/mobx \"mobxjs/mobx: Simple, scalable state management.\")\n\n\n## カウンター\n\n簡単なカウンターを作ってみる。一秒で1インクリメンタルされるやつ。\n\n```js\nimport ReactDOM from \"react-dom\";\nimport React from \"react\";\nimport {observable} from \"mobx\";\nimport {observer} from 'mobx-react'\n\nclass AppState {\n  id = Math.random();\n  @observable counter = 0;\n}\n\n@observer\nclass App extends React.Component {\n  render() {\n    return <div className=\"app\">{this.props.data.counter}</div>\n  }\n}\n\nconst el = document.querySelector(\".main\");\nconst appState = new AppState();\nReactDOM.render(<App data={appState} />, el);\n\nsetInterval(() => {\n  appState.counter += 1;\n}, 1000);\n```\n\nステート側のリアクティブにしたいプロパティにmobx の`@observable` デコレーターを付け、Reactのコンポーネント側にmobx-react の `@observer` デコレーターを付与する。\n\nあとはステート側を書き換えると変更が反映される。たぶんobserver ついたComponentのコンストラクタで observervable なプロパティの変更をフックして、高階コンポーネントのsetStateを呼び出してそう。(中身読んでないけど自分ならそう作るだろうなという予想)\n\nVueのデータバインダを思い出す感じもある。\n\n## @computed \n\n他の値に依存する値を表現する\n\n```js\nclass AppState {\n  @observable counter = 0;\n\n  constructor(counter) {\n    this.counter = counter;\n  }\n\n  @computed get twice() {\n    return this.counter * 2;\n  }\n}\n\n@observer\nclass App extends React.Component {\n  render() {\n    return <div className=\"app\">{this.props.data.twice}</div>\n  }\n}\n```\n\nこれで2倍表示されるようになった。\n\n[Becoming fully reactive: an in-depth explanation of MobX — Medium](https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.q05zries8 \"Becoming fully reactive: an in-depth explanation of MobX — Medium\")\n\n## @action\n\nsetState と違って値を1つずつ更新するため、非同期で複数更新すると、無駄にrenderが走ることになる。それを抑制するのがactionデコレータ。\n\n```js\nclass AppState {\n  @observable counter;\n\n  constructor() {\n    this.counter = 0;\n  }\n\n  @computed get twice() {\n    return this.counter * 2;\n  }\n\n  @action\n  incrementLazy() {\n    this.counter -= 1;\n    setTimeout(() => {\n      this.counter += 2;\n    }, 100);\n  }\n}\n\n@observer\nclass App extends React.Component {\n  render() {\n    return <div className=\"app\">{this.props.data.twice}</div>\n  }\n}\n```\n\n\nたとえばactionデコレータ を外すとこのような振る舞いになる。\n![](https://i.gyazo.com/6e9fa1454c73e6aacd3eee1225fc3dd2.gif)\nデコレータがついてる場合は-1した部分がスキップされる。\n\n(トランザクションの終了検知を実装するには、関数内部の静的解析が必要な気がするが、どうなってるんだろう?)\n\n## 感想\n\nデコレータでリアクティブプロパティーを作る部分は面白いが、入れ子のプロパティーを作ろうとするとasStructureという専用の構築子を要求されたりしだして、だるい感じ。正直トランザクション管理も自分でつくりだした沼にハマってるだけなので、別にsetStateでいい。\n\nサンプルコード読んだり中身を想像する分には面白かった。\n\n## 参考\n\ntonyspiro/easy-mobx-example: Easy MobX + React Example https://github.com/tonyspiro/easy-mobx-example\n\n[MobX: Ten minute introduction to MobX and React](https://mobxjs.github.io/mobx/getting-started.html \"MobX: Ten minute introduction to MobX and React\")\n",
    "body": "<p>MobXは<code>Simple, scalable state management</code> を謳うステート管理マネージャ。</p>\n\n<p>最近Hacker NewsやEchoJSで、にわかにMobXがフィーチャーされている。</p>\n\n<p><a href=\"https://medium.com/@mweststrate/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e#.94ggj6xa7\" title=\"3 Reasons why I stopped using React.setState — Medium\" rel=\"nofollow\" target=\"_blank\">3 Reasons why I stopped using React.setState — Medium</a></p>\n\n<p>この記事を読んで、そんなにいいもんかなと思い、試してみることにした。</p>\n\n<p><a href=\"https://github.com/mobxjs/mobx\" title=\"mobxjs/mobx: Simple, scalable state management.\" rel=\"nofollow\" target=\"_blank\">mobxjs/mobx: Simple, scalable state management.</a></p>\n\n<h2>\n<span id=\"カウンター\" class=\"fragment\"></span><a href=\"#%E3%82%AB%E3%82%A6%E3%83%B3%E3%82%BF%E3%83%BC\"><i class=\"fa fa-link\"></i></a>カウンター</h2>\n\n<p>簡単なカウンターを作ってみる。一秒で1インクリメンタルされるやつ。</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"nx\">ReactDOM</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react-dom\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"nx\">React</span> <span class=\"nx\">from</span> <span class=\"s2\">\"react\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">observable</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s2\">\"mobx\"</span><span class=\"p\">;</span>\n<span class=\"kr\">import</span> <span class=\"p\">{</span><span class=\"nx\">observer</span><span class=\"p\">}</span> <span class=\"nx\">from</span> <span class=\"s1\">'mobx-react'</span>\n\n<span class=\"kr\">class</span> <span class=\"nx\">AppState</span> <span class=\"p\">{</span>\n  <span class=\"nx\">id</span> <span class=\"o\">=</span> <span class=\"nb\">Math</span><span class=\"p\">.</span><span class=\"nx\">random</span><span class=\"p\">();</span>\n  <span class=\"err\">@</span><span class=\"nx\">observable</span> <span class=\"nx\">counter</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n\n<span class=\"err\">@</span><span class=\"nx\">observer</span>\n<span class=\"kr\">class</span> <span class=\"nx\">App</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">div</span> <span class=\"nx\">className</span><span class=\"o\">=</span><span class=\"s2\">\"app\"</span><span class=\"o\">&gt;</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">data</span><span class=\"p\">.</span><span class=\"nx\">counter</span><span class=\"p\">}</span><span class=\"o\">&lt;</span><span class=\"err\">/div&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"kr\">const</span> <span class=\"nx\">el</span> <span class=\"o\">=</span> <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">querySelector</span><span class=\"p\">(</span><span class=\"s2\">\".main\"</span><span class=\"p\">);</span>\n<span class=\"kr\">const</span> <span class=\"nx\">appState</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">AppState</span><span class=\"p\">();</span>\n<span class=\"nx\">ReactDOM</span><span class=\"p\">.</span><span class=\"nx\">render</span><span class=\"p\">(</span><span class=\"o\">&lt;</span><span class=\"nx\">App</span> <span class=\"nx\">data</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"nx\">appState</span><span class=\"p\">}</span> <span class=\"o\">/&gt;</span><span class=\"p\">,</span> <span class=\"nx\">el</span><span class=\"p\">);</span>\n\n<span class=\"nx\">setInterval</span><span class=\"p\">(()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"nx\">appState</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n<span class=\"p\">},</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n</pre></div></div>\n\n<p>ステート側のリアクティブにしたいプロパティにmobx の<code>@observable</code> デコレーターを付け、Reactのコンポーネント側にmobx-react の <code>@observer</code> デコレーターを付与する。</p>\n\n<p>あとはステート側を書き換えると変更が反映される。たぶんobserver ついたComponentのコンストラクタで observervable なプロパティの変更をフックして、高階コンポーネントのsetStateを呼び出してそう。(中身読んでないけど自分ならそう作るだろうなという予想)</p>\n\n<p>Vueのデータバインダを思い出す感じもある。</p>\n\n<h2>\n<span id=\"computed\" class=\"fragment\"></span><a href=\"#computed\"><i class=\"fa fa-link\"></i></a><a href=\"/computed\" class=\"user-mention js-hovercard\" title=\"computed\" data-hovercard-target-type=\"user\" data-hovercard-target-name=\"computed\">@computed</a>\n</h2>\n\n<p>他の値に依存する値を表現する</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">class</span> <span class=\"nx\">AppState</span> <span class=\"p\">{</span>\n  <span class=\"err\">@</span><span class=\"nx\">observable</span> <span class=\"nx\">counter</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n\n  <span class=\"nx\">constructor</span><span class=\"p\">(</span><span class=\"nx\">counter</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">=</span> <span class=\"nx\">counter</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n\n  <span class=\"err\">@</span><span class=\"nx\">computed</span> <span class=\"nx\">get</span> <span class=\"nx\">twice</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">*</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"err\">@</span><span class=\"nx\">observer</span>\n<span class=\"kr\">class</span> <span class=\"nx\">App</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">div</span> <span class=\"nx\">className</span><span class=\"o\">=</span><span class=\"s2\">\"app\"</span><span class=\"o\">&gt;</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">data</span><span class=\"p\">.</span><span class=\"nx\">twice</span><span class=\"p\">}</span><span class=\"o\">&lt;</span><span class=\"err\">/div&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p>これで2倍表示されるようになった。</p>\n\n<p><a href=\"https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.q05zries8\" title=\"Becoming fully reactive: an in-depth explanation of MobX — Medium\" rel=\"nofollow\" target=\"_blank\">Becoming fully reactive: an in-depth explanation of MobX — Medium</a></p>\n\n<h2>\n<span id=\"action\" class=\"fragment\"></span><a href=\"#action\"><i class=\"fa fa-link\"></i></a><a href=\"/action\" class=\"user-mention js-hovercard\" title=\"action\" data-hovercard-target-type=\"user\" data-hovercard-target-name=\"action\">@action</a>\n</h2>\n\n<p>setState と違って値を1つずつ更新するため、非同期で複数更新すると、無駄にrenderが走ることになる。それを抑制するのがactionデコレータ。</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">class</span> <span class=\"nx\">AppState</span> <span class=\"p\">{</span>\n  <span class=\"err\">@</span><span class=\"nx\">observable</span> <span class=\"nx\">counter</span><span class=\"p\">;</span>\n\n  <span class=\"nx\">constructor</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n\n  <span class=\"err\">@</span><span class=\"nx\">computed</span> <span class=\"nx\">get</span> <span class=\"nx\">twice</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">*</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n\n  <span class=\"err\">@</span><span class=\"nx\">action</span>\n  <span class=\"nx\">incrementLazy</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">-=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n    <span class=\"nx\">setTimeout</span><span class=\"p\">(()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n      <span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">counter</span> <span class=\"o\">+=</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n    <span class=\"p\">},</span> <span class=\"mi\">100</span><span class=\"p\">);</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n\n<span class=\"err\">@</span><span class=\"nx\">observer</span>\n<span class=\"kr\">class</span> <span class=\"nx\">App</span> <span class=\"kr\">extends</span> <span class=\"nx\">React</span><span class=\"p\">.</span><span class=\"nx\">Component</span> <span class=\"p\">{</span>\n  <span class=\"nx\">render</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"o\">&lt;</span><span class=\"nx\">div</span> <span class=\"nx\">className</span><span class=\"o\">=</span><span class=\"s2\">\"app\"</span><span class=\"o\">&gt;</span><span class=\"p\">{</span><span class=\"k\">this</span><span class=\"p\">.</span><span class=\"nx\">props</span><span class=\"p\">.</span><span class=\"nx\">data</span><span class=\"p\">.</span><span class=\"nx\">twice</span><span class=\"p\">}</span><span class=\"o\">&lt;</span><span class=\"err\">/div&gt;</span>\n  <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p>たとえばactionデコレータ を外すとこのような振る舞いになる。<br>\n<a href=\"https://i.gyazo.com/6e9fa1454c73e6aacd3eee1225fc3dd2.gif\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/6e9fa1454c73e6aacd3eee1225fc3dd2.gif\" alt=\"\"></a><br>\nデコレータがついてる場合は-1した部分がスキップされる。</p>\n\n<p>(トランザクションの終了検知を実装するには、関数内部の静的解析が必要な気がするが、どうなってるんだろう?)</p>\n\n<h2>\n<span id=\"感想\" class=\"fragment\"></span><a href=\"#%E6%84%9F%E6%83%B3\"><i class=\"fa fa-link\"></i></a>感想</h2>\n\n<p>デコレータでリアクティブプロパティーを作る部分は面白いが、入れ子のプロパティーを作ろうとするとasStructureという専用の構築子を要求されたりしだして、だるい感じ。正直トランザクション管理も自分でつくりだした沼にハマってるだけなので、別にsetStateでいい。</p>\n\n<p>サンプルコード読んだり中身を想像する分には面白かった。</p>\n\n<h2>\n<span id=\"参考\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%83\"><i class=\"fa fa-link\"></i></a>参考</h2>\n\n<p>tonyspiro/easy-mobx-example: Easy MobX + React Example <a href=\"https://github.com/tonyspiro/easy-mobx-example\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/tonyspiro/easy-mobx-example</a></p>\n\n<p><a href=\"https://mobxjs.github.io/mobx/getting-started.html\" title=\"MobX: Ten minute introduction to MobX and React\" rel=\"nofollow\" target=\"_blank\">MobX: Ten minute introduction to MobX and React</a></p>\n",
    "stock_users": [
        "mizchi",
        "spi_regu",
        "maechabin",
        "shuhei",
        "Quramy",
        "ooDEMi",
        "yshrsmz@github",
        "iwata-n@github",
        "ecaze",
        "urara",
        "airtoxin",
        "kmkm",
        "shimpeiws",
        "mikehi",
        "ramusara",
        "rooooomania",
        "matsuikazuto",
        "minewebstaff",
        "dkimura",
        "Takamichi-tsutsumi",
        "bokuweb",
        "yasuhiro-okada-aktsk",
        "itoshiki"
    ]
}, {
    "id": 401911,
    "uuid": "bf67bfbe83c0548e33ee",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "ErgoDox/Dvorak 1週間経過時点での感想とキーマップ",
    "created_at": "2016-06-21 15:59:47 +0900",
    "updated_at": "2016-06-21 16:01:09 +0900",
    "created_at_in_words": "3ヶ月",
    "updated_at_in_words": "3ヶ月",
    "tags": [{
        "name": "ergodox",
        "url_name": "ergodox",
        "icon_url": "icons/medium/missing.png",
        "following": false,
        "versions": []
    }],
    "stock_count": 16,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/bf67bfbe83c0548e33ee",
    "created_at_as_seconds": 1466492387,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "\n## 前提\n\n- MacOSX\n- HHK TypeS(無刻印) => ErgoDoxEZ(無刻印)\n- Dvorak歴半年程度\n\nもともと無刻印ユーザーなので、レイアウトが動的に切り替わるのは違和感がない。\n\n## 雑感\n\nセパレートはやはり肩こりに効く感じがある。これが収穫大。(これだけだったらKinesisでもいいんだが)\nMac/Dvorak/ErgoDoxEZという組み合わせで、あんまり参考になる設定がないので、とりあえず毎日ガチャガチャキーマップを書き換えている。デフォルトキーマップはかなり癖があるので、書き換えないと辛い。\n\n生産性は使い始めた初日で30%程度。\n3日経過で60%。\n一週間経過(今)で80%。\n\nまだまだ伸びしろあるはず。\n\n## 周辺ツールの設定\n\nKarabinerでDvorakに設定したが、多段変換される都合でErgoDoxの設定が複雑になるので、KarabinerでQwertyのプロファイルをもう1個作ってメニューに表示し、マウスで切り替えられるようにした。\n\nノートPCなので、何かとErgoDox使わない環境は残しておかないと不便\n\n## 今抱えている問題\n\n押さえっぱなしとタップで挙動を切り替えるやつ便利じゃん、と思って使いまくったら、内部の都合なのかしら知らないが、複数のメタキーを使ったあとにメタキーがおしっぱなしになり戻らないことが多々あった。その周辺キーをガチャガチャやったり、USB抜き差しで治る。今はその設定を最小限にしたら多少マシになった。\n\n調べた結果、レイヤー切り替えキーを押した後、その下に別のキーが設定されているときの挙動が怪しかったので、今は触れないようにしている。\n\n## 読むといい記事\n\n- http://qiita.com/ReSTARTR/items/f84f8f3c4c51c876cb2f\n- http://qiita.com/ReSTARTR/items/970354940f49c67fb9fd\n\n## あるといいもの\n\n- 安全ピン\n\nまち針でもなんでもいいんだけど、なんだかんだでハードリセットキーをよく押すので\n\n## 今現在の keymap.c\n\n```c\n#include \"ergodox_ez.h\"\n#include \"debug.h\"\n#include \"action_layer.h\"\n\n#define BASE 0 // default layer\n#define SYMB 1 // symbols\n#define MDIA 2 // media keys\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n/* Keymap 0: Basic layer\n *\n * ,--------------------------------------------------.           ,--------------------------------------------------.\n * |   ESC  |   1  |   2  |   3  |   4  |   5  |  6   |           |   7  |   8  |   9  |   0  |   +  |   =  |   \\    |\n * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|\n * |   TAB  |   '  |   ,  |   .  |   P  |   Y  |  L1  |           |  L1  |   F  |   G  |   C  |   R  |   L  |   /    |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * |  LCTR  |   A  |   O  |   E  |   U  |   I  |------|           |------|   D  |   H  |   T  |   N  | S/L2 |   -    |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * | LShift |   :  |   Q  |   J  |   K  |   X  |      |           |      |   B  |   M  |   W  |   V  | Z/L1 | RShift |\n * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'\n *   |L1    |ESC/L1| LCTRL| LALT | LGUI |                                       | RGUI |   =  |   [  |   ]  | ~L1  |\n *   `----------------------------------'                                       `----------------------------------'\n *                                        ,-------------.       ,---------------.\n *                                        | LALT | LGui |       | Alt  |Ctrl/Esc|\n *                                 ,------|------|------|       |------+--------+------.\n *                                 |      |      | Home |       | PgUp |        |      |\n *                                 | Space|Backsp|------|       |------|  Tab   |Enter |\n *                                 |      |ace   | End  |       | PgDn |        |      |\n *                                 `--------------------'       `----------------------'\n */\n// If it accepts an argument (i.e, is a function), it doesn't need KC_.\n// Otherwise, it needs KC_*\n[BASE] = KEYMAP(  // layer 0 : default\n        // left hand\n        KC_ESC,         KC_1,           KC_2,    KC_3,   KC_4,   KC_5,   KC_6,\n        KC_TAB,       KC_QUOT,        KC_COMM, KC_DOT, KC_P,   KC_Y,   TG(1),\n        KC_LCTRL,       KC_A,           KC_O,    KC_E,   KC_U,   KC_I,\n\n        KC_LSFT,        KC_SCLN, KC_Q,    KC_J,   KC_K,   KC_X,   KC_NO,\n     LT(SYMB,KC_NO), LT(SYMB, KC_ESC), KC_LCTRL,   KC_LALT,  KC_LGUI,\n                                                  KC_LALT,  KC_LGUI,\n                                                             KC_HOME,\n                                               KC_SPC,KC_BSPC,KC_END,\n        // right hand\n        KC_7,        KC_8,   KC_9,      KC_0,      KC_PLUS,  KC_EQL,         KC_BSLS,\n        TG(SYMB),    KC_F,   KC_G,      KC_C,      KC_R,   KC_L,             KC_SLSH,\n                     KC_D,   KC_H,      KC_T,      KC_N,   LT(MDIA, KC_S),   KC_MINS,\n        KC_NO,       KC_B,   KC_M,      KC_W,      KC_V,   LT(SYMB, KC_Z),   KC_RSFT,\n                              KC_RGUI,   KC_EQL, KC_LBRC,KC_RBRC,             KC_FN1,\n        KC_LALT,        CTL_T(KC_ESC),\n        KC_PGUP,\n        KC_PGDN,KC_TAB, KC_ENT\n    ),\n/* Keymap 1: Symbol Layer\n *\n * ,--------------------------------------------------.           ,--------------------------------------------------.\n * |    =   |  F1  |  F2  |  F3  |  F4  |  F5  |  F6  |           |   F7 |  F8  |  F9  |  F10 |  F11 |  F12 |        |\n * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|\n * |        |   !  |   @  |   {  |   }  |   |  |      |           |      |   Up |   7  |   8  |   9  |   *  |        |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * |        |   #  |   $  |   (  |   )  |   `  |------|           |------| Down |   4  |   5  |   6  |   +  |        |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * |        |   %  |   ^  |   [  |   ]  |   ~  |      |           |      |   &  |   1  |   2  |   3  |      |        |\n * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'\n *   |      |      |      |      |   =  |                                       |      |    . |   0  |   =  |      |\n *   `----------------------------------'                                       `----------------------------------'\n *                                        ,-------------.       ,-------------.\n *                                        |      |      |       |      |      |\n *                                 ,------|------|------|       |------+------+------.\n *                                 |      |      |      |       |      |      |      |\n *                                 |      |      |------|       |------|      |      |\n *                                 |      |      |      |       |      |      |      |\n *                                 `--------------------'       `--------------------'\n */\n// SYMBOLS\n[SYMB] = KEYMAP(\n       // left hand\n       KC_EQL ,KC_F1,  KC_F2,  KC_F3,  KC_F4,  KC_F5,  KC_F6,\n       KC_TRNS,KC_EXLM,KC_AT,  KC_LCBR,KC_RCBR,KC_PIPE,KC_TRNS,\n       KC_TRNS,KC_HASH,KC_DLR, KC_LPRN,KC_RPRN,KC_TRNS,\n       KC_TRNS,KC_PERC,KC_CIRC,KC_LBRC,KC_RBRC,KC_TILD,KC_TRNS,\n       KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,\n                                       KC_TRNS,KC_TRNS,\n                                               KC_TRNS,\n                               KC_TRNS,KC_TRNS,KC_TRNS,\n       // right hand\n       KC_F7,   KC_F8,   KC_F9,  KC_F10,  KC_F11,  KC_F12,  KC_TRNS,\n       KC_TRNS, KC_UP,   KC_7,   KC_8,    KC_9,    KC_ASTR, KC_F12,\n                KC_DOWN, KC_4,   KC_5,    KC_6,    KC_PLUS, KC_TRNS,\n       KC_TRNS, KC_AMPR, KC_1,   KC_2,    KC_3,    KC_TRNS, KC_TRNS,\n                         KC_TRNS,KC_DOT,  KC_0,    KC_EQL,  KC_TRNS,\n       KC_TRNS, KC_TRNS,\n       KC_TRNS,\n       KC_TRNS, KC_TRNS, KC_TRNS\n),\n/* Keymap 2: Media and mouse keys\n *\n * ,--------------------------------------------------.           ,--------------------------------------------------.\n * |  RESET |      |      |      |      |      |      |           |      |      |      |      |      |      |        |\n * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|\n * |        |      | Lclk | MsUp | Rclk |      |      |           |      |      | PGDN |  UP  | PGUP |      |        |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * |        |      |MsLeft|MsDown|MsRght|      |------|           |------| BSPC | LEFT | DOWN |RIGHT |      |  Play  |\n * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|\n * |        |      |      |      |      |      |      |           |      |      | HOME | ESC  | END  |      |        |\n * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'\n *   |      |      |      | Lclk | Rclk |                                       |VolDn |VolUp | Mute |      |      |\n *   `----------------------------------'                                       `----------------------------------'\n *                                        ,-------------.       ,-------------.\n *                                        |      |      |       |      |      |\n *                                 ,------|------|------|       |------+------+------.\n *                                 |      |      |      |       |      |      |Brwser|\n *                                 | Lclk | Rclk |------|       |------|      |Back  |\n *                                 |      |      |      |       |      |      |      |\n *                                 `--------------------'       `--------------------'\n */\n// MEDIA AND MOUS\nKEYMAP(\n       KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,\n       KC_TRNS, KC_TRNS, KC_BTN1, KC_MS_U, KC_BTN2, KC_TRNS, KC_TRNS,\n       KC_TRNS, KC_BSPC, KC_MS_L, KC_MS_D, KC_MS_R, KC_TRNS,\n       KC_TRNS, KC_TRNS, KC_TRNS, KC_ESC,  KC_TRNS, KC_TRNS, KC_TRNS,\n       KC_TRNS, KC_TRNS, KC_TRNS, KC_BTN1, KC_BTN2,\n                                           KC_TRNS, KC_TRNS,\n                                                    KC_TRNS,\n                                  KC_BTN1, KC_BTN2, KC_TRNS,\n    // right hand\n       KC_TRNS,  KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,\n       KC_TRNS,  KC_TRNS, KC_PGDN, KC_UP  , KC_PGUP, KC_TRNS, KC_TRNS,\n                 KC_TRNS, KC_LEFT, KC_DOWN, KC_RGHT, KC_TRNS, KC_MPLY,\n       KC_TRNS,  KC_TRNS, KC_HOME, KC_TRNS, KC_END, KC_TRNS, KC_TRNS,\n                          KC_VOLD, KC_VOLU, KC_MUTE, KC_TRNS, KC_TRNS,\n       KC_TRNS, KC_TRNS,\n       KC_TRNS,\n       KC_TRNS, KC_TRNS, KC_RALT\n),\n};\n\nconst uint16_t PROGMEM fn_actions[] = {\n    [1] = ACTION_LAYER_TAP_TOGGLE(SYMB)                // FN1 - Momentary Layer 1 (Symbols)\n};\n\nconst macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)\n{\n  // MACRODOWN only works in this function\n      switch(id) {\n        case 0:\n        if (record->event.pressed) {\n          register_code(KC_RSFT);\n        } else {\n          unregister_code(KC_RSFT);\n        }\n        break;\n      }\n    return MACRO_NONE;\n};\n\n// Runs just one time when the keyboard initializes.\nvoid matrix_init_user(void) {\n\n};\n\n// Runs constantly in the background, in a loop.\nvoid matrix_scan_user(void) {\n\n    uint8_t layer = biton32(layer_state);\n\n    ergodox_board_led_off();\n    ergodox_right_led_1_off();\n    ergodox_right_led_2_off();\n    ergodox_right_led_3_off();\n    switch (layer) {\n      // TODO: Make this relevant to the ErgoDox EZ.\n        case 1:\n            ergodox_right_led_1_on();\n            break;\n        case 2:\n            ergodox_right_led_2_on();\n            break;\n        default:\n            // none\n            break;\n    }\n\n};\n```\n",
    "body": "\n<h2>\n<span id=\"前提\" class=\"fragment\"></span><a href=\"#%E5%89%8D%E6%8F%90\"><i class=\"fa fa-link\"></i></a>前提</h2>\n\n<ul>\n<li>MacOSX</li>\n<li>HHK TypeS(無刻印) =&gt; ErgoDoxEZ(無刻印)</li>\n<li>Dvorak歴半年程度</li>\n</ul>\n\n<p>もともと無刻印ユーザーなので、レイアウトが動的に切り替わるのは違和感がない。</p>\n\n<h2>\n<span id=\"雑感\" class=\"fragment\"></span><a href=\"#%E9%9B%91%E6%84%9F\"><i class=\"fa fa-link\"></i></a>雑感</h2>\n\n<p>セパレートはやはり肩こりに効く感じがある。これが収穫大。(これだけだったらKinesisでもいいんだが)<br>\nMac/Dvorak/ErgoDoxEZという組み合わせで、あんまり参考になる設定がないので、とりあえず毎日ガチャガチャキーマップを書き換えている。デフォルトキーマップはかなり癖があるので、書き換えないと辛い。</p>\n\n<p>生産性は使い始めた初日で30%程度。<br>\n3日経過で60%。<br>\n一週間経過(今)で80%。</p>\n\n<p>まだまだ伸びしろあるはず。</p>\n\n<h2>\n<span id=\"周辺ツールの設定\" class=\"fragment\"></span><a href=\"#%E5%91%A8%E8%BE%BA%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E8%A8%AD%E5%AE%9A\"><i class=\"fa fa-link\"></i></a>周辺ツールの設定</h2>\n\n<p>KarabinerでDvorakに設定したが、多段変換される都合でErgoDoxの設定が複雑になるので、KarabinerでQwertyのプロファイルをもう1個作ってメニューに表示し、マウスで切り替えられるようにした。</p>\n\n<p>ノートPCなので、何かとErgoDox使わない環境は残しておかないと不便</p>\n\n<h2>\n<span id=\"今抱えている問題\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E6%8A%B1%E3%81%88%E3%81%A6%E3%81%84%E3%82%8B%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>今抱えている問題</h2>\n\n<p>押さえっぱなしとタップで挙動を切り替えるやつ便利じゃん、と思って使いまくったら、内部の都合なのかしら知らないが、複数のメタキーを使ったあとにメタキーがおしっぱなしになり戻らないことが多々あった。その周辺キーをガチャガチャやったり、USB抜き差しで治る。今はその設定を最小限にしたら多少マシになった。</p>\n\n<p>調べた結果、レイヤー切り替えキーを押した後、その下に別のキーが設定されているときの挙動が怪しかったので、今は触れないようにしている。</p>\n\n<h2>\n<span id=\"読むといい記事\" class=\"fragment\"></span><a href=\"#%E8%AA%AD%E3%82%80%E3%81%A8%E3%81%84%E3%81%84%E8%A8%98%E4%BA%8B\"><i class=\"fa fa-link\"></i></a>読むといい記事</h2>\n\n<ul>\n<li><a href=\"http://qiita.com/ReSTARTR/items/f84f8f3c4c51c876cb2f\" class=\"autolink\" id=\"reference-7903fa2f4f5c06008bc1\">http://qiita.com/ReSTARTR/items/f84f8f3c4c51c876cb2f</a></li>\n<li><a href=\"http://qiita.com/ReSTARTR/items/970354940f49c67fb9fd\" class=\"autolink\" id=\"reference-ef2f1d02f76e060ec161\">http://qiita.com/ReSTARTR/items/970354940f49c67fb9fd</a></li>\n</ul>\n\n<h2>\n<span id=\"あるといいもの\" class=\"fragment\"></span><a href=\"#%E3%81%82%E3%82%8B%E3%81%A8%E3%81%84%E3%81%84%E3%82%82%E3%81%AE\"><i class=\"fa fa-link\"></i></a>あるといいもの</h2>\n\n<ul>\n<li>安全ピン</li>\n</ul>\n\n<p>まち針でもなんでもいいんだけど、なんだかんだでハードリセットキーをよく押すので</p>\n\n<h2>\n<span id=\"今現在の-keymapc\" class=\"fragment\"></span><a href=\"#%E4%BB%8A%E7%8F%BE%E5%9C%A8%E3%81%AE-keymapc\"><i class=\"fa fa-link\"></i></a>今現在の keymap.c</h2>\n\n<div class=\"code-frame\" data-lang=\"c\"><div class=\"highlight\"><pre>\n<span class=\"cp\">#include \"ergodox_ez.h\"</span>\n<span class=\"cp\">#include \"debug.h\"</span>\n<span class=\"cp\">#include \"action_layer.h\"</span>\n\n<span class=\"cp\">#define BASE 0 </span><span class=\"c1\">// default layer</span>\n<span class=\"cp\">#define SYMB 1 </span><span class=\"c1\">// symbols</span>\n<span class=\"cp\">#define MDIA 2 </span><span class=\"c1\">// media keys</span>\n\n<span class=\"k\">const</span> <span class=\"kt\">uint16_t</span> <span class=\"n\">PROGMEM</span> <span class=\"n\">keymaps</span><span class=\"p\">[][</span><span class=\"n\">MATRIX_ROWS</span><span class=\"p\">][</span><span class=\"n\">MATRIX_COLS</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n<span class=\"cm\">/* Keymap 0: Basic layer</span>\n<span class=\"cm\"> *</span>\n<span class=\"cm\"> * ,--------------------------------------------------.           ,--------------------------------------------------.</span>\n<span class=\"cm\"> * |   ESC  |   1  |   2  |   3  |   4  |   5  |  6   |           |   7  |   8  |   9  |   0  |   +  |   =  |   \\    |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |   TAB  |   '  |   ,  |   .  |   P  |   Y  |  L1  |           |  L1  |   F  |   G  |   C  |   R  |   L  |   /    |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |  LCTR  |   A  |   O  |   E  |   U  |   I  |------|           |------|   D  |   H  |   T  |   N  | S/L2 |   -    |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * | LShift |   :  |   Q  |   J  |   K  |   X  |      |           |      |   B  |   M  |   W  |   V  | Z/L1 | RShift |</span>\n<span class=\"cm\"> * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'</span>\n<span class=\"cm\"> *   |L1    |ESC/L1| LCTRL| LALT | LGUI |                                       | RGUI |   =  |   [  |   ]  | ~L1  |</span>\n<span class=\"cm\"> *   `----------------------------------'                                       `----------------------------------'</span>\n<span class=\"cm\"> *                                        ,-------------.       ,---------------.</span>\n<span class=\"cm\"> *                                        | LALT | LGui |       | Alt  |Ctrl/Esc|</span>\n<span class=\"cm\"> *                                 ,------|------|------|       |------+--------+------.</span>\n<span class=\"cm\"> *                                 |      |      | Home |       | PgUp |        |      |</span>\n<span class=\"cm\"> *                                 | Space|Backsp|------|       |------|  Tab   |Enter |</span>\n<span class=\"cm\"> *                                 |      |ace   | End  |       | PgDn |        |      |</span>\n<span class=\"cm\"> *                                 `--------------------'       `----------------------'</span>\n<span class=\"cm\"> */</span>\n<span class=\"c1\">// If it accepts an argument (i.e, is a function), it doesn't need KC_.</span>\n<span class=\"c1\">// Otherwise, it needs KC_*</span>\n<span class=\"p\">[</span><span class=\"n\">BASE</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">KEYMAP</span><span class=\"p\">(</span>  <span class=\"c1\">// layer 0 : default</span>\n        <span class=\"c1\">// left hand</span>\n        <span class=\"n\">KC_ESC</span><span class=\"p\">,</span>         <span class=\"n\">KC_1</span><span class=\"p\">,</span>           <span class=\"n\">KC_2</span><span class=\"p\">,</span>    <span class=\"n\">KC_3</span><span class=\"p\">,</span>   <span class=\"n\">KC_4</span><span class=\"p\">,</span>   <span class=\"n\">KC_5</span><span class=\"p\">,</span>   <span class=\"n\">KC_6</span><span class=\"p\">,</span>\n        <span class=\"n\">KC_TAB</span><span class=\"p\">,</span>       <span class=\"n\">KC_QUOT</span><span class=\"p\">,</span>        <span class=\"n\">KC_COMM</span><span class=\"p\">,</span> <span class=\"n\">KC_DOT</span><span class=\"p\">,</span> <span class=\"n\">KC_P</span><span class=\"p\">,</span>   <span class=\"n\">KC_Y</span><span class=\"p\">,</span>   <span class=\"n\">TG</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">),</span>\n        <span class=\"n\">KC_LCTRL</span><span class=\"p\">,</span>       <span class=\"n\">KC_A</span><span class=\"p\">,</span>           <span class=\"n\">KC_O</span><span class=\"p\">,</span>    <span class=\"n\">KC_E</span><span class=\"p\">,</span>   <span class=\"n\">KC_U</span><span class=\"p\">,</span>   <span class=\"n\">KC_I</span><span class=\"p\">,</span>\n\n        <span class=\"n\">KC_LSFT</span><span class=\"p\">,</span>        <span class=\"n\">KC_SCLN</span><span class=\"p\">,</span> <span class=\"n\">KC_Q</span><span class=\"p\">,</span>    <span class=\"n\">KC_J</span><span class=\"p\">,</span>   <span class=\"n\">KC_K</span><span class=\"p\">,</span>   <span class=\"n\">KC_X</span><span class=\"p\">,</span>   <span class=\"n\">KC_NO</span><span class=\"p\">,</span>\n     <span class=\"n\">LT</span><span class=\"p\">(</span><span class=\"n\">SYMB</span><span class=\"p\">,</span><span class=\"n\">KC_NO</span><span class=\"p\">),</span> <span class=\"n\">LT</span><span class=\"p\">(</span><span class=\"n\">SYMB</span><span class=\"p\">,</span> <span class=\"n\">KC_ESC</span><span class=\"p\">),</span> <span class=\"n\">KC_LCTRL</span><span class=\"p\">,</span>   <span class=\"n\">KC_LALT</span><span class=\"p\">,</span>  <span class=\"n\">KC_LGUI</span><span class=\"p\">,</span>\n                                                  <span class=\"n\">KC_LALT</span><span class=\"p\">,</span>  <span class=\"n\">KC_LGUI</span><span class=\"p\">,</span>\n                                                             <span class=\"n\">KC_HOME</span><span class=\"p\">,</span>\n                                               <span class=\"n\">KC_SPC</span><span class=\"p\">,</span><span class=\"n\">KC_BSPC</span><span class=\"p\">,</span><span class=\"n\">KC_END</span><span class=\"p\">,</span>\n        <span class=\"c1\">// right hand</span>\n        <span class=\"n\">KC_7</span><span class=\"p\">,</span>        <span class=\"n\">KC_8</span><span class=\"p\">,</span>   <span class=\"n\">KC_9</span><span class=\"p\">,</span>      <span class=\"n\">KC_0</span><span class=\"p\">,</span>      <span class=\"n\">KC_PLUS</span><span class=\"p\">,</span>  <span class=\"n\">KC_EQL</span><span class=\"p\">,</span>         <span class=\"n\">KC_BSLS</span><span class=\"p\">,</span>\n        <span class=\"n\">TG</span><span class=\"p\">(</span><span class=\"n\">SYMB</span><span class=\"p\">),</span>    <span class=\"n\">KC_F</span><span class=\"p\">,</span>   <span class=\"n\">KC_G</span><span class=\"p\">,</span>      <span class=\"n\">KC_C</span><span class=\"p\">,</span>      <span class=\"n\">KC_R</span><span class=\"p\">,</span>   <span class=\"n\">KC_L</span><span class=\"p\">,</span>             <span class=\"n\">KC_SLSH</span><span class=\"p\">,</span>\n                     <span class=\"n\">KC_D</span><span class=\"p\">,</span>   <span class=\"n\">KC_H</span><span class=\"p\">,</span>      <span class=\"n\">KC_T</span><span class=\"p\">,</span>      <span class=\"n\">KC_N</span><span class=\"p\">,</span>   <span class=\"n\">LT</span><span class=\"p\">(</span><span class=\"n\">MDIA</span><span class=\"p\">,</span> <span class=\"n\">KC_S</span><span class=\"p\">),</span>   <span class=\"n\">KC_MINS</span><span class=\"p\">,</span>\n        <span class=\"n\">KC_NO</span><span class=\"p\">,</span>       <span class=\"n\">KC_B</span><span class=\"p\">,</span>   <span class=\"n\">KC_M</span><span class=\"p\">,</span>      <span class=\"n\">KC_W</span><span class=\"p\">,</span>      <span class=\"n\">KC_V</span><span class=\"p\">,</span>   <span class=\"n\">LT</span><span class=\"p\">(</span><span class=\"n\">SYMB</span><span class=\"p\">,</span> <span class=\"n\">KC_Z</span><span class=\"p\">),</span>   <span class=\"n\">KC_RSFT</span><span class=\"p\">,</span>\n                              <span class=\"n\">KC_RGUI</span><span class=\"p\">,</span>   <span class=\"n\">KC_EQL</span><span class=\"p\">,</span> <span class=\"n\">KC_LBRC</span><span class=\"p\">,</span><span class=\"n\">KC_RBRC</span><span class=\"p\">,</span>             <span class=\"n\">KC_FN1</span><span class=\"p\">,</span>\n        <span class=\"n\">KC_LALT</span><span class=\"p\">,</span>        <span class=\"n\">CTL_T</span><span class=\"p\">(</span><span class=\"n\">KC_ESC</span><span class=\"p\">),</span>\n        <span class=\"n\">KC_PGUP</span><span class=\"p\">,</span>\n        <span class=\"n\">KC_PGDN</span><span class=\"p\">,</span><span class=\"n\">KC_TAB</span><span class=\"p\">,</span> <span class=\"n\">KC_ENT</span>\n    <span class=\"p\">),</span>\n<span class=\"cm\">/* Keymap 1: Symbol Layer</span>\n<span class=\"cm\"> *</span>\n<span class=\"cm\"> * ,--------------------------------------------------.           ,--------------------------------------------------.</span>\n<span class=\"cm\"> * |    =   |  F1  |  F2  |  F3  |  F4  |  F5  |  F6  |           |   F7 |  F8  |  F9  |  F10 |  F11 |  F12 |        |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |   !  |   @  |   {  |   }  |   |  |      |           |      |   Up |   7  |   8  |   9  |   *  |        |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |   #  |   $  |   (  |   )  |   `  |------|           |------| Down |   4  |   5  |   6  |   +  |        |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |   %  |   ^  |   [  |   ]  |   ~  |      |           |      |   &amp;  |   1  |   2  |   3  |      |        |</span>\n<span class=\"cm\"> * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'</span>\n<span class=\"cm\"> *   |      |      |      |      |   =  |                                       |      |    . |   0  |   =  |      |</span>\n<span class=\"cm\"> *   `----------------------------------'                                       `----------------------------------'</span>\n<span class=\"cm\"> *                                        ,-------------.       ,-------------.</span>\n<span class=\"cm\"> *                                        |      |      |       |      |      |</span>\n<span class=\"cm\"> *                                 ,------|------|------|       |------+------+------.</span>\n<span class=\"cm\"> *                                 |      |      |      |       |      |      |      |</span>\n<span class=\"cm\"> *                                 |      |      |------|       |------|      |      |</span>\n<span class=\"cm\"> *                                 |      |      |      |       |      |      |      |</span>\n<span class=\"cm\"> *                                 `--------------------'       `--------------------'</span>\n<span class=\"cm\"> */</span>\n<span class=\"c1\">// SYMBOLS</span>\n<span class=\"p\">[</span><span class=\"n\">SYMB</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">KEYMAP</span><span class=\"p\">(</span>\n       <span class=\"c1\">// left hand</span>\n       <span class=\"n\">KC_EQL</span> <span class=\"p\">,</span><span class=\"n\">KC_F1</span><span class=\"p\">,</span>  <span class=\"n\">KC_F2</span><span class=\"p\">,</span>  <span class=\"n\">KC_F3</span><span class=\"p\">,</span>  <span class=\"n\">KC_F4</span><span class=\"p\">,</span>  <span class=\"n\">KC_F5</span><span class=\"p\">,</span>  <span class=\"n\">KC_F6</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_EXLM</span><span class=\"p\">,</span><span class=\"n\">KC_AT</span><span class=\"p\">,</span>  <span class=\"n\">KC_LCBR</span><span class=\"p\">,</span><span class=\"n\">KC_RCBR</span><span class=\"p\">,</span><span class=\"n\">KC_PIPE</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_HASH</span><span class=\"p\">,</span><span class=\"n\">KC_DLR</span><span class=\"p\">,</span> <span class=\"n\">KC_LPRN</span><span class=\"p\">,</span><span class=\"n\">KC_RPRN</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_PERC</span><span class=\"p\">,</span><span class=\"n\">KC_CIRC</span><span class=\"p\">,</span><span class=\"n\">KC_LBRC</span><span class=\"p\">,</span><span class=\"n\">KC_RBRC</span><span class=\"p\">,</span><span class=\"n\">KC_TILD</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                                       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                                               <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                               <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"c1\">// right hand</span>\n       <span class=\"n\">KC_F7</span><span class=\"p\">,</span>   <span class=\"n\">KC_F8</span><span class=\"p\">,</span>   <span class=\"n\">KC_F9</span><span class=\"p\">,</span>  <span class=\"n\">KC_F10</span><span class=\"p\">,</span>  <span class=\"n\">KC_F11</span><span class=\"p\">,</span>  <span class=\"n\">KC_F12</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_UP</span><span class=\"p\">,</span>   <span class=\"n\">KC_7</span><span class=\"p\">,</span>   <span class=\"n\">KC_8</span><span class=\"p\">,</span>    <span class=\"n\">KC_9</span><span class=\"p\">,</span>    <span class=\"n\">KC_ASTR</span><span class=\"p\">,</span> <span class=\"n\">KC_F12</span><span class=\"p\">,</span>\n                <span class=\"n\">KC_DOWN</span><span class=\"p\">,</span> <span class=\"n\">KC_4</span><span class=\"p\">,</span>   <span class=\"n\">KC_5</span><span class=\"p\">,</span>    <span class=\"n\">KC_6</span><span class=\"p\">,</span>    <span class=\"n\">KC_PLUS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_AMPR</span><span class=\"p\">,</span> <span class=\"n\">KC_1</span><span class=\"p\">,</span>   <span class=\"n\">KC_2</span><span class=\"p\">,</span>    <span class=\"n\">KC_3</span><span class=\"p\">,</span>    <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                         <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span><span class=\"n\">KC_DOT</span><span class=\"p\">,</span>  <span class=\"n\">KC_0</span><span class=\"p\">,</span>    <span class=\"n\">KC_EQL</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span>\n<span class=\"p\">),</span>\n<span class=\"cm\">/* Keymap 2: Media and mouse keys</span>\n<span class=\"cm\"> *</span>\n<span class=\"cm\"> * ,--------------------------------------------------.           ,--------------------------------------------------.</span>\n<span class=\"cm\"> * |  RESET |      |      |      |      |      |      |           |      |      |      |      |      |      |        |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+-------------|           |------+------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |      | Lclk | MsUp | Rclk |      |      |           |      |      | PGDN |  UP  | PGUP |      |        |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |      |MsLeft|MsDown|MsRght|      |------|           |------| BSPC | LEFT | DOWN |RIGHT |      |  Play  |</span>\n<span class=\"cm\"> * |--------+------+------+------+------+------|      |           |      |------+------+------+------+------+--------|</span>\n<span class=\"cm\"> * |        |      |      |      |      |      |      |           |      |      | HOME | ESC  | END  |      |        |</span>\n<span class=\"cm\"> * `--------+------+------+------+------+-------------'           `-------------+------+------+------+------+--------'</span>\n<span class=\"cm\"> *   |      |      |      | Lclk | Rclk |                                       |VolDn |VolUp | Mute |      |      |</span>\n<span class=\"cm\"> *   `----------------------------------'                                       `----------------------------------'</span>\n<span class=\"cm\"> *                                        ,-------------.       ,-------------.</span>\n<span class=\"cm\"> *                                        |      |      |       |      |      |</span>\n<span class=\"cm\"> *                                 ,------|------|------|       |------+------+------.</span>\n<span class=\"cm\"> *                                 |      |      |      |       |      |      |Brwser|</span>\n<span class=\"cm\"> *                                 | Lclk | Rclk |------|       |------|      |Back  |</span>\n<span class=\"cm\"> *                                 |      |      |      |       |      |      |      |</span>\n<span class=\"cm\"> *                                 `--------------------'       `--------------------'</span>\n<span class=\"cm\"> */</span>\n<span class=\"c1\">// MEDIA AND MOUS</span>\n<span class=\"n\">KEYMAP</span><span class=\"p\">(</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_BTN1</span><span class=\"p\">,</span> <span class=\"n\">KC_MS_U</span><span class=\"p\">,</span> <span class=\"n\">KC_BTN2</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_BSPC</span><span class=\"p\">,</span> <span class=\"n\">KC_MS_L</span><span class=\"p\">,</span> <span class=\"n\">KC_MS_D</span><span class=\"p\">,</span> <span class=\"n\">KC_MS_R</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_ESC</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_BTN1</span><span class=\"p\">,</span> <span class=\"n\">KC_BTN2</span><span class=\"p\">,</span>\n                                           <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                                                    <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                                  <span class=\"n\">KC_BTN1</span><span class=\"p\">,</span> <span class=\"n\">KC_BTN2</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n    <span class=\"c1\">// right hand</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_PGDN</span><span class=\"p\">,</span> <span class=\"n\">KC_UP</span>  <span class=\"p\">,</span> <span class=\"n\">KC_PGUP</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                 <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_LEFT</span><span class=\"p\">,</span> <span class=\"n\">KC_DOWN</span><span class=\"p\">,</span> <span class=\"n\">KC_RGHT</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_MPLY</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>  <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_HOME</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_END</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n                          <span class=\"n\">KC_VOLD</span><span class=\"p\">,</span> <span class=\"n\">KC_VOLU</span><span class=\"p\">,</span> <span class=\"n\">KC_MUTE</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span>\n       <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_TRNS</span><span class=\"p\">,</span> <span class=\"n\">KC_RALT</span>\n<span class=\"p\">),</span>\n<span class=\"p\">};</span>\n\n<span class=\"k\">const</span> <span class=\"kt\">uint16_t</span> <span class=\"n\">PROGMEM</span> <span class=\"n\">fn_actions</span><span class=\"p\">[]</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n    <span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">ACTION_LAYER_TAP_TOGGLE</span><span class=\"p\">(</span><span class=\"n\">SYMB</span><span class=\"p\">)</span>                <span class=\"c1\">// FN1 - Momentary Layer 1 (Symbols)</span>\n<span class=\"p\">};</span>\n\n<span class=\"k\">const</span> <span class=\"kt\">macro_t</span> <span class=\"o\">*</span><span class=\"nf\">action_get_macro</span><span class=\"p\">(</span><span class=\"kt\">keyrecord_t</span> <span class=\"o\">*</span><span class=\"n\">record</span><span class=\"p\">,</span> <span class=\"kt\">uint8_t</span> <span class=\"n\">id</span><span class=\"p\">,</span> <span class=\"kt\">uint8_t</span> <span class=\"n\">opt</span><span class=\"p\">)</span>\n<span class=\"p\">{</span>\n  <span class=\"c1\">// MACRODOWN only works in this function</span>\n      <span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">id</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n        <span class=\"k\">case</span> <span class=\"mi\">0</span><span class=\"o\">:</span>\n        <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">record</span><span class=\"o\">-&gt;</span><span class=\"n\">event</span><span class=\"p\">.</span><span class=\"n\">pressed</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n          <span class=\"n\">register_code</span><span class=\"p\">(</span><span class=\"n\">KC_RSFT</span><span class=\"p\">);</span>\n        <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n          <span class=\"n\">unregister_code</span><span class=\"p\">(</span><span class=\"n\">KC_RSFT</span><span class=\"p\">);</span>\n        <span class=\"p\">}</span>\n        <span class=\"k\">break</span><span class=\"p\">;</span>\n      <span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"n\">MACRO_NONE</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n\n<span class=\"c1\">// Runs just one time when the keyboard initializes.</span>\n<span class=\"kt\">void</span> <span class=\"nf\">matrix_init_user</span><span class=\"p\">(</span><span class=\"kt\">void</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n\n<span class=\"p\">};</span>\n\n<span class=\"c1\">// Runs constantly in the background, in a loop.</span>\n<span class=\"kt\">void</span> <span class=\"nf\">matrix_scan_user</span><span class=\"p\">(</span><span class=\"kt\">void</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n\n    <span class=\"kt\">uint8_t</span> <span class=\"n\">layer</span> <span class=\"o\">=</span> <span class=\"n\">biton32</span><span class=\"p\">(</span><span class=\"n\">layer_state</span><span class=\"p\">);</span>\n\n    <span class=\"n\">ergodox_board_led_off</span><span class=\"p\">();</span>\n    <span class=\"n\">ergodox_right_led_1_off</span><span class=\"p\">();</span>\n    <span class=\"n\">ergodox_right_led_2_off</span><span class=\"p\">();</span>\n    <span class=\"n\">ergodox_right_led_3_off</span><span class=\"p\">();</span>\n    <span class=\"k\">switch</span> <span class=\"p\">(</span><span class=\"n\">layer</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n      <span class=\"c1\">// TODO: Make this relevant to the ErgoDox EZ.</span>\n        <span class=\"k\">case</span> <span class=\"mi\">1</span><span class=\"o\">:</span>\n            <span class=\"n\">ergodox_right_led_1_on</span><span class=\"p\">();</span>\n            <span class=\"k\">break</span><span class=\"p\">;</span>\n        <span class=\"k\">case</span> <span class=\"mi\">2</span><span class=\"o\">:</span>\n            <span class=\"n\">ergodox_right_led_2_on</span><span class=\"p\">();</span>\n            <span class=\"k\">break</span><span class=\"p\">;</span>\n        <span class=\"k\">default</span><span class=\"o\">:</span>\n            <span class=\"c1\">// none</span>\n            <span class=\"k\">break</span><span class=\"p\">;</span>\n    <span class=\"p\">}</span>\n\n<span class=\"p\">};</span>\n</pre></div></div>\n",
    "stock_users": [
        "dot",
        "taichi@github",
        "2k0ri",
        "Noboruhi",
        "ntaoo",
        "ReSTARTR",
        "yancya",
        "khrtz",
        "bells17",
        "ledsun",
        "h-michael",
        "tos-miyake",
        "syossan27",
        "nownabe",
        "HirofumiTamori",
        "uyaponz"
    ]
}, {
    "id": 401663,
    "uuid": "99cb64b2c3c25c020423",
    "user": {
        "id": 1727,
        "url_name": "mizchi",
        "profile_image_url": "https://2.gravatar.com/avatar/3aebd86547bfc5cdb451d5f2f95ed5d8?d=https%3A%2F%2Fidenticons.github.com%2F581e8e6b0f12b94f9febbf517b249bbf.png&r=x",
        "following": true
    },
    "title": "React on 現場 ~ あるいは Modern JavaScript on Rails ~",
    "created_at": "2016-06-20 17:59:08 +0900",
    "updated_at": "2016-06-20 17:59:30 +0900",
    "created_at_in_words": "3ヶ月",
    "updated_at_in_words": "3ヶ月",
    "tags": [{
        "name": "react.js",
        "url_name": "react.js",
        "icon_url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/e6867d326364bb2498f72f152c92408bf457de8c/medium.jpg?1426679594",
        "following": true,
        "versions": []
    }],
    "stock_count": 211,
    "comment_count": 0,
    "url": "http://qiita.com/mizchi/items/99cb64b2c3c25c020423",
    "created_at_as_seconds": 1466413148,
    "tweet": false,
    "gist_url": null,
    "private": false,
    "stocked": false,
    "raw_body": "## これは何\n\n[JSer.info 5周年記念イベント - connpass](http://jser.connpass.com/event/24202/ \"JSer.info 5周年記念イベント - connpass\") (2016/01/16) にて発表した資料。特に理由はないが公開するのを忘れていた。\n\nスライドモードのリリースにあたって公開する\n\n----\n\n## 近況(2016/01/16)\n\n- 昨年9月 Kobito for Windows => Qiita開発チーム\n- モダンなJS(当社比)を導入しようとした\n\n----------\n\n## モダンなJSとは(mizchi主観2016版)\n\n1. npm/browserifyで依存を解決\n2. Babel/ES2015\n3. React/Flux\n4. Testable\n5. No More **jQuery plugins**\n\n※これらの基準について今回は割愛\n\n------\n\n## 現実(2015)\n\n1. CoffeeScript\n2. Sprockets / グローバル名前空間渡し\n3. Backbone\n4. JSのテストはjasmineで数件 (※request specは豊富)\n5. jQuery plugins や jQuery UI まみれ\n\n--------\n\n## 初見での評価\n\n- Qiitaは2012年から開発されている\n  - 当時の選択基準としてはスジ悪ではない\n- **十分にユーザーに価値を提供している**(重要)\n  - 価値を生まないコードはメンテする価値が無い\n- ただちょっとCTO(yuku_t)の手癖がちょっと強いかな…\n\n--------\n\n## 降ってきた仕事\n\n=> なんかよくしてくれ\n\n--------\n\n# なんかよくする\n\n--------\n\n## なんかよく\n\n- モダンな開発環境を導入する\n- ~~今のコードを洗練させる~~\n- 再利用するコードとできないコードを分別する\n\n----\n\n## 最初の失敗\n\n-----\n\n# 失敗\n\n- 編集画面を書き換えようとした\n\n![inline](https://i.gyazo.com/6b075f3f4e3f761be20816ec496fe483.png)\n\n-------\n\n# 書き換えようとした理由\n\n- コードが多いので置き換えればごっそり消せる\n- 拡張の要望が多い\n- エディタならKobitoの開発のノウハウを活かせる\n\n----------\n\n# 破綻へ\n\n- まずやっぱり分量が多い\n- つつくと無限に知らない仕様が出てくる\n  - そもそもドメイン知識がなかった\n- 判明する仕様を継ぎ接ぎするとコードが綺麗にならない…\n\n----------\n\n## 進捗\n\n- 「2週間ぐらいで終わらせるわ〜」\n- 書き直したコードが読み易くならず辛い\n- => 2ヶ月たっても終わらず\n\n-------------\n\n## 結果\n\n- 一部のぞいてコードを破棄して中断\n\n------------\n\n## 教訓\n\n- 仕様を理解してないもののコードは書けない\n- モジュールの境界面が明示されてないものは分解できない\n- 「別実装で元の仕様を完全再現」はエネルギーの無駄\n- **見積もりは失敗する**\n\n-------------\n\n## 振り出しに戻る\n\n-----------\n\n## 決意\n\n- エコシステムを整理しよう\n- 現時点での負債と使える部分を認識しよう\n\n-------\n\n## ゴールの設定\n\n- 新規モジュールを負債を引き継ぐことなく受け入れられる環境\n- Turbolinks(PushState)が導入可能な初期化フローを作る(使うかはともかく)\n\n--------\n\n## Rails上のフロントエンドエンジニアの設定\n\n- React.Component提供おじさん\n\n-------\n\n## やらないこと\n\n- CoffeeScriptのコードはそのまま(段階的に破棄する)\n- Backboneのコードはそのまま(段階的に破棄する)\n\n--------------\n\n## 足元を見直す\n\n--------------\n\n## やったこと\n\n1. npmに依存ライブラリを集約\n2. Sprockets => browserify(-rails)\n3. ユニットテストの導入\n4. Reactでコンポーネントを置き換え\n\n----------\n\n## 1. npmに依存ライブラリを集約\n\n----------\n\n## npmに依存ライブラリを集約: 元の状態\n\n- ライブラリごとに異なるCDNを参照\n- オーバーヘッド大\n- どのライブラリのどのバージョンを使ってるか見通しが悪い\n\n----------\n\n## npmに依存ライブラリを集約: 変更後\n\n- npmとbowerでライブラリの依存を解決するようにした\n- 1つのファイルに固めて自前の S3から CloudFront で配信\n- 更にnpmに集約を進めてbowerを削除\n- bowerやめろ\n\n---------\n\n## 2. Sprockets => browserify\n\n------------\n\n## browserify導入\n\n- commonjs形式で書かれたファイルを静的解析して1つのファイルにビルド\n- グローバル変数を使わずにモジュールの解決ができるようになる\n\n----------\n\n2. そして Sprockets を捨てる\n\n----------\n\n## Sprockets\n\n- Rails上のデファクトの標準モジュールシステム\n- 拡張子ごとに変換\n\n-----------\n\n## Sprocketsの問題\n\n- ファイルスコープで返り値を持てない\n- nodeで動かない\n- rubyの問題とjsの問題を切り分けられない\n- JSエコシステムに乗れない\n- Sprocketsで動くJS系のgemメンテされない\n\n-----------\n\n`hoge.js.coffee.erb`\n\n```coffee\n# require_tree ./foo_dir\n# require app\n```\n\n必ず asset_root(app/assets/javascripts) から解決\n\n\n---------\n\n![](https://i.gyazo.com/646bf1555f1af10998b03ddde8d7c66a.png)\n\n----------\n\n## 書き換える\n\n---------\n\n## 書き換えたい…\n\n- 分量が多い\n- 平行して開発している機能がたくさんある\n- ちんたらやってると無限にコンフリクトする\n\n----------\n\n## 解法\n\n- スクリプト書いて一発\n- すべてを相対パスに書き換える\n- browserify - app/assets/javascripts以下のJSを全てcommonjsのrequireに書き換える - Qiita http://qiita.com/mizchi/items/20f529a9d783552d7c7d\n\n--------\n\n- なんかやる\n\n```js\nfunction convertSprocketPathToCommonjsPath(root, fpath, spath) {\n  if (/^\\./.test(spath)) {\n    return spath;\n  }\n  let relToRoot = path.relative(fpath, root);\n  let rel = path.join(relToRoot, spath).replace(/^(\\.\\.\\/)/, \"\");\n  return rel.indexOf(\"..\") > -1 ? rel : \"./\" + rel;\n}\n```\n\nhttps://gist.github.com/mizchi/5ee5cd7d0ca9447b92d5\n\n---------\n\n![fit](https://i.gyazo.com/dd3b74cb97e4d7cb528d894ad2e11f74.png)\n\n\n----------\n\n## 達成\n\n- ファイル単位の依存は明示された\n- グローバル変数依存なのは変わらず\n\n---------\n\n## Qiitaのモジュールシステム(旧)\n\n- Sprocketsでファイル連結\n- グローバル変数渡し\n\n```js\nnew Qiita.views.HomeView();\n```\n---------\n\n旧\n\n```js\n//= require foo\nQiita.util.foo();\nQiita.util.bar = function(){...};\n```\n\n新\n\n```js\nconst foo = require('./foo');\nfoo();\nmodule.exports = function bar() {...}\n```\n\n-----------\n\n## browserify によってもたらされるもの\n\n- 依存がそれぞれのファイルで完結した状態\n- 単体テスト可能な閉じた参照の提供\n- 名前空間の初期化順に左右されなくなる\n\n----------\n\n## この過程でやったこと\n\n-------\n\n## gulp\n\n- 「フロントが更新されたら npm install と gulp を叩いてください~」\n- => しない\n- 「動かないんだけど」\n- => 対応で一日が終わる\n\n-----\n\n## Sprocketsを捨てる準備\n\n- browserify-rails\n- 参考: モダンJavaScript開発環境 on Rails - クックパッド開発者ブログ http://techlife.cookpad.com/entry/2015/12/14/130041\n\n------\n\n## browserify-railsについて\n\n- 中で使ってるのはbrowserify-incremental\n- たまに変なキャッシュ握って更新されない\n  - なんか tmp/cache/browserify-rails 消すと動くぞ!\n- ぶっちゃけwatchifyの方が速い\n  - babelの初期化の差\n- いろんなトレードオフ考えてアリ\n\n-----------\n\n## 結果\n\nすべてを browserifyのtransform で解決した\n\n----------\n\n3. ユニットテストの導入\n\n---------\n\n## 状況\n\n- browserifyによって各モジュールの依存が明示された\n- jasmineが重いのでnodeでユニットテストしたい\n- E2Eテストはまだ考えない\n\n----------\n\n## テスト対象\n\n- 書きなおして分解された再利用性のコード\n- 新規に書かれる react component\n\n重要: Backbone.View/Backbone.Router はテストしない\n\n---------\n\n## テストを書く\n\n- JavaScript - テストがないJS環境にモダンなテスト環境を導入していく - Qiita http://qiita.com/mizchi/items/bdf84f0b1c11c2870290\n- node/mocha/isparta/jsdom\n\n----------\n\n## 書き換え\n\n```diff\n+ module.exports =\nQiita.util.foo = function() {...}\n```\n\n---------\n\n## テスト\n\n```js\nimport foo from \"../../src/util/foo\";\ndescribe(\"util\", () => {\n  describe(\"foo\", () => {\n    it(\"returns foo\", () => {\n      assert.equal(foo(), \"foo\");\n    });\n  });\n});\n```\n\n---------\n\n## 潰す\n\n- `git grep 'Qiita.util.foo'`\n- 全部requireに書き換える\n- Jasmineのコードを全部置き換える\n\n----------\n\n## カバレッジ\n\n----------\n\n## カバレッジ環境を作る\n\n- isparta/mocha\n- ES6 でテスト書ける(SourceMap)\n\n----------\n\n## npm run test-cov\n\n```\n$(npm bin)/babel-node $(npm bin)/isparta cover -x '**/vendor/*' --report text \\\n  node_modules/mocha/bin/_mocha -- --reporter dot -r \\\n  client/spec/spec-helper.js --timeout 10000 --recursive client/spec\n```\n\n長い\n\n----------\n\n## とにかくカバレッジを下げる\n\n- 気合でモックする(jsdom使った)\n- 読み込めるだけ読み込む(カバレッジ30%まで低下)\n- 下がる\n- テストを書く => カバレッジ上がる\n\n------------\n\n## テストの例\n\n```js\n  beforeEach(() => {\n    deleteSrcCache();\n    let jsdom = require('jsdom').jsdom;\n    global.document = jsdom('<html><body></body></html>')\n    global.window = document.defaultView;\n    global.navigator = window.navigator;\n    global.location = window.location;\n  });\n\n  afterEach(() => {\n    delete global.document;\n    delete global.window;\n    delete global.navigator;\n  });\n```\n\n-----------\n\n## 毎回 require cacheは消す\n\n```js\nexport function deleteSrcCache() {\n  Object.keys(require.cache).map(cachePath => {\n    if (/src/.test(cachePath)) {\n      delete require.cache[cachePath];\n    }\n  });\n}\n```\n\nグローバル変数に副作用があると require cacheに引っかかってしまうと再定義されない\n\n-----------\n\n## 3 Reactに書き換え\n\n-----------\n\nQiitaのヘッダ\n\n![fit](https://i.gyazo.com/fe9086d42e429dcf567ca2553328b745.png)\n\n-----------\n\n## React化\n\n- Fluxの選定面倒だったのでベタ書きした\n- react-rails の prerender: false で書き出し\n\n----------\n\n## flumpt\n\n- ヘッダみたいな独立した小さいコンポーネントで小さくFluxできるのが必要\n- https://github.com/mizchi/flumpt 作った\n- 本番でちょっとだけ使ってる\n\n-----------\n\n![fit](http://img3.wikia.nocookie.net/__cb20130219070742/darksouls/images/e/e7/Frampt02.jpg)\n\n----------\n\n## react-rails\n\n- Rails側で `= react_component(\"Header\", {a: 1}, {prerender: false})`\n- `<div data-props='<serialize化されたjson>'..>...</div>`\n- Rails => JSのデータ受け渡しプロトコルがほしかった\n- SSRはサーバーの負荷みて段階的に導入(したい)\n\n-----------\n\n## react-unit\n\n- pzavolinsky/react-unit: Lightweight unit test library for ReactJS https://github.com/pzavolinsky/react-unit\n- reactのテストユーティリティ\n- 使い続けるか迷ってる\n- ReactのshallowRendererのラッパー\n\n-----\n\n## 追記(2016/06/20)\n\n今なら [airbnb/enzyme: JavaScript Testing utilities for React](https://github.com/airbnb/enzyme \"airbnb/enzyme: JavaScript Testing utilities for React\") がおすすめ\n\n----------\n\n## まとめ\n\n--------\n\n\n## まとめ\n\n- 必要なのは「仕様理解」と「勇気」\n- フロントエンドに秩序を取り戻す方法 // Speaker Deck https://speakerdeck.com/fand/hurontoendonizhi-xu-woqu-rili-sufang-fa とか勇気出た\n\n--------------\n\n## これから\n\nQiitaよくするんで待ってて\n\n------\n\n## 参考1\n\n- babel-plugin-typecheck を使って flowtype 文法で書かれたJSをランタイムチェックする - Qiita\n- Node.js - nodeのテストの前後で名前空間が拡張されてないか確認する - Qiita\n- JavaScript - テストがないJS環境にモダンなテスト環境を導入していく - Qiita\n\n---------\n\n## 参考2\n\n- node環境下で .jade ファイルを react-jadeとして読み込む - Qiita\n- browserify - app/assets/javascripts以下のJSを全てcommonjsのrequireに書き換える - Qiita\n- redux への 不満を解消する為に, flumptというFlux実装を作った - Qiita\n\n------\n\n## おわり\n",
    "body": "\n<h2>\n<span id=\"これは何\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%82%8C%E3%81%AF%E4%BD%95\"><i class=\"fa fa-link\"></i></a>これは何</h2>\n\n<p><a href=\"http://jser.connpass.com/event/24202/\" title=\"JSer.info 5周年記念イベント - connpass\" rel=\"nofollow\" target=\"_blank\">JSer.info 5周年記念イベント - connpass</a> (2016/01/16) にて発表した資料。特に理由はないが公開するのを忘れていた。</p>\n\n<p>スライドモードのリリースにあたって公開する</p>\n\n<hr>\n\n<h2>\n<span id=\"近況20160116\" class=\"fragment\"></span><a href=\"#%E8%BF%91%E6%B3%8120160116\"><i class=\"fa fa-link\"></i></a>近況(2016/01/16)</h2>\n\n<ul>\n<li>昨年9月 Kobito for Windows =&gt; Qiita開発チーム</li>\n<li>モダンなJS(当社比)を導入しようとした</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"モダンなjsとはmizchi主観2016版\" class=\"fragment\"></span><a href=\"#%E3%83%A2%E3%83%80%E3%83%B3%E3%81%AAjs%E3%81%A8%E3%81%AFmizchi%E4%B8%BB%E8%A6%B32016%E7%89%88\"><i class=\"fa fa-link\"></i></a>モダンなJSとは(mizchi主観2016版)</h2>\n\n<ol>\n<li>npm/browserifyで依存を解決</li>\n<li>Babel/ES2015</li>\n<li>React/Flux</li>\n<li>Testable</li>\n<li>No More <strong>jQuery plugins</strong>\n</li>\n</ol>\n\n<p>※これらの基準について今回は割愛</p>\n\n<hr>\n\n<h2>\n<span id=\"現実2015\" class=\"fragment\"></span><a href=\"#%E7%8F%BE%E5%AE%9F2015\"><i class=\"fa fa-link\"></i></a>現実(2015)</h2>\n\n<ol>\n<li>CoffeeScript</li>\n<li>Sprockets / グローバル名前空間渡し</li>\n<li>Backbone</li>\n<li>JSのテストはjasmineで数件 (※request specは豊富)</li>\n<li>jQuery plugins や jQuery UI まみれ</li>\n</ol>\n\n<hr>\n\n<h2>\n<span id=\"初見での評価\" class=\"fragment\"></span><a href=\"#%E5%88%9D%E8%A6%8B%E3%81%A7%E3%81%AE%E8%A9%95%E4%BE%A1\"><i class=\"fa fa-link\"></i></a>初見での評価</h2>\n\n<ul>\n<li>Qiitaは2012年から開発されている\n\n<ul>\n<li>当時の選択基準としてはスジ悪ではない</li>\n</ul>\n</li>\n<li>\n<strong>十分にユーザーに価値を提供している</strong>(重要)\n\n<ul>\n<li>価値を生まないコードはメンテする価値が無い</li>\n</ul>\n</li>\n<li>ただちょっとCTO(yuku_t)の手癖がちょっと強いかな…</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"降ってきた仕事\" class=\"fragment\"></span><a href=\"#%E9%99%8D%E3%81%A3%E3%81%A6%E3%81%8D%E3%81%9F%E4%BB%95%E4%BA%8B\"><i class=\"fa fa-link\"></i></a>降ってきた仕事</h2>\n\n<p>=&gt; なんかよくしてくれ</p>\n\n<hr>\n\n<h1>\n<span id=\"なんかよくする\" class=\"fragment\"></span><a href=\"#%E3%81%AA%E3%82%93%E3%81%8B%E3%82%88%E3%81%8F%E3%81%99%E3%82%8B\"><i class=\"fa fa-link\"></i></a>なんかよくする</h1>\n\n<hr>\n\n<h2>\n<span id=\"なんかよく\" class=\"fragment\"></span><a href=\"#%E3%81%AA%E3%82%93%E3%81%8B%E3%82%88%E3%81%8F\"><i class=\"fa fa-link\"></i></a>なんかよく</h2>\n\n<ul>\n<li>モダンな開発環境を導入する</li>\n<li><del>今のコードを洗練させる</del></li>\n<li>再利用するコードとできないコードを分別する</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"最初の失敗\" class=\"fragment\"></span><a href=\"#%E6%9C%80%E5%88%9D%E3%81%AE%E5%A4%B1%E6%95%97\"><i class=\"fa fa-link\"></i></a>最初の失敗</h2>\n\n<hr>\n\n<h1>\n<span id=\"失敗\" class=\"fragment\"></span><a href=\"#%E5%A4%B1%E6%95%97\"><i class=\"fa fa-link\"></i></a>失敗</h1>\n\n<ul>\n<li>編集画面を書き換えようとした</li>\n</ul>\n\n<p><a href=\"https://i.gyazo.com/6b075f3f4e3f761be20816ec496fe483.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/6b075f3f4e3f761be20816ec496fe483.png\" alt=\"inline\"></a></p>\n\n<hr>\n\n<h1>\n<span id=\"書き換えようとした理由\" class=\"fragment\"></span><a href=\"#%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1\"><i class=\"fa fa-link\"></i></a>書き換えようとした理由</h1>\n\n<ul>\n<li>コードが多いので置き換えればごっそり消せる</li>\n<li>拡張の要望が多い</li>\n<li>エディタならKobitoの開発のノウハウを活かせる</li>\n</ul>\n\n<hr>\n\n<h1>\n<span id=\"破綻へ\" class=\"fragment\"></span><a href=\"#%E7%A0%B4%E7%B6%BB%E3%81%B8\"><i class=\"fa fa-link\"></i></a>破綻へ</h1>\n\n<ul>\n<li>まずやっぱり分量が多い</li>\n<li>つつくと無限に知らない仕様が出てくる\n\n<ul>\n<li>そもそもドメイン知識がなかった</li>\n</ul>\n</li>\n<li>判明する仕様を継ぎ接ぎするとコードが綺麗にならない…</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"進捗\" class=\"fragment\"></span><a href=\"#%E9%80%B2%E6%8D%97\"><i class=\"fa fa-link\"></i></a>進捗</h2>\n\n<ul>\n<li>「2週間ぐらいで終わらせるわ〜」</li>\n<li>書き直したコードが読み易くならず辛い</li>\n<li>=&gt; 2ヶ月たっても終わらず</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"結果\" class=\"fragment\"></span><a href=\"#%E7%B5%90%E6%9E%9C\"><i class=\"fa fa-link\"></i></a>結果</h2>\n\n<ul>\n<li>一部のぞいてコードを破棄して中断</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"教訓\" class=\"fragment\"></span><a href=\"#%E6%95%99%E8%A8%93\"><i class=\"fa fa-link\"></i></a>教訓</h2>\n\n<ul>\n<li>仕様を理解してないもののコードは書けない</li>\n<li>モジュールの境界面が明示されてないものは分解できない</li>\n<li>「別実装で元の仕様を完全再現」はエネルギーの無駄</li>\n<li><strong>見積もりは失敗する</strong></li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"振り出しに戻る\" class=\"fragment\"></span><a href=\"#%E6%8C%AF%E3%82%8A%E5%87%BA%E3%81%97%E3%81%AB%E6%88%BB%E3%82%8B\"><i class=\"fa fa-link\"></i></a>振り出しに戻る</h2>\n\n<hr>\n\n<h2>\n<span id=\"決意\" class=\"fragment\"></span><a href=\"#%E6%B1%BA%E6%84%8F\"><i class=\"fa fa-link\"></i></a>決意</h2>\n\n<ul>\n<li>エコシステムを整理しよう</li>\n<li>現時点での負債と使える部分を認識しよう</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"ゴールの設定\" class=\"fragment\"></span><a href=\"#%E3%82%B4%E3%83%BC%E3%83%AB%E3%81%AE%E8%A8%AD%E5%AE%9A\"><i class=\"fa fa-link\"></i></a>ゴールの設定</h2>\n\n<ul>\n<li>新規モジュールを負債を引き継ぐことなく受け入れられる環境</li>\n<li>Turbolinks(PushState)が導入可能な初期化フローを作る(使うかはともかく)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"rails上のフロントエンドエンジニアの設定\" class=\"fragment\"></span><a href=\"#rails%E4%B8%8A%E3%81%AE%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E8%A8%AD%E5%AE%9A\"><i class=\"fa fa-link\"></i></a>Rails上のフロントエンドエンジニアの設定</h2>\n\n<ul>\n<li>React.Component提供おじさん</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"やらないこと\" class=\"fragment\"></span><a href=\"#%E3%82%84%E3%82%89%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8\"><i class=\"fa fa-link\"></i></a>やらないこと</h2>\n\n<ul>\n<li>CoffeeScriptのコードはそのまま(段階的に破棄する)</li>\n<li>Backboneのコードはそのまま(段階的に破棄する)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"足元を見直す\" class=\"fragment\"></span><a href=\"#%E8%B6%B3%E5%85%83%E3%82%92%E8%A6%8B%E7%9B%B4%E3%81%99\"><i class=\"fa fa-link\"></i></a>足元を見直す</h2>\n\n<hr>\n\n<h2>\n<span id=\"やったこと\" class=\"fragment\"></span><a href=\"#%E3%82%84%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8\"><i class=\"fa fa-link\"></i></a>やったこと</h2>\n\n<ol>\n<li>npmに依存ライブラリを集約</li>\n<li>Sprockets =&gt; browserify(-rails)</li>\n<li>ユニットテストの導入</li>\n<li>Reactでコンポーネントを置き換え</li>\n</ol>\n\n<hr>\n\n<h2>\n<span id=\"1-npmに依存ライブラリを集約\" class=\"fragment\"></span><a href=\"#1-npm%E3%81%AB%E4%BE%9D%E5%AD%98%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E9%9B%86%E7%B4%84\"><i class=\"fa fa-link\"></i></a>1. npmに依存ライブラリを集約</h2>\n\n<hr>\n\n<h2>\n<span id=\"npmに依存ライブラリを集約-元の状態\" class=\"fragment\"></span><a href=\"#npm%E3%81%AB%E4%BE%9D%E5%AD%98%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E9%9B%86%E7%B4%84-%E5%85%83%E3%81%AE%E7%8A%B6%E6%85%8B\"><i class=\"fa fa-link\"></i></a>npmに依存ライブラリを集約: 元の状態</h2>\n\n<ul>\n<li>ライブラリごとに異なるCDNを参照</li>\n<li>オーバーヘッド大</li>\n<li>どのライブラリのどのバージョンを使ってるか見通しが悪い</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"npmに依存ライブラリを集約-変更後\" class=\"fragment\"></span><a href=\"#npm%E3%81%AB%E4%BE%9D%E5%AD%98%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E9%9B%86%E7%B4%84-%E5%A4%89%E6%9B%B4%E5%BE%8C\"><i class=\"fa fa-link\"></i></a>npmに依存ライブラリを集約: 変更後</h2>\n\n<ul>\n<li>npmとbowerでライブラリの依存を解決するようにした</li>\n<li>1つのファイルに固めて自前の S3から CloudFront で配信</li>\n<li>更にnpmに集約を進めてbowerを削除</li>\n<li>bowerやめろ</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"2-sprockets--browserify\" class=\"fragment\"></span><a href=\"#2-sprockets--browserify\"><i class=\"fa fa-link\"></i></a>2. Sprockets =&gt; browserify</h2>\n\n<hr>\n\n<h2>\n<span id=\"browserify導入\" class=\"fragment\"></span><a href=\"#browserify%E5%B0%8E%E5%85%A5\"><i class=\"fa fa-link\"></i></a>browserify導入</h2>\n\n<ul>\n<li>commonjs形式で書かれたファイルを静的解析して1つのファイルにビルド</li>\n<li>グローバル変数を使わずにモジュールの解決ができるようになる</li>\n</ul>\n\n<hr>\n\n<ol>\n<li>そして Sprockets を捨てる</li>\n</ol>\n\n<hr>\n\n<h2>\n<span id=\"sprockets\" class=\"fragment\"></span><a href=\"#sprockets\"><i class=\"fa fa-link\"></i></a>Sprockets</h2>\n\n<ul>\n<li>Rails上のデファクトの標準モジュールシステム</li>\n<li>拡張子ごとに変換</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"sprocketsの問題\" class=\"fragment\"></span><a href=\"#sprockets%E3%81%AE%E5%95%8F%E9%A1%8C\"><i class=\"fa fa-link\"></i></a>Sprocketsの問題</h2>\n\n<ul>\n<li>ファイルスコープで返り値を持てない</li>\n<li>nodeで動かない</li>\n<li>rubyの問題とjsの問題を切り分けられない</li>\n<li>JSエコシステムに乗れない</li>\n<li>Sprocketsで動くJS系のgemメンテされない</li>\n</ul>\n\n<hr>\n\n<p><code>hoge.js.coffee.erb</code></p>\n\n<div class=\"code-frame\" data-lang=\"coffee\"><div class=\"highlight\"><pre>\n<span class=\"c1\"># require_tree ./foo_dir</span>\n<span class=\"c1\"># require app</span>\n</pre></div></div>\n\n<p>必ず asset_root(app/assets/javascripts) から解決</p>\n\n<hr>\n\n<p><a href=\"https://i.gyazo.com/646bf1555f1af10998b03ddde8d7c66a.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/646bf1555f1af10998b03ddde8d7c66a.png\" alt=\"\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"書き換える\" class=\"fragment\"></span><a href=\"#%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%8B\"><i class=\"fa fa-link\"></i></a>書き換える</h2>\n\n<hr>\n\n<h2>\n<span id=\"書き換えたい\" class=\"fragment\"></span><a href=\"#%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%9F%E3%81%84\"><i class=\"fa fa-link\"></i></a>書き換えたい…</h2>\n\n<ul>\n<li>分量が多い</li>\n<li>平行して開発している機能がたくさんある</li>\n<li>ちんたらやってると無限にコンフリクトする</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"解法\" class=\"fragment\"></span><a href=\"#%E8%A7%A3%E6%B3%95\"><i class=\"fa fa-link\"></i></a>解法</h2>\n\n<ul>\n<li>スクリプト書いて一発</li>\n<li>すべてを相対パスに書き換える</li>\n<li>browserify - app/assets/javascripts以下のJSを全てcommonjsのrequireに書き換える - Qiita <a href=\"http://qiita.com/mizchi/items/20f529a9d783552d7c7d\" class=\"autolink\" id=\"reference-c8d19203ffd3d9a35ac8\">http://qiita.com/mizchi/items/20f529a9d783552d7c7d</a>\n</li>\n</ul>\n\n<hr>\n\n<ul>\n<li>なんかやる</li>\n</ul>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kd\">function</span> <span class=\"nx\">convertSprocketPathToCommonjsPath</span><span class=\"p\">(</span><span class=\"nx\">root</span><span class=\"p\">,</span> <span class=\"nx\">fpath</span><span class=\"p\">,</span> <span class=\"nx\">spath</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n  <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"sr\">/^\\./</span><span class=\"p\">.</span><span class=\"nx\">test</span><span class=\"p\">(</span><span class=\"nx\">spath</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n    <span class=\"k\">return</span> <span class=\"nx\">spath</span><span class=\"p\">;</span>\n  <span class=\"p\">}</span>\n  <span class=\"kd\">let</span> <span class=\"nx\">relToRoot</span> <span class=\"o\">=</span> <span class=\"nx\">path</span><span class=\"p\">.</span><span class=\"nx\">relative</span><span class=\"p\">(</span><span class=\"nx\">fpath</span><span class=\"p\">,</span> <span class=\"nx\">root</span><span class=\"p\">);</span>\n  <span class=\"kd\">let</span> <span class=\"nx\">rel</span> <span class=\"o\">=</span> <span class=\"nx\">path</span><span class=\"p\">.</span><span class=\"nx\">join</span><span class=\"p\">(</span><span class=\"nx\">relToRoot</span><span class=\"p\">,</span> <span class=\"nx\">spath</span><span class=\"p\">).</span><span class=\"nx\">replace</span><span class=\"p\">(</span><span class=\"sr\">/^(\\.\\.\\/)/</span><span class=\"p\">,</span> <span class=\"s2\">\"\"</span><span class=\"p\">);</span>\n  <span class=\"k\">return</span> <span class=\"nx\">rel</span><span class=\"p\">.</span><span class=\"nx\">indexOf</span><span class=\"p\">(</span><span class=\"s2\">\"..\"</span><span class=\"p\">)</span> <span class=\"o\">&gt;</span> <span class=\"o\">-</span><span class=\"mi\">1</span> <span class=\"o\">?</span> <span class=\"nx\">rel</span> <span class=\"o\">:</span> <span class=\"s2\">\"./\"</span> <span class=\"o\">+</span> <span class=\"nx\">rel</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p><a href=\"https://gist.github.com/mizchi/5ee5cd7d0ca9447b92d5\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://gist.github.com/mizchi/5ee5cd7d0ca9447b92d5</a></p>\n\n<hr>\n\n<p><a href=\"https://i.gyazo.com/dd3b74cb97e4d7cb528d894ad2e11f74.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/dd3b74cb97e4d7cb528d894ad2e11f74.png\" alt=\"fit\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"達成\" class=\"fragment\"></span><a href=\"#%E9%81%94%E6%88%90\"><i class=\"fa fa-link\"></i></a>達成</h2>\n\n<ul>\n<li>ファイル単位の依存は明示された</li>\n<li>グローバル変数依存なのは変わらず</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"qiitaのモジュールシステム旧\" class=\"fragment\"></span><a href=\"#qiita%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%97%A7\"><i class=\"fa fa-link\"></i></a>Qiitaのモジュールシステム(旧)</h2>\n\n<ul>\n<li>Sprocketsでファイル連結</li>\n<li>グローバル変数渡し</li>\n</ul>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"k\">new</span> <span class=\"nx\">Qiita</span><span class=\"p\">.</span><span class=\"nx\">views</span><span class=\"p\">.</span><span class=\"nx\">HomeView</span><span class=\"p\">();</span>\n</pre></div></div>\n\n<hr>\n\n<p>旧</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"c1\">//= require foo</span>\n<span class=\"nx\">Qiita</span><span class=\"p\">.</span><span class=\"nx\">util</span><span class=\"p\">.</span><span class=\"nx\">foo</span><span class=\"p\">();</span>\n<span class=\"nx\">Qiita</span><span class=\"p\">.</span><span class=\"nx\">util</span><span class=\"p\">.</span><span class=\"nx\">bar</span> <span class=\"o\">=</span> <span class=\"kd\">function</span><span class=\"p\">(){...};</span>\n</pre></div></div>\n\n<p>新</p>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">const</span> <span class=\"nx\">foo</span> <span class=\"o\">=</span> <span class=\"nx\">require</span><span class=\"p\">(</span><span class=\"s1\">'./foo'</span><span class=\"p\">);</span>\n<span class=\"nx\">foo</span><span class=\"p\">();</span>\n<span class=\"nx\">module</span><span class=\"p\">.</span><span class=\"nx\">exports</span> <span class=\"o\">=</span> <span class=\"kd\">function</span> <span class=\"nx\">bar</span><span class=\"p\">()</span> <span class=\"p\">{...}</span>\n</pre></div></div>\n\n<hr>\n\n<h2>\n<span id=\"browserify-によってもたらされるもの\" class=\"fragment\"></span><a href=\"#browserify-%E3%81%AB%E3%82%88%E3%81%A3%E3%81%A6%E3%82%82%E3%81%9F%E3%82%89%E3%81%95%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE\"><i class=\"fa fa-link\"></i></a>browserify によってもたらされるもの</h2>\n\n<ul>\n<li>依存がそれぞれのファイルで完結した状態</li>\n<li>単体テスト可能な閉じた参照の提供</li>\n<li>名前空間の初期化順に左右されなくなる</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"この過程でやったこと\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%81%AE%E9%81%8E%E7%A8%8B%E3%81%A7%E3%82%84%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8\"><i class=\"fa fa-link\"></i></a>この過程でやったこと</h2>\n\n<hr>\n\n<h2>\n<span id=\"gulp\" class=\"fragment\"></span><a href=\"#gulp\"><i class=\"fa fa-link\"></i></a>gulp</h2>\n\n<ul>\n<li>「フロントが更新されたら npm install と gulp を叩いてください~」</li>\n<li>=&gt; しない</li>\n<li>「動かないんだけど」</li>\n<li>=&gt; 対応で一日が終わる</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"sprocketsを捨てる準備\" class=\"fragment\"></span><a href=\"#sprockets%E3%82%92%E6%8D%A8%E3%81%A6%E3%82%8B%E6%BA%96%E5%82%99\"><i class=\"fa fa-link\"></i></a>Sprocketsを捨てる準備</h2>\n\n<ul>\n<li>browserify-rails</li>\n<li>参考: モダンJavaScript開発環境 on Rails - クックパッド開発者ブログ <a href=\"http://techlife.cookpad.com/entry/2015/12/14/130041\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">http://techlife.cookpad.com/entry/2015/12/14/130041</a>\n</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"browserify-railsについて\" class=\"fragment\"></span><a href=\"#browserify-rails%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6\"><i class=\"fa fa-link\"></i></a>browserify-railsについて</h2>\n\n<ul>\n<li>中で使ってるのはbrowserify-incremental</li>\n<li>たまに変なキャッシュ握って更新されない\n\n<ul>\n<li>なんか tmp/cache/browserify-rails 消すと動くぞ!</li>\n</ul>\n</li>\n<li>ぶっちゃけwatchifyの方が速い\n\n<ul>\n<li>babelの初期化の差</li>\n</ul>\n</li>\n<li>いろんなトレードオフ考えてアリ</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"結果-1\" class=\"fragment\"></span><a href=\"#%E7%B5%90%E6%9E%9C-1\"><i class=\"fa fa-link\"></i></a>結果</h2>\n\n<p>すべてを browserifyのtransform で解決した</p>\n\n<hr>\n\n<ol>\n<li>ユニットテストの導入</li>\n</ol>\n\n<hr>\n\n<h2>\n<span id=\"状況\" class=\"fragment\"></span><a href=\"#%E7%8A%B6%E6%B3%81\"><i class=\"fa fa-link\"></i></a>状況</h2>\n\n<ul>\n<li>browserifyによって各モジュールの依存が明示された</li>\n<li>jasmineが重いのでnodeでユニットテストしたい</li>\n<li>E2Eテストはまだ考えない</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"テスト対象\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88%E5%AF%BE%E8%B1%A1\"><i class=\"fa fa-link\"></i></a>テスト対象</h2>\n\n<ul>\n<li>書きなおして分解された再利用性のコード</li>\n<li>新規に書かれる react component</li>\n</ul>\n\n<p>重要: Backbone.View/Backbone.Router はテストしない</p>\n\n<hr>\n\n<h2>\n<span id=\"テストを書く\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E6%9B%B8%E3%81%8F\"><i class=\"fa fa-link\"></i></a>テストを書く</h2>\n\n<ul>\n<li>JavaScript - テストがないJS環境にモダンなテスト環境を導入していく - Qiita <a href=\"http://qiita.com/mizchi/items/bdf84f0b1c11c2870290\" class=\"autolink\" id=\"reference-0fc07e0535124cb9bac8\">http://qiita.com/mizchi/items/bdf84f0b1c11c2870290</a>\n</li>\n<li>node/mocha/isparta/jsdom</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"書き換え\" class=\"fragment\"></span><a href=\"#%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88\"><i class=\"fa fa-link\"></i></a>書き換え</h2>\n\n<div class=\"code-frame\" data-lang=\"diff\"><div class=\"highlight\"><pre>\n<span class=\"gi\">+ module.exports =</span>\nQiita.util.foo = function() {...}\n</pre></div></div>\n\n<hr>\n\n<h2>\n<span id=\"テスト\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88\"><i class=\"fa fa-link\"></i></a>テスト</h2>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">import</span> <span class=\"nx\">foo</span> <span class=\"nx\">from</span> <span class=\"s2\">\"../../src/util/foo\"</span><span class=\"p\">;</span>\n<span class=\"nx\">describe</span><span class=\"p\">(</span><span class=\"s2\">\"util\"</span><span class=\"p\">,</span> <span class=\"p\">()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n  <span class=\"nx\">describe</span><span class=\"p\">(</span><span class=\"s2\">\"foo\"</span><span class=\"p\">,</span> <span class=\"p\">()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"nx\">it</span><span class=\"p\">(</span><span class=\"s2\">\"returns foo\"</span><span class=\"p\">,</span> <span class=\"p\">()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n      <span class=\"nx\">assert</span><span class=\"p\">.</span><span class=\"nx\">equal</span><span class=\"p\">(</span><span class=\"nx\">foo</span><span class=\"p\">(),</span> <span class=\"s2\">\"foo\"</span><span class=\"p\">);</span>\n    <span class=\"p\">});</span>\n  <span class=\"p\">});</span>\n<span class=\"p\">});</span>\n</pre></div></div>\n\n<hr>\n\n<h2>\n<span id=\"潰す\" class=\"fragment\"></span><a href=\"#%E6%BD%B0%E3%81%99\"><i class=\"fa fa-link\"></i></a>潰す</h2>\n\n<ul>\n<li><code>git grep 'Qiita.util.foo'</code></li>\n<li>全部requireに書き換える</li>\n<li>Jasmineのコードを全部置き換える</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"カバレッジ\" class=\"fragment\"></span><a href=\"#%E3%82%AB%E3%83%90%E3%83%AC%E3%83%83%E3%82%B8\"><i class=\"fa fa-link\"></i></a>カバレッジ</h2>\n\n<hr>\n\n<h2>\n<span id=\"カバレッジ環境を作る\" class=\"fragment\"></span><a href=\"#%E3%82%AB%E3%83%90%E3%83%AC%E3%83%83%E3%82%B8%E7%92%B0%E5%A2%83%E3%82%92%E4%BD%9C%E3%82%8B\"><i class=\"fa fa-link\"></i></a>カバレッジ環境を作る</h2>\n\n<ul>\n<li>isparta/mocha</li>\n<li>ES6 でテスト書ける(SourceMap)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"npm-run-test-cov\" class=\"fragment\"></span><a href=\"#npm-run-test-cov\"><i class=\"fa fa-link\"></i></a>npm run test-cov</h2>\n\n<div class=\"code-frame\" data-lang=\"text\"><div class=\"highlight\"><pre>\n$(npm bin)/babel-node $(npm bin)/isparta cover -x '**/vendor/*' --report text \\\n  node_modules/mocha/bin/_mocha -- --reporter dot -r \\\n  client/spec/spec-helper.js --timeout 10000 --recursive client/spec\n</pre></div></div>\n\n<p>長い</p>\n\n<hr>\n\n<h2>\n<span id=\"とにかくカバレッジを下げる\" class=\"fragment\"></span><a href=\"#%E3%81%A8%E3%81%AB%E3%81%8B%E3%81%8F%E3%82%AB%E3%83%90%E3%83%AC%E3%83%83%E3%82%B8%E3%82%92%E4%B8%8B%E3%81%92%E3%82%8B\"><i class=\"fa fa-link\"></i></a>とにかくカバレッジを下げる</h2>\n\n<ul>\n<li>気合でモックする(jsdom使った)</li>\n<li>読み込めるだけ読み込む(カバレッジ30%まで低下)</li>\n<li>下がる</li>\n<li>テストを書く =&gt; カバレッジ上がる</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"テストの例\" class=\"fragment\"></span><a href=\"#%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E4%BE%8B\"><i class=\"fa fa-link\"></i></a>テストの例</h2>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n  <span class=\"nx\">beforeEach</span><span class=\"p\">(()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"nx\">deleteSrcCache</span><span class=\"p\">();</span>\n    <span class=\"kd\">let</span> <span class=\"nx\">jsdom</span> <span class=\"o\">=</span> <span class=\"nx\">require</span><span class=\"p\">(</span><span class=\"s1\">'jsdom'</span><span class=\"p\">).</span><span class=\"nx\">jsdom</span><span class=\"p\">;</span>\n    <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nb\">document</span> <span class=\"o\">=</span> <span class=\"nx\">jsdom</span><span class=\"p\">(</span><span class=\"s1\">'&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;'</span><span class=\"p\">)</span>\n    <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nb\">window</span> <span class=\"o\">=</span> <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">defaultView</span><span class=\"p\">;</span>\n    <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nx\">navigator</span> <span class=\"o\">=</span> <span class=\"nb\">window</span><span class=\"p\">.</span><span class=\"nx\">navigator</span><span class=\"p\">;</span>\n    <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nx\">location</span> <span class=\"o\">=</span> <span class=\"nb\">window</span><span class=\"p\">.</span><span class=\"nx\">location</span><span class=\"p\">;</span>\n  <span class=\"p\">});</span>\n\n  <span class=\"nx\">afterEach</span><span class=\"p\">(()</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"k\">delete</span> <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nb\">document</span><span class=\"p\">;</span>\n    <span class=\"k\">delete</span> <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nb\">window</span><span class=\"p\">;</span>\n    <span class=\"k\">delete</span> <span class=\"nx\">global</span><span class=\"p\">.</span><span class=\"nx\">navigator</span><span class=\"p\">;</span>\n  <span class=\"p\">});</span>\n</pre></div></div>\n\n<hr>\n\n<h2>\n<span id=\"毎回-require-cacheは消す\" class=\"fragment\"></span><a href=\"#%E6%AF%8E%E5%9B%9E-require-cache%E3%81%AF%E6%B6%88%E3%81%99\"><i class=\"fa fa-link\"></i></a>毎回 require cacheは消す</h2>\n\n<div class=\"code-frame\" data-lang=\"js\"><div class=\"highlight\"><pre>\n<span class=\"kr\">export</span> <span class=\"kd\">function</span> <span class=\"nx\">deleteSrcCache</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n  <span class=\"nb\">Object</span><span class=\"p\">.</span><span class=\"nx\">keys</span><span class=\"p\">(</span><span class=\"nx\">require</span><span class=\"p\">.</span><span class=\"nx\">cache</span><span class=\"p\">).</span><span class=\"nx\">map</span><span class=\"p\">(</span><span class=\"nx\">cachePath</span> <span class=\"o\">=&gt;</span> <span class=\"p\">{</span>\n    <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"sr\">/src/</span><span class=\"p\">.</span><span class=\"nx\">test</span><span class=\"p\">(</span><span class=\"nx\">cachePath</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n      <span class=\"k\">delete</span> <span class=\"nx\">require</span><span class=\"p\">.</span><span class=\"nx\">cache</span><span class=\"p\">[</span><span class=\"nx\">cachePath</span><span class=\"p\">];</span>\n    <span class=\"p\">}</span>\n  <span class=\"p\">});</span>\n<span class=\"p\">}</span>\n</pre></div></div>\n\n<p>グローバル変数に副作用があると require cacheに引っかかってしまうと再定義されない</p>\n\n<hr>\n\n<h2>\n<span id=\"3-reactに書き換え\" class=\"fragment\"></span><a href=\"#3-react%E3%81%AB%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88\"><i class=\"fa fa-link\"></i></a>3 Reactに書き換え</h2>\n\n<hr>\n\n<p>Qiitaのヘッダ</p>\n\n<p><a href=\"https://i.gyazo.com/fe9086d42e429dcf567ca2553328b745.png\" target=\"_blank\" rel=\"nofollow\"><img src=\"https://i.gyazo.com/fe9086d42e429dcf567ca2553328b745.png\" alt=\"fit\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"react化\" class=\"fragment\"></span><a href=\"#react%E5%8C%96\"><i class=\"fa fa-link\"></i></a>React化</h2>\n\n<ul>\n<li>Fluxの選定面倒だったのでベタ書きした</li>\n<li>react-rails の prerender: false で書き出し</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"flumpt\" class=\"fragment\"></span><a href=\"#flumpt\"><i class=\"fa fa-link\"></i></a>flumpt</h2>\n\n<ul>\n<li>ヘッダみたいな独立した小さいコンポーネントで小さくFluxできるのが必要</li>\n<li>\n<a href=\"https://github.com/mizchi/flumpt\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/mizchi/flumpt</a> 作った</li>\n<li>本番でちょっとだけ使ってる</li>\n</ul>\n\n<hr>\n\n<p><a href=\"http://img3.wikia.nocookie.net/__cb20130219070742/darksouls/images/e/e7/Frampt02.jpg\" target=\"_blank\" rel=\"nofollow\"><img src=\"http://img3.wikia.nocookie.net/__cb20130219070742/darksouls/images/e/e7/Frampt02.jpg\" alt=\"fit\"></a></p>\n\n<hr>\n\n<h2>\n<span id=\"react-rails\" class=\"fragment\"></span><a href=\"#react-rails\"><i class=\"fa fa-link\"></i></a>react-rails</h2>\n\n<ul>\n<li>Rails側で <code>= react_component(\"Header\", {a: 1}, {prerender: false})</code>\n</li>\n<li><code>&lt;div data-props='&lt;serialize化されたjson&gt;'..&gt;...&lt;/div&gt;</code></li>\n<li>Rails =&gt; JSのデータ受け渡しプロトコルがほしかった</li>\n<li>SSRはサーバーの負荷みて段階的に導入(したい)</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"react-unit\" class=\"fragment\"></span><a href=\"#react-unit\"><i class=\"fa fa-link\"></i></a>react-unit</h2>\n\n<ul>\n<li>pzavolinsky/react-unit: Lightweight unit test library for ReactJS <a href=\"https://github.com/pzavolinsky/react-unit\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://github.com/pzavolinsky/react-unit</a>\n</li>\n<li>reactのテストユーティリティ</li>\n<li>使い続けるか迷ってる</li>\n<li>ReactのshallowRendererのラッパー</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"追記20160620\" class=\"fragment\"></span><a href=\"#%E8%BF%BD%E8%A8%9820160620\"><i class=\"fa fa-link\"></i></a>追記(2016/06/20)</h2>\n\n<p>今なら <a href=\"https://github.com/airbnb/enzyme\" title=\"airbnb/enzyme: JavaScript Testing utilities for React\" rel=\"nofollow\" target=\"_blank\">airbnb/enzyme: JavaScript Testing utilities for React</a> がおすすめ</p>\n\n<hr>\n\n<h2>\n<span id=\"まとめ\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<hr>\n\n<h2>\n<span id=\"まとめ-1\" class=\"fragment\"></span><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81-1\"><i class=\"fa fa-link\"></i></a>まとめ</h2>\n\n<ul>\n<li>必要なのは「仕様理解」と「勇気」</li>\n<li>フロントエンドに秩序を取り戻す方法 // Speaker Deck <a href=\"https://speakerdeck.com/fand/hurontoendonizhi-xu-woqu-rili-sufang-fa\" class=\"autolink\" rel=\"nofollow\" target=\"_blank\">https://speakerdeck.com/fand/hurontoendonizhi-xu-woqu-rili-sufang-fa</a> とか勇気出た</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"これから\" class=\"fragment\"></span><a href=\"#%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89\"><i class=\"fa fa-link\"></i></a>これから</h2>\n\n<p>Qiitaよくするんで待ってて</p>\n\n<hr>\n\n<h2>\n<span id=\"参考1\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%831\"><i class=\"fa fa-link\"></i></a>参考1</h2>\n\n<ul>\n<li>babel-plugin-typecheck を使って flowtype 文法で書かれたJSをランタイムチェックする - Qiita</li>\n<li>Node.js - nodeのテストの前後で名前空間が拡張されてないか確認する - Qiita</li>\n<li>JavaScript - テストがないJS環境にモダンなテスト環境を導入していく - Qiita</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"参考2\" class=\"fragment\"></span><a href=\"#%E5%8F%82%E8%80%832\"><i class=\"fa fa-link\"></i></a>参考2</h2>\n\n<ul>\n<li>node環境下で .jade ファイルを react-jadeとして読み込む - Qiita</li>\n<li>browserify - app/assets/javascripts以下のJSを全てcommonjsのrequireに書き換える - Qiita</li>\n<li>redux への 不満を解消する為に, flumptというFlux実装を作った - Qiita</li>\n</ul>\n\n<hr>\n\n<h2>\n<span id=\"おわり\" class=\"fragment\"></span><a href=\"#%E3%81%8A%E3%82%8F%E3%82%8A\"><i class=\"fa fa-link\"></i></a>おわり</h2>\n",
    "stock_users": [
        "dot",
        "htomine",
        "okzk",
        "aki77",
        "kajiken",
        "ysk_1031",
        "hanachin_",
        "sakura_bird1",
        "ponkore",
        "gungle",
        "meganemura",
        "maechabin",
        "shuhei",
        "yagishita",
        "snowin",
        "FiNGAHOLiC",
        "ykominami",
        "zakuni@github",
        "irohiroki",
        "k2zo_o",
        "star__hoshi",
        "takashi",
        "misty_rc",
        "iidukat",
        "nana4gonta",
        "masaha03",
        "laqiiz",
        "takeyuweb",
        "inuscript",
        "kubosho_",
        "snaka",
        "ogomr",
        "ozw_sei",
        "sora0077@github",
        "pasela",
        "ooDEMi",
        "motchang",
        "uehaj",
        "wgkoro@github",
        "snowsunny",
        "angedessin",
        "gaaamii",
        "makotot",
        "cheezenaan",
        "hirokaki",
        "qoo912@github",
        "MasatoYoshioka@github",
        "amagawawaw",
        "groove-murashige",
        "geek_duck",
        "uknmr",
        "fumiyasac@github",
        "yoshioota",
        "armorik83",
        "HaraShun",
        "ecaze",
        "SleepingBear",
        "meganetaaan",
        "hiroponz",
        "a_ishidaaa",
        "knt45",
        "mokoaki",
        "ryz310@github",
        "quoth",
        "hiloki@github",
        "yoshito-maeoka@github",
        "shiranuik@github",
        "wara_be",
        "kimama1997",
        "kentana@github",
        "idahobean",
        "naoto_n",
        "otake84",
        "tq_jappy",
        "sekka",
        "HappyLuckyAkira",
        "h-michael",
        "ic_lifewood",
        "inabe49",
        "ksyundo",
        "goldspring",
        "shimpeiws",
        "mtaniuchi",
        "lw-iharada",
        "tknakatsu",
        "tos-miyake",
        "toshiyuki",
        "Liberty",
        "obanaopon",
        "shshimamo",
        "klaNath",
        "maxmellon",
        "Rittyan",
        "redamoon",
        "wadahiro",
        "mesiobass",
        "akiyoshi83",
        "elbowroomer",
        "diwao",
        "nashcft",
        "11fe",
        "intermezzo-fr",
        "rooooomania",
        "t_daicho",
        "junymn",
        "ukisoft",
        "segasho",
        "11Takanori",
        "milkmeta",
        "ga9ji",
        "shun-k1331",
        "tmizo",
        "hiro93n",
        "nao215",
        "keisukeohta",
        "kikuchy",
        "koichi222",
        "ksoda",
        "00AmeliaMary00",
        "Reyurnible",
        "entesu11",
        "Neos21",
        "minetty99",
        "kobutya7",
        "minewebstaff",
        "dkimura",
        "ox3Bcpz4dO",
        "moaible",
        "b-kaxa",
        "yamanoku",
        "ei-wat-work",
        "j_nakayama",
        "ukyoda",
        "katoken0201",
        "seihmd",
        "tamurayoshiya",
        "nenokido2000",
        "bump_of_kiharu",
        "k-ishimitsu",
        "takuwan0405",
        "ddsystem",
        "e-taka",
        "akiy502",
        "gennei",
        "yamasy1549",
        "KazuyaHara",
        "test2test",
        "iskz",
        "hikariru",
        "syunk38",
        "BathingMiloaHolic5150",
        "testmonstar01",
        "yuyakato",
        "naru0504",
        "tagackt",
        "helloinfoloth",
        "schinen",
        "kawasin73",
        "ShinjiTeramoto",
        "3sho7mi8",
        "mismith",
        "tadauki",
        "nabeliwo",
        "k-takam",
        "lyuich",
        "iKenji",
        "RyutaYoshi",
        "saltharu",
        "SatohJohn",
        "m_nakamura145",
        "potato4d",
        "yh_cake",
        "chuck0523",
        "weathare",
        "Rinaba",
        "usk1107",
        "shy_azusa",
        "sukechansan",
        "kazuki_ste6a",
        "akichim21",
        "mooo_usk",
        "go_nagata",
        "21-Hidetaka-Ko",
        "sndr",
        "Selene-Misso",
        "usk108",
        "yakRx",
        "y4shiro",
        "t15i",
        "ponpoko04",
        "huddle",
        "3naU",
        "myblue",
        "dev-neko",
        "kuroneko",
        "1-AizawaSatoshi",
        "hiromichinomata",
        "rawegg",
        "kkanazaw",
        "svgnankotsu",
        "ma-tu",
        "sylvan-yupa",
        "sec248",
        "nyoro_712",
        "uraben",
        "pinhead",
        "shakkee",
        "tadashiy1012",
        "onionmk2",
        "lcl-minami",
        "knaito-coosy"
    ]
}]