Class: Rollout

Inherits:
Object
  • Object
show all
Includes:
Observable
Defined in:
lib/rollout.rb,
lib/rollout/feature.rb,
lib/rollout/logging.rb,
lib/rollout/version.rb

Defined Under Namespace

Modules: Logging Classes: Feature

Constant Summary collapse

RAND_BASE =
(2**32 - 1) / 100.0
VERSION =
'2.6.1'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(storage, opts = {}) ⇒ Rollout

Returns a new instance of Rollout.



18
19
20
21
22
23
24
# File 'lib/rollout.rb', line 18

def initialize(storage, opts = {})
  @storage = storage
  @options = opts
  @groups  = { all: ->(_user) { true } }

  extend(Logging) if opts[:logging]
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



16
17
18
# File 'lib/rollout.rb', line 16

def options
  @options
end

#storageObject (readonly)

Returns the value of attribute storage.



16
17
18
# File 'lib/rollout.rb', line 16

def storage
  @storage
end

Instance Method Details

#activate(feature) ⇒ Object



30
31
32
33
34
# File 'lib/rollout.rb', line 30

def activate(feature)
  with_feature(feature) do |f|
    f.percentage = 100
  end
end

#activate_group(feature, group) ⇒ Object



61
62
63
64
65
# File 'lib/rollout.rb', line 61

def activate_group(feature, group)
  with_feature(feature) do |f|
    f.add_group(group)
  end
end

#activate_percentage(feature, percentage) ⇒ Object



122
123
124
125
126
# File 'lib/rollout.rb', line 122

def activate_percentage(feature, percentage)
  with_feature(feature) do |f|
    f.percentage = percentage
  end
end

#activate_user(feature, user) ⇒ Object



73
74
75
76
77
# File 'lib/rollout.rb', line 73

def activate_user(feature, user)
  with_feature(feature) do |f|
    f.add_user(user)
  end
end

#activate_users(feature, users) ⇒ Object



85
86
87
88
89
# File 'lib/rollout.rb', line 85

def activate_users(feature, users)
  with_feature(feature) do |f|
    users.each { |user| f.add_user(user) }
  end
end

#active?(feature, user = nil) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
111
# File 'lib/rollout.rb', line 108

def active?(feature, user = nil)
  feature = get(feature)
  feature.active?(user)
end

#active_features(user = nil) ⇒ Object



179
180
181
182
183
# File 'lib/rollout.rb', line 179

def active_features(user = nil)
  multi_get(*features).select do |f|
    f.active?(user)
  end.map(&:name)
end

#active_in_group?(group, user) ⇒ Boolean

Returns:

  • (Boolean)


134
135
136
137
# File 'lib/rollout.rb', line 134

def active_in_group?(group, user)
  f = @groups[group.to_sym]
  f&.call(user)
end

#clear!Object



185
186
187
188
189
190
191
192
# File 'lib/rollout.rb', line 185

def clear!
  features.each do |feature|
    with_feature(feature, &:clear)
    @storage.del(key(feature))
  end

  @storage.del(features_key)
end

#clear_feature_data(feature) ⇒ Object



150
151
152
153
154
# File 'lib/rollout.rb', line 150

def clear_feature_data(feature)
  with_feature(feature) do |f|
    f.data = {}
  end
end

#deactivate(feature) ⇒ Object



36
37
38
# File 'lib/rollout.rb', line 36

def deactivate(feature)
  with_feature(feature, &:clear)
end

#deactivate_group(feature, group) ⇒ Object



67
68
69
70
71
# File 'lib/rollout.rb', line 67

def deactivate_group(feature, group)
  with_feature(feature) do |f|
    f.remove_group(group)
  end
end

#deactivate_percentage(feature) ⇒ Object



128
129
130
131
132
# File 'lib/rollout.rb', line 128

def deactivate_percentage(feature)
  with_feature(feature) do |f|
    f.percentage = 0
  end
end

#deactivate_user(feature, user) ⇒ Object



79
80
81
82
83
# File 'lib/rollout.rb', line 79

def deactivate_user(feature, user)
  with_feature(feature) do |f|
    f.remove_user(user)
  end
end

#deactivate_users(feature, users) ⇒ Object



91
92
93
94
95
# File 'lib/rollout.rb', line 91

def deactivate_users(feature, users)
  with_feature(feature) do |f|
    users.each { |user| f.remove_user(user) }
  end
end

#define_group(group, &block) ⇒ Object



104
105
106
# File 'lib/rollout.rb', line 104

def define_group(group, &block)
  @groups[group.to_sym] = block
end

#delete(feature) ⇒ Object



40
41
42
43
44
45
46
47
48
49
# File 'lib/rollout.rb', line 40

def delete(feature)
  features = (@storage.get(features_key) || '').split(',')
  features.delete(feature.to_s)
  @storage.set(features_key, features.join(','))
  @storage.del(key(feature))

  if respond_to?(:logging)
    logging.delete(feature)
  end
end

#exists?(feature) ⇒ Boolean

Returns:

  • (Boolean)


194
195
196
197
198
199
200
201
202
# File 'lib/rollout.rb', line 194

def exists?(feature)
  # since redis-rb v4.2, `#exists?` replaces `#exists` which now returns integer value instead of boolean
  # https://github.com/redis/redis-rb/pull/918
  if @storage.respond_to?(:exists?)
    @storage.exists?(key(feature))
  else
    @storage.exists(key(feature))
  end
end

#feature_states(user = nil) ⇒ Object



173
174
175
176
177
# File 'lib/rollout.rb', line 173

def feature_states(user = nil)
  multi_get(*features).each_with_object({}) do |f, hash|
    hash[f.name] = f.active?(user)
  end
end

#featuresObject



169
170
171
# File 'lib/rollout.rb', line 169

def features
  (@storage.get(features_key) || '').split(',').map(&:to_sym)
end

#get(feature) ⇒ Object



139
140
141
142
# File 'lib/rollout.rb', line 139

def get(feature)
  string = @storage.get(key(feature))
  Feature.new(feature, state: string, rollout: self, options: @options)
end

#groupsObject



26
27
28
# File 'lib/rollout.rb', line 26

def groups
  @groups.keys
end

#inactive?(feature, user = nil) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/rollout.rb', line 118

def inactive?(feature, user = nil)
  !active?(feature, user)
end

#multi_get(*features) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rollout.rb', line 156

def multi_get(*features)
  return [] if features.empty?

  feature_keys = features.map { |feature| key(feature) }

  @storage
    .mget(*feature_keys)
    .map
    .with_index do |string, index|
      Feature.new(features[index], state: string, rollout: self, options: @options)
    end
end

#set(feature, desired_state) ⇒ Object



51
52
53
54
55
56
57
58
59
# File 'lib/rollout.rb', line 51

def set(feature, desired_state)
  with_feature(feature) do |f|
    if desired_state
      f.percentage = 100
    else
      f.clear
    end
  end
end

#set_feature_data(feature, data) ⇒ Object



144
145
146
147
148
# File 'lib/rollout.rb', line 144

def set_feature_data(feature, data)
  with_feature(feature) do |f|
    f.data.merge!(data) if data.is_a? Hash
  end
end

#set_users(feature, users) ⇒ Object



97
98
99
100
101
102
# File 'lib/rollout.rb', line 97

def set_users(feature, users)
  with_feature(feature) do |f|
    f.users = []
    users.each { |user| f.add_user(user) }
  end
end

#user_in_active_users?(feature, user = nil) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
116
# File 'lib/rollout.rb', line 113

def user_in_active_users?(feature, user = nil)
  feature = get(feature)
  feature.user_in_active_users?(user)
end

#with_feature(feature) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/rollout.rb', line 204

def with_feature(feature)
  f = get(feature)

  if count_observers > 0
    before = f.deep_clone
    yield(f)
    save(f)
    changed
    notify_observers(:update, before, f)
  else
    yield(f)
    save(f)
  end
end