Class: Vanity::Playground

Inherits:
Object show all
Defined in:
lib/vanity/playground.rb

Overview

Playground catalogs all your experiments, holds the Vanity configuration.

Examples:

Vanity.playground.logger = my_logger
puts Vanity.playground.map(&:name)

Constant Summary collapse

DEFAULTS =
{ :collecting => true, :load_path=>"experiments" }

Instance Attribute Summary collapse

Instance Method Summary collapse

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



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/vanity/playground.rb', line 23

def initialize(*args)
  options = args.pop if Hash === args.last
  @options = DEFAULTS.merge(options || {})
  if @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]
  elsif @options[:redis]
    @adapter = RedisAdapter.new(:redis=>@options[:redis])
  else
    connection_spec = args.shift || @options[:connection]
    establish_connection "redis://" + connection_spec if connection_spec
  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 = []
  @collecting = @options[:collecting]
end

Instance Attribute Details

#dbObject

Deprecated. Use redis.server instead.



47
48
49
# File 'lib/vanity/playground.rb', line 47

def db
  @db
end

#hostObject

Deprecated. Use redis.server instead.



47
48
49
# File 'lib/vanity/playground.rb', line 47

def host
  @host
end

#load_pathObject

Path to load experiment files from.



50
51
52
# File 'lib/vanity/playground.rb', line 50

def load_path
  @load_path
end

#loggerObject

Logger.



53
54
55
# File 'lib/vanity/playground.rb', line 53

def logger
  @logger
end

#namespaceObject

Deprecated. Use redis.server instead.



47
48
49
# File 'lib/vanity/playground.rb', line 47

def namespace
  @namespace
end

#passwordObject

Deprecated. Use redis.server instead.



47
48
49
# File 'lib/vanity/playground.rb', line 47

def password
  @password
end

#portObject

Deprecated. Use redis.server instead.



47
48
49
# File 'lib/vanity/playground.rb', line 47

def port
  @port
end

Instance Method Details

#collecting=(enabled) ⇒ Object

Turns data collection on and off.

Since:

  • 1.4.0



129
130
131
# File 'lib/vanity/playground.rb', line 129

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.

Returns:

  • (Boolean)

Since:

  • 1.4.0



122
123
124
# File 'lib/vanity/playground.rb', line 122

def collecting?
  @collecting
end

#connected?Boolean

Returns true if connection is open.

Returns:

  • (Boolean)

Since:

  • 1.4.0



238
239
240
# File 'lib/vanity/playground.rb', line 238

def connected?
  @adapter && @adapter.active?
end

#connectionObject

Returns the current connection. Establishes new connection is necessary.

Since:

  • 1.4.0



231
232
233
# File 'lib/vanity/playground.rb', line 231

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).

See Also:



59
60
61
62
63
64
65
66
67
68
# File 'lib/vanity/playground.rb', line 59

def define(name, type, options = {}, &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, options)
  experiment.instance_eval &block
  experiment.save
  experiments[id] = experiment
end

#disconnect!Object

Closes the current connection.

Since:

  • 1.4.0



245
246
247
# File 'lib/vanity/playground.rb', line 245

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"

Since:

  • 1.4.0



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/vanity/playground.rb', line 192

def establish_connection(spec = nil)
  disconnect! if @adapter
  case spec
  when nil
    if File.exists?("config/vanity.yml")
      env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
      spec = YAML.load(ERB.new(File.read("config/vanity.yml")).result)[env]
      fail "No configuration for #{env}" unless spec
      establish_connection spec
    elsif File.exists?("config/redis.yml")
      env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
      redis = YAML.load(ERB.new(File.read("config/redis.yml")).result)[env]
      fail "No configuration for #{env}" unless redis
      establish_connection "redis://" + redis
    else
      establish_connection :adapter=>"redis"
    end
  when Symbol
    spec = YAML.load(ERB.new(File.read("config/vanity.yml")).result)[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 }
    begin
      require "vanity/adapters/#{spec[:adapter]}_adapter"
    rescue LoadError
      raise "Could not find #{spec[:adapter]} in your load path"
    end
    @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.

See Also:



74
75
76
77
78
# File 'lib/vanity/playground.rb', line 74

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 NameError, "No experiment #{id}"
end

#experimentsObject

Returns hash of experiments (key is experiment id).

See Also:



83
84
85
86
87
88
89
90
91
92
# File 'lib/vanity/playground.rb', line 83

def experiments
  unless @experiments
    @experiments = {}
    @logger.info "Vanity: loading experiments from #{load_path}"
    Dir[File.join(load_path, "*.rb")].each do |file|
      Experiment::Base.load self, @loading, file
    end
  end
  @experiments
end

#load!Object

Loads all metrics and experiments. Rails calls this during initialization.



104
105
106
107
# File 'lib/vanity/playground.rb', line 104

def load!
  experiments
  metrics
end

#metric(id) ⇒ Object

Returns a metric (raises NameError if no metric with that identifier).

See Also:

Since:

  • 1.1.0



113
114
115
# File 'lib/vanity/playground.rb', line 113

def metric(id)
  metrics[id.to_sym] or raise NameError, "No metric #{id}"
end

#metricsObject

Returns hash of metrics (key is metric id).

See Also:

Since:

  • 1.1.0



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/vanity/playground.rb', line 137

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 File.exist?("config/vanity.yml") && remote = YAML.load(ERB.new(File.read("config/vanity.yml")).result)["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.

Since:

  • 1.3.0



252
253
254
# File 'lib/vanity/playground.rb', line 252

def reconnect!
  establish_connection
end

#redisObject



279
280
281
282
# File 'lib/vanity/playground.rb', line 279

def redis
  warn "Deprecated: use connection method instead"
  connection
end

#redis=(spec_or_connection) ⇒ Object

Deprecated. Use establish_connection or configuration file instead.



265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/vanity/playground.rb', line 265

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.



96
97
98
99
100
# File 'lib/vanity/playground.rb', line 96

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.



259
260
261
262
# File 'lib/vanity/playground.rb', line 259

def test!
  warn "Deprecated: use collecting = false instead"
  self.collecting = false
end

#track!(id, count = 1) ⇒ Object

Tracks an action associated with a metric.

Examples:

Vanity.playground.track! :uploaded_video

Since:

  • 1.1.0



162
163
164
# File 'lib/vanity/playground.rb', line 162

def track!(id, count = 1)
  metric(id).track! count
end