experimental 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1c78c9b7d6c365503d9c865f63b3e24f7abbdec5
4
- data.tar.gz: adc2a47c070863a3a87432d4eca968614155d3a9
5
- SHA512:
6
- metadata.gz: 9eee13366381e3d96a44abc8da8b003c405850a140f96091ec7d8410dad321d3501b4ac3577976217d5a46b279b6cdbc7704cd219dedf6ab1633a05431b480b8
7
- data.tar.gz: 755c66a9193290985db2a6852a68c01edd3780fc94672d9691743fc6b02b3407051242ef30c8882b6753e98184e1f9a94850913976b878ba7a3b6cd76541b36d
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
- ### Setup
218
- in `spec_helper.rb` (after inclusion of ActiveSupport)
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
- ```ruby
221
- require 'experimental/rspec_helpers'
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
- *You may want to force experiments off for all tests by default*
225
- ```ruby
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
- include Experimental::RspecHelpers
235
+ set_experimental_bucket(subject, :my_experiment, 1)
236
236
  ```
237
237
 
238
- Shared contexts are available for in_experiment? and in_bucket?
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
- include_context "in experiment"
241
- include_context "not in experiment"
243
+ require 'experimental/test/unit'
242
244
 
243
- include_context "in experiment bucket 0"
244
- include_context "in experiment bucket 1"
245
+ class MyTest < Test::Unit::TestCase
246
+ include Experimental::Test::Unit
247
+ ...
248
+ end
245
249
  ```
246
250
 
247
- Helper methods are also available:
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
- **is_in_experiment**
254
+ ### RSpec
250
255
  ```ruby
251
- # first param is true for in experiment, false for not in experiment
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
- **is_not_in_experiment**
264
- ```ruby
265
- # first param is name of experiment
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
- **has_experiment_bucket**
263
+ ### Cucumber
276
264
  ```ruby
277
- has_experiment_bucket(1, :my_experiment, my_subject)
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(['removed_at IS NULL AND (end_date IS NULL OR ? <= end_date)', Time.now])
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")
@@ -5,8 +5,8 @@ module Experimental
5
5
  Experiment.find_by_name(name)
6
6
  end
7
7
 
8
- def active
9
- Experiment.active.all
8
+ def available
9
+ Experiment.available.all
10
10
  end
11
11
  end
12
12
  end
@@ -6,8 +6,8 @@ module Experimental
6
6
  raise NotImplementedError, 'abstract'
7
7
  end
8
8
 
9
- # Return all active experiments.
10
- def active
9
+ # Return all non-removed experiments.
10
+ def available
11
11
  raise NotImplementedError, 'abstract'
12
12
  end
13
13
  end
@@ -19,7 +19,7 @@ module Experimental
19
19
  cache[name.to_s]
20
20
  end
21
21
 
22
- def active
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.active.each do |experiment|
38
+ source.available.each do |experiment|
39
39
  cache[experiment.name] = experiment
40
40
  end
41
41
  self.last_update = Time.now.to_f
@@ -13,7 +13,7 @@ module Experimental
13
13
  @experiments[name.to_s]
14
14
  end
15
15
 
16
- def active
16
+ def available
17
17
  @experiments.values
18
18
  end
19
19
  end
@@ -0,0 +1,5 @@
1
+ require 'experimental/test'
2
+
3
+ Experimental::Test.initialize
4
+ Before { Experimental::Test.setup }
5
+ World(Experimental::Test)
@@ -0,0 +1,12 @@
1
+ module Experimental
2
+ module Test
3
+ module RSpec
4
+ include Test
5
+
6
+ def self.included(base)
7
+ Test.initialize
8
+ base.before(:each) { Test.setup }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Experimental
2
+ module Test
3
+ module Unit
4
+ include Test
5
+
6
+ def self.included(base)
7
+ Test.initialize
8
+ end
9
+
10
+ def setup
11
+ Test.setup
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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
- def self.included(base)
4
- base.before do
5
- Experimental.source = Experimental::Source::Configuration.new
6
- Experimental.overrides.reset
7
- end
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
- base.after do
10
- Experimental.overrides.reset
11
- end
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+.
@@ -1,5 +1,5 @@
1
1
  module Experimental
2
- VERSION = [0, 2, 0]
2
+ VERSION = [0, 2, 1]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
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.0
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-10-22 00:00:00.000000000 Z
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.1.9
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