flipper 0.16.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +1 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +110 -0
- data/.github/workflows/examples.yml +105 -0
- data/.github/workflows/release.yml +54 -0
- data/.rspec +1 -0
- data/CLAUDE.md +87 -0
- data/Changelog.md +2 -215
- data/Dockerfile +1 -1
- data/Gemfile +28 -20
- data/README.md +72 -62
- data/Rakefile +13 -3
- data/benchmark/enabled_ips.rb +10 -0
- data/benchmark/enabled_multiple_actors_ips.rb +20 -0
- data/benchmark/enabled_profile.rb +20 -0
- data/benchmark/instrumentation_ips.rb +21 -0
- data/benchmark/typecast_ips.rb +27 -0
- data/docker-compose.yml +37 -34
- data/docs/DockerCompose.md +0 -1
- data/docs/README.md +1 -0
- data/docs/images/banner.jpg +0 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +18 -0
- data/examples/api/custom_memoized.ru +36 -0
- data/examples/api/memoized.ru +42 -0
- data/examples/basic.rb +1 -12
- data/examples/cloud/app.ru +12 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/basic.rb +22 -0
- data/examples/cloud/cloud_setup.rb +20 -0
- data/examples/cloud/forked.rb +36 -0
- data/examples/cloud/import.rb +17 -0
- data/examples/cloud/poll_interval/README.md +111 -0
- data/examples/cloud/poll_interval/client.rb +108 -0
- data/examples/cloud/poll_interval/server.rb +98 -0
- data/examples/cloud/threaded.rb +33 -0
- data/examples/configuring_default.rb +2 -5
- data/examples/dsl.rb +10 -35
- data/examples/enabled_for_actor.rb +10 -15
- data/examples/expressions.rb +237 -0
- data/examples/group.rb +3 -6
- data/examples/group_dynamic_lookup.rb +5 -19
- data/examples/group_with_members.rb +4 -14
- data/examples/importing.rb +1 -1
- data/examples/individual_actor.rb +2 -5
- data/examples/instrumentation.rb +2 -2
- data/examples/instrumentation_last_accessed_at.rb +38 -0
- data/examples/memoizing.rb +35 -0
- data/examples/mirroring.rb +59 -0
- data/examples/percentage_of_actors.rb +6 -16
- data/examples/percentage_of_actors_enabled_check.rb +7 -10
- data/examples/percentage_of_actors_group.rb +5 -18
- data/examples/percentage_of_time.rb +3 -6
- data/examples/strict.rb +18 -0
- data/exe/flipper +5 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +10 -7
- data/lib/flipper/actor.rb +10 -3
- data/lib/flipper/adapter.rb +50 -8
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/actor_limit.rb +54 -0
- data/lib/flipper/adapters/cache_base.rb +161 -0
- data/lib/flipper/adapters/dual_write.rb +63 -0
- data/lib/flipper/adapters/failover.rb +85 -0
- data/lib/flipper/adapters/failsafe.rb +72 -0
- data/lib/flipper/adapters/http/client.rb +64 -7
- data/lib/flipper/adapters/http/error.rb +19 -1
- data/lib/flipper/adapters/http.rb +97 -43
- data/lib/flipper/adapters/instrumented.rb +47 -26
- data/lib/flipper/adapters/memoizable.rb +44 -40
- data/lib/flipper/adapters/memory.rb +75 -111
- data/lib/flipper/adapters/operation_logger.rb +22 -78
- data/lib/flipper/adapters/poll/poller.rb +2 -0
- data/lib/flipper/adapters/poll.rb +52 -0
- data/lib/flipper/adapters/pstore.rb +27 -17
- data/lib/flipper/adapters/read_only.rb +8 -41
- data/lib/flipper/adapters/strict.rb +45 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +14 -1
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +2 -7
- data/lib/flipper/adapters/sync/synchronizer.rb +13 -6
- data/lib/flipper/adapters/sync.rb +23 -29
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +314 -0
- data/lib/flipper/cloud/configuration.rb +271 -0
- data/lib/flipper/cloud/dsl.rb +27 -0
- data/lib/flipper/cloud/message_verifier.rb +95 -0
- data/lib/flipper/cloud/middleware.rb +63 -0
- data/lib/flipper/cloud/migrate.rb +71 -0
- data/lib/flipper/cloud/routes.rb +14 -0
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +96 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +100 -0
- data/lib/flipper/cloud/telemetry.rb +191 -0
- data/lib/flipper/cloud.rb +54 -0
- data/lib/flipper/configuration.rb +54 -7
- data/lib/flipper/dsl.rb +58 -47
- data/lib/flipper/engine.rb +102 -0
- data/lib/flipper/errors.rb +3 -21
- data/lib/flipper/export.rb +24 -0
- data/lib/flipper/exporter.rb +17 -0
- data/lib/flipper/exporters/json/export.rb +32 -0
- data/lib/flipper/exporters/json/v1.rb +33 -0
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +9 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/feature_enabled.rb +34 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +16 -0
- data/lib/flipper/feature.rb +95 -28
- data/lib/flipper/feature_check_context.rb +10 -6
- data/lib/flipper/gate.rb +13 -11
- data/lib/flipper/gate_values.rb +5 -18
- data/lib/flipper/gates/actor.rb +10 -17
- data/lib/flipper/gates/boolean.rb +1 -1
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/gates/group.rb +5 -7
- data/lib/flipper/gates/percentage_of_actors.rb +10 -13
- data/lib/flipper/gates/percentage_of_time.rb +1 -2
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/instrumentation/log_subscriber.rb +35 -8
- data/lib/flipper/instrumentation/statsd.rb +4 -2
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -5
- data/lib/flipper/instrumenters/memory.rb +6 -2
- data/lib/flipper/metadata.rb +8 -1
- data/lib/flipper/middleware/memoizer.rb +46 -27
- data/lib/flipper/middleware/setup_env.rb +13 -3
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/poller.rb +157 -0
- data/lib/flipper/serializers/gzip.rb +22 -0
- data/lib/flipper/serializers/json.rb +17 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +122 -56
- data/lib/flipper/test/shared_adapter_test.rb +120 -52
- data/lib/flipper/test_help.rb +43 -0
- data/lib/flipper/typecast.rb +59 -18
- data/lib/flipper/types/actor.rb +19 -13
- data/lib/flipper/types/group.rb +12 -5
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +11 -1
- data/lib/flipper.rb +71 -12
- data/lib/generators/flipper/setup_generator.rb +68 -0
- data/lib/generators/flipper/templates/initializer.rb +45 -0
- data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
- data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
- data/lib/generators/flipper/update_generator.rb +35 -0
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/fixtures/environment.rb +1 -0
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/actor_spec.rb +10 -2
- data/spec/flipper/adapter_builder_spec.rb +72 -0
- data/spec/flipper/adapter_spec.rb +52 -6
- data/spec/flipper/adapters/actor_limit_spec.rb +75 -0
- data/spec/flipper/adapters/dual_write_spec.rb +82 -0
- data/spec/flipper/adapters/failover_spec.rb +141 -0
- data/spec/flipper/adapters/failsafe_spec.rb +58 -0
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +402 -65
- data/spec/flipper/adapters/instrumented_spec.rb +31 -13
- data/spec/flipper/adapters/memoizable_spec.rb +51 -33
- data/spec/flipper/adapters/memory_spec.rb +33 -5
- data/spec/flipper/adapters/operation_logger_spec.rb +38 -12
- data/spec/flipper/adapters/poll_spec.rb +41 -0
- data/spec/flipper/adapters/pstore_spec.rb +0 -2
- data/spec/flipper/adapters/read_only_spec.rb +32 -18
- data/spec/flipper/adapters/strict_spec.rb +64 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +39 -1
- data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +4 -5
- data/spec/flipper/adapters/sync/synchronizer_spec.rb +87 -1
- data/spec/flipper/adapters/sync_spec.rb +17 -6
- data/spec/flipper/cli_spec.rb +217 -0
- data/spec/flipper/cloud/configuration_spec.rb +257 -0
- data/spec/flipper/cloud/dsl_spec.rb +90 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +307 -0
- data/spec/flipper/cloud/migrate_spec.rb +160 -0
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +208 -0
- data/spec/flipper/cloud_spec.rb +186 -0
- data/spec/flipper/configuration_spec.rb +37 -3
- data/spec/flipper/dsl_spec.rb +67 -80
- data/spec/flipper/engine_spec.rb +374 -0
- data/spec/flipper/export_spec.rb +13 -0
- data/spec/flipper/exporter_spec.rb +16 -0
- data/spec/flipper/exporters/json/export_spec.rb +60 -0
- data/spec/flipper/exporters/json/v1_spec.rb +33 -0
- data/spec/flipper/expression/builder_spec.rb +248 -0
- data/spec/flipper/expression_spec.rb +188 -0
- data/spec/flipper/expressions/all_spec.rb +15 -0
- data/spec/flipper/expressions/any_spec.rb +15 -0
- data/spec/flipper/expressions/boolean_spec.rb +15 -0
- data/spec/flipper/expressions/equal_spec.rb +24 -0
- data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/greater_than_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_spec.rb +32 -0
- data/spec/flipper/expressions/not_equal_spec.rb +15 -0
- data/spec/flipper/expressions/now_spec.rb +11 -0
- data/spec/flipper/expressions/number_spec.rb +21 -0
- data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
- data/spec/flipper/expressions/percentage_spec.rb +15 -0
- data/spec/flipper/expressions/property_spec.rb +13 -0
- data/spec/flipper/expressions/random_spec.rb +9 -0
- data/spec/flipper/expressions/string_spec.rb +11 -0
- data/spec/flipper/expressions/time_spec.rb +29 -0
- data/spec/flipper/feature_check_context_spec.rb +18 -20
- data/spec/flipper/feature_spec.rb +461 -48
- data/spec/flipper/gate_spec.rb +0 -2
- data/spec/flipper/gate_values_spec.rb +2 -34
- data/spec/flipper/gates/actor_spec.rb +0 -2
- data/spec/flipper/gates/boolean_spec.rb +1 -3
- data/spec/flipper/gates/expression_spec.rb +190 -0
- data/spec/flipper/gates/group_spec.rb +2 -5
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -7
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -4
- data/spec/flipper/identifier_spec.rb +12 -0
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +24 -7
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -3
- data/spec/flipper/instrumenters/memory_spec.rb +18 -1
- data/spec/flipper/instrumenters/noop_spec.rb +14 -8
- data/spec/flipper/middleware/memoizer_spec.rb +199 -62
- data/spec/flipper/middleware/setup_env_spec.rb +23 -5
- data/spec/flipper/model/active_record_spec.rb +72 -0
- data/spec/flipper/poller_spec.rb +390 -0
- data/spec/flipper/registry_spec.rb +0 -1
- data/spec/flipper/serializers/gzip_spec.rb +13 -0
- data/spec/flipper/serializers/json_spec.rb +13 -0
- data/spec/flipper/typecast_spec.rb +121 -7
- data/spec/flipper/types/actor_spec.rb +63 -47
- data/spec/flipper/types/boolean_spec.rb +0 -1
- data/spec/flipper/types/group_spec.rb +24 -3
- data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
- data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
- data/spec/flipper/types/percentage_spec.rb +0 -1
- data/spec/{integration_spec.rb → flipper_integration_spec.rb} +301 -59
- data/spec/flipper_spec.rb +123 -29
- data/spec/{helper.rb → spec_helper.rb} +23 -21
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/descriptions.yml +1 -0
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +53 -6
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test/test_helper.rb +2 -1
- data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
- data/test_rails/generators/flipper/update_generator_test.rb +96 -0
- data/test_rails/helper.rb +31 -0
- data/test_rails/system/test_help_test.rb +52 -0
- metadata +200 -82
- data/.rubocop.yml +0 -54
- data/.rubocop_todo.yml +0 -199
- data/docs/Adapters.md +0 -124
- data/docs/Caveats.md +0 -4
- data/docs/Gates.md +0 -167
- data/docs/Instrumentation.md +0 -27
- data/docs/Optimization.md +0 -114
- data/docs/api/README.md +0 -849
- data/docs/http/README.md +0 -35
- data/docs/read-only/README.md +0 -21
- data/examples/example_setup.rb +0 -8
- data/test/helper.rb +0 -11
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Flipper
|
|
2
|
+
module Expressions
|
|
3
|
+
class Comparable
|
|
4
|
+
def self.operator
|
|
5
|
+
raise NotImplementedError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.call(left, right)
|
|
9
|
+
left.respond_to?(operator) && right.respond_to?(operator) && left.public_send(operator, right)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
|
|
3
|
+
module Flipper
|
|
4
|
+
module Expressions
|
|
5
|
+
class FeatureEnabled
|
|
6
|
+
EVALUATING_KEY = :flipper_evaluating_features
|
|
7
|
+
|
|
8
|
+
def self.call(feature_name, context:)
|
|
9
|
+
evaluating = Thread.current[EVALUATING_KEY] ||= Set.new
|
|
10
|
+
feature_name = feature_name.to_s
|
|
11
|
+
current_feature = context[:feature_name].to_s
|
|
12
|
+
|
|
13
|
+
# Track the current feature so A -> B -> A is caught
|
|
14
|
+
added_current = evaluating.add?(current_feature)
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
# Circular dependency: return false to break the cycle
|
|
18
|
+
return false if evaluating.include?(feature_name)
|
|
19
|
+
|
|
20
|
+
evaluating.add(feature_name)
|
|
21
|
+
actor = context[:actor]
|
|
22
|
+
if actor
|
|
23
|
+
Flipper.enabled?(feature_name, actor)
|
|
24
|
+
else
|
|
25
|
+
Flipper.enabled?(feature_name)
|
|
26
|
+
end
|
|
27
|
+
ensure
|
|
28
|
+
evaluating.delete(feature_name)
|
|
29
|
+
evaluating.delete(current_feature) if added_current
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Flipper
|
|
2
|
+
module Expressions
|
|
3
|
+
class PercentageOfActors
|
|
4
|
+
SCALING_FACTOR = 1_000
|
|
5
|
+
|
|
6
|
+
def self.call(text, percentage, context: {})
|
|
7
|
+
prefix = context[:feature_name] || ""
|
|
8
|
+
Zlib.crc32("#{prefix}#{text}") % (100 * SCALING_FACTOR) < percentage * SCALING_FACTOR
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/flipper/feature.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'flipper/feature_check_context'
|
|
|
5
5
|
require 'flipper/gate_values'
|
|
6
6
|
|
|
7
7
|
module Flipper
|
|
8
|
-
# rubocop:disable Metrics/ClassLength
|
|
9
8
|
class Feature
|
|
10
9
|
# Private: The name of feature instrumentation events.
|
|
11
10
|
InstrumentationName = "feature_operation.#{InstrumentationNamespace}".freeze
|
|
@@ -97,18 +96,26 @@ module Flipper
|
|
|
97
96
|
instrument(:clear) { adapter.clear(self) }
|
|
98
97
|
end
|
|
99
98
|
|
|
100
|
-
# Public: Check if a feature is enabled for
|
|
99
|
+
# Public: Check if a feature is enabled for zero or more actors.
|
|
101
100
|
#
|
|
102
101
|
# Returns true if enabled, false if not.
|
|
103
|
-
def enabled?(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
def enabled?(*actors)
|
|
103
|
+
actors = Array(actors).
|
|
104
|
+
# Avoids to_ary warning that happens when passing DelegateClass of an
|
|
105
|
+
# ActiveRecord object and using flatten here. This is tested in
|
|
106
|
+
# spec/flipper/model/active_record_spec.rb.
|
|
107
|
+
flat_map { |actor| actor.is_a?(Array) ? actor : [actor] }.
|
|
108
|
+
# Allows null object pattern. See PR for more. https://github.com/flippercloud/flipper/pull/887
|
|
109
|
+
reject(&:nil?).
|
|
110
|
+
map { |actor| Types::Actor.wrap(actor) }
|
|
111
|
+
actors = nil if actors.empty?
|
|
112
|
+
|
|
113
|
+
# thing is left for backwards compatibility
|
|
114
|
+
instrument(:enabled?, thing: actors&.first, actors: actors) do |payload|
|
|
108
115
|
context = FeatureCheckContext.new(
|
|
109
116
|
feature_name: @name,
|
|
110
|
-
values:
|
|
111
|
-
|
|
117
|
+
values: gate_values,
|
|
118
|
+
actors: actors
|
|
112
119
|
)
|
|
113
120
|
|
|
114
121
|
if open_gate = gates.detect { |gate| gate.open?(context) }
|
|
@@ -120,6 +127,28 @@ module Flipper
|
|
|
120
127
|
end
|
|
121
128
|
end
|
|
122
129
|
|
|
130
|
+
# Public: Enables an expression_to_add for a feature.
|
|
131
|
+
#
|
|
132
|
+
# expression - an Expression or Hash that can be converted to an expression.
|
|
133
|
+
#
|
|
134
|
+
# Returns result of enable.
|
|
135
|
+
def enable_expression(expression)
|
|
136
|
+
enable Expression.build(expression)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Public: Add an expression for a feature.
|
|
140
|
+
#
|
|
141
|
+
# expression_to_add - an expression or Hash that can be converted to an expression.
|
|
142
|
+
#
|
|
143
|
+
# Returns result of enable.
|
|
144
|
+
def add_expression(expression_to_add)
|
|
145
|
+
if (current_expression = expression)
|
|
146
|
+
enable current_expression.add(expression_to_add)
|
|
147
|
+
else
|
|
148
|
+
enable expression_to_add
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
123
152
|
# Public: Enables a feature for an actor.
|
|
124
153
|
#
|
|
125
154
|
# actor - a Flipper::Types::Actor instance or an object that responds
|
|
@@ -160,6 +189,27 @@ module Flipper
|
|
|
160
189
|
enable Types::PercentageOfActors.wrap(percentage)
|
|
161
190
|
end
|
|
162
191
|
|
|
192
|
+
# Public: Disables an expression for a feature.
|
|
193
|
+
#
|
|
194
|
+
# expression - an expression or Hash that can be converted to an expression.
|
|
195
|
+
#
|
|
196
|
+
# Returns result of disable.
|
|
197
|
+
def disable_expression
|
|
198
|
+
disable Flipper.all # just need an expression to clear
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Public: Remove an expression from a feature. Does nothing if no expression is
|
|
202
|
+
# currently enabled.
|
|
203
|
+
#
|
|
204
|
+
# expression - an Expression or Hash that can be converted to an expression.
|
|
205
|
+
#
|
|
206
|
+
# Returns result of enable or nil (if no expression enabled).
|
|
207
|
+
def remove_expression(expression_to_remove)
|
|
208
|
+
if (current_expression = expression)
|
|
209
|
+
enable current_expression.remove(expression_to_remove)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
163
213
|
# Public: Disables a feature for an actor.
|
|
164
214
|
#
|
|
165
215
|
# actor - a Flipper::Types::Actor instance or an object that responds
|
|
@@ -206,9 +256,9 @@ module Flipper
|
|
|
206
256
|
boolean = gate(:boolean)
|
|
207
257
|
non_boolean_gates = gates - [boolean]
|
|
208
258
|
|
|
209
|
-
if values.boolean || values.
|
|
259
|
+
if values.boolean || values.percentage_of_time == 100
|
|
210
260
|
:on
|
|
211
|
-
elsif non_boolean_gates.detect { |gate| gate.enabled?(values
|
|
261
|
+
elsif non_boolean_gates.detect { |gate| gate.enabled?(values.send(gate.key)) }
|
|
212
262
|
:conditional
|
|
213
263
|
else
|
|
214
264
|
:off
|
|
@@ -233,7 +283,8 @@ module Flipper
|
|
|
233
283
|
|
|
234
284
|
# Public: Returns the raw gate values stored by the adapter.
|
|
235
285
|
def gate_values
|
|
236
|
-
|
|
286
|
+
adapter_values = adapter.get(self)
|
|
287
|
+
GateValues.new(adapter_values)
|
|
237
288
|
end
|
|
238
289
|
|
|
239
290
|
# Public: Get groups enabled for this feature.
|
|
@@ -251,6 +302,10 @@ module Flipper
|
|
|
251
302
|
Flipper.groups - enabled_groups
|
|
252
303
|
end
|
|
253
304
|
|
|
305
|
+
def expression
|
|
306
|
+
Flipper::Expression.build(expression_value) if expression_value
|
|
307
|
+
end
|
|
308
|
+
|
|
254
309
|
# Public: Get the adapter value for the groups gate.
|
|
255
310
|
#
|
|
256
311
|
# Returns Set of String group names.
|
|
@@ -258,6 +313,13 @@ module Flipper
|
|
|
258
313
|
gate_values.groups
|
|
259
314
|
end
|
|
260
315
|
|
|
316
|
+
# Public: Get the adapter value for the expression gate.
|
|
317
|
+
#
|
|
318
|
+
# Returns expression.
|
|
319
|
+
def expression_value
|
|
320
|
+
gate_values.expression
|
|
321
|
+
end
|
|
322
|
+
|
|
261
323
|
# Public: Get the adapter value for the actors gate.
|
|
262
324
|
#
|
|
263
325
|
# Returns Set of String flipper_id's.
|
|
@@ -291,7 +353,7 @@ module Flipper
|
|
|
291
353
|
# Returns an Array of Flipper::Gate instances.
|
|
292
354
|
def enabled_gates
|
|
293
355
|
values = gate_values
|
|
294
|
-
gates.select { |gate| gate.enabled?(values
|
|
356
|
+
gates.select { |gate| gate.enabled?(values.send(gate.key)) }
|
|
295
357
|
end
|
|
296
358
|
|
|
297
359
|
# Public: Get the names of the enabled gates.
|
|
@@ -340,37 +402,42 @@ module Flipper
|
|
|
340
402
|
#
|
|
341
403
|
# Returns an array of gates
|
|
342
404
|
def gates
|
|
343
|
-
@gates ||=
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
Gates::
|
|
349
|
-
|
|
405
|
+
@gates ||= gates_hash.values.freeze
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def gates_hash
|
|
409
|
+
@gates_hash ||= {
|
|
410
|
+
boolean: Gates::Boolean.new,
|
|
411
|
+
expression: Gates::Expression.new,
|
|
412
|
+
actor: Gates::Actor.new,
|
|
413
|
+
percentage_of_actors: Gates::PercentageOfActors.new,
|
|
414
|
+
percentage_of_time: Gates::PercentageOfTime.new,
|
|
415
|
+
group: Gates::Group.new,
|
|
416
|
+
}.freeze
|
|
350
417
|
end
|
|
351
418
|
|
|
352
419
|
# Public: Find a gate by name.
|
|
353
420
|
#
|
|
354
421
|
# Returns a Flipper::Gate if found, nil if not.
|
|
355
422
|
def gate(name)
|
|
356
|
-
|
|
423
|
+
gates_hash[name.to_sym]
|
|
357
424
|
end
|
|
358
425
|
|
|
359
|
-
# Public: Find the gate that protects
|
|
426
|
+
# Public: Find the gate that protects an actor.
|
|
360
427
|
#
|
|
361
|
-
#
|
|
428
|
+
# actor - The object for which you would like to find a gate
|
|
362
429
|
#
|
|
363
430
|
# Returns a Flipper::Gate.
|
|
364
|
-
# Raises Flipper::GateNotFound if no gate found for
|
|
365
|
-
def gate_for(
|
|
366
|
-
gates.detect { |gate| gate.protects?(
|
|
431
|
+
# Raises Flipper::GateNotFound if no gate found for actor
|
|
432
|
+
def gate_for(actor)
|
|
433
|
+
gates.detect { |gate| gate.protects?(actor) } || raise(GateNotFound, actor)
|
|
367
434
|
end
|
|
368
435
|
|
|
369
436
|
private
|
|
370
437
|
|
|
371
438
|
# Private: Instrument a feature operation.
|
|
372
|
-
def instrument(operation)
|
|
373
|
-
@instrumenter.instrument(InstrumentationName) do |payload|
|
|
439
|
+
def instrument(operation, initial_payload = {})
|
|
440
|
+
@instrumenter.instrument(InstrumentationName, initial_payload) do |payload|
|
|
374
441
|
payload[:feature_name] = name
|
|
375
442
|
payload[:operation] = operation
|
|
376
443
|
payload[:result] = yield(payload) if block_given?
|
|
@@ -7,13 +7,17 @@ module Flipper
|
|
|
7
7
|
# gates for the feature.
|
|
8
8
|
attr_reader :values
|
|
9
9
|
|
|
10
|
-
# Public: The
|
|
11
|
-
attr_reader :
|
|
10
|
+
# Public: The actors we want to know if a feature is enabled for.
|
|
11
|
+
attr_reader :actors
|
|
12
12
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@feature_name =
|
|
15
|
-
@values =
|
|
16
|
-
@
|
|
13
|
+
def initialize(feature_name:, values:, actors:)
|
|
14
|
+
@feature_name = feature_name
|
|
15
|
+
@values = values
|
|
16
|
+
@actors = actors
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def actors?
|
|
20
|
+
!@actors.nil? && !@actors.empty?
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
# Public: Convenience method for groups value like Feature has.
|
data/lib/flipper/gate.rb
CHANGED
|
@@ -18,28 +18,29 @@ module Flipper
|
|
|
18
18
|
raise 'Not implemented'
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def enabled?(
|
|
21
|
+
def enabled?(value)
|
|
22
22
|
raise 'Not implemented'
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# Internal: Check if a gate is open for
|
|
25
|
+
# Internal: Check if a gate is open for one or more actors. Implemented
|
|
26
|
+
# in subclass.
|
|
26
27
|
#
|
|
27
|
-
# Returns true if gate open for
|
|
28
|
-
def open?(
|
|
28
|
+
# Returns true if gate open for any actor, false if not.
|
|
29
|
+
def open?(context)
|
|
29
30
|
false
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
# Internal: Check if a gate is protects
|
|
33
|
+
# Internal: Check if a gate is protects an actor. Implemented in subclass.
|
|
33
34
|
#
|
|
34
|
-
# Returns true if gate protects
|
|
35
|
-
def protects?(
|
|
35
|
+
# Returns true if gate protects actor, false if not.
|
|
36
|
+
def protects?(actor)
|
|
36
37
|
false
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
# Internal: Allows gate to wrap
|
|
40
|
-
# types so adapters always get
|
|
41
|
-
def wrap(
|
|
42
|
-
|
|
40
|
+
# Internal: Allows gate to wrap actor using one of the supported flipper
|
|
41
|
+
# types so adapters always get someactor that responds to value.
|
|
42
|
+
def wrap(actor)
|
|
43
|
+
actor
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
# Public: Pretty string version for debugging.
|
|
@@ -59,3 +60,4 @@ require 'flipper/gates/boolean'
|
|
|
59
60
|
require 'flipper/gates/group'
|
|
60
61
|
require 'flipper/gates/percentage_of_actors'
|
|
61
62
|
require 'flipper/gates/percentage_of_time'
|
|
63
|
+
require 'flipper/gates/expression'
|
data/lib/flipper/gate_values.rb
CHANGED
|
@@ -3,19 +3,10 @@ require 'flipper/typecast'
|
|
|
3
3
|
|
|
4
4
|
module Flipper
|
|
5
5
|
class GateValues
|
|
6
|
-
# Private: Array of instance variables that are readable through the []
|
|
7
|
-
# instance method.
|
|
8
|
-
LegitIvars = {
|
|
9
|
-
'boolean' => '@boolean',
|
|
10
|
-
'actors' => '@actors',
|
|
11
|
-
'groups' => '@groups',
|
|
12
|
-
'percentage_of_time' => '@percentage_of_time',
|
|
13
|
-
'percentage_of_actors' => '@percentage_of_actors',
|
|
14
|
-
}.freeze
|
|
15
|
-
|
|
16
6
|
attr_reader :boolean
|
|
17
7
|
attr_reader :actors
|
|
18
8
|
attr_reader :groups
|
|
9
|
+
attr_reader :expression
|
|
19
10
|
attr_reader :percentage_of_actors
|
|
20
11
|
attr_reader :percentage_of_time
|
|
21
12
|
|
|
@@ -23,14 +14,9 @@ module Flipper
|
|
|
23
14
|
@boolean = Typecast.to_boolean(adapter_values[:boolean])
|
|
24
15
|
@actors = Typecast.to_set(adapter_values[:actors])
|
|
25
16
|
@groups = Typecast.to_set(adapter_values[:groups])
|
|
26
|
-
@
|
|
27
|
-
@
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def [](key)
|
|
31
|
-
if ivar = LegitIvars[key.to_s]
|
|
32
|
-
instance_variable_get(ivar)
|
|
33
|
-
end
|
|
17
|
+
@expression = adapter_values[:expression]
|
|
18
|
+
@percentage_of_actors = Typecast.to_number(adapter_values[:percentage_of_actors])
|
|
19
|
+
@percentage_of_time = Typecast.to_number(adapter_values[:percentage_of_time])
|
|
34
20
|
end
|
|
35
21
|
|
|
36
22
|
def eql?(other)
|
|
@@ -38,6 +24,7 @@ module Flipper
|
|
|
38
24
|
boolean == other.boolean &&
|
|
39
25
|
actors == other.actors &&
|
|
40
26
|
groups == other.groups &&
|
|
27
|
+
expression == other.expression &&
|
|
41
28
|
percentage_of_actors == other.percentage_of_actors &&
|
|
42
29
|
percentage_of_time == other.percentage_of_time
|
|
43
30
|
end
|
data/lib/flipper/gates/actor.rb
CHANGED
|
@@ -19,30 +19,23 @@ module Flipper
|
|
|
19
19
|
!value.empty?
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
# Internal: Checks if the gate is open for
|
|
22
|
+
# Internal: Checks if the gate is open for an actor.
|
|
23
23
|
#
|
|
24
|
-
# Returns true if gate open for
|
|
24
|
+
# Returns true if gate open for actor, false if not.
|
|
25
25
|
def open?(context)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if protects?(context.thing)
|
|
31
|
-
actor = wrap(context.thing)
|
|
32
|
-
enabled_actor_ids = value
|
|
33
|
-
enabled_actor_ids.include?(actor.value)
|
|
34
|
-
else
|
|
35
|
-
false
|
|
36
|
-
end
|
|
26
|
+
return false unless context.actors?
|
|
27
|
+
|
|
28
|
+
context.actors.any? do |actor|
|
|
29
|
+
context.values.actors.include?(actor.value)
|
|
37
30
|
end
|
|
38
31
|
end
|
|
39
32
|
|
|
40
|
-
def wrap(
|
|
41
|
-
Types::Actor.wrap(
|
|
33
|
+
def wrap(actor)
|
|
34
|
+
Types::Actor.wrap(actor)
|
|
42
35
|
end
|
|
43
36
|
|
|
44
|
-
def protects?(
|
|
45
|
-
Types::Actor.wrappable?(
|
|
37
|
+
def protects?(actor)
|
|
38
|
+
Types::Actor.wrappable?(actor)
|
|
46
39
|
end
|
|
47
40
|
end
|
|
48
41
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "flipper/expression"
|
|
2
|
+
|
|
3
|
+
module Flipper
|
|
4
|
+
module Gates
|
|
5
|
+
class Expression < Gate
|
|
6
|
+
# Internal: The name of the gate. Used for instrumentation, etc.
|
|
7
|
+
def name
|
|
8
|
+
:expression
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Internal: Name converted to value safe for adapter.
|
|
12
|
+
def key
|
|
13
|
+
:expression
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def data_type
|
|
17
|
+
:json
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def enabled?(value)
|
|
21
|
+
!value.nil? && !value.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Internal: Checks if the gate is open for a thing.
|
|
25
|
+
#
|
|
26
|
+
# Returns true if gate open for thing, false if not.
|
|
27
|
+
def open?(context)
|
|
28
|
+
data = context.values.expression
|
|
29
|
+
return false if data.nil? || data.empty?
|
|
30
|
+
expression = Flipper::Expression.build(data)
|
|
31
|
+
|
|
32
|
+
if context.actors.nil? || context.actors.empty?
|
|
33
|
+
!!expression.evaluate(feature_name: context.feature_name, properties: DEFAULT_PROPERTIES, actor: nil)
|
|
34
|
+
else
|
|
35
|
+
context.actors.any? do |actor|
|
|
36
|
+
!!expression.evaluate(feature_name: context.feature_name, properties: properties(actor), actor: actor)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def protects?(thing)
|
|
42
|
+
thing.is_a?(Flipper::Expression) || thing.is_a?(Hash)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def wrap(thing)
|
|
46
|
+
Flipper::Expression.build(thing)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Internal
|
|
52
|
+
DEFAULT_PROPERTIES = {}.freeze
|
|
53
|
+
|
|
54
|
+
def properties(actor)
|
|
55
|
+
return DEFAULT_PROPERTIES if actor.nil?
|
|
56
|
+
|
|
57
|
+
properties = {}
|
|
58
|
+
|
|
59
|
+
if actor.respond_to?(:flipper_properties)
|
|
60
|
+
properties.update(actor.flipper_properties)
|
|
61
|
+
else
|
|
62
|
+
warn "#{actor.inspect} does not respond to `flipper_properties` but should."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
properties.transform_keys!(&:to_s)
|
|
66
|
+
|
|
67
|
+
if actor.respond_to?(:flipper_id)
|
|
68
|
+
properties["flipper_id".freeze] = actor.flipper_id
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
properties
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|