feature_flagger 0.7.3 → 1.2.1

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
- SHA1:
3
- metadata.gz: '08ad3deca247deb946f6fd7f95fe12747978303e'
4
- data.tar.gz: 8512e9a0a733eaaa95c8faa61caf340786b10189
2
+ SHA256:
3
+ metadata.gz: bb31f06b2fbb4faf49b9a560ffa6d97b774272dcd5ad6e660628b08380acf44d
4
+ data.tar.gz: 75207d7df33cc3f418a1b798d027c4ef701fbfd2aa15f17dc147e066a6f540c5
5
5
  SHA512:
6
- metadata.gz: 11e2542ada2893c96bd7588a959f424f1904e29e8b7a4a1425557e995a8cefa95c4e47b839282e28bb8a4e7d6b4ef753e24dc538ebdfbba6c8a590b8904af509
7
- data.tar.gz: fd70b4252d581ff110ca37ce59a5e5dcae514fa040e52e1580f4ef5c24ca51313b9ec7c041a8b1c9898c3c0ce4be884605fe1d420e17b6bde0d2594db2a29140
6
+ metadata.gz: b47eb06400b71e1e5fb46effbb5429f9e2299227ea5bb7f555fc31d2b4c913d938f59be84816dd0a55b281ba3a35c6d2768b2d4c0f3b33db48ece7b56cedfdf9
7
+ data.tar.gz: b3c596059e1055c2a3a003961e74091cabb888c71e1f46f1c564097428e717444735195d11b02cc5dee95b032222308b4ac9798b34e8465f1a88c623239ad5ce
data/README.md CHANGED
@@ -4,6 +4,14 @@
4
4
 
5
5
  Partially release your features.
6
6
 
7
+ ## Working with Docker
8
+
9
+ Open IRB
10
+ `docker-compose run feature_flagger`
11
+
12
+ Running tests
13
+ `docker-compose run feature_flagger rspec`
14
+
7
15
  ## Installation
8
16
 
9
17
  Add this line to your application's Gemfile:
@@ -63,7 +71,7 @@ account.release(:email_marketing, :new_email_flow)
63
71
  #=> true
64
72
 
65
73
  # Check feature for a given account
66
- account.rollout?(:email_marketing, :new_email_flow)
74
+ account.released?(:email_marketing, :new_email_flow)
67
75
  #=> true
68
76
 
69
77
  # Remove feature for given account
@@ -71,7 +79,7 @@ account.unrelease(:email_marketing, :new_email_flow)
71
79
  #=> true
72
80
 
73
81
  # If you try to check an inexistent rollout key it will raise an error.
74
- account.rollout?(:email_marketing, :new_email_flow)
82
+ account.released?(:email_marketing, :new_email_flow)
75
83
  FeatureFlagger::KeyNotFoundError: ["account", "email_marketing", "new_email_flo"]
76
84
 
77
85
  # Check feature for a specific account id
@@ -95,7 +103,22 @@ Account.unrelease_to_all(:email_marketing, :new_email_flow)
95
103
  Account.released_features_to_all
96
104
  ```
97
105
 
106
+ ## Clean up action
107
+
108
+ By default when a key is removed from `rollout.yml` file, its data still in the storage.
109
+
110
+ To clean it up, execute or schedule the rake:
111
+
112
+ $ bundle exec rake feature_flagger:cleanup_removed_rollouts
113
+
114
+ ## Upgrading
115
+
116
+ When upgrading from `1.1.x` to `1.2.x` the following command must be executed
117
+ 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.
118
+
119
+ $ bundle exec rake feature_flagger:migrate_to_resource_keys
120
+
98
121
  ## Contributing
99
122
 
100
- Bug reports and pull requests are welcome on GitHub at
101
- https://github.com/ResultadosDigitais/feature_flagger.
123
+ Bug reports and pull requests are welcome!
124
+ Please take a look at our guidelines [here](CONTRIBUTING.md).
@@ -2,10 +2,14 @@ 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'
11
+ require 'feature_flagger/manager'
12
+ require 'feature_flagger/railtie'
9
13
 
10
14
  module FeatureFlagger
11
15
  class << self
@@ -11,10 +11,36 @@ module FeatureFlagger
11
11
  @info ||= YAML.load_file(yaml_filepath) if yaml_filepath
12
12
  end
13
13
 
14
+ def mapped_feature_keys(resource_name = nil)
15
+ info_filtered = resource_name ? info[resource_name] : info
16
+ [].tap do |keys|
17
+ make_keys_recursively(info_filtered).each { |key| keys.push(join_key(resource_name, key)) }
18
+ end
19
+ end
20
+
14
21
  private
15
22
 
16
23
  def default_yaml_filepath
17
24
  "#{Rails.root}/config/rollout.yml" if defined?(Rails)
18
25
  end
26
+
27
+ def make_keys_recursively(hash, keys = [], composed_key = [])
28
+ unless hash.values[0].is_a?(Hash)
29
+ keys.push(composed_key)
30
+ return
31
+ end
32
+
33
+ hash.each do |key, value|
34
+ composed_key_cloned = composed_key.clone
35
+ composed_key_cloned.push(key.to_sym)
36
+ make_keys_recursively(value, keys, composed_key_cloned)
37
+ end
38
+ keys
39
+ end
40
+
41
+ def join_key(resource_name, key)
42
+ key.unshift resource_name if resource_name
43
+ key.join(":")
44
+ end
19
45
  end
20
46
  end
@@ -8,36 +8,36 @@ module FeatureFlagger
8
8
  @storage = storage
9
9
  end
10
10
 
11
- def rollout?(feature_key, resource_id)
11
+ def released?(feature_key, resource_id)
12
12
  @storage.has_value?(RELEASED_FEATURES, feature_key) || @storage.has_value?(feature_key, resource_id)
13
13
  end
14
14
 
15
15
  def release(feature_key, resource_id)
16
- @storage.add(feature_key, resource_id)
16
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
17
+ feature_key
18
+ )
19
+
20
+ @storage.add(feature_key, resource_name, resource_id)
17
21
  end
18
22
 
19
- def release_to_all(feature_key)
20
- @storage.add(RELEASED_FEATURES, feature_key)
23
+ def releases(resource_name, resource_id)
24
+ @storage.fetch_releases(resource_name, resource_id, RELEASED_FEATURES)
21
25
  end
22
26
 
23
- # <b>DEPRECATED:</b> Please use <tt>release</tt> instead.
24
- def release!(feature_key, resource_id)
25
- warn "[DEPRECATION] `release!` is deprecated. Please use `release` instead."
26
- release(feature_key, resource_id)
27
+ def release_to_all(feature_key)
28
+ @storage.add_all(RELEASED_FEATURES, feature_key)
27
29
  end
28
30
 
29
31
  def unrelease(feature_key, resource_id)
30
- @storage.remove(feature_key, resource_id)
31
- end
32
+ resource_name = Storage::Keys.extract_resource_name_from_feature_key(
33
+ feature_key
34
+ )
32
35
 
33
- def unrelease_to_all(feature_key)
34
- @storage.remove(RELEASED_FEATURES, feature_key)
36
+ @storage.remove(feature_key, resource_name, resource_id)
35
37
  end
36
38
 
37
- # <b>DEPRECATED:</b> Please use <tt>unrelease</tt> instead.
38
- def unrelease!(feature_key, resource_id)
39
- warn "[DEPRECATION] `unrelease!` is deprecated. Please use `unrelease` instead."
40
- unrelease(feature_key, resource_id)
39
+ def unrelease_to_all(feature_key)
40
+ @storage.remove_all(RELEASED_FEATURES, feature_key)
41
41
  end
42
42
 
43
43
  def resource_ids(feature_key)
@@ -47,5 +47,19 @@ module FeatureFlagger
47
47
  def released_features_to_all
48
48
  @storage.all_values(RELEASED_FEATURES)
49
49
  end
50
+
51
+ def released_to_all?(feature_key)
52
+ @storage.has_value?(RELEASED_FEATURES, feature_key)
53
+ end
54
+
55
+ # DEPRECATED: this method will be removed from public api on v2.0 version.
56
+ # use instead the feature_keys method.
57
+ def search_keys(query)
58
+ @storage.search_keys(query)
59
+ end
60
+
61
+ def feature_keys
62
+ @storage.feature_keys
63
+ end
50
64
  end
51
65
  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
@@ -0,0 +1,19 @@
1
+ module FeatureFlagger
2
+ class Manager
3
+
4
+ def self.detached_feature_keys
5
+ persisted_features = FeatureFlagger.control.feature_keys
6
+ mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys
7
+
8
+ persisted_features - mapped_feature_keys
9
+ end
10
+
11
+ def self.cleanup_detached(resource_name, *feature_key)
12
+ complete_feature_key = feature_key.map(&:to_s).insert(0, resource_name.to_s)
13
+ key_value = FeatureFlagger.config.info.dig(*complete_feature_key)
14
+ raise "key is still mapped" if key_value
15
+ FeatureFlagger.control.unrelease_to_all(complete_feature_key.join(':'))
16
+ end
17
+
18
+ end
19
+ end
@@ -12,56 +12,65 @@ module FeatureFlagger
12
12
  base.extend ClassMethods
13
13
  end
14
14
 
15
- def rollout?(*feature_key)
16
- self.class.released_id?(id, feature_key)
17
- end
18
-
19
- # <b>DEPRECATED:</b> Please use <tt>release</tt> instead.
20
- def release!(*feature_key)
21
- warn "[DEPRECATION] `release!` is deprecated. Please use `release` instead."
22
- release(*feature_key)
15
+ def released?(*feature_key)
16
+ self.class.released_id?(feature_flagger_identifier, feature_key)
23
17
  end
24
18
 
25
19
  def release(*feature_key)
26
- self.class.release_id(id, *feature_key)
27
- end
28
-
29
- # <b>DEPRECATED:</b> Please use <tt>unrelease</tt> instead.
30
- def unrelease!(*feature_key)
31
- warn "[DEPRECATION] `unrelease!` is deprecated. Please use `unrelease` instead."
32
- unrelease(*feature_key)
20
+ self.class.release_id(feature_flagger_identifier, *feature_key)
33
21
  end
34
22
 
35
23
  def unrelease(*feature_key)
36
- resource_name = self.class.rollout_resource_name
24
+ resource_name = self.class.feature_flagger_model_settings.entity_name
37
25
  feature = Feature.new(feature_key, resource_name)
38
26
  FeatureFlagger.control.unrelease(feature.key, id)
39
27
  end
40
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
+
41
40
  module ClassMethods
41
+ def feature_flagger
42
+ raise ArgumentError unless block_given?
43
+ yield feature_flagger_model_settings
44
+ end
45
+
42
46
  def released_id?(resource_id, *feature_key)
43
- feature = Feature.new(feature_key, rollout_resource_name)
44
- FeatureFlagger.control.rollout?(feature.key, resource_id)
47
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
48
+ FeatureFlagger.control.released?(feature.key, resource_id)
45
49
  end
46
50
 
47
51
  def release_id(resource_id, *feature_key)
48
- feature = Feature.new(feature_key, rollout_resource_name)
52
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
49
53
  FeatureFlagger.control.release(feature.key, resource_id)
50
54
  end
51
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
+
52
61
  def all_released_ids_for(*feature_key)
53
62
  feature_key.flatten!
54
- feature = Feature.new(feature_key, rollout_resource_name)
63
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
55
64
  FeatureFlagger.control.resource_ids(feature.key)
56
65
  end
57
66
 
58
67
  def release_to_all(*feature_key)
59
- feature = Feature.new(feature_key, rollout_resource_name)
68
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
60
69
  FeatureFlagger.control.release_to_all(feature.key)
61
70
  end
62
71
 
63
72
  def unrelease_to_all(*feature_key)
64
- feature = Feature.new(feature_key, rollout_resource_name)
73
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
65
74
  FeatureFlagger.control.unrelease_to_all(feature.key)
66
75
  end
67
76
 
@@ -69,6 +78,25 @@ module FeatureFlagger
69
78
  FeatureFlagger.control.released_features_to_all
70
79
  end
71
80
 
81
+ def released_to_all?(*feature_key)
82
+ feature = Feature.new(feature_key, feature_flagger_model_settings.entity_name)
83
+ FeatureFlagger.control.released_to_all?(feature.key)
84
+ end
85
+
86
+ def detached_feature_keys
87
+ rollout_resource_name = feature_flagger_model_settings.entity_name
88
+ persisted_features = FeatureFlagger.control.search_keys("#{rollout_resource_name}:*").to_a
89
+ mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys(rollout_resource_name)
90
+ (persisted_features - mapped_feature_keys).map { |key| key.sub("#{rollout_resource_name}:",'') }
91
+ end
92
+
93
+ def cleanup_detached(*feature_key)
94
+ complete_feature_key = feature_key.map(&:to_s).insert(0, feature_flagger_model_settings.entity_name)
95
+ key_value = FeatureFlagger.config.info.dig(*complete_feature_key)
96
+ raise "key is still mapped" if key_value
97
+ FeatureFlagger.control.unrelease_to_all(complete_feature_key.join(':'))
98
+ end
99
+
72
100
  def rollout_resource_name
73
101
  klass_name = self.to_s
74
102
  klass_name.gsub!(/::/, '_')
@@ -78,6 +106,17 @@ module FeatureFlagger
78
106
  klass_name.downcase!
79
107
  klass_name
80
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
81
120
  end
82
121
  end
83
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,9 @@
1
+ if defined?(Rails)
2
+ module FeatureFlagger
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ load 'tasks/feature_flagger.rake'
6
+ end
7
+ end
8
+ 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,21 @@
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
+
13
+ raise InvalidResourceNameError if feature_paths.size < MINIMUM_VALID_FEATURE_PATH
14
+
15
+ feature_paths.first
16
+ end
17
+
18
+ class InvalidResourceNameError < StandardError; end
19
+ end
20
+ end
21
+ 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,21 +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)
33
+ def add(feature_key, resource_name, resource_id)
34
+ resource_key = resource_key(resource_name, resource_id)
35
+
36
+ @redis.multi do |redis|
37
+ redis.sadd(feature_key, resource_id)
38
+ redis.sadd(resource_key, feature_key)
39
+ end
40
+ end
41
+
42
+ def remove(feature_key, resource_name, resource_id)
43
+ resource_key = resource_key(resource_name, resource_id)
44
+
45
+ @redis.multi do |redis|
46
+ redis.srem(feature_key, resource_id)
47
+ redis.srem(resource_key, feature_key)
48
+ end
49
+ end
50
+
51
+ def remove_all(global_key, feature_key)
52
+ @redis.srem(global_key, feature_key)
53
+ remove_feature_key_from_resources(feature_key)
26
54
  end
27
55
 
28
- def remove(key, value)
29
- @redis.srem(key, value)
56
+ def add_all(global_key, key)
57
+ @redis.sadd(global_key, key)
58
+ remove_feature_key_from_resources(key)
30
59
  end
31
60
 
32
61
  def all_values(key)
33
62
  @redis.smembers(key)
34
63
  end
64
+
65
+ # DEPRECATED: this method will be removed from public api on v2.0 version.
66
+ # use instead the feature_keys method.
67
+ def search_keys(query)
68
+ @redis.scan_each(match: query)
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
35
120
  end
36
121
  end
37
122
  end
@@ -1,3 +1,3 @@
1
1
  module FeatureFlagger
2
- VERSION = "0.7.3"
2
+ VERSION = "1.2.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: 0.7.3
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Sousa
@@ -9,64 +9,64 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-01-22 00:00:00.000000000 Z
12
+ date: 2020-08-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">"
19
19
  - !ruby/object:Gem::Version
20
20
  version: '3.2'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ">"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '3.2'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: redis-namespace
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '1.3'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ">"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '1.3'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: bundler
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '1.12'
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: '1.12'
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.17'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '='
96
+ - !ruby/object:Gem::Version
97
+ version: '0.17'
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'
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'
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,10 +122,17 @@ 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
127
+ - lib/feature_flagger/manager.rb
112
128
  - lib/feature_flagger/model.rb
129
+ - lib/feature_flagger/model_settings.rb
130
+ - lib/feature_flagger/railtie.rb
131
+ - lib/feature_flagger/storage/feature_keys_migration.rb
132
+ - lib/feature_flagger/storage/keys.rb
113
133
  - lib/feature_flagger/storage/redis.rb
114
134
  - lib/feature_flagger/version.rb
135
+ - lib/tasks/feature_flagger.rake
115
136
  homepage: http://github.com/ResultadosDigitais/feature_flagger
116
137
  licenses:
117
138
  - MIT
@@ -124,15 +145,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
145
  requirements:
125
146
  - - ">="
126
147
  - !ruby/object:Gem::Version
127
- version: '0'
148
+ version: '2.5'
128
149
  required_rubygems_version: !ruby/object:Gem::Requirement
129
150
  requirements:
130
151
  - - ">="
131
152
  - !ruby/object:Gem::Version
132
- version: '0'
153
+ version: 2.0.0
133
154
  requirements: []
134
- rubyforge_project:
135
- rubygems_version: 2.5.2.3
155
+ rubygems_version: 3.1.4
136
156
  signing_key:
137
157
  specification_version: 4
138
158
  summary: Partial release your features.