ablab 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 12f71c1bce10f9c02b35a34690b8c5a3f9116591
4
- data.tar.gz: ac5b44830c77f57d30efa75bcd95b0c33648e5f1
3
+ metadata.gz: aa8125e2a869a953786f4b1bcacab2ac1752d00c
4
+ data.tar.gz: c661d695bcad5f817801b42aa1e4ffc99920d346
5
5
  SHA512:
6
- metadata.gz: 149c7facb01e5c55f04890ed49afb0281d49068bf319e603b35df286098829d5d9f77c06b81e087a53b7578c1ca219f5f8828ee647b082315b5865b602d74aa9
7
- data.tar.gz: 3f6446406904c78461b83d40a4b343cac2a13ae4a03b84f84e425cf3c1d9e4bf6ee87601e7d52c5993f1e919bcab673b528e6765755b704c167c8bd1c79fc246
6
+ metadata.gz: 7f459d5d133435d8c828d0626c0846575c3eb49d941fb21bb430cb1c91853162f5ff82fe89c2c49df0e2ace5eafcc1eba65adcd05865e6e84fcb875de792848d
7
+ data.tar.gz: 2a9e3c91a04cec5ee3691604b806dce7b53c4cf3871ee1dea791033326f16f0be0f13475ac507f4ece116e858adedf0537fbbb645baaaf671e33a8204587ca88
@@ -76,7 +76,7 @@ module Ablab
76
76
 
77
77
  def initialize(name, &block)
78
78
  @name = name.to_sym
79
- @control = Group.new(:control, 'control group')
79
+ @control = Group.control
80
80
  @groups = [@control]
81
81
  @callbacks = []
82
82
  instance_exec(&block)
@@ -98,7 +98,7 @@ module Ablab
98
98
  end
99
99
 
100
100
  def group(name, options = {})
101
- group = Group.new(name, options[:description])
101
+ group = Group.new(name, options[:description], options[:weight])
102
102
  @groups << group
103
103
  end
104
104
 
@@ -137,17 +137,23 @@ module Ablab
137
137
 
138
138
  def group
139
139
  return @group unless @group.nil?
140
- forced = forced_group
141
- return forced if forced
142
- size = 1000.0 * (experiment.percentage_of_visitors) / 100.0
143
- idx = (draw * experiment.groups.size / size).floor
144
- @group = experiment.groups[idx].try(:name)
140
+ if forced = forced_group
141
+ return forced
142
+ end
143
+ d = draw / experiment.percentage_of_visitors
144
+ return nil if d >= 1
145
+ tot_weight = experiment.groups.map(&:weight).reduce(:+).to_f
146
+ @group = experiment.groups.reduce(0) do |t, group|
147
+ t += group.weight / tot_weight
148
+ break group if d < t
149
+ t
150
+ end
145
151
  end
146
152
 
147
153
  def draw
148
154
  sid_hash = Digest::SHA1.hexdigest(session_id)[-8..-1].to_i(16)
149
155
  exp_hash = Digest::SHA1.hexdigest(experiment.name.to_s)[-8..-1].to_i(16)
150
- (sid_hash ^ exp_hash) % 1000
156
+ ((sid_hash ^ exp_hash) % 1000) / 10.0
151
157
  end
152
158
 
153
159
  def perform_callbacks!(event)
@@ -165,7 +171,7 @@ module Ablab
165
171
  return nil unless request && request.respond_to?(:params)
166
172
  groups = parse_groups(request.params[:ablab_group])
167
173
  group = groups[experiment.name.to_s]
168
- group.to_sym if group && experiment.groups.map { |g| g.name.to_s }.include?(group)
174
+ experiment.groups.find { |g| g == group }
169
175
  end
170
176
 
171
177
  private def parse_groups(str)
@@ -195,9 +201,39 @@ module Ablab
195
201
  end
196
202
 
197
203
  class Group
198
- attr_reader :name, :description
199
- def initialize(name, description = nil)
204
+ attr_reader :name, :description, :weight
205
+ alias_method :eql?, :==
206
+ def initialize(name, description = nil, weight = nil)
200
207
  @name, @description = name.to_sym, description
208
+ @weight = weight || 1
209
+ end
210
+
211
+ def control?
212
+ name == :control
213
+ end
214
+
215
+ def experimental?
216
+ !control?
217
+ end
218
+
219
+ def ==(o)
220
+ if o.is_a?(Symbol)
221
+ name == o
222
+ elsif o.is_a?(String)
223
+ name.to_s == o
224
+ elsif o.is_a?(self.class)
225
+ name == o.name && description == o.description
226
+ else
227
+ false
228
+ end
229
+ end
230
+
231
+ def hash
232
+ name.hash
233
+ end
234
+
235
+ def self.control
236
+ new(:control, 'control group')
201
237
  end
202
238
  end
203
239
 
@@ -1,3 +1,3 @@
1
1
  module Ablab
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -186,10 +186,26 @@ describe Ablab do
186
186
  allow(run).to receive(:draw).and_return 0
187
187
  expect(run).to be_in_group(:control)
188
188
  run = Ablab::Run.new(experiment, 'abc', request)
189
- allow(run).to receive(:draw).and_return 334
189
+ allow(run).to receive(:draw).and_return 33.4
190
190
  expect(run).to be_in_group(:a)
191
191
  run = Ablab::Run.new(experiment, 'abc', request)
192
- allow(run).to receive(:draw).and_return 999
192
+ allow(run).to receive(:draw).and_return 99.9
193
+ expect(run).to be_in_group(:b)
194
+ end
195
+
196
+ it 'respects group weights' do
197
+ experiment = Ablab::Experiment.new(:foo) do
198
+ group :a
199
+ group :b, weight: 2
200
+ end
201
+ run = Ablab::Run.new(experiment, 'abc', request)
202
+ allow(run).to receive(:draw).and_return 24.0
203
+ expect(run).to be_in_group(:control)
204
+ run = Ablab::Run.new(experiment, 'abc', request)
205
+ allow(run).to receive(:draw).and_return 25.0
206
+ expect(run).to be_in_group(:a)
207
+ run = Ablab::Run.new(experiment, 'abc', request)
208
+ allow(run).to receive(:draw).and_return 50.0
193
209
  expect(run).to be_in_group(:b)
194
210
  end
195
211
 
@@ -202,16 +218,16 @@ describe Ablab do
202
218
  it 'selects only the given percentage of users' do
203
219
  experiment.percentage_of_visitors 30
204
220
  run = Ablab::Run.new(experiment, 'abc', request)
205
- allow(run).to receive(:draw).and_return 0
221
+ allow(run).to receive(:draw).and_return 0.0
206
222
  expect(run).to be_in_group(:control)
207
223
  run = Ablab::Run.new(experiment, 'abc', request)
208
- allow(run).to receive(:draw).and_return 100
224
+ allow(run).to receive(:draw).and_return 10.0
209
225
  expect(run).to be_in_group(:a)
210
226
  run = Ablab::Run.new(experiment, 'abc', request)
211
- allow(run).to receive(:draw).and_return 200
227
+ allow(run).to receive(:draw).and_return 20.0
212
228
  expect(run).to be_in_group(:b)
213
229
  run = Ablab::Run.new(experiment, 'abc', request)
214
- allow(run).to receive(:draw).and_return 300
230
+ allow(run).to receive(:draw).and_return 30.0
215
231
  expect(run.group).to be_nil
216
232
  end
217
233
 
@@ -220,25 +236,26 @@ describe Ablab do
220
236
  d1 = Ablab::Run.new(experiment, '8asd7f8asf7', request).draw
221
237
  dir = File.expand_path(File.dirname(__FILE__), '../lib')
222
238
  d2 = `bundle exec ruby -I#{dir} -e "require 'ablab'; require 'ostruct'; puts Ablab::Run.new(OpenStruct.new(name: :foo), '8asd7f8asf7', nil).draw"`
223
- expect(d1).to eq(d2.to_i)
239
+ expect(d1).to eq(d2.to_f)
224
240
  end
225
241
 
226
- it "returns an integer number < 1000" do
242
+ it "returns an float number < 100" do
227
243
  expect(
228
- (0..100).map { |i| Ablab::Run.new(experiment, "#{i}", request).draw }.all? { |x| x.is_a?(Integer) && x < 1000 }
244
+ (0..100).map { |i| Ablab::Run.new(experiment, "#{i}", request).draw }.all? { |x| x.is_a?(Float) && x < 100 }
229
245
  ).to be(true)
230
246
  end
231
247
  end
232
248
 
233
249
  describe "#group" do
234
250
  it "returns one of the groups" do
235
- expect(Ablab::Run.new(experiment, rand(12345).to_s, request).group).to be_in([:a, :b, :control])
251
+ expect([:a, :b, :control]).to include(Ablab::Run.new(experiment, rand(12345).to_s, request).group)
236
252
  end
237
253
 
238
254
  it "returns the forced group, if set with the 'ablab_group' param" do
239
255
  params = { ablab_group: 'bar:baz,foo:a' }
240
256
  allow(request).to receive(:params).and_return(params)
241
257
  run = Ablab::Run.new(experiment, '6q5wed', request)
258
+ expect(run.group).to be_a(Ablab::Group)
242
259
  expect(run.group).to eq(:a)
243
260
  end
244
261
  end
@@ -341,4 +358,51 @@ describe Ablab do
341
358
  end
342
359
  end
343
360
  end
361
+
362
+ describe Ablab::Group do
363
+ let(:control_group) { Ablab::Group.new(:control, "control group") }
364
+ let(:experimental_group) { Ablab::Group.new(:foo, "foo group") }
365
+
366
+ describe "#control?" do
367
+ it 'is true for the control group' do
368
+ expect(control_group.control?).to be true
369
+ end
370
+
371
+ it 'is false for other groups' do
372
+ expect(experimental_group.control?).to be false
373
+ end
374
+ end
375
+
376
+ describe "#experimental?" do
377
+ it 'is false for the control group' do
378
+ expect(control_group.experimental?).to be false
379
+ end
380
+
381
+ it 'is true for other groups' do
382
+ expect(experimental_group.experimental?).to be true
383
+ end
384
+ end
385
+
386
+ describe "#==" do
387
+ it 'accepts comparison to Symbol instances' do
388
+ expect(control_group).to eq(:control)
389
+ expect(control_group).not_to eq(:foo)
390
+ end
391
+
392
+ it 'accepts comparison to String instances' do
393
+ expect(control_group).to eq('control')
394
+ expect(control_group).not_to eq('foo')
395
+ end
396
+
397
+ it 'accepts comparison to Ablab::Group instances' do
398
+ expect(control_group).to eq(control_group)
399
+ expect(control_group).to eq(Ablab::Group.new(:control, "control group"))
400
+ expect(control_group).not_to eq(experimental_group)
401
+ end
402
+
403
+ it 'is false for everyhing else' do
404
+ expect(control_group).not_to eq(Object.new)
405
+ end
406
+ end
407
+ end
344
408
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ablab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Ongaro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-23 00:00:00.000000000 Z
11
+ date: 2016-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails