rollout 2.4.5 → 2.4.6
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/.circleci/config.yml +18 -3
- data/README.md +8 -0
- data/lib/rollout.rb +8 -128
- data/lib/rollout/feature.rb +130 -0
- data/lib/rollout/version.rb +1 -1
- data/rollout.gemspec +3 -3
- data/spec/rollout_spec.rb +1 -2
- data/spec/spec_helper.rb +9 -3
- metadata +10 -10
- data/Gemfile.lock +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3e6e9521ed03059b5b673616e985f89231d77d1ca42d8fa7ff00797ef906768
|
4
|
+
data.tar.gz: ac66f6f5f0da7e03bbf506d09f62d34e6afd7f1ea09b248b5193d74f3b6a720e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccebf1c2024c332cd921a49b400a7ee8e9a5c0ce95cdf59f5e89fb302bef08684cc04ffae30c952e14ffd5acc97d583871ee590aebe6aa2c519cc416afc17b9f
|
7
|
+
data.tar.gz: fe0a400d1f694a6aefb5b1e7d89afb64a8cb28c07a1f510d01655d724bfb97d5b38e7c60e40669aec5fded5581e760549b53564078018cad92c968b6900753ee
|
data/.circleci/config.yml
CHANGED
@@ -3,24 +3,33 @@ version: 2.1
|
|
3
3
|
workflows:
|
4
4
|
main:
|
5
5
|
jobs:
|
6
|
+
- ruby27
|
6
7
|
- ruby26
|
7
8
|
- ruby25
|
8
9
|
- ruby24
|
9
10
|
- ruby23
|
10
11
|
|
11
12
|
executors:
|
13
|
+
ruby27:
|
14
|
+
docker:
|
15
|
+
- image: circleci/ruby:2.7
|
16
|
+
- image: circleci/redis:alpine
|
12
17
|
ruby26:
|
13
18
|
docker:
|
14
19
|
- image: circleci/ruby:2.6
|
20
|
+
- image: circleci/redis:alpine
|
15
21
|
ruby25:
|
16
22
|
docker:
|
17
23
|
- image: circleci/ruby:2.5
|
24
|
+
- image: circleci/redis:alpine
|
18
25
|
ruby24:
|
19
26
|
docker:
|
20
27
|
- image: circleci/ruby:2.4
|
28
|
+
- image: circleci/redis:alpine
|
21
29
|
ruby23:
|
22
30
|
docker:
|
23
31
|
- image: circleci/ruby:2.3
|
32
|
+
- image: circleci/redis:alpine
|
24
33
|
|
25
34
|
commands:
|
26
35
|
test:
|
@@ -31,7 +40,7 @@ commands:
|
|
31
40
|
|
32
41
|
- run:
|
33
42
|
name: Bundle Install
|
34
|
-
command: bundle check --path vendor/bundle || bundle install
|
43
|
+
command: bundle check --path vendor/bundle || bundle install
|
35
44
|
|
36
45
|
- save_cache:
|
37
46
|
key: bundler-{{ checksum "Gemfile.lock" }}
|
@@ -44,8 +53,8 @@ commands:
|
|
44
53
|
bundle exec rspec --format documentation --format RspecJunitFormatter --out test_results/rspec.xml
|
45
54
|
|
46
55
|
jobs:
|
47
|
-
|
48
|
-
executor:
|
56
|
+
ruby27:
|
57
|
+
executor: ruby27
|
49
58
|
steps:
|
50
59
|
- checkout
|
51
60
|
- test
|
@@ -61,6 +70,12 @@ jobs:
|
|
61
70
|
- store_test_results:
|
62
71
|
path: test_results
|
63
72
|
|
73
|
+
ruby26:
|
74
|
+
executor: ruby26
|
75
|
+
steps:
|
76
|
+
- checkout
|
77
|
+
- test
|
78
|
+
|
64
79
|
ruby25:
|
65
80
|
executor: ruby25
|
66
81
|
steps:
|
data/README.md
CHANGED
@@ -24,6 +24,14 @@ $redis = Redis.new
|
|
24
24
|
$rollout = Rollout.new($redis)
|
25
25
|
```
|
26
26
|
|
27
|
+
or even simpler
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'redis'
|
31
|
+
$rollout = Rollout.new(Redis.current) # Will use REDIS_URL env var or default redis url
|
32
|
+
```
|
33
|
+
|
34
|
+
|
27
35
|
Update data specific to a feature:
|
28
36
|
|
29
37
|
```ruby
|
data/lib/rollout.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rollout/feature'
|
3
4
|
require 'rollout/version'
|
4
5
|
require 'zlib'
|
5
6
|
require 'set'
|
@@ -8,133 +9,6 @@ require 'json'
|
|
8
9
|
class Rollout
|
9
10
|
RAND_BASE = (2**32 - 1) / 100.0
|
10
11
|
|
11
|
-
class Feature
|
12
|
-
attr_accessor :groups, :users, :percentage, :data
|
13
|
-
attr_reader :name, :options
|
14
|
-
|
15
|
-
def initialize(name, string = nil, opts = {})
|
16
|
-
@options = opts
|
17
|
-
@name = name
|
18
|
-
|
19
|
-
if string
|
20
|
-
raw_percentage, raw_users, raw_groups, raw_data = string.split('|', 4)
|
21
|
-
@percentage = raw_percentage.to_f
|
22
|
-
@users = users_from_string(raw_users)
|
23
|
-
@groups = groups_from_string(raw_groups)
|
24
|
-
@data = raw_data.nil? || raw_data.strip.empty? ? {} : JSON.parse(raw_data)
|
25
|
-
else
|
26
|
-
clear
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def serialize
|
31
|
-
"#{@percentage}|#{@users.to_a.join(',')}|#{@groups.to_a.join(',')}|#{serialize_data}"
|
32
|
-
end
|
33
|
-
|
34
|
-
def add_user(user)
|
35
|
-
id = user_id(user)
|
36
|
-
@users << id unless @users.include?(id)
|
37
|
-
end
|
38
|
-
|
39
|
-
def remove_user(user)
|
40
|
-
@users.delete(user_id(user))
|
41
|
-
end
|
42
|
-
|
43
|
-
def add_group(group)
|
44
|
-
@groups << group.to_sym unless @groups.include?(group.to_sym)
|
45
|
-
end
|
46
|
-
|
47
|
-
def remove_group(group)
|
48
|
-
@groups.delete(group.to_sym)
|
49
|
-
end
|
50
|
-
|
51
|
-
def clear
|
52
|
-
@groups = groups_from_string('')
|
53
|
-
@users = users_from_string('')
|
54
|
-
@percentage = 0
|
55
|
-
@data = {}
|
56
|
-
end
|
57
|
-
|
58
|
-
def active?(rollout, user)
|
59
|
-
if user
|
60
|
-
id = user_id(user)
|
61
|
-
user_in_percentage?(id) ||
|
62
|
-
user_in_active_users?(id) ||
|
63
|
-
user_in_active_group?(user, rollout)
|
64
|
-
else
|
65
|
-
@percentage == 100
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def user_in_active_users?(user)
|
70
|
-
@users.include?(user_id(user))
|
71
|
-
end
|
72
|
-
|
73
|
-
def to_hash
|
74
|
-
{
|
75
|
-
percentage: @percentage,
|
76
|
-
groups: @groups,
|
77
|
-
users: @users
|
78
|
-
}
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def user_id(user)
|
84
|
-
if user.is_a?(Integer) || user.is_a?(String)
|
85
|
-
user.to_s
|
86
|
-
else
|
87
|
-
user.send(id_user_by).to_s
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def id_user_by
|
92
|
-
@options[:id_user_by] || :id
|
93
|
-
end
|
94
|
-
|
95
|
-
def user_in_percentage?(user)
|
96
|
-
Zlib.crc32(user_id_for_percentage(user)) < RAND_BASE * @percentage
|
97
|
-
end
|
98
|
-
|
99
|
-
def user_id_for_percentage(user)
|
100
|
-
if @options[:randomize_percentage]
|
101
|
-
user_id(user).to_s + @name.to_s
|
102
|
-
else
|
103
|
-
user_id(user)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def user_in_active_group?(user, rollout)
|
108
|
-
@groups.any? do |g|
|
109
|
-
rollout.active_in_group?(g, user)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def serialize_data
|
114
|
-
return '' unless @data.is_a? Hash
|
115
|
-
|
116
|
-
@data.to_json
|
117
|
-
end
|
118
|
-
|
119
|
-
def users_from_string(raw_users)
|
120
|
-
users = (raw_users || '').split(',').map(&:to_s)
|
121
|
-
if @options[:use_sets]
|
122
|
-
users.to_set
|
123
|
-
else
|
124
|
-
users
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def groups_from_string(raw_groups)
|
129
|
-
groups = (raw_groups || '').split(',').map(&:to_sym)
|
130
|
-
if @options[:use_sets]
|
131
|
-
groups.to_set
|
132
|
-
else
|
133
|
-
groups
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
12
|
def initialize(storage, opts = {})
|
139
13
|
@storage = storage
|
140
14
|
@options = opts
|
@@ -296,7 +170,13 @@ class Rollout
|
|
296
170
|
end
|
297
171
|
|
298
172
|
def exists?(feature)
|
299
|
-
|
173
|
+
# since redis-rb v4.2, `#exists?` replaces `#exists` which now returns integer value instead of boolean
|
174
|
+
# https://github.com/redis/redis-rb/pull/918
|
175
|
+
if @storage.respond_to?(:exists?)
|
176
|
+
@storage.exists?(key(feature))
|
177
|
+
else
|
178
|
+
@storage.exists(key(feature))
|
179
|
+
end
|
300
180
|
end
|
301
181
|
|
302
182
|
private
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rollout
|
4
|
+
class Feature
|
5
|
+
attr_accessor :groups, :users, :percentage, :data
|
6
|
+
attr_reader :name, :options
|
7
|
+
|
8
|
+
def initialize(name, string = nil, opts = {})
|
9
|
+
@options = opts
|
10
|
+
@name = name
|
11
|
+
|
12
|
+
if string
|
13
|
+
raw_percentage, raw_users, raw_groups, raw_data = string.split('|', 4)
|
14
|
+
@percentage = raw_percentage.to_f
|
15
|
+
@users = users_from_string(raw_users)
|
16
|
+
@groups = groups_from_string(raw_groups)
|
17
|
+
@data = raw_data.nil? || raw_data.strip.empty? ? {} : JSON.parse(raw_data)
|
18
|
+
else
|
19
|
+
clear
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def serialize
|
24
|
+
"#{@percentage}|#{@users.to_a.join(',')}|#{@groups.to_a.join(',')}|#{serialize_data}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_user(user)
|
28
|
+
id = user_id(user)
|
29
|
+
@users << id unless @users.include?(id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_user(user)
|
33
|
+
@users.delete(user_id(user))
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_group(group)
|
37
|
+
@groups << group.to_sym unless @groups.include?(group.to_sym)
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_group(group)
|
41
|
+
@groups.delete(group.to_sym)
|
42
|
+
end
|
43
|
+
|
44
|
+
def clear
|
45
|
+
@groups = groups_from_string('')
|
46
|
+
@users = users_from_string('')
|
47
|
+
@percentage = 0
|
48
|
+
@data = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def active?(rollout, user)
|
52
|
+
if user
|
53
|
+
id = user_id(user)
|
54
|
+
user_in_percentage?(id) ||
|
55
|
+
user_in_active_users?(id) ||
|
56
|
+
user_in_active_group?(user, rollout)
|
57
|
+
else
|
58
|
+
@percentage == 100
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def user_in_active_users?(user)
|
63
|
+
@users.include?(user_id(user))
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_hash
|
67
|
+
{
|
68
|
+
percentage: @percentage,
|
69
|
+
groups: @groups,
|
70
|
+
users: @users
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def user_id(user)
|
77
|
+
if user.is_a?(Integer) || user.is_a?(String)
|
78
|
+
user.to_s
|
79
|
+
else
|
80
|
+
user.send(id_user_by).to_s
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def id_user_by
|
85
|
+
@options[:id_user_by] || :id
|
86
|
+
end
|
87
|
+
|
88
|
+
def user_in_percentage?(user)
|
89
|
+
Zlib.crc32(user_id_for_percentage(user)) < RAND_BASE * @percentage
|
90
|
+
end
|
91
|
+
|
92
|
+
def user_id_for_percentage(user)
|
93
|
+
if @options[:randomize_percentage]
|
94
|
+
user_id(user).to_s + @name.to_s
|
95
|
+
else
|
96
|
+
user_id(user)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def user_in_active_group?(user, rollout)
|
101
|
+
@groups.any? do |g|
|
102
|
+
rollout.active_in_group?(g, user)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def serialize_data
|
107
|
+
return '' unless @data.is_a? Hash
|
108
|
+
|
109
|
+
@data.to_json
|
110
|
+
end
|
111
|
+
|
112
|
+
def users_from_string(raw_users)
|
113
|
+
users = (raw_users || '').split(',').map(&:to_s)
|
114
|
+
if @options[:use_sets]
|
115
|
+
users.to_set
|
116
|
+
else
|
117
|
+
users
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def groups_from_string(raw_groups)
|
122
|
+
groups = (raw_groups || '').split(',').map(&:to_sym)
|
123
|
+
if @options[:use_sets]
|
124
|
+
groups.to_set
|
125
|
+
else
|
126
|
+
groups
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/rollout/version.rb
CHANGED
data/rollout.gemspec
CHANGED
@@ -22,10 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_dependency 'redis', '~> 4.0'
|
24
24
|
|
25
|
-
spec.add_development_dependency 'bundler', '
|
26
|
-
spec.add_development_dependency '
|
25
|
+
spec.add_development_dependency 'bundler', '>= 1.17'
|
26
|
+
spec.add_development_dependency 'pry'
|
27
27
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
28
28
|
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4'
|
29
29
|
spec.add_development_dependency 'rubocop', '~> 0.71'
|
30
|
-
spec.add_development_dependency 'simplecov', '
|
30
|
+
spec.add_development_dependency 'simplecov', '0.17'
|
31
31
|
end
|
data/spec/rollout_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -5,8 +5,14 @@ require 'simplecov'
|
|
5
5
|
SimpleCov.start
|
6
6
|
|
7
7
|
require 'bundler/setup'
|
8
|
-
require
|
9
|
-
require
|
8
|
+
require 'redis'
|
9
|
+
require 'rollout'
|
10
|
+
|
11
|
+
Redis.current = Redis.new(
|
12
|
+
host: ENV.fetch('REDIS_HOST', '127.0.0.1'),
|
13
|
+
port: ENV.fetch('REDIS_PORT', '6379'),
|
14
|
+
db: ENV.fetch('REDIS_DB', '7'),
|
15
|
+
)
|
10
16
|
|
11
17
|
RSpec.configure do |config|
|
12
18
|
config.example_status_persistence_file_path = '.rspec_status'
|
@@ -17,5 +23,5 @@ RSpec.configure do |config|
|
|
17
23
|
c.syntax = :expect
|
18
24
|
end
|
19
25
|
|
20
|
-
config.before { Redis.
|
26
|
+
config.before { Redis.current.flushdb }
|
21
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rollout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Golick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -28,18 +28,18 @@ dependencies:
|
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.17'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.17'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: pry
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -98,16 +98,16 @@ dependencies:
|
|
98
98
|
name: simplecov
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
103
|
+
version: '0.17'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
110
|
+
version: '0.17'
|
111
111
|
description: Feature flippers with redis.
|
112
112
|
email:
|
113
113
|
- jamesgolick@gmail.com
|
@@ -121,11 +121,11 @@ files:
|
|
121
121
|
- ".rubocop.yml"
|
122
122
|
- ".travis.yml"
|
123
123
|
- Gemfile
|
124
|
-
- Gemfile.lock
|
125
124
|
- LICENSE
|
126
125
|
- README.md
|
127
126
|
- Rakefile
|
128
127
|
- lib/rollout.rb
|
128
|
+
- lib/rollout/feature.rb
|
129
129
|
- lib/rollout/version.rb
|
130
130
|
- rollout.gemspec
|
131
131
|
- spec/rollout_spec.rb
|
data/Gemfile.lock
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
rollout (2.4.5)
|
5
|
-
redis (~> 4.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
ast (2.4.0)
|
11
|
-
diff-lcs (1.3)
|
12
|
-
docile (1.3.2)
|
13
|
-
fakeredis (0.7.0)
|
14
|
-
redis (>= 3.2, < 5.0)
|
15
|
-
jaro_winkler (1.5.3)
|
16
|
-
json (2.2.0)
|
17
|
-
parallel (1.17.0)
|
18
|
-
parser (2.6.3.0)
|
19
|
-
ast (~> 2.4.0)
|
20
|
-
rainbow (3.0.0)
|
21
|
-
redis (4.1.2)
|
22
|
-
rspec (3.8.0)
|
23
|
-
rspec-core (~> 3.8.0)
|
24
|
-
rspec-expectations (~> 3.8.0)
|
25
|
-
rspec-mocks (~> 3.8.0)
|
26
|
-
rspec-core (3.8.2)
|
27
|
-
rspec-support (~> 3.8.0)
|
28
|
-
rspec-expectations (3.8.4)
|
29
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
-
rspec-support (~> 3.8.0)
|
31
|
-
rspec-mocks (3.8.1)
|
32
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
-
rspec-support (~> 3.8.0)
|
34
|
-
rspec-support (3.8.2)
|
35
|
-
rspec_junit_formatter (0.4.1)
|
36
|
-
rspec-core (>= 2, < 4, != 2.12.0)
|
37
|
-
rubocop (0.72.0)
|
38
|
-
jaro_winkler (~> 1.5.1)
|
39
|
-
parallel (~> 1.10)
|
40
|
-
parser (>= 2.6)
|
41
|
-
rainbow (>= 2.2.2, < 4.0)
|
42
|
-
ruby-progressbar (~> 1.7)
|
43
|
-
unicode-display_width (>= 1.4.0, < 1.7)
|
44
|
-
ruby-progressbar (1.10.1)
|
45
|
-
simplecov (0.16.1)
|
46
|
-
docile (~> 1.1)
|
47
|
-
json (>= 1.8, < 3)
|
48
|
-
simplecov-html (~> 0.10.0)
|
49
|
-
simplecov-html (0.10.2)
|
50
|
-
unicode-display_width (1.6.0)
|
51
|
-
|
52
|
-
PLATFORMS
|
53
|
-
ruby
|
54
|
-
|
55
|
-
DEPENDENCIES
|
56
|
-
bundler (~> 1.17)
|
57
|
-
fakeredis
|
58
|
-
rollout!
|
59
|
-
rspec (~> 3.0)
|
60
|
-
rspec_junit_formatter (~> 0.4)
|
61
|
-
rubocop (~> 0.71)
|
62
|
-
simplecov (~> 0.16)
|
63
|
-
|
64
|
-
BUNDLED WITH
|
65
|
-
1.17.2
|