feature_flagger 1.0.1 → 2.0.1

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: 1e6648b809fb71f619b7f183de45faff5c8f7e180b716bb7ed4f41178b9b6a7b
4
- data.tar.gz: ba3ec6da4184d199d2a74e360fcb789fd2ac9aeb95dea738de7dc650129f6369
3
+ metadata.gz: 89d925393dab41d5de3c0fd8872e9d0914936ed98140d5dea69e9ff9286cde7f
4
+ data.tar.gz: 68cd51804dbc9a1b8547ed1ddbd0be9630704afd0493e99b3c1a740f4c460c99
5
5
  SHA512:
6
- metadata.gz: 196c76cad916494a7bb3438a69a4ddfe4dbc3a1efc6584078f4eee681c7dc9d3e53ece3e37796aed9d4e4d865602cf12deb4e165f1fc3a62f43eee0f5f71878f
7
- data.tar.gz: 7b81c8fd3679000af48b11f05996155962215928d14c28cd841b0874bbf19ce09d1aa6149e7831f4de69e11ce09cad94daf8837ae94a2387173bb20c5f38c198
6
+ metadata.gz: 6d1be4855ef2b133f1c0233f12f21833bdfd81bf35b3dd172e562fdd404bd6d674c429aeef4ec67a0f3062741b84f1ee99ba5bf6d317ec12b7f6517a9ab77ce1
7
+ data.tar.gz: 434ad5fda2e78b6bc63157bdee07000fcfde62ae102db74e543a09852914441c4bdd72fd15ddbf72bf1dec55e7acc2ff735d69bf9ca49e8482d56356ea032d6c
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.
@@ -60,6 +59,29 @@ class Account < ActiveRecord::Base
60
59
  # ....
61
60
  end
62
61
  ```
62
+ #### Notifier
63
+ The notifier_callback property in config, enables the dispatch of events when a release operation happens.
64
+ ```ruby
65
+ config.notifier_callback = -> {|event| do something with event }
66
+ ```
67
+
68
+
69
+ It accepts a lambda function that will receive a hash with the operation triggered like:
70
+ ```ruby
71
+ {
72
+ type: 'release',
73
+ model: 'account',
74
+ key: 'somefeature:somerolloutkey'
75
+ id: 'account_id' #In realease_to_all and unrelease_to_all operations id will be nil
76
+ }
77
+ ```
78
+
79
+ The supported operations are:
80
+ * release
81
+ * unrelease
82
+ * release_to_all
83
+ * unrelease_to_all
84
+
63
85
 
64
86
  ## Usage
65
87
 
@@ -111,6 +133,13 @@ To clean it up, execute or schedule the rake:
111
133
 
112
134
  $ bundle exec rake feature_flagger:cleanup_removed_rollouts
113
135
 
136
+ ## Upgrading
137
+
138
+ When upgrading from `1.1.x` to `1.2.x` the following command must be executed
139
+ 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.
140
+
141
+ $ bundle exec rake feature_flagger:migrate_to_resource_keys
142
+
114
143
  ## Contributing
115
144
 
116
145
  Bug reports and pull requests are welcome!
@@ -2,18 +2,22 @@ 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'
8
+ require 'feature_flagger/model_settings'
7
9
  require 'feature_flagger/feature'
8
10
  require 'feature_flagger/configuration'
9
11
  require 'feature_flagger/manager'
10
12
  require 'feature_flagger/railtie'
13
+ require 'feature_flagger/notifier'
11
14
 
12
15
  module FeatureFlagger
13
16
  class << self
14
17
  def configure
15
18
  @@configuration = nil
16
19
  @@control = nil
20
+ @@notifier = nil
17
21
  yield config if block_given?
18
22
  end
19
23
 
@@ -21,8 +25,12 @@ module FeatureFlagger
21
25
  @@configuration ||= Configuration.new
22
26
  end
23
27
 
28
+ def notifier
29
+ @@notifier ||= Notifier.new(config.notifier_callback)
30
+ end
31
+
24
32
  def control
25
- @@control ||= Control.new(config.storage)
33
+ @@control ||= Control.new(config.storage, notifier)
26
34
  end
27
35
  end
28
36
  end
@@ -1,10 +1,11 @@
1
1
  module FeatureFlagger
2
2
  class Configuration
3
- attr_accessor :storage, :yaml_filepath
3
+ attr_accessor :storage, :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
8
9
  end
9
10
 
10
11
  def info
@@ -4,8 +4,9 @@ module FeatureFlagger
4
4
 
5
5
  RELEASED_FEATURES = 'released_features'
6
6
 
7
- def initialize(storage)
7
+ def initialize(storage, notifier)
8
8
  @storage = storage
9
+ @notifier = notifier
9
10
  end
10
11
 
11
12
  def released?(feature_key, resource_id)
@@ -13,18 +14,33 @@ module FeatureFlagger
13
14
  end
14
15
 
15
16
  def release(feature_key, resource_id)
16
- @storage.add(feature_key, resource_id)
17
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
18
+ feature_key
19
+ )
20
+
21
+ @notifier.send(FeatureFlagger::Notifier::RELEASE, feature_key, resource_id)
22
+ @storage.add(feature_key, resource_name, resource_id)
23
+ end
24
+
25
+ def releases(resource_name, resource_id)
26
+ @storage.fetch_releases(resource_name, resource_id, RELEASED_FEATURES)
17
27
  end
18
28
 
19
29
  def release_to_all(feature_key)
30
+ @notifier.send(FeatureFlagger::Notifier::RELEASE_TO_ALL, feature_key)
20
31
  @storage.add_all(RELEASED_FEATURES, feature_key)
21
32
  end
22
33
 
23
34
  def unrelease(feature_key, resource_id)
24
- @storage.remove(feature_key, resource_id)
35
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
36
+ feature_key
37
+ )
38
+ @notifier.send(FeatureFlagger::Notifier::UNRELEASE, feature_key, resource_id)
39
+ @storage.remove(feature_key, resource_name, resource_id)
25
40
  end
26
41
 
27
42
  def unrelease_to_all(feature_key)
43
+ @notifier.send(FeatureFlagger::Notifier::UNRELEASE_TO_ALL, feature_key)
28
44
  @storage.remove_all(RELEASED_FEATURES, feature_key)
29
45
  end
30
46
 
@@ -40,8 +56,14 @@ module FeatureFlagger
40
56
  @storage.has_value?(RELEASED_FEATURES, feature_key)
41
57
  end
42
58
 
59
+ # DEPRECATED: this method will be removed from public api on v2.0 version.
60
+ # use instead the feature_keys method.
43
61
  def search_keys(query)
44
62
  @storage.search_keys(query)
45
63
  end
64
+
65
+ def feature_keys
66
+ @storage.feature_keys - [FeatureFlagger::Control::RELEASED_FEATURES]
67
+ end
46
68
  end
47
69
  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
 
@@ -13,44 +13,64 @@ module FeatureFlagger
13
13
  end
14
14
 
15
15
  def released?(*feature_key)
16
- self.class.released_id?(id, feature_key)
16
+ self.class.released_id?(feature_flagger_identifier, feature_key)
17
17
  end
18
18
 
19
19
  def release(*feature_key)
20
- self.class.release_id(id, *feature_key)
20
+ self.class.release_id(feature_flagger_identifier, *feature_key)
21
21
  end
22
22
 
23
23
  def unrelease(*feature_key)
24
- resource_name = self.class.rollout_resource_name
24
+ resource_name = self.class.feature_flagger_model_settings.entity_name
25
25
  feature = Feature.new(feature_key, resource_name)
26
26
  FeatureFlagger.control.unrelease(feature.key, id)
27
27
  end
28
28
 
29
+ def releases
30
+ resource_name = self.class.feature_flagger_model_settings.entity_name
31
+ FeatureFlagger.control.releases(resource_name, id)
32
+ end
33
+
34
+ private
35
+
36
+ def feature_flagger_identifier
37
+ public_send(self.class.feature_flagger_model_settings.identifier_field)
38
+ end
39
+
29
40
  module ClassMethods
41
+ def feature_flagger
42
+ raise ArgumentError unless block_given?
43
+ yield feature_flagger_model_settings
44
+ end
30
45
 
31
46
  def released_id?(resource_id, *feature_key)
32
- feature = Feature.new(feature_key, rollout_resource_name)
47
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
33
48
  FeatureFlagger.control.released?(feature.key, resource_id)
34
49
  end
35
50
 
36
51
  def release_id(resource_id, *feature_key)
37
- feature = Feature.new(feature_key, rollout_resource_name)
52
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
38
53
  FeatureFlagger.control.release(feature.key, resource_id)
39
54
  end
40
55
 
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
+
41
61
  def all_released_ids_for(*feature_key)
42
62
  feature_key.flatten!
43
- feature = Feature.new(feature_key, rollout_resource_name)
63
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
44
64
  FeatureFlagger.control.resource_ids(feature.key)
45
65
  end
46
66
 
47
67
  def release_to_all(*feature_key)
48
- feature = Feature.new(feature_key, rollout_resource_name)
68
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
49
69
  FeatureFlagger.control.release_to_all(feature.key)
50
70
  end
51
71
 
52
72
  def unrelease_to_all(*feature_key)
53
- feature = Feature.new(feature_key, rollout_resource_name)
73
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
54
74
  FeatureFlagger.control.unrelease_to_all(feature.key)
55
75
  end
56
76
 
@@ -59,18 +79,19 @@ module FeatureFlagger
59
79
  end
60
80
 
61
81
  def released_to_all?(*feature_key)
62
- feature = Feature.new(feature_key, rollout_resource_name)
82
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
63
83
  FeatureFlagger.control.released_to_all?(feature.key)
64
84
  end
65
85
 
66
86
  def detached_feature_keys
87
+ rollout_resource_name = feature_flagger_model_settings.entity_name
67
88
  persisted_features = FeatureFlagger.control.search_keys("#{rollout_resource_name}:*").to_a
68
89
  mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys(rollout_resource_name)
69
90
  (persisted_features - mapped_feature_keys).map { |key| key.sub("#{rollout_resource_name}:",'') }
70
91
  end
71
92
 
72
93
  def cleanup_detached(*feature_key)
73
- complete_feature_key = feature_key.map(&:to_s).insert(0, rollout_resource_name)
94
+ complete_feature_key = feature_key.map(&:to_s).insert(0, feature_flagger_model_settings.entity_name)
74
95
  key_value = FeatureFlagger.config.info.dig(*complete_feature_key)
75
96
  raise "key is still mapped" if key_value
76
97
  FeatureFlagger.control.unrelease_to_all(complete_feature_key.join(':'))
@@ -85,6 +106,17 @@ module FeatureFlagger
85
106
  klass_name.downcase!
86
107
  klass_name
87
108
  end
109
+
110
+ def feature_flagger_model_settings
111
+ @feature_flagger_model_settings ||= FeatureFlagger::ModelSettings.new(
112
+ identifier_field: :id,
113
+ entity_name: rollout_resource_name
114
+ )
115
+ end
116
+
117
+ def feature_flagger_identifier
118
+ public_send(feature_flagger_model_settings.identifier_field)
119
+ end
88
120
  end
89
121
  end
90
122
  end
@@ -0,0 +1,36 @@
1
+ module FeatureFlagger
2
+ class ModelSettings
3
+ def initialize(arguments)
4
+ arguments.each do |field, value|
5
+ self.public_send("#{field}=", value)
6
+ end
7
+ end
8
+
9
+ # Public: identifier_field Refers to which field must represent the unique model
10
+ # id.
11
+ attr_accessor :identifier_field
12
+
13
+ # Public: entity_name to which entity the model is targeting.
14
+ # Take this yaml file as example:
15
+ #
16
+ # account:
17
+ # email_marketing:
18
+ # whitelabel:
19
+ # description: a rollout
20
+ # owner: core
21
+ # account_in_migration:
22
+ # email_marketing:
23
+ # whitelabel:
24
+ # description: a rollout
25
+ # owner: core
26
+ #
27
+ # class Account < ActiveRecord::Base
28
+ # include FeatureFlagger::Model
29
+ #
30
+ # feature_flagger do |config|
31
+ # config.identifier_field = :cdp_tenant_id
32
+ # config.entity_name = :account_in_migration
33
+ # end
34
+ attr_accessor :entity_name
35
+ end
36
+ end
@@ -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.0.1"
2
+ VERSION = "2.0.1"
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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feature_flagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Sousa
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-08-09 00:00:00.000000000 Z
12
+ date: 2021-01-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -43,30 +43,30 @@ dependencies:
43
43
  name: bundler
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '2.0'
48
+ version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - "~>"
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: '2.0'
55
+ version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rake
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '10.0'
62
+ version: '13.0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '10.0'
69
+ version: '13.0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rspec
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -81,20 +81,34 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '3.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: simplecov
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '='
89
+ - !ruby/object:Gem::Version
90
+ version: 0.21.2
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '='
96
+ - !ruby/object:Gem::Version
97
+ version: 0.21.2
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: fakeredis
86
100
  requirement: !ruby/object:Gem::Requirement
87
101
  requirements:
88
102
  - - '='
89
103
  - !ruby/object:Gem::Version
90
- version: 0.7.0
104
+ version: 0.8.0
91
105
  type: :development
92
106
  prerelease: false
93
107
  version_requirements: !ruby/object:Gem::Requirement
94
108
  requirements:
95
109
  - - '='
96
110
  - !ruby/object:Gem::Version
97
- version: 0.7.0
111
+ version: 0.8.0
98
112
  description: Management tool to make it easier rollouting features to customers.
99
113
  email:
100
114
  - nandosousafr@gmail.com
@@ -108,13 +122,18 @@ files:
108
122
  - lib/feature_flagger.rb
109
123
  - lib/feature_flagger/configuration.rb
110
124
  - lib/feature_flagger/control.rb
125
+ - lib/feature_flagger/core_ext.rb
111
126
  - lib/feature_flagger/feature.rb
112
127
  - lib/feature_flagger/manager.rb
113
128
  - lib/feature_flagger/model.rb
129
+ - lib/feature_flagger/model_settings.rb
130
+ - lib/feature_flagger/notifier.rb
114
131
  - lib/feature_flagger/railtie.rb
132
+ - lib/feature_flagger/storage/feature_keys_migration.rb
133
+ - lib/feature_flagger/storage/keys.rb
115
134
  - lib/feature_flagger/storage/redis.rb
116
135
  - lib/feature_flagger/version.rb
117
- - lib/tasks/cleanup.rake
136
+ - lib/tasks/feature_flagger.rake
118
137
  homepage: http://github.com/ResultadosDigitais/feature_flagger
119
138
  licenses:
120
139
  - MIT
@@ -134,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
153
  - !ruby/object:Gem::Version
135
154
  version: 2.0.0
136
155
  requirements: []
137
- rubygems_version: 3.0.3
156
+ rubygems_version: 3.2.3
138
157
  signing_key:
139
158
  specification_version: 4
140
159
  summary: Partial release your features.
@@ -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