ablab 0.3.0 → 0.4.0

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