Skip to content

sorah/chanko

 
 

Repository files navigation

Chanko

What is Chanko

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.

Supported versions

Ruby 1.8.7, 1.9.2

Rails 3.0.10 ~

Install

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
include Chanko::Helper

Files

Generate chanko.

rails generate chanko sample
      create  app/units/sample/sample.rb
      create  app/units/sample/views/_show.html.haml
      create  app/units/sample/stylesheets/tutorial.scss
      create  app/units/sample/images/logo.png
      create  app/units/sample/specs/models/sample_spec.rb
      create  app/units/sample/specs/controllers/sample_controller_spec.rb
      create  app/units/sample/specs/helpers/sample_helper_spec.rb

app/units/sample/sample.rb

Main file. You write your model and controller logic in this file.

app/units/sample/views

The view files for your chanko. Each chanko refers to its own views directory.

app/units/sample/stylesheets/tutorial.scss

Write styles for your chanko in this file. These files will be merged during startup or first access.

app/units/sample/images/logo.png

The image files for this chanko. if you manually created this directory, you should create a symbolic link from app/units/sample/images to (public or assets)/images/units/#{unit_name} with attention to use the relative path. If you used generator, the symbolic link is automaticlly generated.

app/units/sample/specs/

Tests for this chanko go here.

Syntax

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 function.
  active_if :always_true do |context, options|
    true
  end

  shared(:hello) do |name|
    "hello #{name}"
  end

  scope(:controller) do
    function(:controller_show) do
      # controller code here
    end
  end

  scope(:view) do
    function(: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

      # should use lambda
      scope :exists, lambda { where(: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

“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 function.
 which invoking function 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 method

“shared()” is the syntax for shared method definitions. you can use the defined method through functions 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
  function(:hello) do
    hello("alice")
  end
end

function

A “function()” 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 function provides a :locals option that is similar to :locals use in render. A function is scoped, and its scope is a restriction for the function. The function is only called from scoped context.

#scope can receive specified context such as "scope('UsersController')".
scope(:controller) do
  function(:controller_show) do
    # controller code here
  end
end

“invoke()” in your controller runs a function 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 function 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 function block

function(:hello) do
  result = run_default
  "#{result} + hello"
end

“invoke()” can receive multiple functions. “invoke” tries to run the functions in turn. Only the first enabled function is executed.

#invoke doesn't run second_unit function if first_unit is enabled.
invoke([:first_unit, :show], [:second_unit, :show])

“invoke”() doesn’t run if the specified chanko in the :if is disabled

invoke(:sample, :show, :if => :depend_on_unit)

expand

“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)])

Helpers

You can write chanko helpers.

helpers do
  def helper_method
  end
end

And can use helpers in views and controllers via ext proxy.

function(:sample) { ext.hello }
helpers { def hello; 'hello'; end }

Tips

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(:your_unit, :before_function)
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

About

Extend rails application

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 100.0%