Gonの使い方 まとめ
- 自分用のメモを公開したものです。ドキュメント/ソースコード等をまとめただけで試していないコードも多いので、信頼性は低いです。
Gonとは
- Railsの変数をJSで使えるようになる
導入
1. インストール
# Gemfile gem 'gon'
$ bundle
2. セットアップ
# application.html.erb <head> ... # application.jsの前に読み込めばロード前にgon変数にアクセスできる # # 以下のようなHTMLを生成してくれる # <script> # //<![CDATA[ # window.gon={};gon.hoge=1; # //]]> # </script> <%= Gon::Base.render_data %> <%= javascript_include_tag "application" %> ... </head>
3. 使ってみる
class ProductsController < ApplicationController def index # Rails側で変数をセット gon.hoge = 1 ... end end
# coffee # JS側で使用できる # 型キャストやエスケープもいい感じにやってくれる gon.hoge // => 1
設定
使い方
Gon::Base.render_data
のオプションとして指定する
Gon::Base.render_data(watch: true)
watch: watch機能を有効化する
- watch機能 を参照
- 実際にはfalseでもwatch機能は使える。明示的に宣言するだけ
- デフォルト: false
camel_case: キャメルケースに変換する
- デフォルト: false
# 設定 Gon::Base.render_data(camel_case: true) # ruby gon.int_cased = 1 # js gon.intCased #=> 1
camel_depth: キャメルケースを適用する深さ
- デフォルト: 1
# 設定 Gon::Base.render_data(:camel_case => true, :camel_depth => 2) # js gon.testHash.testDepthOne.test_depth_two
namespace: gonという名前空間を変える
- デフォルト: 'gon'
# 設定 Gon::Base.render_data(:namespace => 'serverExports') # js serverExports.your_int
init: window.gon = {}
で初期化する
- デフォルト: true
# js # 初期化されてるので、データがない場合でもエラーにならない window.gon // => {}
type: <script>
にtype="text/javascript"
を追加する
- false
Gon::Base.render_data(type: true) #=> <script type="text/javascript">window.gon=...</script>
nonce: <script>
にnonce=...
を追加する
- CSP対応
- デフォルト: nil
Gon::Base.render_data(nonce: 'test') #=> "<script nonce=\"test">...
need_tag: <script>
タグあり
- デフォルト: true
Gon::Base.render_data(need_tag: true) #=> <script> #=> //<![CDATA[ #=> window.gon={};gon.hoge="piyo"; #=> //]]> #=> </script>
Gon::Base.render_data(need_tag: false) #=> window.gon={};gon.hoge="piyo";
cdata: CDATAあり
- デフォルト: true
global_root: globalという名前空間を変える
- デフォルト: 'global'
amd: AMD対応
- デフォルト: false
include_gon_amd
と同じ
<script>
タグのrenderメソッド*3
Gon::Base.render_data
- 基本コレを使えばok
include_gon
- Rails3の場合はこっちらしい
- 内部で
Gon::Base.render_data
を利用してる
<%= include_gon %>
include_gon_amd
- AMDの場合はこっち
- 内部で
Gon::Base.render_data
を利用してる
<%= include_gon_amd %>
メソッド
gon.hoge=: set
gon.hoge = 1
gon.push: set
gon.push(hoge: 1, piyo: 2)
gon.hoge: get
gon.hoge #=> 1
gon.all_variables: setした全ての値
gon.hoge = 1 gon.piyo = 2 gon.all_variables #=> {"hoge"=>1, "piyo"=>2}
gon.clear: setした値を消す
gon.hoge = 1 gon.piyo = 2 gon.all_variables #=> {"hoge"=>1, "piyo"=>2} gon.clear gon.all_variables #=> {}
watch機能
- Ajaxでポーリングして、gonのデータをリアルタイムで取得する
導入
1. watchオプションをtrueにする
- どうも
watch
オプションは明示的にする意味しかなく、指定しなくてもOKっぽい
# app/views/layouts/application.html.erb <%= include_gon(watch: true) %>
2. アプリ作成
- コントローラーで
gon.watch
を利用するのがポイント
# app/controllers/home_controller.rb # ここにAjaxリクエストが送られる。そのたびにusers_countの値がレスポンスして更新される def index @users_count = User.count gon.watch.users_count = @users_count end
# app/views/home/index.html.erb # Ajax成功時に、コールバックでここの表示が変わる <div id='users-counter'></div>
3. JS
gon.watch()
は一定間隔でリクエストを送り、コントローラーでgonにセットした変数をコールバックで利用する
# app/assets/javascripts/home.js.coffee # コールバック # uesrs_count: Ajaxのレスポンス値 renewUsers = (uesrs_count) -> $('#users-counter').text(users_count) # gon.watchの使い方 # gon.watchを使うとポーリングできる # ここでは1秒ごとに`/home`にAjaxリクエストを送って、`#users-counter`の表示を変更している # # gon.watch(name_of_variable, options, callback) # name_of_variable: 変数名(コントローラーで`gon.watch.users_count = @users_count`とすれば、'users_count'となる) # options # interval: Ajaxリクエストする間隔。ms # method: HTTPメソッド。デフォルトはGET # url: 陸ストを送るURL # callback: Ajaxのコールバック gon.watch('users_count', interval: 1000, renewUsers)
止める
gon.unwatch
で止める
# ビュー <a href='#' id='stop-renewing'> Stop renewing </a>
# coffee $('#stop-renewing').click -> # 止める gon.unwatch('users_count', renewUsers) return false
グローバルに使う
Gon.global
を使う
# config/initializers/some_initializer.rb Gon.global.variable = 1
# coffee gon.global.variable // => 1
RSpecでコントローラーテスト
# spec/support/shared_contexts/gon.rb shared_context :gon do # Gonは内部でRequestStore(リクエストグローバルな変数。gem)を利用している # RequestStoreはRackミドルウェアで変数を削除する # しかしテストではRackミドルウェアを経由しない # そのためテストケース毎にGonの変数を手動でクリアする必要がある # 参考: https://tech.misoca.jp/entry/2015/06/15/151419 let(:gon) { RequestStore.store[:gon].gon } before { Gon.clear } end # spec/controllers/thingies_controller_spec.rb RSpec.describe ThingiesController do include_context :gon describe 'GET #new' do it 'gonifies as expected' do get :new, {}, valid_session # Gonに変数がsetされていることをテスト expect(gon['key']).to eq :value end end end
ざっくりコードリーディング
gon.gemspec
- 依存gem
- actionpack: Railsのcontroller/routing
- request_store: リクエストグローバルな変数
- multi_json: 主要なJSON Engineに対応したJSONパーサ
lib/gon.rb
- メインとなるGonクラス
- コントローラーの
gon
の正体はGon
gon.hoge = 1
やgon.hoge
にはmethod_missingを利用している。
lib/gon/base.rb
Gon::Base.render_data
-Gon::Base.render_data
のオプションのデフォルト値
VALID_OPTION_DEFAULTS = { namespace: 'gon', camel_case: false, camel_depth: 1, watch: false, need_tag: true, type: false, cdata: true, global_root: 'global', namespace_check: false, amd: false, nonce: nil }
lib/gon/global.rb
- Gon::GlobalはGonのサブクラス
- Gon::Globalはgon.globalに対応
- Gonとの違いはあんまりないっぽい
lib/gon/watch.rb
- Gon::WatchはGonのサブクラス
- Gon::Watchはgon.watchに対応
coffee/watch.coffee
gon.watch
、gon.unwatch
、gon.unwatchAll
が定義- jQuery依存っぽい
js/watch.js
- coffee/watch.coffeeをコンパイルしたファイル