a_b_split 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 68847be6d549eec7d1c38762adf5e8f364b9c6cd
4
+ data.tar.gz: 6f2a635a1b583d16628cc869f1fd5821de88dfb6
5
+ SHA512:
6
+ metadata.gz: d696a619d46b093f519db62503fd7ba00db839f13aa4940d78d1bb0cfb51d927494d642aa991a15e24cb8e0104d225c863f2dc003ca8c490375e95307d5d56da
7
+ data.tar.gz: 7dff2394c950bdf30ee68999214c7d2a3c5db7073de1ae6c384504362568b3f20dfaed63821d0a7877021e928592263715e52b695f7ffd9277b2b658884353d8
@@ -0,0 +1,9 @@
1
+ module ABSplit
2
+ class Configuration
3
+ attr_accessor :experiments
4
+
5
+ def initialize
6
+ @experiments = {}
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ require 'bigdecimal'
2
+
3
+ module ABSplit
4
+ module Functions
5
+ class Sigmoid
6
+ def self.value_for(x, *params)
7
+ BigDecimal.new("#{1.0/(1 + Math.exp(-2 * x))}")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ module ABSplit
2
+ module Functions
3
+ class WeightedSplit
4
+ MAX_POSITIONS = (9999999999999999999 * 2) + 1 #capacity of Fixnum
5
+
6
+ def self.value_for(x, *params)
7
+ given_weights = validate(params)
8
+
9
+ experiments = split_weights(params, params.size, given_weights)
10
+
11
+ select_experiment_for(x, experiments)
12
+ end
13
+
14
+ private
15
+
16
+ def self.validate(experiments)
17
+ given_weights = experiments.each_with_object([]) do |param, memo|
18
+ memo << param['weight'] if param.has_key?('weight')
19
+ end
20
+
21
+ unless experiments.any? && experiments.size > 1 && given_weights.reduce(0, &:+) <= 100
22
+ raise ABSplit::NoValidExperiment
23
+ end
24
+
25
+ given_weights
26
+ end
27
+
28
+ def self.markers(experiments)
29
+ experiments.map do |experiment|
30
+ (MAX_POSITIONS * (experiment['weight'] / 100)) - ( MAX_POSITIONS / 2)
31
+ end
32
+ end
33
+
34
+ def self.select_experiment_for(x, experiments)
35
+ x_position = x.hash
36
+
37
+ markers(experiments).each_with_index do |limit, i|
38
+ if x_position <= limit
39
+ return experiments[i]['name']
40
+ end
41
+ end
42
+
43
+ experiments.last['name']
44
+ end
45
+
46
+ def self.split_weights(experiments, parts, given_percentage)
47
+ return experiments if given_percentage.size >= parts
48
+
49
+ missing_weights = parts - given_percentage.size
50
+ missing_percentage = 100 - given_percentage.reduce(0, &:+)
51
+
52
+ experiments.map do |experiment|
53
+ if experiment['weight']
54
+ experiment['weight'] = experiment['weight'].to_f
55
+ else
56
+ experiment['weight'] = missing_percentage.to_f / missing_weights.to_f
57
+ end
58
+
59
+ experiment
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), *%w(functions *))].each do |function|
2
+ require function
3
+ end
@@ -0,0 +1,11 @@
1
+ module ABSplit
2
+ module Test
3
+ def self.split(name, x)
4
+ experiment = ABSplit.configuration.experiments[name]
5
+
6
+ raise ABSplit::NoValidExperiment unless experiment
7
+
8
+ ABSplit::Functions::WeightedSplit.value_for(x,*experiment)
9
+ end
10
+ end
11
+ end
data/lib/a_b_split.rb ADDED
@@ -0,0 +1,22 @@
1
+ $:.unshift File.expand_path('..', __FILE__)
2
+
3
+ require 'a_b_split/functions'
4
+ require 'a_b_split/configuration'
5
+ require 'a_b_split/test'
6
+ require 'yaml'
7
+
8
+ module ABSplit
9
+ class NoValidExperiment < RuntimeError; end
10
+ end
11
+
12
+ module ABSplit
13
+ extend self
14
+
15
+ attr_accessor :configuration
16
+
17
+ def configure
18
+ self.configuration ||= Configuration.new
19
+
20
+ yield(configuration) if block_given?
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: a_b_split
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Enrique Figuerola
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.3'
55
+ description: A/B split testing gem
56
+ email: hard_rock15@msn.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/a_b_split.rb
62
+ - lib/a_b_split/configuration.rb
63
+ - lib/a_b_split/functions.rb
64
+ - lib/a_b_split/functions/sigmoid.rb
65
+ - lib/a_b_split/functions/weighted_split.rb
66
+ - lib/a_b_split/test.rb
67
+ homepage: https://github.com/emfigo/absplit
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.2.2
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: ABSplit
91
+ test_files: []