feature_flagger 1.1.0 → 2.1.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 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