- 今日の目標
- Chankoを使って特定のユーザには違う機能を出す
- 今日の流れ
- Chankoとは
- Chanko概要
- Chankoの導入
- その他
- Cookpadが本番で使っているRailsの拡張機能
- 特徴
- 安全:障害が発生した場合も影響を最小限に抑える
- 高品質:メインのコードと区別することで、低品質と高品質のコードを使い分ける
- 速い開発:プロトタイプで作成中のコードを機能単位で実装
- ざっくり言うと
- 例:新しい検索ボタンを付けたい場合
- active_ifで「誰に対して有効にするか?」を定義
- contextにはcontollerのコンテキストが入っている
- 有効になった時のロジック(たとえばviewの場合)
- scopeでviewやcontroller単位でfunctionを作れる
- 元々のsearchボタンが以下のようにrenderされていた場合には
- invokeをそこに置くことでchankoが呼ばれる
- raiseしたり、無効な状態ではdo以下が実行される
- invokeの第一引数がmoduleで第二引数がfunction
- これで無効な状態(一般ユーザ)と有効な状態(staff)とで表示が変わる
- ChankoのREADMEが詳しい
- まずはrailsアプリを始める
% rails new chanko_app
- Gemfileにchankoを追加
# vim Gemfile
gem 'chanko', :git => 'git://github.com/cookpad/chanko.git'
gem 'devise' # 特定ユーザに対しての処理を試したいのでdevise
- bundle installでgemをインストール
% bundle install
- アプリにchankoをインストール
% rails generate chanko:install
initializer chanko_initializer.rb
run ln -ns ../app/units test/units from "."
create lib/chanko/active_if
create lib/chanko/active_if/active_if.rb
* active_if.rbが作られる。中身はactive_ifのためのdefine
# cat lib/chanko/active_if/active_if.rb
Chanko::ActiveIf.define(:always_true) do |context, options|
true
end
Chanko::ActiveIf.define(:always_false) do |context, options|
false
end
Chanko::ActiveIf.define(:not_production) do |context, options|
!Rails.env.production?
end
* これ以外にも発動させたい条件を定義したい場合には``lib/chanko/active_if/main.rb``に追加すればいい
# 例えばstaffだけ有効にしたい機能を付けたい場合には:staffを定義
Chanko::ActiveIf.define(:staff) do |context, options|
user = options[:user] || context.instance_variable_get('@login_user')
next false unless user
next false unless user.staff?
next true
end
- chankoを利用できるように
app/helpers/application_helper.rb
を以下のように追加
module ApplicationHelper
include Chanko::Invoker
include Chanko::Helper
end
-
ここまでの作業で
rails generate chanko hoge
で「hoge」という機能をchankoを通して使えるようになる -
monmonという機能(Chanko Unit)を作ってみる
% rails generate chanko monmon
create app/units/monmon/monmon.rb # このファイルの中にModel、Controller、Helper、Viewのロジックを書く
create app/units/monmon/views/_show.html.haml
create app/units/monmon/specs/controllers/monmon_controller_spec.rb
create app/units/monmon/specs/models/monmon_model_spec.rb
create app/units/monmon/specs/helpers/monmon_helper_spec.rb
create app/units/monmon/stylesheets/monmon.scss
create app/assets/stylesheets/units/monmon # unitsへのシンボリックリンク
create app/units/monmon/javascripts/monmon.js
create app/assets/javascripts/units/monmon # unitsへのシンボリックリンク
create app/units/monmon/images/logo.png
create app/assets/images/units/monmon # unitsへのシンボリックリンク
* active_ifがtrueならchanko発動
active_if do |context, options|
true
end
* 複数の定義が全てtrueなら発動(AND)
active_if :staff, :not_production do |context, options|
# some additional conditions
end
* 複数の定義のどれかがtrueなら発動(OR)
active_if any(:staff, :not_production)
- invokeを呼び出した所でchankoが発動する
# Monmonモジュールのmessage functionが呼び出される
invoke(:monmon, :message)
* 第3引数にhashを渡すことでlocal変数をfunctionに渡すことも可能
invoke(:sample, :show, :if => :depend_on_unit)
* unitを複数作っている場合に配列にして渡すと両方発動できる
invoke([:first_unit, :show], [:second_unit, :show])
* ブロックを渡せばraiseが起きた時はchankoが発動しないときのdefault挙動になる(ここに元の機能を置けばいい)
invoke(:sample, :controller_show) do
default_behaviour
end
- モデルの拡張もできる(joinやmethodの追加など)
models do
expand("ExpandedModel") do
#expanded_model.ext.has_many_associations
has_many :has_many_associations
has_one :has_one_association
has_many :through_associations, :through => label(:has_many_associations)
named_scope :exists, :conditions => {:deleted_at => nil}
# expanded_model_instance.ext.new_method
def new_method
end
#expanded_mode.ext.new_method
class_methods do
def cmethod
end
end
end
end
* 詳しくは[ChankoのREADME](https://github.com/cookpad/chanko)
- COOKPAD資料
- 読み進める