feature_flagger 1.1.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b54bc71d733eb04b2e52156763557a45f94d9722918b12b09e89a07553b88ee4
4
- data.tar.gz: 5984d522ae2d19f324de342db5810cd9bf351c43a9e60e159f8442209f38575f
3
+ metadata.gz: 79074c1692fc84013e4a00bfaf72483e5267420f66d6a3af668cf8211af5abef
4
+ data.tar.gz: c50835a33526f4a3e58a24101d80bab6f784affd33bea935da82e921f74613b0
5
5
  SHA512:
6
- metadata.gz: ece7587ed9528ca583e61645bc3e5e9fcb710f37308ebbc8bb278390966e8edec6e8dc0a282fbb5864893623da631a1d4f3bffc0876dd484d3523e8008c90c11
7
- data.tar.gz: 9ccdf791f1c7b6c5f10674753ecfbbfb81b4fa69546057e8061b8c0c269c347999503cb4fc868a215073e70cd6b8cb1382c39bcccfb8c11a0fcd2a346601f82d
6
+ metadata.gz: 8ff4fc2a7c9a76252cab626fe285b2adda27348f009f7801fb7ed5c4cb2b49e389d4a29a7b42d5486faadf695cebaf538198da97382240e02cbd0e1f647d762d
7
+ data.tar.gz: c393c77c9a02634d3533c1731536a2cbde8cb7420eee0b696f431cc8d1e79553ddeb59a253cc84274ebb079af6bbe60e762483e92fbdc169a899f60f66d20dfe
data/README.md CHANGED
@@ -28,7 +28,6 @@ Or install it yourself as:
28
28
 
29
29
  $ gem install feature_flagger
30
30
 
31
-
32
31
  ## Configuration
33
32
 
34
33
  By default, feature_flagger uses the REDIS_URL env var to setup it's storage.
@@ -44,6 +43,15 @@ FeatureFlagger.configure do |config|
44
43
  end
45
44
  ```
46
45
 
46
+ It's also possible to configure an additional cache layer by using ActiveSupport::Cache APIs. You can configure it the same way you would setup cache_store for Rails Apps. Caching is not enabled by default.
47
+
48
+
49
+ ```ruby
50
+ configuration.cache_store = :memory_store, { expires_in: 100 }
51
+
52
+ ```
53
+
54
+
47
55
  1. Create a `rollout.yml` in _config_ path and declare a rollout:
48
56
  ```yml
49
57
  account: # model name
@@ -60,6 +68,29 @@ class Account < ActiveRecord::Base
60
68
  # ....
61
69
  end
62
70
  ```
71
+ #### Notifier
72
+ The notifier_callback property in config, enables the dispatch of events when a release operation happens.
73
+ ```ruby
74
+ config.notifier_callback = -> {|event| do something with event }
75
+ ```
76
+
77
+
78
+ It accepts a lambda function that will receive a hash with the operation triggered like:
79
+ ```ruby
80
+ {
81
+ type: 'release',
82
+ model: 'account',
83
+ key: 'somefeature:somerolloutkey'
84
+ id: 'account_id' #In realease_to_all and unrelease_to_all operations id will be nil
85
+ }
86
+ ```
87
+
88
+ The supported operations are:
89
+ * release
90
+ * unrelease
91
+ * release_to_all
92
+ * unrelease_to_all
93
+
63
94
 
64
95
  ## Usage
65
96
 
@@ -74,6 +105,10 @@ account.release(:email_marketing, :new_email_flow)
74
105
  account.released?(:email_marketing, :new_email_flow)
75
106
  #=> true
76
107
 
108
+ # In order to bypass the cache if cache_store is configured
109
+ account.released?(:email_marketing, :new_email_flow, skip_cache: true)
110
+ #=> true
111
+
77
112
  # Remove feature for given account
78
113
  account.unrelease(:email_marketing, :new_email_flow)
79
114
  #=> true
@@ -86,6 +121,10 @@ FeatureFlagger::KeyNotFoundError: ["account", "email_marketing", "new_email_flo"
86
121
  Account.released_id?(42, :email_marketing, :new_email_flow)
87
122
  #=> true
88
123
 
124
+ # In order to bypass the cache if cache_store is configured
125
+ Account.released_id?(42, :email_marketing, :new_email_flow, skip_cache: true)
126
+ #=> true
127
+
89
128
  # Release a feature for a specific account id
90
129
  Account.release_id(42, :email_marketing, :new_email_flow)
91
130
  #=> true
@@ -101,6 +140,10 @@ Account.unrelease_to_all(:email_marketing, :new_email_flow)
101
140
 
102
141
  # Return an array with all features released for all
103
142
  Account.released_features_to_all
143
+
144
+ # In order to bypass the cache if cache_store is configured
145
+ Account.released_features_to_all(skip_cache: true)
146
+
104
147
  ```
105
148
 
106
149
  ## Clean up action
@@ -111,6 +154,13 @@ To clean it up, execute or schedule the rake:
111
154
 
112
155
  $ bundle exec rake feature_flagger:cleanup_removed_rollouts
113
156
 
157
+ ## Upgrading
158
+
159
+ When upgrading from `1.1.x` to `1.2.x` the following command must be executed
160
+ to ensure the data stored in Redis storage is right. Check [#67](https://github.com/ResultadosDigitais/feature_flagger/pull/67) and [#68](https://github.com/ResultadosDigitais/feature_flagger/pull/68) for more info.
161
+
162
+ $ bundle exec rake feature_flagger:migrate_to_resource_keys
163
+
114
164
  ## Contributing
115
165
 
116
166
  Bug reports and pull requests are welcome!
@@ -2,6 +2,7 @@ require 'yaml'
2
2
 
3
3
  require 'feature_flagger/version'
4
4
  require 'feature_flagger/storage/redis'
5
+ require 'feature_flagger/storage/feature_keys_migration'
5
6
  require 'feature_flagger/control'
6
7
  require 'feature_flagger/model'
7
8
  require 'feature_flagger/model_settings'
@@ -9,12 +10,14 @@ require 'feature_flagger/feature'
9
10
  require 'feature_flagger/configuration'
10
11
  require 'feature_flagger/manager'
11
12
  require 'feature_flagger/railtie'
13
+ require 'feature_flagger/notifier'
12
14
 
13
15
  module FeatureFlagger
14
16
  class << self
15
17
  def configure
16
18
  @@configuration = nil
17
19
  @@control = nil
20
+ @@notifier = nil
18
21
  yield config if block_given?
19
22
  end
20
23
 
@@ -22,8 +25,12 @@ module FeatureFlagger
22
25
  @@configuration ||= Configuration.new
23
26
  end
24
27
 
28
+ def notifier
29
+ @@notifier ||= Notifier.new(config.notifier_callback)
30
+ end
31
+
25
32
  def control
26
- @@control ||= Control.new(config.storage)
33
+ @@control ||= Control.new(config.storage, notifier, config.cache_store)
27
34
  end
28
35
  end
29
36
  end
@@ -1,10 +1,17 @@
1
1
  module FeatureFlagger
2
2
  class Configuration
3
- attr_accessor :storage, :yaml_filepath
3
+ attr_accessor :storage, :cache_store, :yaml_filepath, :notifier_callback
4
4
 
5
5
  def initialize
6
6
  @storage ||= Storage::Redis.default_client
7
7
  @yaml_filepath ||= default_yaml_filepath
8
+ @notifier_callback = nil
9
+ @cache_store = nil
10
+ end
11
+
12
+ def cache_store=(cache_store)
13
+ raise ArgumentError, "Cache is only support when used with ActiveSupport" unless defined?(ActiveSupport)
14
+ @cache_store = ActiveSupport::Cache.lookup_store(*cache_store)
8
15
  end
9
16
 
10
17
  def info
@@ -4,44 +4,88 @@ module FeatureFlagger
4
4
 
5
5
  RELEASED_FEATURES = 'released_features'
6
6
 
7
- def initialize(storage)
7
+ def initialize(storage, notifier, cache_store = nil)
8
8
  @storage = storage
9
+ @notifier = notifier
10
+ @cache_store = cache_store
9
11
  end
10
12
 
11
- def released?(feature_key, resource_id)
12
- @storage.has_value?(RELEASED_FEATURES, feature_key) || @storage.has_value?(feature_key, resource_id)
13
+ def released?(feature_key, resource_id, options = {})
14
+ cache "released/#{feature_key}", options do
15
+ @storage.has_value?(RELEASED_FEATURES, feature_key) || @storage.has_value?(feature_key, resource_id)
16
+ end
13
17
  end
14
18
 
15
19
  def release(feature_key, resource_id)
16
- @storage.add(feature_key, resource_id)
20
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
21
+ feature_key
22
+ )
23
+
24
+ @notifier.send(FeatureFlagger::Notifier::RELEASE, feature_key, resource_id)
25
+ @storage.add(feature_key, resource_name, resource_id)
26
+ end
27
+
28
+ def releases(resource_name, resource_id, options = {})
29
+ cache "releases/#{RELEASED_FEATURES}", options do
30
+ @storage.fetch_releases(resource_name, resource_id, RELEASED_FEATURES)
31
+ end
17
32
  end
18
33
 
19
34
  def release_to_all(feature_key)
35
+ @notifier.send(FeatureFlagger::Notifier::RELEASE_TO_ALL, feature_key)
20
36
  @storage.add_all(RELEASED_FEATURES, feature_key)
21
37
  end
22
38
 
23
39
  def unrelease(feature_key, resource_id)
24
- @storage.remove(feature_key, resource_id)
40
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
41
+ feature_key
42
+ )
43
+ @notifier.send(FeatureFlagger::Notifier::UNRELEASE, feature_key, resource_id)
44
+ @storage.remove(feature_key, resource_name, resource_id)
25
45
  end
26
46
 
27
47
  def unrelease_to_all(feature_key)
48
+ @notifier.send(FeatureFlagger::Notifier::UNRELEASE_TO_ALL, feature_key)
28
49
  @storage.remove_all(RELEASED_FEATURES, feature_key)
29
50
  end
30
51
 
31
- def resource_ids(feature_key)
32
- @storage.all_values(feature_key)
52
+ def resource_ids(feature_key, options = {})
53
+ cache "all_values/#{feature_key}", options do
54
+ @storage.all_values(feature_key)
55
+ end
33
56
  end
34
57
 
35
- def released_features_to_all
36
- @storage.all_values(RELEASED_FEATURES)
58
+ def released_features_to_all(options = {})
59
+ cache "all_values/#{RELEASED_FEATURES}", options do
60
+ @storage.all_values(RELEASED_FEATURES)
61
+ end
37
62
  end
38
63
 
39
- def released_to_all?(feature_key)
40
- @storage.has_value?(RELEASED_FEATURES, feature_key)
64
+ def released_to_all?(feature_key, options = {})
65
+ cache "has_value/#{RELEASED_FEATURES}", options do
66
+ @storage.has_value?(RELEASED_FEATURES, feature_key)
67
+ end
41
68
  end
42
69
 
70
+ # DEPRECATED: this method will be removed from public api on v2.0 version.
71
+ # use instead the feature_keys method.
43
72
  def search_keys(query)
44
73
  @storage.search_keys(query)
45
74
  end
75
+
76
+ def feature_keys
77
+ @storage.feature_keys - [FeatureFlagger::Control::RELEASED_FEATURES]
78
+ end
79
+
80
+ def cache(name, options, &block)
81
+ if @cache_store
82
+ @cache_store.fetch(name, force: options[:skip_cache]) do
83
+ block.call
84
+ end
85
+ else
86
+ block.call
87
+ end
88
+ end
89
+
46
90
  end
47
91
  end
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'active_support/core_ext/string/inflections'
3
+ rescue LoadError
4
+ unless ''.respond_to?(:constantize)
5
+ class String
6
+ def constantize
7
+ names = split('::')
8
+ names.shift if names.empty? || names.first.empty?
9
+
10
+ constant = Object
11
+ names.each do |name|
12
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
13
+ end
14
+ constant
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,8 +2,9 @@ module FeatureFlagger
2
2
  class Manager
3
3
 
4
4
  def self.detached_feature_keys
5
- persisted_features = FeatureFlagger.control.search_keys("*").to_a
5
+ persisted_features = FeatureFlagger.control.feature_keys
6
6
  mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys
7
+
7
8
  persisted_features - mapped_feature_keys
8
9
  end
9
10
 
@@ -12,8 +12,8 @@ module FeatureFlagger
12
12
  base.extend ClassMethods
13
13
  end
14
14
 
15
- def released?(*feature_key)
16
- self.class.released_id?(feature_flagger_identifier, feature_key)
15
+ def released?(*feature_key, **options)
16
+ self.class.released_id?(feature_flagger_identifier, feature_key, options)
17
17
  end
18
18
 
19
19
  def release(*feature_key)
@@ -26,6 +26,11 @@ module FeatureFlagger
26
26
  FeatureFlagger.control.unrelease(feature.key, id)
27
27
  end
28
28
 
29
+ def releases(options = {})
30
+ resource_name = self.class.feature_flagger_model_settings.entity_name
31
+ FeatureFlagger.control.releases(resource_name, id, options)
32
+ end
33
+
29
34
  private
30
35
 
31
36
  def feature_flagger_identifier
@@ -38,9 +43,9 @@ module FeatureFlagger
38
43
  yield feature_flagger_model_settings
39
44
  end
40
45
 
41
- def released_id?(resource_id, *feature_key)
46
+ def released_id?(resource_id, *feature_key, **options)
42
47
  feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
43
- FeatureFlagger.control.released?(feature.key, resource_id)
48
+ FeatureFlagger.control.released?(feature.key, resource_id, options)
44
49
  end
45
50
 
46
51
  def release_id(resource_id, *feature_key)
@@ -48,10 +53,15 @@ module FeatureFlagger
48
53
  FeatureFlagger.control.release(feature.key, resource_id)
49
54
  end
50
55
 
51
- def all_released_ids_for(*feature_key)
56
+ def unrelease_id(resource_id, *feature_key)
57
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
58
+ FeatureFlagger.control.unrelease(feature.key, resource_id)
59
+ end
60
+
61
+ def all_released_ids_for(*feature_key, **options)
52
62
  feature_key.flatten!
53
63
  feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
54
- FeatureFlagger.control.resource_ids(feature.key)
64
+ FeatureFlagger.control.resource_ids(feature.key, options)
55
65
  end
56
66
 
57
67
  def release_to_all(*feature_key)
@@ -64,13 +74,13 @@ module FeatureFlagger
64
74
  FeatureFlagger.control.unrelease_to_all(feature.key)
65
75
  end
66
76
 
67
- def released_features_to_all
68
- FeatureFlagger.control.released_features_to_all
77
+ def released_features_to_all(options = {})
78
+ FeatureFlagger.control.released_features_to_all(options)
69
79
  end
70
80
 
71
- def released_to_all?(*feature_key)
81
+ def released_to_all?(*feature_key, **options)
72
82
  feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
73
- FeatureFlagger.control.released_to_all?(feature.key)
83
+ FeatureFlagger.control.released_to_all?(feature.key, options)
74
84
  end
75
85
 
76
86
  def detached_feature_keys
@@ -0,0 +1,45 @@
1
+ module FeatureFlagger
2
+ class Notifier
3
+ attr_reader :notify
4
+
5
+ RELEASE = 'release'.freeze
6
+ UNRELEASE = 'unrelease'.freeze
7
+ RELEASE_TO_ALL = 'release_to_all'.freeze
8
+ UNRELEASE_TO_ALL = 'unrelease_to_all'.freeze
9
+
10
+ def initialize(notify = nil)
11
+ @notify = valid_notify?(notify) ? notify : nullNotify
12
+ end
13
+
14
+ def send(operation, feature_key, resource_id = nil)
15
+ @notify.call(build_event(operation, extract_resource_from_key(feature_key), feature_key, resource_id))
16
+ end
17
+
18
+ private
19
+
20
+ def nullNotify
21
+ lambda {|e| }
22
+ end
23
+
24
+ def valid_notify?(notify)
25
+ !notify.nil? && notify.is_a?(Proc)
26
+ end
27
+
28
+ def extract_resource_from_key(key)
29
+ Storage::Keys.extract_resource_name_from_feature_key(
30
+ key
31
+ )
32
+ rescue FeatureFlagger::Storage::Keys::InvalidResourceNameError
33
+ "legacy key"
34
+ end
35
+
36
+ def build_event(operation, resource_name, feature_key, resource_id)
37
+ {
38
+ type: operation,
39
+ model: resource_name,
40
+ feature: feature_key,
41
+ id: resource_id
42
+ }
43
+ end
44
+ end
45
+ end
@@ -2,8 +2,8 @@ if defined?(Rails)
2
2
  module FeatureFlagger
3
3
  class Railtie < Rails::Railtie
4
4
  rake_tasks do
5
- load 'tasks/cleanup.rake'
5
+ load 'tasks/feature_flagger.rake'
6
6
  end
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FeatureFlagger
4
+ module Storage
5
+ class FeatureKeysMigration
6
+
7
+ def initialize(from_redis, to_control)
8
+ @from_redis = from_redis
9
+ @to_control = to_control
10
+ end
11
+
12
+ # call migrates features key from the old fashioned to the new
13
+ # format.
14
+ #
15
+ # It must replicate feature keys with changes:
16
+ #
17
+ # from "avenue:traffic_lights" => 42
18
+ # to "avenue:42" => traffic_lights
19
+ def call
20
+ @from_redis.scan_each(match: "*", count: FeatureFlagger::Storage::Redis::SCAN_EACH_BATCH_SIZE) do |redis_key|
21
+ # filter out resource_keys
22
+ next if redis_key.start_with?("#{FeatureFlagger::Storage::Redis::RESOURCE_PREFIX}:")
23
+
24
+ migrate_key(redis_key)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def migrate_key(key)
31
+ return migrate_release_to_all(key) if feature_released_to_all?(key)
32
+
33
+ migrate_release(key)
34
+ end
35
+
36
+ def migrate_release_to_all(key)
37
+ features = @from_redis.smembers(key)
38
+
39
+ features.each do |feature_key|
40
+ @to_control.release_to_all(feature_key)
41
+ end
42
+ end
43
+
44
+ def feature_released_to_all?(key)
45
+ FeatureFlagger::Control::RELEASED_FEATURES == key
46
+ end
47
+
48
+ def migrate_release(key)
49
+ resource_ids = @from_redis.smembers(key)
50
+
51
+ resource_ids.each do |id|
52
+ @to_control.release(key, id)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ module FeatureFlagger
2
+ module Storage
3
+ module Keys
4
+ MINIMUM_VALID_FEATURE_PATH = 2.freeze
5
+
6
+ def self.resource_key(prefix, resource_name, resource_id)
7
+ "#{prefix}:#{resource_name}:#{resource_id}"
8
+ end
9
+
10
+ def self.extract_resource_name_from_feature_key(feature_key)
11
+ feature_paths = feature_key.split(':')
12
+ raise InvalidResourceNameError if feature_paths.size < MINIMUM_VALID_FEATURE_PATH
13
+
14
+ feature_paths.first
15
+ end
16
+
17
+ class InvalidResourceNameError < StandardError; end
18
+ end
19
+ end
20
+ end
@@ -1,11 +1,13 @@
1
1
  require 'redis'
2
2
  require 'redis-namespace'
3
+ require_relative './keys'
3
4
 
4
5
  module FeatureFlagger
5
6
  module Storage
6
7
  class Redis
7
-
8
8
  DEFAULT_NAMESPACE = :feature_flagger
9
+ RESOURCE_PREFIX = "_r".freeze
10
+ SCAN_EACH_BATCH_SIZE = 1000.freeze
9
11
 
10
12
  def initialize(redis)
11
13
  @redis = redis
@@ -17,39 +19,104 @@ module FeatureFlagger
17
19
  new(ns)
18
20
  end
19
21
 
22
+ def fetch_releases(resource_name, resource_id, global_key)
23
+ resource_key = resource_key(resource_name, resource_id)
24
+ releases = @redis.sunion(resource_key, global_key)
25
+
26
+ releases.select{ |release| release.start_with?(resource_name) }
27
+ end
28
+
20
29
  def has_value?(key, value)
21
30
  @redis.sismember(key, value)
22
31
  end
23
32
 
24
- def add(key, value)
25
- @redis.sadd(key, value)
26
- end
33
+ def add(feature_key, resource_name, resource_id)
34
+ resource_key = resource_key(resource_name, resource_id)
27
35
 
28
- def remove(key, value)
29
- @redis.srem(key, value)
36
+ @redis.multi do |redis|
37
+ redis.sadd(feature_key, resource_id)
38
+ redis.sadd(resource_key, feature_key)
39
+ end
30
40
  end
31
41
 
32
- def remove_all(global_key, key)
42
+ def remove(feature_key, resource_name, resource_id)
43
+ resource_key = resource_key(resource_name, resource_id)
44
+
33
45
  @redis.multi do |redis|
34
- redis.srem(global_key, key)
35
- redis.del(key)
46
+ redis.srem(feature_key, resource_id)
47
+ redis.srem(resource_key, feature_key)
36
48
  end
37
49
  end
38
50
 
51
+ def remove_all(global_key, feature_key)
52
+ @redis.srem(global_key, feature_key)
53
+ remove_feature_key_from_resources(feature_key)
54
+ end
55
+
39
56
  def add_all(global_key, key)
40
- @redis.multi do |redis|
41
- redis.sadd(global_key, key)
42
- redis.del(key)
43
- end
57
+ @redis.sadd(global_key, key)
58
+ remove_feature_key_from_resources(key)
44
59
  end
45
60
 
46
61
  def all_values(key)
47
62
  @redis.smembers(key)
48
63
  end
49
64
 
65
+ # DEPRECATED: this method will be removed from public api on v2.0 version.
66
+ # use instead the feature_keys method.
50
67
  def search_keys(query)
51
68
  @redis.scan_each(match: query)
52
69
  end
70
+
71
+ def feature_keys
72
+ feature_keys = []
73
+
74
+ @redis.scan_each(match: "*") do |key|
75
+ # Reject keys related to feature responsible for return
76
+ # released features for a given account.
77
+ next if key.start_with?("#{RESOURCE_PREFIX}:")
78
+
79
+ feature_keys << key
80
+ end
81
+
82
+ feature_keys
83
+ end
84
+
85
+ def synchronize_feature_and_resource
86
+ FeatureFlagger::Storage::FeatureKeysMigration.new(
87
+ @redis,
88
+ FeatureFlagger.control,
89
+ ).call
90
+ end
91
+
92
+ private
93
+
94
+ def resource_key(resource_name, resource_id)
95
+ FeatureFlagger::Storage::Keys.resource_key(
96
+ RESOURCE_PREFIX,
97
+ resource_name,
98
+ resource_id,
99
+ )
100
+ end
101
+
102
+ def remove_feature_key_from_resources(feature_key)
103
+ cursor = 0
104
+ resource_name = feature_key.split(":").first
105
+
106
+ loop do
107
+ cursor, resource_ids = @redis.sscan(feature_key, cursor, count: SCAN_EACH_BATCH_SIZE)
108
+
109
+ @redis.multi do |redis|
110
+ resource_ids.each do |resource_id|
111
+ key = resource_key(resource_name, resource_id)
112
+ redis.srem(key, feature_key)
113
+ redis.srem(feature_key, resource_id)
114
+ end
115
+ end
116
+
117
+ break if cursor == "0"
118
+ end
119
+ end
53
120
  end
54
121
  end
55
122
  end
@@ -1,3 +1,3 @@
1
1
  module FeatureFlagger
2
- VERSION = "1.1.0"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -0,0 +1,41 @@
1
+ namespace :feature_flagger do
2
+ desc "cleaning up keys from storage that are no longer in the rollout.yml file"
3
+ task :cleanup_removed_rollouts => :environment do
4
+ keys = FeatureFlagger::Manager.detached_feature_keys
5
+ puts "Found keys to remove: #{keys}"
6
+ keys.each do |key|
7
+ FeatureFlagger::Manager.cleanup_detached key
8
+ end
9
+ end
10
+
11
+ desc "Synchronizes resource_keys with feature_keys, recommended to apps that installed feature flagger before v.1.2.0"
12
+ task :migrate_to_resource_keys => :environment do
13
+ storage = FeatureFlagger.config.storage
14
+ storage.synchronize_feature_and_resource
15
+ end
16
+
17
+ desc "Release feature to given identifiers, Usage: `$ bundle exec rake feature_flagger:release\[Account,email_marketing:whitelabel,1,2,3,4\]`"
18
+ task :release, [:entity_name, :feature_key] => :environment do |_, args|
19
+ entity = args.entity_name.constantize
20
+ entity_ids = args.extras
21
+ entity.release_id(entity_ids, *args.feature_key.split(':'))
22
+ end
23
+
24
+ desc "Unrelease feature to given identifiers, Usage: `$ bundle exec rake feature_flagger:unrelease\[Account,email_marketing:whitelabel,1,2,3,4\]`"
25
+ task :unrelease, [:entity_name, :feature_key] => :environment do |_, args|
26
+ entity, entity_ids = args.entity_name.constantize, args.extras
27
+ entity.unrelease_id(entity_ids, *args.feature_key.split(':'))
28
+ end
29
+
30
+ desc "Release one feature to all entity ids, Usage: `$ bundle exec rake feature_flagger:release_to_all\[Account,email_marketing:whitelabel\]`"
31
+ task :release_to_all, [:entity_name, :feature_key] => :environment do |_, args|
32
+ entity = args.entity_name.constantize
33
+ entity.release_to_all(*args.feature_key.split(':'))
34
+ end
35
+
36
+ desc "Unrelease one feature to all entity ids, Usage: `$ bundle exec rake feature_flagger:unrelease_to_all\[Account,email_marketing:whitelabel\]`"
37
+ task :unrelease_to_all, [:entity_name, :feature_key] => :environment do |_, args|
38
+ entity = args.entity_name.constantize
39
+ entity.unrelease_to_all(*args.feature_key.split(':'))
40
+ end
41
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feature_flagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Sousa
8
8
  - Geison Biazus
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-10-14 00:00:00.000000000 Z
12
+ date: 2021-06-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -39,34 +39,48 @@ dependencies:
39
39
  - - ">"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: activesupport
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">"
47
+ - !ruby/object:Gem::Version
48
+ version: '6.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">"
54
+ - !ruby/object:Gem::Version
55
+ version: '6.0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: bundler
44
58
  requirement: !ruby/object:Gem::Requirement
45
59
  requirements:
46
- - - "~>"
60
+ - - ">="
47
61
  - !ruby/object:Gem::Version
48
- version: '2.0'
62
+ version: '0'
49
63
  type: :development
50
64
  prerelease: false
51
65
  version_requirements: !ruby/object:Gem::Requirement
52
66
  requirements:
53
- - - "~>"
67
+ - - ">="
54
68
  - !ruby/object:Gem::Version
55
- version: '2.0'
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: rake
58
72
  requirement: !ruby/object:Gem::Requirement
59
73
  requirements:
60
74
  - - "~>"
61
75
  - !ruby/object:Gem::Version
62
- version: '10.0'
76
+ version: '13.0'
63
77
  type: :development
64
78
  prerelease: false
65
79
  version_requirements: !ruby/object:Gem::Requirement
66
80
  requirements:
67
81
  - - "~>"
68
82
  - !ruby/object:Gem::Version
69
- version: '10.0'
83
+ version: '13.0'
70
84
  - !ruby/object:Gem::Dependency
71
85
  name: rspec
72
86
  requirement: !ruby/object:Gem::Requirement
@@ -81,20 +95,34 @@ dependencies:
81
95
  - - "~>"
82
96
  - !ruby/object:Gem::Version
83
97
  version: '3.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: simplecov
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '='
103
+ - !ruby/object:Gem::Version
104
+ version: 0.21.2
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '='
110
+ - !ruby/object:Gem::Version
111
+ version: 0.21.2
84
112
  - !ruby/object:Gem::Dependency
85
113
  name: fakeredis
86
114
  requirement: !ruby/object:Gem::Requirement
87
115
  requirements:
88
116
  - - '='
89
117
  - !ruby/object:Gem::Version
90
- version: 0.7.0
118
+ version: 0.8.0
91
119
  type: :development
92
120
  prerelease: false
93
121
  version_requirements: !ruby/object:Gem::Requirement
94
122
  requirements:
95
123
  - - '='
96
124
  - !ruby/object:Gem::Version
97
- version: 0.7.0
125
+ version: 0.8.0
98
126
  description: Management tool to make it easier rollouting features to customers.
99
127
  email:
100
128
  - nandosousafr@gmail.com
@@ -108,19 +136,23 @@ files:
108
136
  - lib/feature_flagger.rb
109
137
  - lib/feature_flagger/configuration.rb
110
138
  - lib/feature_flagger/control.rb
139
+ - lib/feature_flagger/core_ext.rb
111
140
  - lib/feature_flagger/feature.rb
112
141
  - lib/feature_flagger/manager.rb
113
142
  - lib/feature_flagger/model.rb
114
143
  - lib/feature_flagger/model_settings.rb
144
+ - lib/feature_flagger/notifier.rb
115
145
  - lib/feature_flagger/railtie.rb
146
+ - lib/feature_flagger/storage/feature_keys_migration.rb
147
+ - lib/feature_flagger/storage/keys.rb
116
148
  - lib/feature_flagger/storage/redis.rb
117
149
  - lib/feature_flagger/version.rb
118
- - lib/tasks/cleanup.rake
150
+ - lib/tasks/feature_flagger.rake
119
151
  homepage: http://github.com/ResultadosDigitais/feature_flagger
120
152
  licenses:
121
153
  - MIT
122
154
  metadata: {}
123
- post_install_message:
155
+ post_install_message:
124
156
  rdoc_options: []
125
157
  require_paths:
126
158
  - lib
@@ -135,9 +167,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
167
  - !ruby/object:Gem::Version
136
168
  version: 2.0.0
137
169
  requirements: []
138
- rubyforge_project:
139
- rubygems_version: 2.7.9
140
- signing_key:
170
+ rubygems_version: 3.2.3
171
+ signing_key:
141
172
  specification_version: 4
142
173
  summary: Partial release your features.
143
174
  test_files: []
@@ -1,10 +0,0 @@
1
- namespace :feature_flagger do
2
- desc "cleaning up keys from storage that are no longer in the rollout.yml file"
3
- task :cleanup_removed_rollouts => :environment do
4
- keys = FeatureFlagger::Manager.detached_feature_keys
5
- puts "Found keys to remove: #{keys}"
6
- keys.each do |key|
7
- FeatureFlagger::Manager.cleanup_detached key
8
- end
9
- end
10
- end