feature_flagger 0.7.3 → 1.2.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
- 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.