flipper 0.26.0 → 1.1.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 +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +19 -13
- data/.github/workflows/examples.yml +32 -15
- data/Changelog.md +294 -154
- data/Gemfile +15 -10
- data/README.md +13 -11
- 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/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +3 -4
- data/examples/api/custom_memoized.ru +3 -4
- data/examples/api/memoized.ru +3 -4
- 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/threaded.rb +33 -0
- data/examples/dsl.rb +1 -15
- data/examples/enabled_for_actor.rb +4 -2
- data/examples/expressions.rb +213 -0
- data/examples/mirroring.rb +59 -0
- data/examples/strict.rb +18 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +3 -5
- data/lib/flipper/actor.rb +6 -3
- data/lib/flipper/adapter.rb +33 -7
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/dual_write.rb +1 -3
- data/lib/flipper/adapters/failover.rb +0 -4
- data/lib/flipper/adapters/failsafe.rb +0 -4
- data/lib/flipper/adapters/http/client.rb +26 -7
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +29 -16
- data/lib/flipper/adapters/instrumented.rb +25 -6
- data/lib/flipper/adapters/memoizable.rb +33 -21
- data/lib/flipper/adapters/memory.rb +81 -46
- data/lib/flipper/adapters/operation_logger.rb +16 -7
- data/lib/flipper/adapters/poll/poller.rb +2 -125
- data/lib/flipper/adapters/poll.rb +5 -3
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +4 -4
- data/lib/flipper/adapters/strict.rb +47 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/cloud/configuration.rb +258 -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/routes.rb +14 -0
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -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 +98 -0
- data/lib/flipper/cloud/telemetry.rb +183 -0
- data/lib/flipper/cloud.rb +53 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +46 -45
- data/lib/flipper/engine.rb +88 -0
- data/lib/flipper/errors.rb +3 -3
- data/lib/flipper/export.rb +26 -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 +11 -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/duration.rb +28 -0
- data/lib/flipper/expressions/equal.rb +9 -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 +9 -0
- data/lib/flipper/feature.rb +87 -26
- 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 +2 -2
- data/lib/flipper/instrumentation/log_subscriber.rb +24 -5
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -1
- data/lib/flipper/metadata.rb +5 -1
- data/lib/flipper/middleware/memoizer.rb +30 -14
- data/lib/flipper/poller.rb +117 -0
- data/lib/flipper/serializers/gzip.rb +24 -0
- data/lib/flipper/serializers/json.rb +19 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +95 -54
- data/lib/flipper/test/shared_adapter_test.rb +91 -48
- data/lib/flipper/typecast.rb +56 -15
- data/lib/flipper/types/actor.rb +13 -13
- data/lib/flipper/types/group.rb +4 -4
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +47 -10
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/adapter_builder_spec.rb +73 -0
- data/spec/flipper/adapter_spec.rb +30 -2
- data/spec/flipper/adapters/dual_write_spec.rb +2 -2
- data/spec/flipper/adapters/http_spec.rb +64 -8
- data/spec/flipper/adapters/instrumented_spec.rb +29 -11
- data/spec/flipper/adapters/memoizable_spec.rb +51 -31
- data/spec/flipper/adapters/memory_spec.rb +14 -3
- data/spec/flipper/adapters/operation_logger_spec.rb +31 -12
- data/spec/flipper/adapters/read_only_spec.rb +32 -17
- data/spec/flipper/adapters/strict_spec.rb +62 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cloud/configuration_spec.rb +252 -0
- data/spec/flipper/cloud/dsl_spec.rb +82 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +289 -0
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -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 +156 -0
- data/spec/flipper/cloud_spec.rb +180 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -73
- data/spec/flipper/engine_spec.rb +291 -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/duration_spec.rb +43 -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 +13 -0
- data/spec/flipper/feature_check_context_spec.rb +17 -17
- data/spec/flipper/feature_spec.rb +436 -33
- data/spec/flipper/gate_values_spec.rb +2 -33
- data/spec/flipper/gates/boolean_spec.rb +1 -1
- data/spec/flipper/gates/expression_spec.rb +108 -0
- data/spec/flipper/gates/group_spec.rb +2 -3
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
- data/spec/flipper/identifier_spec.rb +4 -5
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +15 -5
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
- data/spec/flipper/middleware/memoizer_spec.rb +67 -0
- data/spec/flipper/poller_spec.rb +47 -0
- 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 -6
- data/spec/flipper/types/actor_spec.rb +63 -46
- data/spec/flipper/types/group_spec.rb +2 -2
- data/spec/flipper_integration_spec.rb +168 -58
- data/spec/flipper_spec.rb +92 -28
- data/spec/spec_helper.rb +6 -13
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/climate_control.rb +7 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +11 -3
- metadata +166 -13
- data/.github/workflows/release.yml +0 -44
- data/.tool-versions +0 -1
- data/lib/flipper/railtie.rb +0 -47
- data/spec/flipper/railtie_spec.rb +0 -109
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
|
4
|
+
def assert(value)
|
5
|
+
if value
|
6
|
+
p value
|
7
|
+
else
|
8
|
+
puts "Expected true but was #{value}. Please correct."
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def refute(value)
|
14
|
+
if value
|
15
|
+
puts "Expected false but was #{value}. Please correct."
|
16
|
+
exit 1
|
17
|
+
else
|
18
|
+
p value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reset
|
23
|
+
Flipper.disable_expression :something
|
24
|
+
end
|
25
|
+
|
26
|
+
class User < Struct.new(:id, :flipper_properties)
|
27
|
+
include Flipper::Identifier
|
28
|
+
end
|
29
|
+
|
30
|
+
class Org < Struct.new(:id, :flipper_properties)
|
31
|
+
include Flipper::Identifier
|
32
|
+
end
|
33
|
+
|
34
|
+
NOW = Time.now.to_i
|
35
|
+
DAY = 60 * 60 * 24
|
36
|
+
|
37
|
+
org = Org.new(1, {
|
38
|
+
"type" => "Org",
|
39
|
+
"id" => 1,
|
40
|
+
"now" => NOW,
|
41
|
+
})
|
42
|
+
|
43
|
+
user = User.new(1, {
|
44
|
+
"type" => "User",
|
45
|
+
"id" => 1,
|
46
|
+
"plan" => "basic",
|
47
|
+
"age" => 39,
|
48
|
+
"team_user" => true,
|
49
|
+
"now" => NOW,
|
50
|
+
})
|
51
|
+
|
52
|
+
admin_user = User.new(2, {
|
53
|
+
"type" => "User",
|
54
|
+
"id" => 2,
|
55
|
+
"admin" => true,
|
56
|
+
"team_user" => true,
|
57
|
+
"now" => NOW,
|
58
|
+
})
|
59
|
+
|
60
|
+
other_user = User.new(3, {
|
61
|
+
"type" => "User",
|
62
|
+
"id" => 3,
|
63
|
+
"plan" => "plus",
|
64
|
+
"age" => 18,
|
65
|
+
"org_admin" => true,
|
66
|
+
"now" => NOW - DAY,
|
67
|
+
})
|
68
|
+
|
69
|
+
age_expression = Flipper.property(:age).gte(21)
|
70
|
+
plan_expression = Flipper.property(:plan).eq("basic")
|
71
|
+
admin_expression = Flipper.property(:admin).eq(true)
|
72
|
+
|
73
|
+
puts "Single Expression"
|
74
|
+
refute Flipper.enabled?(:something, user)
|
75
|
+
|
76
|
+
puts "Enabling single expression"
|
77
|
+
Flipper.enable :something, plan_expression
|
78
|
+
assert Flipper.enabled?(:something, user)
|
79
|
+
refute Flipper.enabled?(:something, admin_user)
|
80
|
+
refute Flipper.enabled?(:something, other_user)
|
81
|
+
|
82
|
+
puts "Disabling single expression"
|
83
|
+
reset
|
84
|
+
refute Flipper.enabled?(:something, user)
|
85
|
+
|
86
|
+
puts "\n\nAny Expression"
|
87
|
+
any_expression = Flipper.any(plan_expression, age_expression)
|
88
|
+
refute Flipper.enabled?(:something, user)
|
89
|
+
|
90
|
+
puts "Enabling any expression"
|
91
|
+
Flipper.enable :something, any_expression
|
92
|
+
assert Flipper.enabled?(:something, user)
|
93
|
+
refute Flipper.enabled?(:something, admin_user)
|
94
|
+
refute Flipper.enabled?(:something, other_user)
|
95
|
+
|
96
|
+
puts "Disabling any expression"
|
97
|
+
reset
|
98
|
+
refute Flipper.enabled?(:something, user)
|
99
|
+
|
100
|
+
puts "\n\nAll Expression"
|
101
|
+
all_expression = Flipper.all(plan_expression, age_expression)
|
102
|
+
refute Flipper.enabled?(:something, user)
|
103
|
+
|
104
|
+
puts "Enabling all expression"
|
105
|
+
Flipper.enable :something, all_expression
|
106
|
+
assert Flipper.enabled?(:something, user)
|
107
|
+
refute Flipper.enabled?(:something, admin_user)
|
108
|
+
refute Flipper.enabled?(:something, other_user)
|
109
|
+
|
110
|
+
puts "Disabling all expression"
|
111
|
+
reset
|
112
|
+
refute Flipper.enabled?(:something, user)
|
113
|
+
|
114
|
+
puts "\n\nNested Expression"
|
115
|
+
nested_expression = Flipper.any(admin_expression, all_expression)
|
116
|
+
refute Flipper.enabled?(:something, user)
|
117
|
+
refute Flipper.enabled?(:something, admin_user)
|
118
|
+
refute Flipper.enabled?(:something, other_user)
|
119
|
+
|
120
|
+
puts "Enabling nested expression"
|
121
|
+
Flipper.enable :something, nested_expression
|
122
|
+
assert Flipper.enabled?(:something, user)
|
123
|
+
assert Flipper.enabled?(:something, admin_user)
|
124
|
+
refute Flipper.enabled?(:something, other_user)
|
125
|
+
|
126
|
+
puts "Disabling nested expression"
|
127
|
+
reset
|
128
|
+
refute Flipper.enabled?(:something, user)
|
129
|
+
refute Flipper.enabled?(:something, admin_user)
|
130
|
+
refute Flipper.enabled?(:something, other_user)
|
131
|
+
|
132
|
+
puts "\n\nBoolean Expression"
|
133
|
+
boolean_expression = Flipper.boolean(true)
|
134
|
+
Flipper.enable :something, boolean_expression
|
135
|
+
assert Flipper.enabled?(:something)
|
136
|
+
assert Flipper.enabled?(:something, user)
|
137
|
+
reset
|
138
|
+
|
139
|
+
puts "\n\nSet of Actors Expression"
|
140
|
+
set_of_actors_expression = Flipper.any(
|
141
|
+
Flipper.property(:flipper_id).eq("User;1"),
|
142
|
+
Flipper.property(:flipper_id).eq("User;3"),
|
143
|
+
)
|
144
|
+
Flipper.enable :something, set_of_actors_expression
|
145
|
+
assert Flipper.enabled?(:something, user)
|
146
|
+
assert Flipper.enabled?(:something, other_user)
|
147
|
+
refute Flipper.enabled?(:something, admin_user)
|
148
|
+
reset
|
149
|
+
|
150
|
+
puts "\n\n% of Actors Expression"
|
151
|
+
percentage_of_actors = Flipper.property(:flipper_id).percentage_of_actors(30)
|
152
|
+
Flipper.enable :something, percentage_of_actors
|
153
|
+
refute Flipper.enabled?(:something, user)
|
154
|
+
refute Flipper.enabled?(:something, other_user)
|
155
|
+
assert Flipper.enabled?(:something, admin_user)
|
156
|
+
reset
|
157
|
+
|
158
|
+
puts "\n\n% of Actors Per Type Expression"
|
159
|
+
percentage_of_actors_per_type = Flipper.any(
|
160
|
+
Flipper.all(
|
161
|
+
Flipper.property(:type).eq("User"),
|
162
|
+
Flipper.property(:flipper_id).percentage_of_actors(40),
|
163
|
+
),
|
164
|
+
Flipper.all(
|
165
|
+
Flipper.property(:type).eq("Org"),
|
166
|
+
Flipper.property(:flipper_id).percentage_of_actors(10),
|
167
|
+
)
|
168
|
+
)
|
169
|
+
Flipper.enable :something, percentage_of_actors_per_type
|
170
|
+
refute Flipper.enabled?(:something, user) # not in the 40% enabled for Users
|
171
|
+
assert Flipper.enabled?(:something, other_user)
|
172
|
+
assert Flipper.enabled?(:something, admin_user)
|
173
|
+
refute Flipper.enabled?(:something, org) # not in the 10% of enabled for Orgs
|
174
|
+
reset
|
175
|
+
|
176
|
+
puts "\n\nPercentage of Time Expression"
|
177
|
+
percentage_of_time_expression = Flipper.random(100).lt(50)
|
178
|
+
Flipper.enable :something, percentage_of_time_expression
|
179
|
+
results = (1..10000).map { |n| Flipper.enabled?(:something, user) }
|
180
|
+
enabled, disabled = results.partition { |r| r }
|
181
|
+
p enabled: enabled.size
|
182
|
+
p disabled: disabled.size
|
183
|
+
assert (4_700..5_200).include?(enabled.size)
|
184
|
+
assert (4_700..5_200).include?(disabled.size)
|
185
|
+
reset
|
186
|
+
|
187
|
+
puts "\n\nChanging single expression to all expression"
|
188
|
+
Flipper.enable :something, plan_expression
|
189
|
+
Flipper.enable :something, Flipper.expression(:something).all.add(age_expression)
|
190
|
+
assert Flipper.enabled?(:something, user)
|
191
|
+
refute Flipper.enabled?(:something, admin_user)
|
192
|
+
refute Flipper.enabled?(:something, other_user)
|
193
|
+
|
194
|
+
puts "\n\nChanging single expression to any expression"
|
195
|
+
Flipper.enable :something, plan_expression
|
196
|
+
Flipper.enable :something, Flipper.expression(:something).any.add(age_expression, admin_expression)
|
197
|
+
assert Flipper.enabled?(:something, user)
|
198
|
+
assert Flipper.enabled?(:something, admin_user)
|
199
|
+
refute Flipper.enabled?(:something, other_user)
|
200
|
+
|
201
|
+
puts "\n\nChanging single expression to any expression by adding to condition"
|
202
|
+
Flipper.enable :something, plan_expression
|
203
|
+
Flipper.enable :something, Flipper.expression(:something).add(admin_expression)
|
204
|
+
assert Flipper.enabled?(:something, user)
|
205
|
+
assert Flipper.enabled?(:something, admin_user)
|
206
|
+
refute Flipper.enabled?(:something, other_user)
|
207
|
+
|
208
|
+
puts "\n\nEnabling based on time"
|
209
|
+
scheduled_time_expression = Flipper.property(:now).gte(NOW)
|
210
|
+
Flipper.enable :something, scheduled_time_expression
|
211
|
+
assert Flipper.enabled?(:something, user)
|
212
|
+
assert Flipper.enabled?(:something, admin_user)
|
213
|
+
refute Flipper.enabled?(:something, other_user)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require_relative 'active_record/ar_setup'
|
3
|
+
require 'flipper'
|
4
|
+
require 'flipper/adapters/redis'
|
5
|
+
require 'flipper/adapters/active_record'
|
6
|
+
|
7
|
+
# Say you have production...
|
8
|
+
production_adapter = Flipper::Adapters::Memory.new
|
9
|
+
production = Flipper.new(production_adapter)
|
10
|
+
|
11
|
+
# And production has some stuff enabled...
|
12
|
+
production.enable(:search)
|
13
|
+
production.enable_percentage_of_time(:verbose_logging, 5)
|
14
|
+
production.enable_percentage_of_actors(:new_feature, 5)
|
15
|
+
production.enable_actor(:issues, Flipper::Actor.new('1'))
|
16
|
+
production.enable_actor(:issues, Flipper::Actor.new('2'))
|
17
|
+
production.enable_group(:request_tracing, :staff)
|
18
|
+
|
19
|
+
# And you would like to mirror production to staging...
|
20
|
+
staging_adapter = Flipper::Adapters::Memory.new
|
21
|
+
staging = Flipper.new(staging_adapter)
|
22
|
+
staging_export = staging.export
|
23
|
+
|
24
|
+
puts "Here is the state of the world for staging and production..."
|
25
|
+
puts "Staging"
|
26
|
+
staging.features.each do |feature|
|
27
|
+
pp feature: feature.key, values: feature.gate_values
|
28
|
+
end
|
29
|
+
puts "Production"
|
30
|
+
production.features.each do |feature|
|
31
|
+
pp feature: feature.key, values: feature.gate_values
|
32
|
+
end
|
33
|
+
|
34
|
+
# NOTE: This wipes active record clean and copies features/gates from redis into active record.
|
35
|
+
puts "Mirroring production to staging..."
|
36
|
+
staging.import(production.export)
|
37
|
+
puts "Staging is now identical to Production."
|
38
|
+
|
39
|
+
puts "Staging"
|
40
|
+
staging.features.each do |feature|
|
41
|
+
pp feature: feature.key, values: feature.gate_values
|
42
|
+
end
|
43
|
+
puts "Production"
|
44
|
+
production.features.each do |feature|
|
45
|
+
pp feature: feature.key, values: feature.gate_values
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "Restoring staging to original state..."
|
49
|
+
staging.import(staging_export)
|
50
|
+
puts "Staging restored."
|
51
|
+
|
52
|
+
puts "Staging"
|
53
|
+
staging.features.each do |feature|
|
54
|
+
pp feature: feature.key, values: feature.gate_values
|
55
|
+
end
|
56
|
+
puts "Production"
|
57
|
+
production.features.each do |feature|
|
58
|
+
pp feature: feature.key, values: feature.gate_values
|
59
|
+
end
|
data/examples/strict.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'flipper'
|
3
|
+
|
4
|
+
adapter = Flipper::Adapters::Strict.new(Flipper::Adapters::Memory.new)
|
5
|
+
flipper = Flipper.new(adapter)
|
6
|
+
|
7
|
+
begin
|
8
|
+
puts "Checking :unknown_feature, which should raise an error."
|
9
|
+
flipper.enabled?(:unknown_feature)
|
10
|
+
warn "An error was not raised, but should have been"
|
11
|
+
exit 1
|
12
|
+
rescue Flipper::Adapters::Strict::NotFound => exception
|
13
|
+
puts "Ok, the exepcted error was raised: #{exception.message}"
|
14
|
+
end
|
15
|
+
|
16
|
+
puts "Flipper.add(:new_feature)"
|
17
|
+
flipper.add(:new_feature)
|
18
|
+
puts "Flipper.enabled?(:new_feature) => #{flipper.enabled?(:new_feature)}"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/flipper/version', __FILE__)
|
3
|
+
require File.expand_path('../lib/flipper/metadata', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ['John Nunemaker']
|
7
|
+
gem.email = 'support@flippercloud.io'
|
8
|
+
gem.summary = '[DEPRECATED] This gem has been merged into the `flipper` gem'
|
9
|
+
gem.license = 'MIT'
|
10
|
+
gem.homepage = 'https://www.flippercloud.io'
|
11
|
+
|
12
|
+
gem.files = [ 'lib/flipper-cloud.rb', 'lib/flipper/version.rb' ]
|
13
|
+
gem.name = 'flipper-cloud'
|
14
|
+
gem.require_paths = ['lib']
|
15
|
+
gem.version = Flipper::VERSION
|
16
|
+
gem.metadata = Flipper::METADATA
|
17
|
+
|
18
|
+
gem.add_dependency 'flipper', "~> #{Flipper::VERSION}"
|
19
|
+
end
|
data/flipper.gemspec
CHANGED
@@ -13,7 +13,6 @@ end
|
|
13
13
|
|
14
14
|
ignored_files = plugin_files
|
15
15
|
ignored_files << Dir['script/*']
|
16
|
-
ignored_files << '.travis.yml'
|
17
16
|
ignored_files << '.gitignore'
|
18
17
|
ignored_files << 'Guardfile'
|
19
18
|
ignored_files.flatten!.uniq!
|
@@ -23,12 +22,11 @@ ignored_test_files.flatten!.uniq!
|
|
23
22
|
|
24
23
|
Gem::Specification.new do |gem|
|
25
24
|
gem.authors = ['John Nunemaker']
|
26
|
-
gem.email =
|
27
|
-
gem.summary = '
|
28
|
-
gem.homepage = 'https://
|
25
|
+
gem.email = 'support@flippercloud.io'
|
26
|
+
gem.summary = 'Beautiful, performant feature flags for Ruby.'
|
27
|
+
gem.homepage = 'https://www.flippercloud.io/docs'
|
29
28
|
gem.license = 'MIT'
|
30
29
|
|
31
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
32
30
|
gem.files = `git ls-files`.split("\n") - ignored_files + ['lib/flipper/version.rb']
|
33
31
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - ignored_test_files
|
34
32
|
gem.name = 'flipper'
|
data/lib/flipper/actor.rb
CHANGED
@@ -2,14 +2,17 @@
|
|
2
2
|
# to Flipper::Feature#enabled?.
|
3
3
|
module Flipper
|
4
4
|
class Actor
|
5
|
-
attr_reader :flipper_id
|
5
|
+
attr_reader :flipper_id, :flipper_properties
|
6
6
|
|
7
|
-
def initialize(flipper_id)
|
7
|
+
def initialize(flipper_id, flipper_properties = {})
|
8
8
|
@flipper_id = flipper_id
|
9
|
+
@flipper_properties = flipper_properties
|
9
10
|
end
|
10
11
|
|
11
12
|
def eql?(other)
|
12
|
-
self.class.eql?(other.class) &&
|
13
|
+
self.class.eql?(other.class) &&
|
14
|
+
@flipper_id == other.flipper_id &&
|
15
|
+
@flipper_properties == other.flipper_properties
|
13
16
|
end
|
14
17
|
alias_method :==, :eql?
|
15
18
|
|
data/lib/flipper/adapter.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require "set"
|
2
|
-
require "flipper/feature"
|
3
|
-
require "flipper/adapters/sync/synchronizer"
|
4
|
-
|
5
1
|
module Flipper
|
6
2
|
# Adding a module include so we have some hooks for stuff down the road
|
7
3
|
module Adapter
|
@@ -16,10 +12,20 @@ module Flipper
|
|
16
12
|
boolean: nil,
|
17
13
|
groups: Set.new,
|
18
14
|
actors: Set.new,
|
15
|
+
expression: nil,
|
19
16
|
percentage_of_actors: nil,
|
20
17
|
percentage_of_time: nil,
|
21
18
|
}
|
22
19
|
end
|
20
|
+
|
21
|
+
def from(source)
|
22
|
+
return source if source.is_a?(Flipper::Adapter)
|
23
|
+
source.adapter
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_only?
|
28
|
+
false
|
23
29
|
end
|
24
30
|
|
25
31
|
# Public: Get all features and gate values in one call. Defaults to one call
|
@@ -43,14 +49,34 @@ module Flipper
|
|
43
49
|
|
44
50
|
# Public: Ensure that adapter is in sync with source adapter provided.
|
45
51
|
#
|
46
|
-
#
|
47
|
-
|
48
|
-
|
52
|
+
# source - The source dsl, adapter or export to import.
|
53
|
+
#
|
54
|
+
# Returns true if successful.
|
55
|
+
def import(source)
|
56
|
+
Adapters::Sync::Synchronizer.new(self, self.class.from(source), raise: true).call
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Exports the adapter in a given format for a given format version.
|
61
|
+
#
|
62
|
+
# Returns a Flipper::Export instance.
|
63
|
+
def export(format: :json, version: 1)
|
64
|
+
Flipper::Exporter.build(format: format, version: version).call(self)
|
49
65
|
end
|
50
66
|
|
51
67
|
# Public: Default config for a feature's gate values.
|
52
68
|
def default_config
|
53
69
|
self.class.default_config
|
54
70
|
end
|
71
|
+
|
72
|
+
# Public: default name of the adapter
|
73
|
+
def name
|
74
|
+
@name ||= self.class.name.split('::').last.split(/(?=[A-Z])/).join('_').downcase.to_sym
|
75
|
+
end
|
55
76
|
end
|
56
77
|
end
|
78
|
+
|
79
|
+
require "set"
|
80
|
+
require "flipper/exporter"
|
81
|
+
require "flipper/feature"
|
82
|
+
require "flipper/adapters/sync/synchronizer"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Flipper
|
2
|
+
# Builds an adapter from a stack of adapters.
|
3
|
+
#
|
4
|
+
# adapter = Flipper::AdapterBuilder.new do
|
5
|
+
# use Flipper::Adapters::Strict
|
6
|
+
# use Flipper::Adapters::Memoizer
|
7
|
+
# store Flipper::Adapters::Memory
|
8
|
+
# end.to_adapter
|
9
|
+
#
|
10
|
+
class AdapterBuilder
|
11
|
+
def initialize(&block)
|
12
|
+
@stack = []
|
13
|
+
|
14
|
+
# Default to memory adapter
|
15
|
+
store Flipper::Adapters::Memory
|
16
|
+
|
17
|
+
block.arity == 0 ? instance_eval(&block) : block.call(self) if block
|
18
|
+
end
|
19
|
+
|
20
|
+
if RUBY_VERSION >= '3.0'
|
21
|
+
def use(klass, *args, **kwargs, &block)
|
22
|
+
@stack.push ->(adapter) { klass.new(adapter, *args, **kwargs, &block) }
|
23
|
+
end
|
24
|
+
else
|
25
|
+
def use(klass, *args, &block)
|
26
|
+
@stack.push ->(adapter) { klass.new(adapter, *args, &block) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if RUBY_VERSION >= '3.0'
|
31
|
+
def store(adapter, *args, **kwargs, &block)
|
32
|
+
@store = adapter.respond_to?(:call) ? adapter : -> { adapter.new(*args, **kwargs, &block) }
|
33
|
+
end
|
34
|
+
else
|
35
|
+
def store(adapter, *args, &block)
|
36
|
+
@store = adapter.respond_to?(:call) ? adapter : -> { adapter.new(*args, &block) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_adapter
|
41
|
+
@stack.reverse.inject(@store.call) { |adapter, wrapper| wrapper.call(adapter) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -3,8 +3,7 @@ module Flipper
|
|
3
3
|
class DualWrite
|
4
4
|
include ::Flipper::Adapter
|
5
5
|
|
6
|
-
|
7
|
-
attr_reader :name, :local, :remote
|
6
|
+
attr_reader :local, :remote
|
8
7
|
|
9
8
|
# Public: Build a new sync instance.
|
10
9
|
#
|
@@ -12,7 +11,6 @@ module Flipper
|
|
12
11
|
# remote - The remote flipper adapter that writes should go to first (in
|
13
12
|
# addition to the local adapter).
|
14
13
|
def initialize(local, remote, options = {})
|
15
|
-
@name = :dual_write
|
16
14
|
@local = local
|
17
15
|
@remote = remote
|
18
16
|
end
|
@@ -3,9 +3,6 @@ module Flipper
|
|
3
3
|
class Failover
|
4
4
|
include ::Flipper::Adapter
|
5
5
|
|
6
|
-
# Public: The name of the adapter.
|
7
|
-
attr_reader :name
|
8
|
-
|
9
6
|
# Public: Build a new failover instance.
|
10
7
|
#
|
11
8
|
# primary - The primary flipper adapter.
|
@@ -17,7 +14,6 @@ module Flipper
|
|
17
14
|
# :errors - Array of exception types for which to failover
|
18
15
|
|
19
16
|
def initialize(primary, secondary, options = {})
|
20
|
-
@name = :failover
|
21
17
|
@primary = primary
|
22
18
|
@secondary = secondary
|
23
19
|
|
@@ -3,9 +3,6 @@ module Flipper
|
|
3
3
|
class Failsafe
|
4
4
|
include ::Flipper::Adapter
|
5
5
|
|
6
|
-
# Public: The name of the adapter.
|
7
|
-
attr_reader :name
|
8
|
-
|
9
6
|
# Public: Build a new Failsafe instance.
|
10
7
|
#
|
11
8
|
# adapter - Flipper adapter to guard.
|
@@ -15,7 +12,6 @@ module Flipper
|
|
15
12
|
def initialize(adapter, options = {})
|
16
13
|
@adapter = adapter
|
17
14
|
@errors = options.fetch(:errors, [StandardError])
|
18
|
-
@name = :failsafe
|
19
15
|
end
|
20
16
|
|
21
17
|
def features
|
@@ -14,6 +14,12 @@ module Flipper
|
|
14
14
|
|
15
15
|
HTTPS_SCHEME = "https".freeze
|
16
16
|
|
17
|
+
CLIENT_FRAMEWORKS = {
|
18
|
+
rails: -> { Rails.version if defined?(Rails) },
|
19
|
+
sinatra: -> { Sinatra::VERSION if defined?(Sinatra) },
|
20
|
+
hanami: -> { Hanami::VERSION if defined?(Hanami) },
|
21
|
+
}
|
22
|
+
|
17
23
|
attr_reader :uri, :headers
|
18
24
|
attr_reader :basic_auth_username, :basic_auth_password
|
19
25
|
attr_reader :read_timeout, :open_timeout, :write_timeout, :max_retries, :debug_output
|
@@ -30,6 +36,10 @@ module Flipper
|
|
30
36
|
@debug_output = options[:debug_output]
|
31
37
|
end
|
32
38
|
|
39
|
+
def add_header(key, value)
|
40
|
+
@headers[key] = value
|
41
|
+
end
|
42
|
+
|
33
43
|
def get(path)
|
34
44
|
perform Net::HTTP::Get, path, @headers
|
35
45
|
end
|
@@ -77,18 +87,23 @@ module Flipper
|
|
77
87
|
|
78
88
|
def build_request(http_method, uri, headers, options)
|
79
89
|
request_headers = {
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
90
|
+
client_language: "ruby",
|
91
|
+
client_language_version: "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
|
92
|
+
client_platform: RUBY_PLATFORM,
|
93
|
+
client_engine: defined?(RUBY_ENGINE) ? RUBY_ENGINE : "",
|
94
|
+
client_pid: Process.pid.to_s,
|
95
|
+
client_thread: Thread.current.object_id.to_s,
|
96
|
+
client_hostname: Socket.gethostname,
|
87
97
|
}.merge(headers)
|
88
98
|
|
89
99
|
body = options[:body]
|
90
100
|
request = http_method.new(uri.request_uri)
|
91
101
|
request.initialize_http_header(request_headers)
|
102
|
+
|
103
|
+
client_frameworks.each do |framework, version|
|
104
|
+
request.add_field("Client-Framework", [framework, version].join("="))
|
105
|
+
end
|
106
|
+
|
92
107
|
request.body = body if body
|
93
108
|
|
94
109
|
if @basic_auth_username && @basic_auth_password
|
@@ -97,6 +112,10 @@ module Flipper
|
|
97
112
|
|
98
113
|
request
|
99
114
|
end
|
115
|
+
|
116
|
+
def client_frameworks
|
117
|
+
CLIENT_FRAMEWORKS.transform_values { |detect| detect.call rescue nil }.compact
|
118
|
+
end
|
100
119
|
end
|
101
120
|
end
|
102
121
|
end
|