Add default included associations feature

sozuuuuu/jsonapi-resources at default_included · GitHub

active_model_serializersからjsonapi_resourcesに乗り換えた

背景

趣味プロジェクトでモデルのシリアライズactive_model_serializers を使っていたが、なんだかissueとか見てると開発に暗雲が立ち込めているぞ???最近rails_apiのデフォルトかなんかになったかで盛り上がっていたはずなのにどうした。 ブレイキンなチェンジも多いし、JSON APIの仕様を満たせていない部分があってember-dataのうまみが生かせないので変えることにした。JSON APIの公式ページからImplementationsを探すと jsonapi_resources というスター数の多いイケてそうなgemを見つけたので使ってみることにした。

所感

READMEが懇切丁寧に書いていて間違いなくいいgemだなーと思った。すごくカスタマイザブルに作ってあるし、さくさくAPIが作れる。最高。 クエリパラメタによるfilterとかsort機能がデフォルトで付いていて素晴らしい。

ember-dataと組み合わせれば3分でSPAが作れちゃいそう。

簡単な処理の流れ

JSONAPI::Resourceクラスメソッドrecords , apply_filter, find もしくは find_by_key という順番でリソースが絞り込まれていく。最終的に見つかったリソースをJSON API準拠のフォーマットにする。

self.records

検索されるデータのおおもとでデフォルトでは Class.all になる。 以下の用に current_user がもつものだけ出すとかここで制約かけちゃうことができる。

class PostResource < JSONAPI::Resource
  def self.records(...)
    current_user.posts
  end
end

self.apply_filter

Resourceクラスは filters メソッドを提供していて、ここで宣言すると完全一致でフィルタリングできるようになる。

my-resources?filter[my-property]=hoge

完全一致以外がいいとき、たとえば date が範囲内にあるかいう場合は self,apply_filter をオーバーライドする。

class PostResouce < JSONAPI::Resources
  filter :date

  self apply_filter(filter, value ...)
     case filter
     when date
       records.date(value)
     else
       super
     end
  end
end

self.find, self.find_by_key

前者はindex、後者はshowに使われる。オーバーライドでさらに細かな調整ができるのが一点注意がある。

superで得られるデータの型がArrayなのでscopeとか使えない。

ページネーター

jsonapi_resouces はデフォルトでページネーションをサポートします。ページネーターというものをリソースに適用して利用します。実装済みページネーターには pagedoffset の二種類があります。

paged

いわゆる普通のページネーションの形。10ページ区切りのうち、何ページ目。

パラメータはnumberとsize。 numberは指定されなければ1ページ目が出力される。 sizeが指定されなければdefault_page_sizeが使われる。

params => page[number], page[size]

offset

いくつ目のデータから、何個のデータを表示するか指定するページネータ。

○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○
        |---------------->|
      offset         limit(how many)

5番目のデータから10個のデータを取得

params => page[offset], page[limit]

docker触ってみた

[ぽえむ]動機

良いエンジニアになるには、継続的な勉強が必要だ。分かっている。ただ、何していいのか分からない。なんとなくVagrant + Chefでの環境構築が古い気がしたのでDockerを触ってみることにした。

チュートリアルした

ひとまずチュートリアルをやってみた。Docker Hubにあるコンテナを起動したり、そのコンテナを編集したり、編集したコンテナをDocker Hubに上げたり、またそのコンテナをDocker Hubからpullしたり。たりたり。

DockerとVirtual Machineの違い

そして次にDockerとVMの違いを調べた。VMが完全な仮想環境(つまりは、違うカーネル)で大きくリソースを使う。Dockerは仮想環境であるが同じカーネルで動くので軽い。コンテナなら1000個立ち上げることだってできるらしい、起動も早い。

適当なる感想

サーバ構築するときはよく分からなくなったらクリーンインストールする、というのを繰り返していたので、すごく助かる気がしました(小並感)。

active_model_serializersの処理の流れメモ

ancestryとactive_model_serializersの連携について調べていたらactive_model_serializersを読み込むはめになったのでメモしておきます。

基本的な動作

どうやってシリアライズ(=> JSON)しているか

  1. ActiveModel::Serializerにシリアライズ対象(ActiveModelのオブジェクトもしくはその配列)とシリアライズオプションを渡してserializerオブジェクトを生成
  2. ActiveModel::Serializer::Adapterに1で作成したオブジェクトとオプション(どのアダプタを使うか。デフォルトはJSON)を渡してadapterオブジェクトを生成
  3. ActiveModel::Serializer::Adapter#as_jsonで1のシリアライザが適用される

respond_withからの流れを含めたシリアライズの流れ

(active_model_serializersをインストールするとActionControllerにactive_model_serializersのActionController::Serializationがincludeされ、respondersのメソッド(respond_withなど)をラップします)

  1. respond_with object
  2. ラップしたrespond_withで「シリアライズの流れ」1, 2を実施
  3. respondersのrespond_withに2で作成したadapterを渡す(adapterはrespond_withで使用するJSON変換メソッドを提供するのでシリアライズできる)

ちょっとした発見

  1. ActiveModel::Serializer.attributesに入れたものはserializerが適用できない
  2. ActiveModel::Serializer.attributeはkeyを変更するだけのもの(attribute :hoge, key:mogeとすると、"moge": { //hoge }の形でJSONが出てくる

ツリー構造をもつモデルの場合ツリー構造のJSONを吐きたい

1.の原則の通りツリー構造でデータを吐き出すときはちょっとした工夫が必要です。そのうちQiitaに書きます

QUnitでメソッドのテストをいろいろな条件でやりたいとき

あるメソッドがオブジェクトの状態により返す値が変わる時。 今までこうやってました。そして、すごくめんどくさいと感じていました。

test('myMethod: when x is true, y is false', function(assert) {
  let target = this.subject({
    x: true,
    y: false
  });
  assert.equal(target.myMethod(), 'hoge', 'returns "hoge"');
});

test('myMethod: when x is true, y is true', function(assert) {
  let target = this.subject({
    x: true,
    y: true
  });
  assert.equal(target.myMethod(), 'huga', 'returns "huga"');
});

こんな方法を思いつきました。テスト条件とテスト対象がクローズアップされて大変分かりやすいと思います。そしてBDD使えばいいと思います。

let myMethodTest = function(condition, tests) {
  test(`myMethod when x:${condition.x}, y:${condition.y}`, function(assert) {
    let target = this.subject({
      x: condition.x,
      y: condition.y
    });
    tests(assert, target.myMethod);
  });
};


myMethodTest({
  x: true,
  y: false
}, function(assert, subject) {
  assert.equal(subject(), 'hoge');
});

myMethodTest({
  x: true,
  y: true
}, function(assert, subject) {
  assert.equal(subject(), 'huga');
});

VimからAtomに移行したよ

下記の設定で快適Atom生活が実現したので。

needs:

  • vim-mode
  • move-panes
".editor.command-mode":
  "s H": "move-panes:move-left"
  "s L": "move-panes:move-right"
  "s J": "move-panes:move-down"
  "s h": "vim-mode:focus-pane-view-on-left"
  "s l": "vim-mode:focus-pane-view-on-right"

"body":
  "cmd-k h": "pane:split-left"
  "cmd-k l": "pane:split-right"
  "cmd-k j": "pane:split-down"
  "cmd-k k": "pane:split-up"

"atom-text-editor.vim-mode.command-mode":
  "s": null
  "shift-S": null

解説

背景

Vimを使うときは、paneを大量に出して作業していた。Atomでもそれをやりたいと思った。 vim-modeを入れることによりctrl-w hjklでpane間を移動できるようになったが、s + hjklでできたらいいなと思った。

できること

  • s + (Vimキーバインド)方向キーでpane間を移動
  • s + キャピタル方向キーでpaneを移動
  • cmd-k + 方向キーでpaneを分割(cmd + pやtreeviewでも使える)

Atomへの乗り換えを試みたがterm2が入らなくて心が折れた

より快適な環境を求めてAtomを使おうと思った。いま使っているエディタはvim

vimはターミナルから使えるので同じ使用感が欲しく、手始めにterm2を入れることにした。そしたら入らなかった。

下記と同じ症状だったので書いてある通りにやってみたが駄目だった。xcode-select --install するとcommand line tools already installed と言われる。uninstallを試みたがさっぱりやり方が分からない。お手上げ。

GitHub - AtomにTerm2を入れた時に若干ハマった話 - Qiita