trailguide 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,7 +38,7 @@ module TrailGuide
38
38
  return false unless variant && adapter.key?(variant.storage_key)
39
39
 
40
40
  chosen_at = Time.at(adapter[variant.storage_key].to_i)
41
- chosen_at >= experiment.started_at
41
+ return variant if chosen_at >= experiment.started_at
42
42
  end
43
43
 
44
44
  def converted?(experiment, checkpoint=nil)
@@ -68,6 +68,10 @@ module TrailGuide
68
68
  end
69
69
  end
70
70
 
71
+ def variant(experiment)
72
+ participating?(experiment) || nil
73
+ end
74
+
71
75
  def participating!(variant)
72
76
  adapter[variant.experiment.storage_key] = variant.name
73
77
  adapter[variant.storage_key] = Time.now.to_i
@@ -81,7 +85,7 @@ module TrailGuide
81
85
  adapter.delete(variant.experiment.storage_key)
82
86
  adapter.delete(variant.storage_key)
83
87
  adapter.delete(storage_key)
84
- experiment.funnels.each do |funnel|
88
+ variant.experiment.funnels.each do |funnel|
85
89
  funnel_key = "#{variant.experiment.storage_key}:#{funnel.to_s}"
86
90
  adapter.delete(funnel_key)
87
91
  end
@@ -90,11 +94,22 @@ module TrailGuide
90
94
  end
91
95
  end
92
96
 
97
+ def exit!(experiment)
98
+ chosen = variant(experiment)
99
+ return true if chosen.nil?
100
+ adapter.delete(experiment.storage_key)
101
+ adapter.delete(chosen.storage_key)
102
+ experiment.goals.each do |goal|
103
+ adapter.delete("#{experiment.storage_key}:#{goal.to_s}")
104
+ end
105
+ return true
106
+ end
107
+
93
108
  def active_experiments(include_control=true)
94
109
  return false if adapter.keys.empty?
95
110
  adapter.keys.map { |key| key.to_s.split(":").first.to_sym }.uniq.map do |key|
96
111
  experiment = TrailGuide.catalog.find(key)
97
- next unless experiment && experiment.running? && participating?(experiment, include_control)
112
+ next unless experiment && !experiment.combined? && experiment.running? && participating?(experiment, include_control)
98
113
  [ experiment.experiment_name, adapter[experiment.storage_key] ]
99
114
  end.compact.to_h
100
115
  end
@@ -105,7 +120,7 @@ module TrailGuide
105
120
  adapter.keys.any? do |key|
106
121
  experiment_name = key.to_s.split(":").first.to_sym
107
122
  experiment = TrailGuide.catalog.find(experiment_name)
108
- experiment && experiment.running? && participating?(experiment, include_control)
123
+ experiment && !experiment.combined? && experiment.running? && participating?(experiment, include_control)
109
124
  end
110
125
  end
111
126
  end
@@ -2,7 +2,7 @@ module TrailGuide
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- PATCH = 13
5
+ PATCH = 14
6
6
  VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
 
8
8
  class << self
data/lib/trailguide.rb CHANGED
@@ -6,6 +6,7 @@ require "trail_guide/algorithms"
6
6
  require "trail_guide/participant"
7
7
  require "trail_guide/variant"
8
8
  require "trail_guide/experiment"
9
+ require "trail_guide/combined_experiment"
9
10
  require "trail_guide/catalog"
10
11
  require "trail_guide/helper"
11
12
  require "trail_guide/engine"
@@ -24,6 +25,7 @@ module TrailGuide
24
25
  config.algorithm = :weighted
25
26
  config.adapter = :multi
26
27
  config.allow_multiple_experiments = true # false / :control
28
+ config.track_winner_conversions = false
27
29
  config.allow_multiple_conversions = false
28
30
  config.allow_multiple_goals = false
29
31
 
@@ -34,9 +36,12 @@ module TrailGuide
34
36
  config.on_experiment_start = nil # -> (experiment) { ... }
35
37
  config.on_experiment_stop = nil # -> (experiment) { ... }
36
38
  config.on_experiment_resume = nil # -> (experiment) { ... }
39
+ config.on_experiment_winner = nil # -> (experiment, winner) { ... }
37
40
  config.on_experiment_reset = nil # -> (experiment) { ... }
38
41
  config.on_experiment_delete = nil # -> (experiment) { ... }
39
42
 
43
+ config.return_experiment_winner = nil # -> (experiment, winner) { ... return variant }
44
+
40
45
  config.filtered_user_agents = []
41
46
  config.filtered_ip_addresses = []
42
47
  config.request_filter = -> (context) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailguide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rebec
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-06 00:00:00.000000000 Z
11
+ date: 2019-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -111,6 +111,7 @@ files:
111
111
  - app/controllers/trail_guide/admin/experiments_controller.rb
112
112
  - app/controllers/trail_guide/experiments_controller.rb
113
113
  - app/views/layouts/trail_guide/admin/application.html.erb
114
+ - app/views/trail_guide/admin/experiments/_combined_experiment.html.erb
114
115
  - app/views/trail_guide/admin/experiments/_experiment.html.erb
115
116
  - app/views/trail_guide/admin/experiments/index.html.erb
116
117
  - config/routes.rb
@@ -130,10 +131,13 @@ files:
130
131
  - lib/trail_guide/algorithms/random.rb
131
132
  - lib/trail_guide/algorithms/weighted.rb
132
133
  - lib/trail_guide/catalog.rb
134
+ - lib/trail_guide/combined_experiment.rb
133
135
  - lib/trail_guide/engine.rb
134
136
  - lib/trail_guide/errors.rb
135
137
  - lib/trail_guide/experiment.rb
136
- - lib/trail_guide/experiment_config.rb
138
+ - lib/trail_guide/experiments/base.rb
139
+ - lib/trail_guide/experiments/combined_config.rb
140
+ - lib/trail_guide/experiments/config.rb
137
141
  - lib/trail_guide/helper.rb
138
142
  - lib/trail_guide/participant.rb
139
143
  - lib/trail_guide/unity.rb
@@ -1,137 +0,0 @@
1
- module TrailGuide
2
- class ExperimentConfig < Canfig::Config
3
- ENGINE_CONFIG_KEYS = [
4
- :start_manually, :reset_manually, :store_override, :track_override,
5
- :algorithm, :allow_multiple_conversions, :allow_multiple_goals
6
- ].freeze
7
-
8
- def self.engine_config
9
- ENGINE_CONFIG_KEYS.map do |key|
10
- [key, TrailGuide.configuration.send(key.to_sym)]
11
- end.to_h
12
- end
13
-
14
- def self.default_config
15
- { name: nil, metric: nil, variants: [], goals: [] }
16
- end
17
-
18
- def self.callbacks_config
19
- {
20
- callbacks: {
21
- on_choose: [TrailGuide.configuration.on_experiment_choose].flatten.compact,
22
- on_use: [TrailGuide.configuration.on_experiment_use].flatten.compact,
23
- on_convert: [TrailGuide.configuration.on_experiment_convert].flatten.compact,
24
- on_start: [TrailGuide.configuration.on_experiment_start].flatten.compact,
25
- on_stop: [TrailGuide.configuration.on_experiment_stop].flatten.compact,
26
- on_resume: [TrailGuide.configuration.on_experiment_resume].flatten.compact,
27
- on_reset: [TrailGuide.configuration.on_experiment_reset].flatten.compact,
28
- on_delete: [TrailGuide.configuration.on_experiment_delete].flatten.compact,
29
- }
30
- }
31
- end
32
-
33
- attr_reader :experiment
34
-
35
- def initialize(experiment, *args, **opts, &block)
36
- @experiment = experiment
37
- opts = opts.merge(self.class.engine_config)
38
- opts = opts.merge(self.class.default_config)
39
- opts = opts.merge(self.class.callbacks_config)
40
- super(*args, **opts, &block)
41
- end
42
-
43
- def resettable?
44
- !reset_manually
45
- end
46
-
47
- def allow_multiple_conversions?
48
- allow_multiple_conversions
49
- end
50
-
51
- def allow_multiple_goals?
52
- allow_multiple_goals
53
- end
54
-
55
- def name
56
- @name ||= (self[:name] || experiment.name).try(:to_s).try(:underscore).try(:to_sym)
57
- end
58
-
59
- def metric
60
- @metric ||= (self[:metric] || name).try(:to_s).try(:underscore).try(:to_sym)
61
- end
62
-
63
- def algorithm
64
- @algorithm ||= TrailGuide::Algorithms.algorithm(self[:algorithm])
65
- end
66
-
67
- def variant(varname, metadata: {}, weight: 1, control: false)
68
- raise ArgumentError, "The variant `#{varname}` already exists in the experiment `#{name}`" if variants.any? { |var| var == varname }
69
- control = true if variants.empty?
70
- variants.each(&:variant!) if control
71
- variant = Variant.new(experiment, varname, metadata: metadata, weight: weight, control: control)
72
- variants << variant
73
- variant
74
- end
75
-
76
- def control
77
- return variants.find { |var| var.control? } || variants.first
78
- end
79
-
80
- def control=(name)
81
- variants.each(&:variant!)
82
- var_idx = variants.index { |var| var == name }
83
-
84
- if var_idx.nil?
85
- variant = Variant.new(experiment, name, control: true)
86
- else
87
- variant = variants.slice!(var_idx, 1)[0]
88
- variant.control!
89
- end
90
-
91
- variants.unshift(variant)
92
- variant
93
- end
94
-
95
- def goal(name)
96
- goals << name.to_s.underscore.to_sym
97
- end
98
- alias_method :funnel, :goal
99
-
100
- def goals
101
- self[:goals]
102
- end
103
- alias_method :funnels, :goals
104
-
105
- def on_choose(meth=nil, &block)
106
- callbacks[:on_choose] << (meth || block)
107
- end
108
-
109
- def on_use(meth=nil, &block)
110
- callbacks[:on_use] << (meth || block)
111
- end
112
-
113
- def on_convert(meth=nil, &block)
114
- callbacks[:on_convert] << (meth || block)
115
- end
116
-
117
- def on_start(meth=nil, &block)
118
- callbacks[:on_start] << (meth || block)
119
- end
120
-
121
- def on_stop(meth=nil, &block)
122
- callbacks[:on_stop] << (meth || block)
123
- end
124
-
125
- def on_resume(meth=nil, &block)
126
- callbacks[:on_resume] << (meth || block)
127
- end
128
-
129
- def on_reset(meth=nil, &block)
130
- callbacks[:on_reset] << (meth || block)
131
- end
132
-
133
- def on_delete(meth=nil, &block)
134
- callbacks[:on_delete] << (meth || block)
135
- end
136
- end
137
- end