togglefy 1.2.0 → 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 +4 -4
- data/README.md +79 -54
- data/app/models/togglefy/feature.rb +87 -8
- data/app/models/togglefy/feature_assignment.rb +12 -0
- data/lib/generators/togglefy/install_generator.rb +32 -3
- data/lib/generators/togglefy/templates/older_rails_create_feature_assignments.rb +14 -0
- data/lib/generators/togglefy/templates/older_rails_create_features.rb +18 -0
- data/lib/togglefy/assignable.rb +29 -1
- data/lib/togglefy/engine.rb +2 -0
- data/lib/togglefy/errors/assignables_not_found.rb +5 -0
- data/lib/togglefy/errors/bulk_toggle_failed.rb +9 -0
- data/lib/togglefy/errors/dependency_missing.rb +6 -0
- data/lib/togglefy/errors/error.rb +2 -0
- data/lib/togglefy/errors/feature_not_found.rb +11 -2
- data/lib/togglefy/errors/invalid_feature_attribute.rb +5 -0
- data/lib/togglefy/errors.rb +16 -0
- data/lib/togglefy/feature_assignable_manager.rb +21 -0
- data/lib/togglefy/feature_manager.rb +32 -1
- data/lib/togglefy/feature_query.rb +47 -5
- data/lib/togglefy/featureable.rb +3 -0
- data/lib/togglefy/scoped_bulk_wrapper.rb +8 -0
- data/lib/togglefy/services/bulk_toggler.rb +96 -5
- data/lib/togglefy/version.rb +2 -1
- data/lib/togglefy.rb +154 -9
- data/togglefy.gemspec +6 -2
- metadata +37 -5
- data/lib/togglefy/exceptions.rb +0 -7
- /data/{LICENSE.txt → LICENSE} +0 -0
data/lib/togglefy/engine.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The Engine class integrates the Togglefy gem with a Rails application.
|
5
|
+
# It isolates the namespace to avoid conflicts with other parts of the application.
|
4
6
|
class Engine < ::Rails::Engine
|
5
7
|
isolate_namespace Togglefy
|
6
8
|
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The AssignablesNotFound class represents an error raised when no assignables
|
5
|
+
# match the provided features and filters.
|
4
6
|
class AssignablesNotFound < Togglefy::Error
|
7
|
+
# Initializes a new AssignablesNotFound error.
|
8
|
+
#
|
9
|
+
# @param klass [Class] The class of the assignable.
|
5
10
|
def initialize(klass)
|
6
11
|
super("No #{klass.name} found matching features and filters sent")
|
7
12
|
end
|
@@ -1,13 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The BulkToggleFailed error is raised when a bulk toggle operation fails.
|
5
|
+
# This error provides additional context by allowing an optional cause to be specified.
|
4
6
|
class BulkToggleFailed < Togglefy::Error
|
7
|
+
# Initializes a new BulkToggleFailed error.
|
8
|
+
#
|
9
|
+
# @param message [String] The error message (default: "Bulk toggle operation failed").
|
10
|
+
# @param cause [Exception, nil] The underlying cause of the error, if any.
|
11
|
+
# @return [BulkToggleFailed] A new instance of the error.
|
5
12
|
def initialize(message = "Bulk toggle operation failed", cause = nil)
|
6
13
|
super(message)
|
7
14
|
set_backtrace(cause.backtrace) if cause
|
8
15
|
@cause = cause
|
9
16
|
end
|
10
17
|
|
18
|
+
# @!attribute [r] cause
|
19
|
+
# @return [Exception, nil] The underlying cause of the error, if any.
|
11
20
|
attr_reader :cause
|
12
21
|
end
|
13
22
|
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The DependencyMissing class represents an error raised when a feature
|
5
|
+
# is missing a required dependency.
|
4
6
|
class DependencyMissing < Togglefy::Error
|
7
|
+
# Initializes a new DependencyMissing error.
|
8
|
+
#
|
9
|
+
# @param feature [String] The name of the feature.
|
10
|
+
# @param required [String] The name of the missing dependency.
|
5
11
|
def initialize(feature, required)
|
6
12
|
super("Feature '#{feature}' is missing dependency: '#{required}'")
|
7
13
|
end
|
@@ -1,9 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The FeatureNotFound class represents an error raised when no features
|
5
|
+
# match the provided criteria or filters.
|
4
6
|
class FeatureNotFound < Togglefy::Error
|
5
|
-
|
6
|
-
|
7
|
+
# Initializes a new FeatureNotFound error.
|
8
|
+
def initialize(message = "No features found matching features, identifiers and/or filters sent", cause = nil)
|
9
|
+
super(message)
|
10
|
+
set_backtrace(cause.backtrace) if cause
|
11
|
+
@cause = cause
|
7
12
|
end
|
13
|
+
|
14
|
+
# @!attribute [r] cause
|
15
|
+
# @return [Exception, nil] The underlying cause of the error, if any.
|
16
|
+
attr_reader :cause
|
8
17
|
end
|
9
18
|
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The InvalidFeatureAttribute class represents an error raised when an
|
5
|
+
# invalid attribute is provided for a Togglefy::Feature.
|
4
6
|
class InvalidFeatureAttribute < Togglefy::Error
|
7
|
+
# Initializes a new InvalidFeatureAttribute error.
|
8
|
+
#
|
9
|
+
# @param attr [String] The name of the invalid attribute.
|
5
10
|
def initialize(attr)
|
6
11
|
super("The attribute '#{attr}' is not valid for Togglefy::Feature.")
|
7
12
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file loads all custom exception classes used in the Togglefy gem.
|
4
|
+
require "togglefy/errors/error"
|
5
|
+
|
6
|
+
require "togglefy/errors/feature_not_found"
|
7
|
+
require "togglefy/errors/assignables_not_found"
|
8
|
+
require "togglefy/errors/bulk_toggle_failed"
|
9
|
+
|
10
|
+
module Togglefy
|
11
|
+
# Custom error class for Togglefy-specific errors.
|
12
|
+
class Error < ::StandardError; end
|
13
|
+
|
14
|
+
# Overwrites the default StandardError class to provide a custom error class for Togglefy.
|
15
|
+
StandardError = Class.new(Error)
|
16
|
+
end
|
@@ -1,32 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The FeatureAssignableManager class provides methods to manage features
|
5
|
+
# for an assignable object, such as enabling, disabling, and clearing features.
|
4
6
|
class FeatureAssignableManager
|
7
|
+
# Initializes a new FeatureAssignableManager.
|
8
|
+
#
|
9
|
+
# @param assignable [Object] The assignable object to manage features for.
|
5
10
|
def initialize(assignable)
|
6
11
|
@assignable = assignable
|
7
12
|
end
|
8
13
|
|
14
|
+
# Enables a feature for the assignable.
|
15
|
+
#
|
16
|
+
# @param feature [Togglefy::Feature, Symbol, String] The feature or its identifier.
|
17
|
+
# @return [FeatureAssignableManager] Returns self for method chaining.
|
9
18
|
def enable(feature)
|
10
19
|
assignable.add_feature(feature)
|
11
20
|
self
|
12
21
|
end
|
13
22
|
|
23
|
+
# Disables a feature for the assignable.
|
24
|
+
#
|
25
|
+
# @param feature [Togglefy::Feature, Symbol, String] The feature or its identifier.
|
26
|
+
# @return [FeatureAssignableManager] Returns self for method chaining.
|
14
27
|
def disable(feature)
|
15
28
|
assignable.remove_feature(feature)
|
16
29
|
self
|
17
30
|
end
|
18
31
|
|
32
|
+
# Clears all features from the assignable.
|
33
|
+
#
|
34
|
+
# @return [FeatureAssignableManager] Returns self for method chaining.
|
19
35
|
def clear
|
20
36
|
assignable.clear_features
|
21
37
|
self
|
22
38
|
end
|
23
39
|
|
40
|
+
# Checks if the assignable has a specific feature.
|
41
|
+
#
|
42
|
+
# @param feature [Togglefy::Feature, Symbol, String] The feature or its identifier.
|
43
|
+
# @return [Boolean] True if the feature exists, false otherwise.
|
24
44
|
def has?(feature)
|
25
45
|
assignable.has_feature?(feature)
|
26
46
|
end
|
27
47
|
|
28
48
|
private
|
29
49
|
|
50
|
+
# @return [Object] The assignable object being managed.
|
30
51
|
attr_reader :assignable
|
31
52
|
end
|
32
53
|
end
|
@@ -1,43 +1,74 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The FeatureManager class provides methods to manage features, including
|
5
|
+
# creating, updating, toggling, and destroying them.
|
4
6
|
class FeatureManager
|
7
|
+
# Initializes a new FeatureManager.
|
8
|
+
#
|
9
|
+
# nil only applies when creating a new feature.
|
10
|
+
#
|
11
|
+
# @param identifier [String, nil] The identifier of the feature to manage.
|
5
12
|
def initialize(identifier = nil)
|
6
13
|
@identifier = identifier unless identifier.nil?
|
7
14
|
end
|
8
15
|
|
16
|
+
# Creates a new feature with the given parameters.
|
17
|
+
#
|
18
|
+
# @param params [Hash] The attributes for the new feature.
|
19
|
+
# @return [Togglefy::Feature] The created feature.
|
9
20
|
def create(**params)
|
10
21
|
Togglefy::Feature.create!(**params)
|
11
22
|
end
|
12
23
|
|
24
|
+
# Updates the feature with the given parameters.
|
25
|
+
#
|
26
|
+
# @param params [Hash] The attributes to update the feature with.
|
27
|
+
# @return [Togglefy::Feature] The updated feature.
|
13
28
|
def update(**params)
|
14
29
|
feature.update!(**params)
|
15
30
|
end
|
16
31
|
|
32
|
+
# Destroys the feature.
|
33
|
+
#
|
34
|
+
# @return [Togglefy::Feature] The destroyed feature.
|
17
35
|
def destroy
|
18
36
|
feature.destroy
|
19
37
|
end
|
20
38
|
|
39
|
+
# Toggles the feature's status between active and inactive.
|
40
|
+
#
|
41
|
+
# @return [Togglefy::Feature] The toggled feature.
|
21
42
|
def toggle
|
22
43
|
return feature.inactive! if feature.active?
|
23
44
|
|
24
45
|
feature.active!
|
25
46
|
end
|
26
47
|
|
48
|
+
# Activates the feature.
|
49
|
+
#
|
50
|
+
# @return [Togglefy::Feature] The activated feature.
|
27
51
|
def active!
|
28
52
|
feature.active!
|
29
53
|
end
|
30
54
|
|
55
|
+
# Deactivates the feature.
|
56
|
+
#
|
57
|
+
# @return [Togglefy::Feature] The deactivated feature.
|
31
58
|
def inactive!
|
32
59
|
feature.inactive!
|
33
60
|
end
|
34
61
|
|
35
62
|
private
|
36
63
|
|
64
|
+
# @return [String, nil] The identifier of the feature being managed.
|
37
65
|
attr_reader :identifier
|
38
66
|
|
67
|
+
# Finds the feature by its identifier.
|
68
|
+
#
|
69
|
+
# @return [Togglefy::Feature] The found feature.
|
39
70
|
def feature
|
40
|
-
Togglefy::Feature.find_by!(identifier:)
|
71
|
+
Togglefy::Feature.find_by!(identifier: identifier)
|
41
72
|
end
|
42
73
|
end
|
43
74
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The FeatureQuery class provides methods to query features based on various filters.
|
4
5
|
class FeatureQuery
|
6
|
+
# A mapping of filter keys to their corresponding query scopes.
|
5
7
|
FILTERS = {
|
6
8
|
identifier: :identifier,
|
7
9
|
group: :for_group,
|
@@ -12,48 +14,88 @@ module Togglefy
|
|
12
14
|
status: :with_status
|
13
15
|
}.freeze
|
14
16
|
|
17
|
+
# Retrieves all features.
|
18
|
+
#
|
19
|
+
# @return [ActiveRecord::Relation] A relation of all features.
|
15
20
|
def features
|
16
21
|
Togglefy::Feature.all
|
17
22
|
end
|
18
23
|
|
24
|
+
# Finds a feature by its identifier.
|
25
|
+
#
|
26
|
+
# @param identifier [Symbol, Array<Symbol>, String, Array<String>] The identifier(s) of the feature(s).
|
27
|
+
# @return [Togglefy::Feature, ActiveRecord::Relation] The feature or features matching the identifier(s).
|
19
28
|
def feature(identifier)
|
20
29
|
return Togglefy::Feature.identifier(identifier) if identifier.is_a?(Array)
|
21
30
|
|
22
|
-
Togglefy::Feature.find_by!(identifier:)
|
31
|
+
Togglefy::Feature.find_by!(identifier: identifier)
|
23
32
|
end
|
24
33
|
|
34
|
+
# Retrieves feature assignments for a specific type.
|
35
|
+
#
|
36
|
+
# @param klass [Class] The class type to filter by.
|
37
|
+
# @return [ActiveRecord::Relation] A relation of feature assignments for the given type.
|
25
38
|
def for_type(klass)
|
26
39
|
Togglefy::FeatureAssignment.for_type(klass)
|
27
40
|
end
|
28
41
|
|
42
|
+
# Retrieves features for a specific group.
|
43
|
+
#
|
44
|
+
# @param group [Symbol, String] The group to filter by.
|
45
|
+
# @return [ActiveRecord::Relation] A relation of features for the given group.
|
29
46
|
def for_group(group)
|
30
47
|
Togglefy::Feature.for_group(group)
|
31
48
|
end
|
32
49
|
|
50
|
+
# Retrieves features without a group.
|
51
|
+
#
|
52
|
+
# @return [ActiveRecord::Relation] A relation of features without a group.
|
33
53
|
def without_group
|
34
54
|
Togglefy::Feature.without_group
|
35
55
|
end
|
36
56
|
|
57
|
+
# Retrieves features for a specific environment.
|
58
|
+
#
|
59
|
+
# @param environment [Symbol, String] The environment to filter by.
|
60
|
+
# @return [ActiveRecord::Relation] A relation of features for the given environment.
|
37
61
|
def for_environment(environment)
|
38
62
|
Togglefy::Feature.for_environment(environment)
|
39
63
|
end
|
40
64
|
|
65
|
+
# Retrieves features without an environment.
|
66
|
+
#
|
67
|
+
# @return [ActiveRecord::Relation] A relation of features without an environment.
|
41
68
|
def without_environment
|
42
69
|
Togglefy::Feature.without_environment
|
43
70
|
end
|
44
71
|
|
72
|
+
# Retrieves features for a specific tenant.
|
73
|
+
#
|
74
|
+
# @param tenant_id [String] The tenant_id to filter by.
|
75
|
+
# @return [ActiveRecord::Relation] A relation of features for the given tenant.
|
45
76
|
def for_tenant(tenant_id)
|
46
77
|
Togglefy::Feature.for_tenant(tenant_id)
|
47
78
|
end
|
48
79
|
|
80
|
+
# Retrieves features without a tenant.
|
81
|
+
#
|
82
|
+
# @return [ActiveRecord::Relation] A relation of features without a tenant.
|
49
83
|
def without_tenant
|
50
84
|
Togglefy::Feature.without_tenant
|
51
85
|
end
|
52
86
|
|
87
|
+
# Retrieves features with a specific status.
|
88
|
+
#
|
89
|
+
# @param status [Symbol, String, Integer] The status to filter by.
|
90
|
+
# @return [ActiveRecord::Relation] A relation of features with the given status.
|
53
91
|
def with_status(status)
|
54
92
|
Togglefy::Feature.with_status(status)
|
55
93
|
end
|
56
94
|
|
95
|
+
# Applies filters to retrieve features.
|
96
|
+
#
|
97
|
+
# @param filters [Hash] The filters to apply.
|
98
|
+
# @return [ActiveRecord::Relation] A relation of features matching the filters.
|
57
99
|
def for_filters(filters)
|
58
100
|
FILTERS.reduce(Togglefy::Feature) do |query, (key, scope)|
|
59
101
|
value = filters[key]
|
@@ -65,10 +107,10 @@ module Togglefy
|
|
65
107
|
|
66
108
|
private
|
67
109
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
110
|
+
# Checks if a value is nil or not blank.
|
111
|
+
#
|
112
|
+
# @param value [Symbol, String, Integer] The value to check.
|
113
|
+
# @return [Boolean] True if the value is nil or not blank, false otherwise.
|
72
114
|
def nil_or_not_blank?(value)
|
73
115
|
value.nil? || !value.blank?
|
74
116
|
end
|
data/lib/togglefy/featureable.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require "togglefy/assignable"
|
4
4
|
|
5
5
|
module Togglefy
|
6
|
+
# `Featureable` is an alias for `Assignable`.
|
7
|
+
#
|
8
|
+
# @deprecated Use `Togglefy::Assignable` instead.
|
6
9
|
Featureable = Assignable
|
7
10
|
warn "[DEPRECATION] `Togglefy::Featureable` is deprecated. Use `Togglefy::Assignable` instead."
|
8
11
|
end
|
@@ -3,11 +3,19 @@
|
|
3
3
|
require "togglefy/services/bulk_toggler"
|
4
4
|
|
5
5
|
module Togglefy
|
6
|
+
# The ScopedBulkWrapper class provides a wrapper for performing bulk operations
|
7
|
+
# on a specific class using the BulkToggler service.
|
6
8
|
class ScopedBulkWrapper
|
9
|
+
# Initializes a new ScopedBulkWrapper.
|
10
|
+
#
|
11
|
+
# @param klass [Class] The class to perform bulk operations on.
|
7
12
|
def initialize(klass)
|
8
13
|
@klass = klass
|
9
14
|
end
|
10
15
|
|
16
|
+
# Returns a BulkToggler instance for the specified class.
|
17
|
+
#
|
18
|
+
# @return [BulkToggler] The BulkToggler instance.
|
11
19
|
def bulk
|
12
20
|
BulkToggler.new(@klass)
|
13
21
|
end
|
@@ -1,25 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Togglefy
|
4
|
+
# The BulkToggler class provides functionality to enable or disable features
|
5
|
+
# in bulk for assignables, such as users or accounts.
|
4
6
|
class BulkToggler
|
7
|
+
# List of allowed filters for assignables.
|
5
8
|
ALLOWED_ASSIGNABLE_FILTERS = %i[group role environment env tenant_id].freeze
|
6
9
|
|
10
|
+
# Initializes a new BulkToggler instance.
|
11
|
+
#
|
12
|
+
# @param klass [Class] The assignable class (e.g., User, Account).
|
7
13
|
def initialize(klass)
|
8
14
|
@klass = klass
|
9
15
|
end
|
10
16
|
|
17
|
+
# Enables features for assignables based on filters.
|
18
|
+
# @note All parameters but the first (identifiers) should be passed as keyword arguments.
|
19
|
+
#
|
20
|
+
# @param identifiers [Array<String>, String] The feature identifiers to enable.
|
21
|
+
# @param group [String] The group name to filter assignables by.
|
22
|
+
# @param role [String] The role name to filter assignables by.
|
23
|
+
# @param environment [String] The environment name to filter assignables by.
|
24
|
+
# @param env [String] The environment name to filter assignables by.
|
25
|
+
# @param tenant_id [String] The tenant_id to filter assignables by.
|
26
|
+
# @param percentage [Integer] The percentage of assignables to include.
|
11
27
|
def enable(identifiers, **filters)
|
12
28
|
toggle(:enable, identifiers, filters)
|
29
|
+
true
|
13
30
|
end
|
14
31
|
|
32
|
+
# Disables features for assignables based on filters.
|
33
|
+
# @note All parameters but the first (identifiers) should be passed as keyword arguments.
|
34
|
+
#
|
35
|
+
# @param identifiers [Array<String>, String] The feature identifiers to disable.
|
36
|
+
# @param group [String] The group name to filter assignables by.
|
37
|
+
# @param role [String] The role name to filter assignables by.
|
38
|
+
# @param environment [String] The environment name to filter assignables by.
|
39
|
+
# @param env [String] The environment name to filter assignables by.
|
40
|
+
# @param tenant_id [String] The tenant_id to filter assignables by.
|
41
|
+
# @param percentage [Integer] The percentage of assignables to include.
|
15
42
|
def disable(identifiers, **filters)
|
16
43
|
toggle(:disable, identifiers, filters)
|
44
|
+
true
|
17
45
|
end
|
18
46
|
|
19
47
|
private
|
20
48
|
|
21
49
|
attr_reader :klass
|
22
50
|
|
51
|
+
# Toggles features for assignables based on the action.
|
52
|
+
#
|
53
|
+
# @param action [Symbol] The action to perform (:enable or :disable).
|
54
|
+
# @param identifiers [Array<String>, String] The feature identifiers.
|
55
|
+
# @param filters [Hash] Additional filters for assignables.
|
23
56
|
def toggle(action, identifiers, filters)
|
24
57
|
identifiers = Array(identifiers)
|
25
58
|
features = get_features(identifiers, filters)
|
@@ -34,6 +67,12 @@ module Togglefy
|
|
34
67
|
disable_flow(assignables, features, identifiers) if action == :disable
|
35
68
|
end
|
36
69
|
|
70
|
+
# Retrieves features based on identifiers and filters.
|
71
|
+
#
|
72
|
+
# @param identifiers [Array<String>] The feature identifiers.
|
73
|
+
# @param filters [Hash] Additional filters for features.
|
74
|
+
# @return [Array<Togglefy::Feature>] The matching features.
|
75
|
+
# @raise [Togglefy::FeatureNotFound] If no features are found.
|
37
76
|
def get_features(identifiers, filters)
|
38
77
|
features = Togglefy.for_filters(filters: { identifier: identifiers }.merge(build_scope_filters(filters))).to_a
|
39
78
|
|
@@ -42,6 +81,12 @@ module Togglefy
|
|
42
81
|
features
|
43
82
|
end
|
44
83
|
|
84
|
+
# Retrieves assignables based on the action and feature IDs.
|
85
|
+
#
|
86
|
+
# @param action [Symbol] The action to perform (:enable or :disable).
|
87
|
+
# @param feature_ids [Array<Integer>] The feature IDs.
|
88
|
+
# @return [Array<Assignable>] The matching assignables.
|
89
|
+
# @raise [Togglefy::AssignablesNotFound] If no assignables are found.
|
45
90
|
def get_assignables(action, feature_ids)
|
46
91
|
assignables = klass.without_features(feature_ids) if action == :enable
|
47
92
|
assignables = klass.with_features(feature_ids) if action == :disable
|
@@ -51,15 +96,29 @@ module Togglefy
|
|
51
96
|
assignables
|
52
97
|
end
|
53
98
|
|
99
|
+
# Builds scope filters for assignables.
|
100
|
+
#
|
101
|
+
# @param filters [Hash] The filters to process.
|
102
|
+
# @return [Hash] The processed filters.
|
54
103
|
def build_scope_filters(filters)
|
55
104
|
filters.slice(*ALLOWED_ASSIGNABLE_FILTERS).compact
|
56
105
|
end
|
57
106
|
|
107
|
+
# Samples assignables based on a percentage.
|
108
|
+
#
|
109
|
+
# @param assignables [Array<Assignable>] The assignables to sample.
|
110
|
+
# @param percentage [Float] The percentage of assignables to include.
|
111
|
+
# @return [Array<Assignable>] The sampled assignables.
|
58
112
|
def sample_assignables(assignables, percentage)
|
59
113
|
count = (assignables.size * percentage.to_f / 100).round
|
60
114
|
assignables.sample(count)
|
61
115
|
end
|
62
116
|
|
117
|
+
# Enables features for assignables.
|
118
|
+
#
|
119
|
+
# @param assignables [Array<Assignable>] The assignables to update.
|
120
|
+
# @param features [Array<Togglefy::Feature>] The features to enable.
|
121
|
+
# @param identifiers [Array<String>] The feature identifiers.
|
63
122
|
def enable_flow(assignables, features, identifiers)
|
64
123
|
rows = []
|
65
124
|
|
@@ -72,8 +131,17 @@ module Togglefy
|
|
72
131
|
mass_insert(rows, identifiers)
|
73
132
|
end
|
74
133
|
|
134
|
+
# Inserts feature assignments in bulk.
|
135
|
+
#
|
136
|
+
# @param rows [Array<Hash>] The rows to insert.
|
137
|
+
# @param identifiers [Array<String>] The feature identifiers.
|
138
|
+
# @raise [Togglefy::BulkToggleFailed] If the bulk insert fails.
|
75
139
|
def mass_insert(rows, identifiers)
|
76
|
-
|
140
|
+
return unless rows.any?
|
141
|
+
|
142
|
+
ActiveRecord::Base.transaction do
|
143
|
+
Togglefy::FeatureAssignment.insert_all(rows)
|
144
|
+
end
|
77
145
|
rescue Togglefy::Error => e
|
78
146
|
raise Togglefy::BulkToggleFailed.new(
|
79
147
|
"Bulk toggle enable failed for #{klass.name} with identifiers #{identifiers.inspect}",
|
@@ -81,6 +149,11 @@ module Togglefy
|
|
81
149
|
)
|
82
150
|
end
|
83
151
|
|
152
|
+
# Disables features for assignables.
|
153
|
+
#
|
154
|
+
# @param assignables [Array<Assignable>] The assignables to update.
|
155
|
+
# @param features [Array<Togglefy::Feature>] The features to disable.
|
156
|
+
# @param identifiers [Array<String>] The feature identifiers.
|
84
157
|
def disable_flow(assignables, features, identifiers)
|
85
158
|
ids_to_remove = []
|
86
159
|
|
@@ -93,11 +166,16 @@ module Togglefy
|
|
93
166
|
mass_delete(ids_to_remove, identifiers)
|
94
167
|
end
|
95
168
|
|
169
|
+
# Deletes feature assignments in bulk.
|
170
|
+
#
|
171
|
+
# @param ids_to_remove [Array<Array>] The IDs to remove.
|
172
|
+
# @param identifiers [Array<String>] The feature identifiers.
|
173
|
+
# @raise [Togglefy::BulkToggleFailed] If the bulk delete fails.
|
96
174
|
def mass_delete(ids_to_remove, identifiers)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
).delete_all
|
175
|
+
return unless ids_to_remove.any?
|
176
|
+
|
177
|
+
ActiveRecord::Base.transaction do
|
178
|
+
Togglefy::FeatureAssignment.where(mass_delete_scope(ids_to_remove, klass.name)).delete_all
|
101
179
|
end
|
102
180
|
rescue Togglefy::Error => e
|
103
181
|
raise Togglefy::BulkToggleFailed.new(
|
@@ -105,5 +183,18 @@ module Togglefy
|
|
105
183
|
e
|
106
184
|
)
|
107
185
|
end
|
186
|
+
|
187
|
+
# Builds the scope for mass deletion.
|
188
|
+
#
|
189
|
+
# @param ids [Array<Array>] The IDs of features to delete from the assignables.
|
190
|
+
# @param klass_name [String] The class name of the assignable.
|
191
|
+
# @return [Hash] The scope for mass deletion to be used in the query.
|
192
|
+
def mass_delete_scope(ids, klass_name)
|
193
|
+
{
|
194
|
+
assignable_id: ids.map(&:first),
|
195
|
+
assignable_type: klass_name,
|
196
|
+
feature_id: ids.map(&:last)
|
197
|
+
}
|
198
|
+
end
|
108
199
|
end
|
109
200
|
end
|