experimental 0.2.0 → 0.2.1
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 +14 -6
- data/README.markdown +33 -50
- data/app/models/experimental/experiment.rb +6 -1
- data/lib/experimental/source/active_record.rb +2 -2
- data/lib/experimental/source/base.rb +2 -2
- data/lib/experimental/source/cache.rb +2 -2
- data/lib/experimental/source/configuration.rb +1 -1
- data/lib/experimental/test/cucumber.rb +5 -0
- data/lib/experimental/test/rspec.rb +12 -0
- data/lib/experimental/test/unit.rb +16 -0
- data/lib/experimental/test.rb +16 -8
- data/lib/experimental/version.rb +1 -1
- metadata +22 -19
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2Y3MWRjY2E3ZThhNzZhNjFhNWJjMmVhYjA2YTlhNzA3NWUwYzljNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDRmMjU5MTg5MDMwMmI4MjM5Y2IwMmU4M2ZmZDY1ZmIyYzZkODliOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDVhMDA3ZDg3YzI1ZjY5ZGI3ZjMzYzU5YWViYWIwZTVhMzg5Mjg2ZTMxMzAy
|
10
|
+
YjIwYzllY2E3OWMyZTI3NmUwM2JlZGM3ZWU3N2UxOTFjMDU5Yzc1YmJjNGJh
|
11
|
+
NTY0ODhlMWQwMjgzYTRlNGE0YzMwMmUyMWEyYzAzZmFmNzkyYmQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZWNiMzRkYzhmMzU2MmQ3NTA3YjVmOGVjOGZiODI2OTFiNmMwOWIzZDdkMmE2
|
14
|
+
YWQ4MDE2OGIyYjQ4MTU3MzE2NmEyMDcwYmI1NjgxYWJmZjE4ODkwOTVmZTA2
|
15
|
+
YTk0YzM4N2UxZjliMTM5MDdjM2MxODc5YTc1NzBkYzA5MmExZDc=
|
data/README.markdown
CHANGED
@@ -185,6 +185,12 @@ user.in_bucket?(:my_experiment, 0)
|
|
185
185
|
To see if a user is in the experiment population **ONLY**
|
186
186
|
```ruby
|
187
187
|
user.in_experiment?(:my_experiment)
|
188
|
+
user.not_in_experiment?(:my_experiment) # inverse
|
189
|
+
```
|
190
|
+
|
191
|
+
To see which bucket of an experiment a user is in:
|
192
|
+
```ruby
|
193
|
+
user.experiment_bucket(:my_experiment)
|
188
194
|
```
|
189
195
|
|
190
196
|
### Ending an experiment
|
@@ -214,72 +220,49 @@ Then run `rake experimental:sync`
|
|
214
220
|
|
215
221
|
## Testing
|
216
222
|
|
217
|
-
|
218
|
-
|
223
|
+
In your test suite, you typically want to have an neutral starting state across
|
224
|
+
all your tests. For experiments, this means all subjects are out of all
|
225
|
+
experiments. You then opt a particular subject into a particular bucket for any
|
226
|
+
experiment as your test requires.
|
219
227
|
|
220
|
-
|
221
|
-
|
222
|
-
```
|
228
|
+
Experimental ships with support to do this in a number of popular test
|
229
|
+
frameworks. Setup instructions for each framework are in the following sections.
|
223
230
|
|
224
|
-
|
225
|
-
|
226
|
-
config.before(:each) do
|
227
|
-
User.any_instance.stub(:in_experiment?).and_return(false)
|
228
|
-
end
|
229
|
-
```
|
231
|
+
Once set up, you can then force a subject into a bucket for an experiment as
|
232
|
+
follows:
|
230
233
|
|
231
|
-
### Testing experiments
|
232
|
-
|
233
|
-
Include the Rspec helpers in your spec class or spec_helper
|
234
234
|
```ruby
|
235
|
-
|
235
|
+
set_experimental_bucket(subject, :my_experiment, 1)
|
236
236
|
```
|
237
237
|
|
238
|
-
|
238
|
+
If you set the bucket (1 in the above example) to `nil`, this means set the
|
239
|
+
subject to be out of the experiment (the default state).
|
240
|
+
|
241
|
+
### Minitest
|
239
242
|
```ruby
|
240
|
-
|
241
|
-
include_context "not in experiment"
|
243
|
+
require 'experimental/test/unit'
|
242
244
|
|
243
|
-
|
244
|
-
|
245
|
+
class MyTest < Test::Unit::TestCase
|
246
|
+
include Experimental::Test::Unit
|
247
|
+
...
|
248
|
+
end
|
245
249
|
```
|
246
250
|
|
247
|
-
|
251
|
+
Note that if you define a `setup` method, then you must remember to call
|
252
|
+
`super` (always good practice in general).
|
248
253
|
|
249
|
-
|
254
|
+
### RSpec
|
250
255
|
```ruby
|
251
|
-
|
252
|
-
# second param is the experiment name
|
253
|
-
# third param is the subject object
|
254
|
-
is_in_experiment(true, :my_experiment, my_subject)
|
255
|
-
|
256
|
-
# if user and experiment_name are defined, you can do
|
257
|
-
let(:experiment_name) { :my_experiment }
|
258
|
-
let(:user) { User.new }
|
259
|
-
is_in_experiment # true if in experiment
|
260
|
-
is_in_experiment(false) # true if NOT in experiment
|
261
|
-
```
|
256
|
+
require 'experimental/test/rspec'
|
262
257
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
# second param is subject object
|
267
|
-
is_not_in_experiment(:my_experiment, my_subject)
|
268
|
-
|
269
|
-
# if user and experiment_name are defined, you can do
|
270
|
-
let(:experiment_name) { :my_experiment }
|
271
|
-
let(:user) { User.new }
|
272
|
-
is_not_in_experiment
|
258
|
+
RSpec.configure do |config|
|
259
|
+
config.include Experimental::Test::RSpec
|
260
|
+
end
|
273
261
|
```
|
274
262
|
|
275
|
-
|
263
|
+
### Cucumber
|
276
264
|
```ruby
|
277
|
-
|
278
|
-
|
279
|
-
# if user and experiment_name are defined, you can do
|
280
|
-
let(:experiment_name) { :my_experiment }
|
281
|
-
let(:user) { User.new }
|
282
|
-
has_experiment_bucket(1)
|
265
|
+
require 'experimental/test/cucumber'
|
283
266
|
```
|
284
267
|
|
285
268
|
## Developer Workflow
|
@@ -67,6 +67,7 @@ module Experimental
|
|
67
67
|
self.winning_bucket = nil
|
68
68
|
self.start_date = Time.now
|
69
69
|
self.end_date = nil
|
70
|
+
self.removed_at = nil
|
70
71
|
|
71
72
|
save
|
72
73
|
end
|
@@ -95,8 +96,12 @@ module Experimental
|
|
95
96
|
!removed? && !ended?
|
96
97
|
end
|
97
98
|
|
99
|
+
def self.available
|
100
|
+
where(removed_at: nil)
|
101
|
+
end
|
102
|
+
|
98
103
|
def self.active
|
99
|
-
where(['
|
104
|
+
available.where(['end_date IS NULL OR ? <= end_date', Time.now])
|
100
105
|
end
|
101
106
|
|
102
107
|
def to_sql_formula(subject_table = "users")
|
@@ -19,7 +19,7 @@ module Experimental
|
|
19
19
|
cache[name.to_s]
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def available
|
23
23
|
refresh if dirty?
|
24
24
|
cache.values
|
25
25
|
end
|
@@ -35,7 +35,7 @@ module Experimental
|
|
35
35
|
|
36
36
|
def refresh
|
37
37
|
cache.clear
|
38
|
-
source.
|
38
|
+
source.available.each do |experiment|
|
39
39
|
cache[experiment.name] = experiment
|
40
40
|
end
|
41
41
|
self.last_update = Time.now.to_f
|
data/lib/experimental/test.rb
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
module Experimental
|
2
|
+
# Test helpers for applications that use Experimental.
|
3
|
+
#
|
4
|
+
# For popular test frameworks, simply require the appropriate
|
5
|
+
# experimental/test/*.rb file. If those doesn't cover you, check one of those
|
6
|
+
# to see how to hook up your favorite framework.
|
2
7
|
module Test
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
# Call this once to initialize Experimental for your test suite.
|
9
|
+
#
|
10
|
+
# Calling it again isn't harmful, just unnecessary.
|
11
|
+
def self.initialize
|
12
|
+
return if @initialized
|
13
|
+
Experimental.source = Experimental::Source::Configuration.new
|
14
|
+
@initialized = true
|
15
|
+
end
|
8
16
|
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
# Call this before each test.
|
18
|
+
def self.setup
|
19
|
+
Experimental.overrides.reset
|
12
20
|
end
|
13
21
|
|
14
22
|
# Force the given subject into the given +bucket+ of the given +experiment+.
|
data/lib/experimental/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: experimental
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HowAboutWe.com
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -30,98 +30,98 @@ dependencies:
|
|
30
30
|
name: jquery-rails
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- - '>='
|
33
|
+
- - ! '>='
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- - '>='
|
40
|
+
- - ! '>='
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: sqlite3
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - '>='
|
47
|
+
- - ! '>='
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '0'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- - '>='
|
54
|
+
- - ! '>='
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: timecop
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- - '>='
|
61
|
+
- - ! '>='
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0'
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- - '>='
|
68
|
+
- - ! '>='
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: rspec-rails
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- - '>='
|
75
|
+
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- - '>='
|
82
|
+
- - ! '>='
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: activeadmin
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
88
88
|
requirements:
|
89
|
-
- - '>='
|
89
|
+
- - ! '>='
|
90
90
|
- !ruby/object:Gem::Version
|
91
91
|
version: '0'
|
92
92
|
type: :development
|
93
93
|
prerelease: false
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- - '>='
|
96
|
+
- - ! '>='
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
- !ruby/object:Gem::Dependency
|
100
100
|
name: sass-rails
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
102
102
|
requirements:
|
103
|
-
- - '>='
|
103
|
+
- - ! '>='
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0'
|
106
106
|
type: :development
|
107
107
|
prerelease: false
|
108
108
|
version_requirements: !ruby/object:Gem::Requirement
|
109
109
|
requirements:
|
110
|
-
- - '>='
|
110
|
+
- - ! '>='
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: '0'
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: coffee-rails
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
|
-
- - '>='
|
117
|
+
- - ! '>='
|
118
118
|
- !ruby/object:Gem::Version
|
119
119
|
version: '0'
|
120
120
|
type: :development
|
121
121
|
prerelease: false
|
122
122
|
version_requirements: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
|
-
- - '>='
|
124
|
+
- - ! '>='
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
127
|
description: AB Test framework for Rails
|
@@ -152,6 +152,9 @@ files:
|
|
152
152
|
- lib/experimental/source/cache.rb
|
153
153
|
- lib/experimental/source/configuration.rb
|
154
154
|
- lib/experimental/source.rb
|
155
|
+
- lib/experimental/test/cucumber.rb
|
156
|
+
- lib/experimental/test/rspec.rb
|
157
|
+
- lib/experimental/test/unit.rb
|
155
158
|
- lib/experimental/test.rb
|
156
159
|
- lib/experimental/version.rb
|
157
160
|
- lib/experimental.rb
|
@@ -173,17 +176,17 @@ require_paths:
|
|
173
176
|
- lib
|
174
177
|
required_ruby_version: !ruby/object:Gem::Requirement
|
175
178
|
requirements:
|
176
|
-
- - '>='
|
179
|
+
- - ! '>='
|
177
180
|
- !ruby/object:Gem::Version
|
178
181
|
version: '0'
|
179
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
183
|
requirements:
|
181
|
-
- - '>='
|
184
|
+
- - ! '>='
|
182
185
|
- !ruby/object:Gem::Version
|
183
186
|
version: '0'
|
184
187
|
requirements: []
|
185
188
|
rubyforge_project:
|
186
|
-
rubygems_version: 2.
|
189
|
+
rubygems_version: 2.0.3
|
187
190
|
signing_key:
|
188
191
|
specification_version: 4
|
189
192
|
summary: Adds support for database-backed AB tests in Rails apps
|