eeny-meeny 2.3.0 → 2.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/eeny-meeny.gemspec +4 -4
- data/lib/eeny-meeny/middleware.rb +8 -12
- data/lib/eeny-meeny/models/experiment.rb +1 -0
- data/lib/eeny-meeny/version.rb +1 -1
- data/lib/eeny-meeny.rb +1 -1
- data/spec/eeny-meeny/experiment_helper_spec.rb +20 -29
- data/spec/eeny-meeny/middleware_spec.rb +29 -24
- data/spec/eeny-meeny/models/experiment_spec.rb +38 -42
- data/spec/mock_rack_app.rb +0 -3
- data/spec/spec_helper.rb +5 -0
- data/spec/support/custom_expectations/expect_cookie_with.rb +8 -0
- data/spec/support/factories/experiment.rb +13 -0
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d7c9f1940d946cd5a0477150804281712e40fa6153d4924e8d5a06ab1576f00
|
4
|
+
data.tar.gz: 6c9a03ce11ba41d70d3a1b9900685f63ad7c45983a40ae520308c37f868a95de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c248029984be24e20a0267967b2741db67a8a4a0fa224463ad032d5a438d15915e73367a8befc932510fd46837baeb75640f41a073c9586944aed323182fd44
|
7
|
+
data.tar.gz: 94345325d181119efcd8086f32ff267e47a67e98af0a315ef9b93a3d717c43472421d1aa7c2225de7d727732056df94ebad2ca7cf3dd2836d10f0ce83efac4cc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 2.4.0 (2025-08-27)
|
2
|
+
|
3
|
+
Changes:
|
4
|
+
|
5
|
+
- Support Rails 7.
|
6
|
+
- Support rack 3.x.
|
7
|
+
- Drop support for rack 1.x.
|
8
|
+
- Use header constants from rack to support header case-change from rack 2.x to 3.x.
|
9
|
+
- Changed default "same_site" cookie config from "strict" to "lax".
|
10
|
+
|
1
11
|
### 2.3.0 (2021-08-11)
|
2
12
|
|
3
13
|
Changes:
|
data/eeny-meeny.gemspec
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../lib/eeny-meeny/version', __FILE__)
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'eeny-meeny'
|
5
5
|
s.version = EenyMeeny::VERSION.dup
|
6
|
-
s.date = '
|
6
|
+
s.date = '2025-08-27'
|
7
7
|
s.summary = "A simple split and smoke testing tool for Rails"
|
8
8
|
s.authors = ["Christian Orthmann"]
|
9
9
|
s.email = 'christian.orthmann@gmail.com'
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_development_dependency('simplecov', '~> 0')
|
19
19
|
s.add_development_dependency('simplecov-rcov', '~> 0')
|
20
20
|
s.add_development_dependency('yard', '>= 0.9.11', '< 1.0.0')
|
21
|
-
s.add_development_dependency('rack-test', '~>
|
22
|
-
s.add_runtime_dependency('rack', '>=
|
23
|
-
s.add_runtime_dependency('activesupport', '>= 3.0.0', '<
|
21
|
+
s.add_development_dependency('rack-test', '~> 2')
|
22
|
+
s.add_runtime_dependency('rack', '>= 2.0.0', '< 4')
|
23
|
+
s.add_runtime_dependency('activesupport', '>= 3.0.0', '< 8')
|
24
24
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'time'
|
3
|
+
require 'active_support'
|
3
4
|
require 'active_support/time'
|
4
5
|
require 'eeny-meeny/models/experiment'
|
5
6
|
require 'eeny-meeny/models/encryptor'
|
@@ -8,11 +9,6 @@ require 'eeny-meeny/models/cookie'
|
|
8
9
|
module EenyMeeny
|
9
10
|
class Middleware
|
10
11
|
|
11
|
-
# Headers
|
12
|
-
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
13
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
14
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
15
|
-
|
16
12
|
def initialize(app)
|
17
13
|
@app = app
|
18
14
|
@experiments = EenyMeeny::Experiment.find_all
|
@@ -20,7 +16,7 @@ module EenyMeeny
|
|
20
16
|
end
|
21
17
|
|
22
18
|
def call(env)
|
23
|
-
cookies = Rack::Utils.parse_query(env[HTTP_COOKIE],';,') { |s| Rack::Utils.unescape(s) rescue s }
|
19
|
+
cookies = Rack::Utils.parse_query(env[Rack::HTTP_COOKIE],';,') { |s| Rack::Utils.unescape(s) rescue s }
|
24
20
|
query_parameters = query_hash(env)
|
25
21
|
now = Time.zone.now
|
26
22
|
new_cookies = {}
|
@@ -95,17 +91,17 @@ module EenyMeeny
|
|
95
91
|
# Query Params are only relevant if EenyMeeny.config have them enabled.
|
96
92
|
return {} unless EenyMeeny.config.query_parameters[:experiment] || EenyMeeny.config.query_parameters[:smoke_test]
|
97
93
|
# Query Params are only relevant to HTTP GET requests.
|
98
|
-
return {} unless env[REQUEST_METHOD] == 'GET'
|
99
|
-
Rack::Utils.parse_query(env[QUERY_STRING], '&;')
|
94
|
+
return {} unless env[Rack::REQUEST_METHOD] == 'GET'
|
95
|
+
Rack::Utils.parse_query(env[Rack::QUERY_STRING], '&;')
|
100
96
|
end
|
101
97
|
|
102
98
|
def add_or_replace_http_cookie(env, cookie)
|
103
99
|
cookie_name_escaped = Rack::Utils.escape(cookie.name)
|
104
100
|
cookie_string = "#{cookie_name_escaped}=#{Rack::Utils.escape(cookie.value)}"
|
105
|
-
env[HTTP_COOKIE] = '' if env[HTTP_COOKIE].nil?
|
106
|
-
return env if env[HTTP_COOKIE].sub!(/#{Regexp.escape(cookie_name_escaped)}=[^;]+/, cookie_string)
|
107
|
-
env[HTTP_COOKIE] += '; ' unless env[HTTP_COOKIE].empty?
|
108
|
-
env[HTTP_COOKIE] += cookie_string
|
101
|
+
env[Rack::HTTP_COOKIE] = '' if env[Rack::HTTP_COOKIE].nil?
|
102
|
+
return env if env[Rack::HTTP_COOKIE].sub!(/#{Regexp.escape(cookie_name_escaped)}=[^;]+/, cookie_string)
|
103
|
+
env[Rack::HTTP_COOKIE] += '; ' unless env[Rack::HTTP_COOKIE].empty?
|
104
|
+
env[Rack::HTTP_COOKIE] += cookie_string
|
109
105
|
env
|
110
106
|
end
|
111
107
|
end
|
data/lib/eeny-meeny/version.rb
CHANGED
data/lib/eeny-meeny.rb
CHANGED
@@ -10,7 +10,7 @@ module EenyMeeny
|
|
10
10
|
attr_reader :encryptor
|
11
11
|
|
12
12
|
def initialize
|
13
|
-
@cookies = { http_only: true, path: '/', same_site: :
|
13
|
+
@cookies = { http_only: true, path: '/', same_site: :lax }
|
14
14
|
@experiments = {}
|
15
15
|
@secret = '9fc8b966eca7d03d55df40c01c10b8e02bf1f9d12d27b8968d53eb53e8c239902d00bf6afae5e726ce1111159eeb2f8f0e77233405db1d82dd71397f651a0a4f'
|
16
16
|
@secure = true
|
@@ -11,33 +11,24 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
11
11
|
Object.new.extend(EenyMeeny::ExperimentHelper)
|
12
12
|
end
|
13
13
|
|
14
|
-
let(:
|
15
|
-
|
16
|
-
end
|
14
|
+
let(:app) { EenyMeeny::Middleware.new(MockRackApp.new) }
|
15
|
+
let(:experiment) { EenyMeeny::Experiment.find_by_id(:my_page) }
|
17
16
|
|
18
17
|
describe '#participates_in?' do
|
19
18
|
context 'given an experiment id' do
|
20
|
-
let(:request_with_cookie) do
|
21
|
-
request.set_cookie(EenyMeeny::Cookie.create_for_experiment(EenyMeeny::Experiment.find_by_id(:my_page)).to_s)
|
22
|
-
request
|
23
|
-
end
|
24
|
-
|
25
19
|
context 'of an active experiment' do
|
26
20
|
context 'with a valid experiment cookie' do
|
27
21
|
it "returns the user's experiment variation" do
|
28
|
-
|
22
|
+
get '/test'
|
23
|
+
allow(subject).to receive(:cookies).and_return(current_session.cookie_jar)
|
29
24
|
expect(subject.participates_in?(:my_page)).to be_a EenyMeeny::Variation
|
30
25
|
end
|
31
26
|
|
32
27
|
context 'and given a variation id' do
|
33
|
-
let(:request_with_variation_cookie) do
|
34
|
-
request.set_cookie(EenyMeeny::Cookie.create_for_experiment_variation(EenyMeeny::Experiment.find_by_id(:my_page), :new).to_s)
|
35
|
-
request
|
36
|
-
end
|
37
|
-
|
38
28
|
context 'that matches the variation id the cookie' do
|
39
29
|
it "returns the user's experiment variation" do
|
40
|
-
|
30
|
+
get '/test' , EenyMeeny::Cookie.cookie_name(experiment) => :new
|
31
|
+
allow(subject).to receive(:cookies).and_return(current_session.cookie_jar)
|
41
32
|
expect(subject.participates_in?(:my_page, variation_id: :new)).to be_a EenyMeeny::Variation
|
42
33
|
expect(subject.participates_in?(:my_page, variation_id: 'new')).to be_a EenyMeeny::Variation
|
43
34
|
end
|
@@ -45,7 +36,8 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
45
36
|
|
46
37
|
context 'that does not match the variation id the cookie' do
|
47
38
|
it 'returns nil' do
|
48
|
-
|
39
|
+
get '/test', EenyMeeny::Cookie.cookie_name(experiment) => :new
|
40
|
+
allow(subject).to receive(:cookies).and_return(current_session.cookie_jar)
|
49
41
|
expect(subject.participates_in?(:my_page, variation_id: :old)).to be_nil
|
50
42
|
expect(subject.participates_in?(:my_page, variation_id: 'old')).to be_nil
|
51
43
|
end
|
@@ -55,7 +47,7 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
55
47
|
|
56
48
|
context 'without an experiment cookie' do
|
57
49
|
it 'returns nil' do
|
58
|
-
allow(subject).to receive(:cookies).and_return(
|
50
|
+
allow(subject).to receive(:cookies).and_return({})
|
59
51
|
expect(subject.participates_in?(:my_page)).to be_nil
|
60
52
|
end
|
61
53
|
end
|
@@ -63,13 +55,15 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
63
55
|
|
64
56
|
context 'of an inactive experiment' do
|
65
57
|
context 'with a valid experiment cookie' do
|
66
|
-
let(:
|
67
|
-
|
68
|
-
|
58
|
+
let(:experiment) { EenyMeeny::Experiment.find_by_id(:expired) }
|
59
|
+
let(:cookie_jar) do
|
60
|
+
{
|
61
|
+
"#{EenyMeeny::Cookie.cookie_name(experiment)}" => EenyMeeny::Cookie.create_for_experiment(experiment).to_s
|
62
|
+
}
|
69
63
|
end
|
70
64
|
|
71
65
|
it 'returns nil' do
|
72
|
-
allow(subject).to receive(:cookies).and_return(
|
66
|
+
allow(subject).to receive(:cookies).and_return(cookie_jar)
|
73
67
|
expect(subject.participates_in?(:expired)).to be_nil
|
74
68
|
end
|
75
69
|
end
|
@@ -77,7 +71,8 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
77
71
|
|
78
72
|
context 'that does not exist among the experiments' do
|
79
73
|
it 'returns nil' do
|
80
|
-
|
74
|
+
get '/test'
|
75
|
+
allow(subject).to receive(:cookies).and_return(current_session.cookie_jar)
|
81
76
|
expect(subject.participates_in?(:this_does_not_exist)).to be_nil
|
82
77
|
end
|
83
78
|
end
|
@@ -86,21 +81,17 @@ describe EenyMeeny::ExperimentHelper, experiments: true do
|
|
86
81
|
|
87
82
|
describe '#smoke_test?' do
|
88
83
|
context 'given a smoke test id' do
|
89
|
-
let(:request_with_smoke_test) do
|
90
|
-
request.set_cookie(EenyMeeny::Cookie.create_for_smoke_test(:my_smoke_test).to_s)
|
91
|
-
request
|
92
|
-
end
|
93
|
-
|
94
84
|
context 'and a request with a valid smoke test cookie' do
|
95
85
|
it 'returns the smoke test' do
|
96
|
-
|
86
|
+
get '/test', smoke_test_id: "my_smoke_test"
|
87
|
+
allow(subject).to receive(:cookies).and_return(current_session.cookie_jar)
|
97
88
|
expect(subject.smoke_test?(:my_smoke_test)).to be
|
98
89
|
end
|
99
90
|
end
|
100
91
|
|
101
92
|
context 'and a request without a smoke test cookie' do
|
102
93
|
it 'returns nil' do
|
103
|
-
allow(subject).to receive(:cookies).and_return(
|
94
|
+
allow(subject).to receive(:cookies).and_return({})
|
104
95
|
expect(subject.smoke_test?(:my_smoke_test)).to be_nil
|
105
96
|
end
|
106
97
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'eeny-meeny/models/encryptor'
|
2
2
|
require 'eeny-meeny/middleware'
|
3
3
|
|
4
|
-
def initialize_app(secure: true, secret: 'test', path: '/', same_site: :
|
4
|
+
def initialize_app(secure: true, secret: 'test', path: '/', same_site: :lax)
|
5
5
|
EenyMeeny.reset!
|
6
6
|
EenyMeeny.configure do |config|
|
7
7
|
config.cookies = { path: path, same_site: same_site }
|
@@ -12,6 +12,16 @@ def initialize_app(secure: true, secret: 'test', path: '/', same_site: :strict)
|
|
12
12
|
described_class.new(app)
|
13
13
|
end
|
14
14
|
|
15
|
+
def request_options(http_cookie_header: nil)
|
16
|
+
options = {
|
17
|
+
'CONTENT_TYPE' => 'text/plain',
|
18
|
+
lint: true
|
19
|
+
}
|
20
|
+
return options unless http_cookie_header
|
21
|
+
|
22
|
+
options.merge({ Rack::HTTP_COOKIE => http_cookie_header })
|
23
|
+
end
|
24
|
+
|
15
25
|
describe EenyMeeny::Middleware do
|
16
26
|
|
17
27
|
let(:app) { MockRackApp.new }
|
@@ -40,13 +50,13 @@ describe EenyMeeny::Middleware do
|
|
40
50
|
end
|
41
51
|
|
42
52
|
it "sets the 'HTTP_COOKIE' header on the request" do
|
43
|
-
expect(app[
|
44
|
-
expect(app[
|
53
|
+
expect(app[Rack::HTTP_COOKIE]).to be
|
54
|
+
expect(app[Rack::HTTP_COOKIE].length).to be > 0
|
45
55
|
end
|
46
56
|
|
47
57
|
it "sets the 'Set-Cookie' header on the response" do
|
48
|
-
expect(@response[
|
49
|
-
expect(@response[
|
58
|
+
expect(@response[Rack::SET_COOKIE]).to be
|
59
|
+
expect(@response[Rack::SET_COOKIE].length).to be > 0
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
@@ -56,16 +66,15 @@ describe EenyMeeny::Middleware do
|
|
56
66
|
before(:example) do
|
57
67
|
@original_request_cookies = 'test=abc;eeny_meeny_my_page_v1=on1tOQ5hiKdA0biVZVwvTUQcmkODacwdpi%2FedQJIYQz9KdWYAXqzCafF5Dqqa6xtHFBdXYVmz%2Bp4%2FigmKz4hBVYZbJU%2FMwBbvYG%2BIoBelk10PxwtyxbA%2BiDzFT4jZeiTkNOmZ3rp1Gzz74JjT4aocqB187p7SrpeM2jfyZ8ZKPOiZs6tXf0QoXkV%2BZbtxJLRPr5lgmGxslfM8vCIm1%2F0HQ%3D%3D;eeny_meeny_versioned_v3=UUgXwn3j0%2BOL2cpov4duTnuCJPc621yHd6GjuXpN0gnYLDASTsDpyk01CnFY5ZYCAo%2BgLO%2BwTbsYObP8dp30rA%3D%3D;'
|
58
68
|
@response = request.get('/test',
|
59
|
-
|
60
|
-
'HTTP_COOKIE' => @original_request_cookies)
|
69
|
+
request_options(http_cookie_header: @original_request_cookies))
|
61
70
|
end
|
62
71
|
|
63
72
|
it "does not change the 'HTTP_COOKIE' header on the request" do
|
64
|
-
expect(app[
|
73
|
+
expect(app[Rack::HTTP_COOKIE]).to eq(@original_request_cookies)
|
65
74
|
end
|
66
75
|
|
67
76
|
it "does not set the 'Set-Cookie' header on the response" do
|
68
|
-
expect(@response[
|
77
|
+
expect(@response[Rack::SET_COOKIE]).to be nil
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
@@ -73,19 +82,18 @@ describe EenyMeeny::Middleware do
|
|
73
82
|
let(:request) { Rack::MockRequest.new(subject) }
|
74
83
|
let(:cookie_value) { 'eeny_meeny_undefined_experiment_v1=thevaluedoesntmatter' }
|
75
84
|
let(:return_value) do
|
76
|
-
|
85
|
+
'eeny_meeny_undefined_experiment_v1=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=lax'
|
77
86
|
end
|
78
87
|
|
79
88
|
before(:example) do
|
80
89
|
@original_request_cookies = "test=abc;#{cookie_value};"
|
81
90
|
@response = request.get('/test',
|
82
|
-
|
83
|
-
'HTTP_COOKIE' => @original_request_cookies)
|
91
|
+
request_options(http_cookie_header: @original_request_cookies))
|
84
92
|
end
|
85
93
|
|
86
94
|
|
87
95
|
it "instructs the browser to remove through the 'Set-Cookie' header on the response" do
|
88
|
-
expect(@response[
|
96
|
+
expect(@response[Rack::SET_COOKIE]).to include(return_value)
|
89
97
|
end
|
90
98
|
end
|
91
99
|
|
@@ -97,18 +105,17 @@ describe EenyMeeny::Middleware do
|
|
97
105
|
end
|
98
106
|
|
99
107
|
it 'selects the correct variation' do
|
100
|
-
expect(app[
|
101
|
-
expect(app[
|
108
|
+
expect(app[Rack::HTTP_COOKIE]).to include('eeny_meeny_my_page_v1=old')
|
109
|
+
expect(app[Rack::HTTP_COOKIE]).to_not include('eeny_meeny_my_page_v1=new')
|
102
110
|
end
|
103
111
|
|
104
112
|
it "sets the 'HTTP_COOKIE' header on the request" do
|
105
|
-
expect(app[
|
106
|
-
expect(app[
|
113
|
+
expect(app[Rack::HTTP_COOKIE]).to be
|
114
|
+
expect(app[Rack::HTTP_COOKIE]).to include('eeny_meeny_my_page_v1=')
|
107
115
|
end
|
108
116
|
|
109
117
|
it "sets the 'Set-Cookie' header on the response" do
|
110
|
-
|
111
|
-
expect(@response['Set-Cookie']).to include('eeny_meeny_my_page_v1=')
|
118
|
+
expect_set_cookie_with(response: @response, value: 'eeny_meeny_my_page_v1=')
|
112
119
|
end
|
113
120
|
end
|
114
121
|
|
@@ -120,15 +127,13 @@ describe EenyMeeny::Middleware do
|
|
120
127
|
end
|
121
128
|
|
122
129
|
it "sets the 'HTTP_COOKIE' header on the request" do
|
123
|
-
expect(app[
|
124
|
-
expect(app[
|
130
|
+
expect(app[Rack::HTTP_COOKIE]).to be
|
131
|
+
expect(app[Rack::HTTP_COOKIE]).to include('smoke_test_my_smoke_test_v1=')
|
125
132
|
end
|
126
133
|
|
127
134
|
it "sets the 'Set-Cookie' header on the response" do
|
128
|
-
|
129
|
-
expect(@response['Set-Cookie']).to include('smoke_test_my_smoke_test_v1=')
|
135
|
+
expect_set_cookie_with(response: @response, value: 'smoke_test_my_smoke_test_v1=')
|
130
136
|
end
|
131
137
|
end
|
132
138
|
end
|
133
|
-
|
134
139
|
end
|
@@ -1,27 +1,21 @@
|
|
1
1
|
require 'eeny-meeny/models/experiment'
|
2
2
|
require 'eeny-meeny/models/variation'
|
3
3
|
|
4
|
-
def experiment_with_time(time = {})
|
5
|
-
experiment_options = {
|
6
|
-
name: 'Test 1',
|
7
|
-
variations: {
|
8
|
-
a: { name: 'A' },
|
9
|
-
b: { name: 'B' }}
|
10
|
-
}.merge(time)
|
11
|
-
described_class.new(:experiment_1,
|
12
|
-
**experiment_options)
|
13
|
-
end
|
14
|
-
|
15
4
|
describe EenyMeeny::Experiment do
|
16
5
|
describe 'when initialized' do
|
6
|
+
let(:experiment_options) { {} }
|
7
|
+
subject do
|
8
|
+
build_experiment(**experiment_options)
|
9
|
+
end
|
17
10
|
|
18
11
|
context 'with weighted variations' do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
12
|
+
let(:experiment_options) do
|
13
|
+
{
|
14
|
+
variations: {
|
15
|
+
a: { name: 'A', weight: 0.5 },
|
16
|
+
b: { name: 'B', weight: 0.3 }
|
17
|
+
}
|
18
|
+
}
|
25
19
|
end
|
26
20
|
|
27
21
|
it 'sets the instance variables' do
|
@@ -43,14 +37,6 @@ describe EenyMeeny::Experiment do
|
|
43
37
|
end
|
44
38
|
|
45
39
|
context 'with non-weighted variations' do
|
46
|
-
subject do
|
47
|
-
described_class.new(:experiment_1,
|
48
|
-
name: 'Test 1',
|
49
|
-
variations: {
|
50
|
-
a: { name: 'A' },
|
51
|
-
b: { name: 'B' }})
|
52
|
-
end
|
53
|
-
|
54
40
|
it 'sets the instance variables' do
|
55
41
|
expect(subject.id).to eq(:experiment_1)
|
56
42
|
expect(subject.name).to eq('Test 1')
|
@@ -93,58 +79,68 @@ describe EenyMeeny::Experiment do
|
|
93
79
|
|
94
80
|
context 'when the experiment only have an end_at time' do
|
95
81
|
context 'and the current time < end_at' do
|
82
|
+
let(:experiment_options) { { end_at: (Time.zone.now+3600).iso8601 }}
|
83
|
+
|
96
84
|
it 'returns true' do
|
97
|
-
|
98
|
-
expect(instance.active?).to be true
|
85
|
+
expect(subject.active?).to be true
|
99
86
|
end
|
100
87
|
end
|
101
88
|
|
102
89
|
context 'and the current time > end_at' do
|
90
|
+
let(:experiment_options) { { end_at: (Time.zone.now-3600).iso8601 }}
|
91
|
+
|
103
92
|
it 'returns false' do
|
104
|
-
|
105
|
-
expect(instance.active?).to be false
|
93
|
+
expect(subject.active?).to be false
|
106
94
|
end
|
107
95
|
end
|
108
96
|
end
|
109
97
|
|
110
98
|
context 'when the experiment only have a start_at time' do
|
111
99
|
context 'and the current time < start_at' do
|
100
|
+
let(:experiment_options) { { start_at: (Time.zone.now+3600).iso8601 } }
|
101
|
+
|
112
102
|
it 'returns false' do
|
113
|
-
|
114
|
-
expect(instance.active?).to be false
|
103
|
+
expect(subject.active?).to be false
|
115
104
|
end
|
116
105
|
end
|
117
106
|
|
118
107
|
context 'and the current time > start_at' do
|
108
|
+
let(:experiment_options) { { start_at: (Time.zone.now-3600).iso8601 } }
|
109
|
+
|
119
110
|
it 'returns true' do
|
120
|
-
|
121
|
-
expect(instance.active?).to be true
|
111
|
+
expect(subject.active?).to be true
|
122
112
|
end
|
123
113
|
end
|
124
114
|
end
|
125
115
|
|
126
116
|
context 'when the experiment both have a start_at and end_at time' do
|
127
117
|
context 'and current_time < start_at' do
|
118
|
+
let(:experiment_options) do
|
119
|
+
{ start_at: (Time.zone.now+3600).iso8601, end_at: (Time.zone.now+7200).iso8601 }
|
120
|
+
end
|
121
|
+
|
128
122
|
it 'returns false' do
|
129
|
-
|
130
|
-
end_at: (Time.zone.now+7200).iso8601)
|
131
|
-
expect(instance.active?).to be false
|
123
|
+
expect(subject.active?).to be false
|
132
124
|
end
|
133
125
|
end
|
134
126
|
|
135
127
|
context 'and current_time > start_at and current time < end_at' do
|
128
|
+
let(:experiment_options) do
|
129
|
+
{ start_at: (Time.zone.now-3600).iso8601, end_at: (Time.zone.now+7200).iso8601 }
|
130
|
+
end
|
131
|
+
|
136
132
|
it 'returns true' do
|
137
|
-
|
138
|
-
end_at: (Time.zone.now+7200).iso8601)
|
139
|
-
expect(instance.active?).to be true
|
133
|
+
expect(subject.active?).to be true
|
140
134
|
end
|
141
135
|
end
|
142
136
|
|
143
137
|
context 'and current time > start_at and current time > end_at' do
|
138
|
+
let(:experiment_options) do
|
139
|
+
{ start_at: (Time.zone.now-7200).iso8601, end_at: (Time.zone.now-3600).iso8601 }
|
140
|
+
end
|
141
|
+
|
144
142
|
it 'returns false' do
|
145
|
-
|
146
|
-
end_at: (Time.zone.now-3600).iso8601)
|
147
|
-
expect(instance.active?).to be false
|
143
|
+
expect(subject.active?).to be false
|
148
144
|
end
|
149
145
|
end
|
150
146
|
end
|
data/spec/mock_rack_app.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
require 'simplecov'
|
2
|
+
require 'active_support'
|
2
3
|
require 'active_support/time'
|
3
4
|
require 'rspec'
|
5
|
+
require 'rack/test'
|
4
6
|
require 'yaml'
|
5
7
|
require 'mock_rack_app'
|
6
8
|
require 'eeny-meeny'
|
7
9
|
|
10
|
+
Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
11
|
+
|
8
12
|
RSpec.configure do |config|
|
13
|
+
config.include Rack::Test::Methods
|
9
14
|
config.run_all_when_everything_filtered = true
|
10
15
|
config.filter_run :focus
|
11
16
|
config.order = "random"
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def expect_set_cookie_with(response: {}, value: '')
|
4
|
+
expect(response).to be
|
5
|
+
expect(response[Rack::SET_COOKIE]).to be_an(Array)
|
6
|
+
expect(response[Rack::SET_COOKIE]).to_not be_empty
|
7
|
+
expect(response[Rack::SET_COOKIE].find { |cookie_string| cookie_string.include?(value) }).to be
|
8
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'eeny-meeny/models/experiment'
|
4
|
+
|
5
|
+
def build_experiment(id: :experiment_1, **options)
|
6
|
+
experiment_options = {
|
7
|
+
name: 'Test 1',
|
8
|
+
variations: {
|
9
|
+
a: { name: 'A' },
|
10
|
+
b: { name: 'B' }}
|
11
|
+
}.merge(options)
|
12
|
+
EenyMeeny::Experiment.new(id, **experiment_options)
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eeny-meeny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Orthmann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -92,34 +92,34 @@ dependencies:
|
|
92
92
|
requirements:
|
93
93
|
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
95
|
+
version: '2'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
100
|
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '
|
102
|
+
version: '2'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: rack
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 2.0.0
|
110
110
|
- - "<"
|
111
111
|
- !ruby/object:Gem::Version
|
112
|
-
version: '
|
112
|
+
version: '4'
|
113
113
|
type: :runtime
|
114
114
|
prerelease: false
|
115
115
|
version_requirements: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
117
|
- - ">="
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
119
|
+
version: 2.0.0
|
120
120
|
- - "<"
|
121
121
|
- !ruby/object:Gem::Version
|
122
|
-
version: '
|
122
|
+
version: '4'
|
123
123
|
- !ruby/object:Gem::Dependency
|
124
124
|
name: activesupport
|
125
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,7 +129,7 @@ dependencies:
|
|
129
129
|
version: 3.0.0
|
130
130
|
- - "<"
|
131
131
|
- !ruby/object:Gem::Version
|
132
|
-
version:
|
132
|
+
version: '8'
|
133
133
|
type: :runtime
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -139,7 +139,7 @@ dependencies:
|
|
139
139
|
version: 3.0.0
|
140
140
|
- - "<"
|
141
141
|
- !ruby/object:Gem::Version
|
142
|
-
version:
|
142
|
+
version: '8'
|
143
143
|
description:
|
144
144
|
email: christian.orthmann@gmail.com
|
145
145
|
executables: []
|
@@ -178,6 +178,8 @@ files:
|
|
178
178
|
- spec/fixtures/experiments.yml
|
179
179
|
- spec/mock_rack_app.rb
|
180
180
|
- spec/spec_helper.rb
|
181
|
+
- spec/support/custom_expectations/expect_cookie_with.rb
|
182
|
+
- spec/support/factories/experiment.rb
|
181
183
|
- spec/tasks/cookie_task_spec.rb
|
182
184
|
homepage: http://rubygems.org/gems/eeny-meeny
|
183
185
|
licenses:
|
@@ -198,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
200
|
- !ruby/object:Gem::Version
|
199
201
|
version: '0'
|
200
202
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
203
|
+
rubygems_version: 3.5.3
|
202
204
|
signing_key:
|
203
205
|
specification_version: 4
|
204
206
|
summary: A simple split and smoke testing tool for Rails
|