module Vanity # Helper methods for use in your controllers. # # 1) Use Vanity from within your controller: # # class ApplicationController < ActionController::Base # use_vanity :current_user end # end # # 2) Present different options for an A/B test: # # Get started for only $<%= ab_test :pricing %> a month! # # 3) Measure conversion: # # def signup # ab_goal! :pricing # . . . # end module Rails module ClassMethods # Defines the vanity_identity method, and the set_identity_context before # filter. # # Single argument names a method that returns an object whose identity is # the vanity identity. Identity is used to present an experiment # consistently to the same person or people. It can be the user's # identity, group, project. The object must provide its identity in # response to the method +id+. # # For example, if +current_user+ returns a +User+ object, then to use the # user's id: # class ApplicationController < ActionController::Base # use_vanity :current_user # end # # If that method returns nil (e.g. the user has not signed in), a random # value will be used, instead. That random value is maintained using a # cookie. # # Alternatively, if you call use_vanity with a block, it will yield to the # block with controller. # # If there is no identity you can use, call use_vanity with the value +nil+. # # For example: # class ApplicationController < ActionController::Base # use_vanity :current_user # end # # class ProjectController < ApplicationController # use_vanity { |controller| controller.project_id } # end def use_vanity(symbol = nil, &block) define_method :vanity_identity do return @vanity_identity if @vanity_identity if block @vanity_identity = block.call(self) elsif symbol && object = send(symbol) @vanity_identity = object.id elsif response # everyday use @vanity_identity = cookies["vanity_id"] || OpenSSL::Random.random_bytes(16).unpack("H*")[0] cookies["vanity_id"] = { value: @vanity_identity, expires: 1.month.from_now } @vanity_identity else # during functional testing @vanity_identity = "test" end end define_method :set_vanity_context do Vanity.context = self end before_filter :set_vanity_context end end def self.included(base) #:nodoc: base.extend ClassMethods end # This method returns one of the alternative values in the named A/B test. # # Examples using ab_test inside controller: # def index # if ab_test(:new_page) # true/false test # render action: "new_page" # else # render action: "index" # end # end # # def index # render action: ab_test(:new_page) # alternatives are page names # end # # Examples using ab_test inside view: # <%= if ab_test(:banner) %>100% less complexity!<% end %> # # <%= ab_test(:greeting) %> <%= current_user.name %> # # <% ab_test :features do |count| %> # <%= count %> features to choose from! # <% end %> def ab_test(name, &block) value = Vanity.playground.experiment(name).choose if block content = capture(value, &block) block_called_from_erb?(block) ? concat(content) : content else value end end # This method records conversion on the named A/B test. For example: # def create # ab_goal! :call_to_action # Acccount.create! params[:account] # end def ab_goal!(name) Vanity.playground.experiment(name).conversion! end end end