Chanko provides a simple framework for rapidly and safely prototyping new features in your production Rails app, and exposing these prototypes to specified segments of your user base.
With Chanko, you can release many concurrent features and independently manage which users see them. If there are errors with any chanko, it will be automatially removed, without impacting your site.
Please take a look at github.com/cookpad/chanko_sample which is a simple example app using chanko.
Chanko is currently released as a beta.
Ruby 1.8.7, 1.9.2
Rails 3.0.10 ~
Rails 3.2: you might want to use beta branch.
Add your Gemfile.
gem 'chanko', :git => 'git://github.com/cookpad/chanko.git'
Run install.
rails generate chanko:install
Add this code to ApplicationHelper.
include Chanko::Invoker
Generate chanko.
rails generate chanko sample create app/chankos/sample/sample.rb create app/chankos/sample/views/_show.html.haml create app/chankos/sample/stylesheets/tutorial.scss create app/chankos/sample/images/logo.png create app/chankos/sample/specs/models/sample_spec.rb create app/chankos/sample/specs/controllers/sample_controller_spec.rb create app/chankos/sample/specs/helpers/sample_helper_spec.rb
Main file. You write your model and controller logic in this file.
The view files for your chanko. Each chanko refers to its own views directory.
Write styles for your chanko in this file. These files will be merged during startup or first access.
The image files for this chanko. if you manually created this directory, you should create a symbolic link from app/chankos/sample/images to (public or assets)/images/chankos/#{ext_name} with attention to use the relative path. If you used generator, the symbolic link is automaticlly generated.
Tests for this chanko go here.
module Sample include Chanko::Unit # active_if's block is used to decide if the chanko is active or not. # context is the object which invoked the callback. active_if :always_true do |context, options| true end shared(:hello) do |name| "hello #{name}" end scope(:controller) do callback(:controller_show) do # controller code here end end scope(:view) do callback(:view_show) do render :partial => "/show" end end models do expand("ExpandedModel") do #expanded_model.ext(:sample).has_many_associations has_many :has_many_associations has_one :has_one_association 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 new_class_method end end end end helpers do # ext.helper_method def helper_method end end end
“active_if()” decides if the chanko is enabled or not. active_if receives an arg of the invoked context, such as the controller. The chanko is enabled when the block returns true.
# activeif's block is used to decide if the chanko is active or not. # context is the object which invoked the callback. which invoking callback object. active_if do |context, options| true end
Also, “active_if()” accepts pre defined symbols. “active_if()” evaluates the AND result for all symbols and the block.
# This definition means "user is staff and environment is not production" active_if :staff, :not_production do |context, options| # some additional conditions end
You can define additional symbols in lib/active_if_files/main.rb.
Chanko::ActiveIf.define(:not_production?) do |context, options| !Rails.env.production? end 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
When you want to use an OR condition, use “any()”.
# This means "current user is staff or paid user. And environment is not production" active_if any(:staff, :paid), :not_production
“shared()” is the syntax for shared method definitions. you can use the defined method through callbacks of either :controller or :view. A block of a shared method behaves as an instance method.
shared(:hello) do |name| "hello #{name}" end scope(:view) do callback(:hello) do hello("alice") end end
A “callback()” is expanded in the calling code. This block’s context behaves as an invoked context, so you can access the original context’s instance variables. if you need to access local variables, a callback provides a :locals option that is similar to :locals use in render. A callback is scoped, and its scope is a restriction for the callback. The callback is only called from scoped context.
#scope can receive specified context such as "scope('UsersController')". scope(:controller) do callback(:controller_show) do # controller code here end end
“invoke()” in your controller runs a callback block if the active_if block returns true. In this case, if active_if for the sample chanko is true, then controller_show will be invoked in the chanko main.rb
class UsersController def show invoke(:sample, :controller_show) end end
“invoke()” can receive a block as a default fallback. The block is executed if the active_if block returns false or the callback raises an error.
invoke(:sample, :controller_show) do default_behaviour end
“run_default()” method runs default block and return a result as string. it is used by callback block
callback(:hello) do result = run_default "#{result} + hello" end
“invoke()” can receive multiple callbacks. “invoke” tries to run the callbacks in turn. Only the first enabled callback is executed.
#invoke doesn't run next_ext callback if first_ext is enabled. invoke([:first_ext, :show], [:next_ext, :show])
“invoke”() doesn’t run if the specified chanko in the :if is disabled
invoke(:sample, :show, :if => :depend_on_ext)
“expand()” expands existing model methods and adds chanko helper methods. The expanded method is only used by the expanding chanko. All expanded method must be used in your code via ext proxy as below.
user_instance.ext.expanded_method
You can write expanding methods for current models. The “models()” block provides association and named_scope and class method syntax.
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
When used as a symbol as an :include option for ActiveRecord, you must wrap label with “ext.label()” syntax.
User.find(:first, :include => [ext.label(:recipes)])
You can write chanko helpers.
helpers do def helper_method end end
And can use helpers in views and controllers via ext proxy.
callback(:sample) { ext.hello } helpers { def hello; 'hello'; end }
return from inside of invoke block. Use following code.
invoke(:hoge, :aaa) do redirect_to xxx end return if performed?
always activate or deactivate a chanko. Use :always_true/:always_false on active_if.
active_if :always_true # or :always_false
invoke with before_filter. Use block and invoke.
before_filter :only => :index do |controller| controller.invoke(:contest_noseru_ext, :store_contest_id) end
check status of a chanko. you can check the status of an chanko. In almost all situations, context is controller.(deprecated ?)
ext(:sample).active? #=> return boolean