Class: Vanity::Playground
Overview
Playground catalogs all your experiments, holds the Vanity configuration.
Constant Summary collapse
- DEFAULTS =
{ :collecting => true, :load_path=>"experiments" }
- DEFAULT_ADD_PARTICIPANT_PATH =
'/vanity/add_participant'
Instance Attribute Summary collapse
-
#add_participant_path ⇒ Object
Path to the add_participant action, necessary if you have called use_js!.
-
#db ⇒ Object
Deprecated.
-
#host ⇒ Object
Deprecated.
-
#load_path ⇒ Object
Path to load experiment files from.
-
#logger ⇒ Object
Logger.
-
#namespace ⇒ Object
Deprecated.
-
#password ⇒ Object
Deprecated.
-
#port ⇒ Object
Deprecated.
Instance Method Summary collapse
-
#collecting=(enabled) ⇒ Object
Turns data collection on and off.
-
#collecting? ⇒ Boolean
True if collection data (metrics and experiments).
- #config_file_exists?(basename = "vanity.yml") ⇒ Boolean
- #config_file_root ⇒ Object
-
#connected? ⇒ Boolean
Returns true if connection is open.
-
#connection ⇒ Object
Returns the current connection.
-
#define(name, type, options = {}, &block) ⇒ Object
Defines a new experiment.
-
#disconnect! ⇒ Object
Closes the current connection.
-
#establish_connection(spec = nil) ⇒ Object
This is the preferred way to programmatically create a new connection (or switch to a new connection).
-
#experiment(name) ⇒ Object
Returns the experiment.
-
#experiments ⇒ Object
Returns hash of experiments (key is experiment id).
-
#initialize(*args) ⇒ Playground
constructor
Created new Playground.
-
#load! ⇒ Object
Loads all metrics and experiments.
- #load_config_file(basename = "vanity.yml") ⇒ Object
-
#metric(id) ⇒ Object
Returns a metric (raises NameError if no metric with that identifier).
-
#metrics ⇒ Object
Returns hash of metrics (key is metric id).
-
#reconnect! ⇒ Object
Closes the current connection and establishes a new one.
- #redis ⇒ Object
-
#redis=(spec_or_connection) ⇒ Object
Deprecated.
-
#reload! ⇒ Object
Reloads all metrics and experiments.
-
#test! ⇒ Object
Deprecated.
-
#track!(id, count = 1) ⇒ Object
Tracks an action associated with a metric.
-
#use_js! ⇒ Object
Call to indicate that participants should be added via js This helps keep robots from participating in the ab test and skewing results.
- #using_js? ⇒ Boolean
Constructor Details
#initialize(*args) ⇒ Playground
Created new Playground. Unless you need to, use the global Vanity.playground.
First argument is connection specification (see #redis=), last argument is a set of options, both are optional. Supported options are:
-
connection – Connection specification
-
namespace – Namespace to use
-
load_path – Path to load experiments/metrics from
-
logger – Logger to use
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/vanity/playground.rb', line 24 def initialize(*args) = Hash === args.last ? args.pop : {} # In the case of Rails, use the Rails logger and collect only for # production environment by default. defaults = [:rails] ? DEFAULTS.merge(:collecting => ::Rails.env.production?, :logger => ::Rails.logger) : DEFAULTS if config_file_exists? env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development" config = load_config_file[env] if Hash === config config = config.inject({}) { |h,kv| h[kv.first.to_sym] = kv.last ; h } else config = { :connection=>config } end else config = {} end @options = defaults.merge(config).merge() if @options[:host] == 'redis' && @options.values_at(:host, :port, :db).any? warn "Deprecated: please specify Redis connection as URL (\"redis://host:port/db\")" establish_connection :adapter=>"redis", :host=>@options[:host], :port=>@options[:port], :database=>@options[:db] || @options[:database] elsif @options[:redis] @adapter = RedisAdapter.new(:redis=>@options[:redis]) else connection_spec = args.shift || @options[:connection] if connection_spec connection_spec = "redis://" + connection_spec unless connection_spec[/^\w+:/] establish_connection connection_spec end end warn "Deprecated: namespace option no longer supported directly" if @options[:namespace] @load_path = @options[:load_path] || DEFAULTS[:load_path] unless @logger = @options[:logger] @logger = Logger.new(STDOUT) @logger.level = Logger::ERROR end @loading = [] @use_js = false self.add_participant_path = DEFAULT_ADD_PARTICIPANT_PATH @collecting = !!@options[:collecting] end |
Instance Attribute Details
#add_participant_path ⇒ Object
Path to the add_participant action, necessary if you have called use_js!
77 78 79 |
# File 'lib/vanity/playground.rb', line 77 def add_participant_path @add_participant_path end |
#db ⇒ Object
Deprecated. Use redis.server instead.
68 69 70 |
# File 'lib/vanity/playground.rb', line 68 def db @db end |
#host ⇒ Object
Deprecated. Use redis.server instead.
68 69 70 |
# File 'lib/vanity/playground.rb', line 68 def host @host end |
#load_path ⇒ Object
Path to load experiment files from.
71 72 73 |
# File 'lib/vanity/playground.rb', line 71 def load_path @load_path end |
#namespace ⇒ Object
Deprecated. Use redis.server instead.
68 69 70 |
# File 'lib/vanity/playground.rb', line 68 def namespace @namespace end |
#password ⇒ Object
Deprecated. Use redis.server instead.
68 69 70 |
# File 'lib/vanity/playground.rb', line 68 def password @password end |
#port ⇒ Object
Deprecated. Use redis.server instead.
68 69 70 |
# File 'lib/vanity/playground.rb', line 68 def port @port end |
Instance Method Details
#collecting=(enabled) ⇒ Object
Turns data collection on and off.
183 184 185 |
# File 'lib/vanity/playground.rb', line 183 def collecting=(enabled) @collecting = !!enabled end |
#collecting? ⇒ Boolean
True if collection data (metrics and experiments). You only want to collect data in production environment, everywhere else run with collection off.
176 177 178 |
# File 'lib/vanity/playground.rb', line 176 def collecting? @collecting end |
#config_file_exists?(basename = "vanity.yml") ⇒ Boolean
282 283 284 |
# File 'lib/vanity/playground.rb', line 282 def config_file_exists?(basename = "vanity.yml") File.exists?(config_file_root + basename) end |
#config_file_root ⇒ Object
278 279 280 |
# File 'lib/vanity/playground.rb', line 278 def config_file_root (defined?(::Rails) ? ::Rails.root : Pathname.new(".")) + "config" end |
#connected? ⇒ Boolean
Returns true if connection is open.
300 301 302 |
# File 'lib/vanity/playground.rb', line 300 def connected? @adapter && @adapter.active? end |
#connection ⇒ Object
Returns the current connection. Establishes new connection is necessary.
293 294 295 |
# File 'lib/vanity/playground.rb', line 293 def connection @adapter || establish_connection end |
#define(name, type, options = {}, &block) ⇒ Object
Defines a new experiment. Generally, do not call this directly, use one of the definition methods (ab_test, measure, etc).
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/vanity/playground.rb', line 83 def define(name, type, = {}, &block) warn "Deprecated: if you need this functionality let's make a better API" id = name.to_s.downcase.gsub(/\W/, "_").to_sym raise "Experiment #{id} already defined once" if experiments[id] klass = Experiment.const_get(type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }) experiment = klass.new(self, id, name, ) experiment.instance_eval &block experiment.save experiments[id] = experiment end |
#disconnect! ⇒ Object
Closes the current connection.
307 308 309 |
# File 'lib/vanity/playground.rb', line 307 def disconnect! @adapter.disconnect! if @adapter end |
#establish_connection(spec = nil) ⇒ Object
This is the preferred way to programmatically create a new connection (or switch to a new connection). If no connection was established, the playground will create a new one by calling this method with no arguments.
With no argument, uses the connection specified in config/vanity.yml file for the current environment (RACK_ENV, RAILS_ENV or development). If there is no config/vanity.yml file, picks the configuration from config/redis.yml, or defaults to Redis on localhost, port 6379.
If the argument is a symbol, uses the connection specified in config/vanity.yml for that environment. For example:
Vanity.playground.establish_connection :production
If the argument is a string, it is processed as a URL. For example:
Vanity.playground.establish_connection "redis://redis.local/5"
Otherwise, the argument is a hash and specifies the adapter name and any additional options understood by that adapter (as with config/vanity.yml). For example:
Vanity.playground.establish_connection :adapter=>:redis,
:host=>"redis.local"
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/vanity/playground.rb', line 246 def establish_connection(spec = nil) @spec = spec disconnect! if @adapter case spec when nil if config_file_exists? env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development" spec = load_config_file[env] fail "No configuration for #{env}" unless spec establish_connection spec elsif config_file_exists?("redis.yml") env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development" redis = load_config_file("redis.yml")[env] fail "No configuration for #{env}" unless redis establish_connection "redis://" + redis else establish_connection :adapter=>"redis" end when Symbol spec = load_config_file[spec.to_s] establish_connection spec when String uri = URI.parse(spec) params = CGI.parse(uri.query) if uri.query establish_connection :adapter=>uri.scheme, :username=>uri.user, :password=>uri.password, :host=>uri.host, :port=>uri.port, :path=>uri.path, :params=>params else spec = spec.inject({}) { |hash,(k,v)| hash[k.to_sym] = v ; hash } @adapter = Adapters.establish_connection(spec) end end |
#experiment(name) ⇒ Object
Returns the experiment. You may not have guessed, but this method raises an exception if it cannot load the experiment’s definition.
99 100 101 102 103 |
# File 'lib/vanity/playground.rb', line 99 def experiment(name) id = name.to_s.downcase.gsub(/\W/, "_").to_sym warn "Deprecated: pleae call experiment method with experiment identifier (a Ruby symbol)" unless id == name experiments[id.to_sym] or raise NoExperimentError, "No experiment #{id}" end |
#experiments ⇒ Object
Returns hash of experiments (key is experiment id).
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/vanity/playground.rb', line 136 def experiments unless @experiments @experiments = {} @logger.info "Vanity: loading experiments from #{load_path}" Dir[File.join(load_path, "*.rb")].each do |file| experiment = Experiment::Base.load(self, @loading, file) experiment.save end end @experiments end |
#load! ⇒ Object
Loads all metrics and experiments. Rails calls this during initialization.
158 159 160 161 |
# File 'lib/vanity/playground.rb', line 158 def load! experiments metrics end |
#load_config_file(basename = "vanity.yml") ⇒ Object
286 287 288 |
# File 'lib/vanity/playground.rb', line 286 def load_config_file(basename = "vanity.yml") YAML.load(ERB.new(File.read(config_file_root + basename)).result) end |
#metric(id) ⇒ Object
Returns a metric (raises NameError if no metric with that identifier).
167 168 169 |
# File 'lib/vanity/playground.rb', line 167 def metric(id) metrics[id.to_sym] or raise NameError, "No metric #{id}" end |
#metrics ⇒ Object
Returns hash of metrics (key is metric id).
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/vanity/playground.rb', line 191 def metrics unless @metrics @metrics = {} @logger.info "Vanity: loading metrics from #{load_path}/metrics" Dir[File.join(load_path, "metrics/*.rb")].each do |file| Metric.load self, @loading, file end if config_file_exists? && remote = load_config_file["metrics"] remote.each do |id, url| fail "Metric #{id} already defined in playground" if metrics[id.to_sym] metric = Metric.new(self, id) metric.remote url metrics[id.to_sym] = metric end end end @metrics end |
#reconnect! ⇒ Object
Closes the current connection and establishes a new one.
314 315 316 |
# File 'lib/vanity/playground.rb', line 314 def reconnect! establish_connection(@spec) end |
#redis ⇒ Object
341 342 343 344 |
# File 'lib/vanity/playground.rb', line 341 def redis warn "Deprecated: use connection method instead" connection end |
#redis=(spec_or_connection) ⇒ Object
Deprecated. Use establish_connection or configuration file instead.
327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/vanity/playground.rb', line 327 def redis=(spec_or_connection) warn "Deprecated: use establish_connection method instead" case spec_or_connection when String establish_connection "redis://" + spec_or_connection when ::Redis @connection = Adapters::RedisAdapter.new(spec_or_connection) when :mock establish_connection :adapter=>:mock else raise "I don't know what to do with #{spec_or_connection.inspect}" end end |
#reload! ⇒ Object
Reloads all metrics and experiments. Rails calls this for each request in development mode.
150 151 152 153 154 |
# File 'lib/vanity/playground.rb', line 150 def reload! @experiments = nil @metrics = nil load! end |
#test! ⇒ Object
Deprecated. Use Vanity.playground.collecting = true/false instead. Under Rails, collecting is true in production environment, false in all other environments, which is exactly what you want.
321 322 323 324 |
# File 'lib/vanity/playground.rb', line 321 def test! warn "Deprecated: use collecting = false instead" self.collecting = false end |
#track!(id, count = 1) ⇒ Object
Tracks an action associated with a metric.
216 217 218 |
# File 'lib/vanity/playground.rb', line 216 def track!(id, count = 1) metric(id).track! count end |
#use_js! ⇒ Object
Call to indicate that participants should be added via js This helps keep robots from participating in the ab test and skewing results.
If you use this, there are two more steps:
-
Set Vanity.playground.add_participant_path = ‘/path/to/vanity/action’, this should point to the add_participant path that is added with Vanity::Rails::Dashboard, make sure that this action is available to all users
-
Add <%= vanity_js %> to any page that needs uses an ab_test. vanity_js needs to be included after your call to ab_test so that it knows which version of the experiment the participant is a member of. The helper will render nothing if the there are no ab_tests running on the current page, so adding vanity_js to the bottom of your layouts is a good option. Keep in mind that if you call use_js! and don’t include vanity_js in your view no participants will be recorded.
124 125 126 |
# File 'lib/vanity/playground.rb', line 124 def use_js! @use_js = true end |
#using_js? ⇒ Boolean
128 129 130 |
# File 'lib/vanity/playground.rb', line 128 def using_js? @use_js end |