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.
Files changed (285) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +1 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/dependabot.yml +6 -0
  5. data/.github/workflows/ci.yml +110 -0
  6. data/.github/workflows/examples.yml +105 -0
  7. data/.github/workflows/release.yml +54 -0
  8. data/.rspec +1 -0
  9. data/CLAUDE.md +87 -0
  10. data/Changelog.md +2 -215
  11. data/Dockerfile +1 -1
  12. data/Gemfile +28 -20
  13. data/README.md +72 -62
  14. data/Rakefile +13 -3
  15. data/benchmark/enabled_ips.rb +10 -0
  16. data/benchmark/enabled_multiple_actors_ips.rb +20 -0
  17. data/benchmark/enabled_profile.rb +20 -0
  18. data/benchmark/instrumentation_ips.rb +21 -0
  19. data/benchmark/typecast_ips.rb +27 -0
  20. data/docker-compose.yml +37 -34
  21. data/docs/DockerCompose.md +0 -1
  22. data/docs/README.md +1 -0
  23. data/docs/images/banner.jpg +0 -0
  24. data/docs/images/flipper_cloud.png +0 -0
  25. data/examples/api/basic.ru +18 -0
  26. data/examples/api/custom_memoized.ru +36 -0
  27. data/examples/api/memoized.ru +42 -0
  28. data/examples/basic.rb +1 -12
  29. data/examples/cloud/app.ru +12 -0
  30. data/examples/cloud/backoff_policy.rb +13 -0
  31. data/examples/cloud/basic.rb +22 -0
  32. data/examples/cloud/cloud_setup.rb +20 -0
  33. data/examples/cloud/forked.rb +36 -0
  34. data/examples/cloud/import.rb +17 -0
  35. data/examples/cloud/poll_interval/README.md +111 -0
  36. data/examples/cloud/poll_interval/client.rb +108 -0
  37. data/examples/cloud/poll_interval/server.rb +98 -0
  38. data/examples/cloud/threaded.rb +33 -0
  39. data/examples/configuring_default.rb +2 -5
  40. data/examples/dsl.rb +10 -35
  41. data/examples/enabled_for_actor.rb +10 -15
  42. data/examples/expressions.rb +237 -0
  43. data/examples/group.rb +3 -6
  44. data/examples/group_dynamic_lookup.rb +5 -19
  45. data/examples/group_with_members.rb +4 -14
  46. data/examples/importing.rb +1 -1
  47. data/examples/individual_actor.rb +2 -5
  48. data/examples/instrumentation.rb +2 -2
  49. data/examples/instrumentation_last_accessed_at.rb +38 -0
  50. data/examples/memoizing.rb +35 -0
  51. data/examples/mirroring.rb +59 -0
  52. data/examples/percentage_of_actors.rb +6 -16
  53. data/examples/percentage_of_actors_enabled_check.rb +7 -10
  54. data/examples/percentage_of_actors_group.rb +5 -18
  55. data/examples/percentage_of_time.rb +3 -6
  56. data/examples/strict.rb +18 -0
  57. data/exe/flipper +5 -0
  58. data/flipper-cloud.gemspec +19 -0
  59. data/flipper.gemspec +10 -7
  60. data/lib/flipper/actor.rb +10 -3
  61. data/lib/flipper/adapter.rb +50 -8
  62. data/lib/flipper/adapter_builder.rb +44 -0
  63. data/lib/flipper/adapters/actor_limit.rb +54 -0
  64. data/lib/flipper/adapters/cache_base.rb +161 -0
  65. data/lib/flipper/adapters/dual_write.rb +63 -0
  66. data/lib/flipper/adapters/failover.rb +85 -0
  67. data/lib/flipper/adapters/failsafe.rb +72 -0
  68. data/lib/flipper/adapters/http/client.rb +64 -7
  69. data/lib/flipper/adapters/http/error.rb +19 -1
  70. data/lib/flipper/adapters/http.rb +97 -43
  71. data/lib/flipper/adapters/instrumented.rb +47 -26
  72. data/lib/flipper/adapters/memoizable.rb +44 -40
  73. data/lib/flipper/adapters/memory.rb +75 -111
  74. data/lib/flipper/adapters/operation_logger.rb +22 -78
  75. data/lib/flipper/adapters/poll/poller.rb +2 -0
  76. data/lib/flipper/adapters/poll.rb +52 -0
  77. data/lib/flipper/adapters/pstore.rb +27 -17
  78. data/lib/flipper/adapters/read_only.rb +8 -41
  79. data/lib/flipper/adapters/strict.rb +45 -0
  80. data/lib/flipper/adapters/sync/feature_synchronizer.rb +14 -1
  81. data/lib/flipper/adapters/sync/interval_synchronizer.rb +2 -7
  82. data/lib/flipper/adapters/sync/synchronizer.rb +13 -6
  83. data/lib/flipper/adapters/sync.rb +23 -29
  84. data/lib/flipper/adapters/wrapper.rb +54 -0
  85. data/lib/flipper/cli.rb +314 -0
  86. data/lib/flipper/cloud/configuration.rb +271 -0
  87. data/lib/flipper/cloud/dsl.rb +27 -0
  88. data/lib/flipper/cloud/message_verifier.rb +95 -0
  89. data/lib/flipper/cloud/middleware.rb +63 -0
  90. data/lib/flipper/cloud/migrate.rb +71 -0
  91. data/lib/flipper/cloud/routes.rb +14 -0
  92. data/lib/flipper/cloud/telemetry/backoff_policy.rb +96 -0
  93. data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
  94. data/lib/flipper/cloud/telemetry/metric.rb +39 -0
  95. data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
  96. data/lib/flipper/cloud/telemetry/submitter.rb +100 -0
  97. data/lib/flipper/cloud/telemetry.rb +191 -0
  98. data/lib/flipper/cloud.rb +54 -0
  99. data/lib/flipper/configuration.rb +54 -7
  100. data/lib/flipper/dsl.rb +58 -47
  101. data/lib/flipper/engine.rb +102 -0
  102. data/lib/flipper/errors.rb +3 -21
  103. data/lib/flipper/export.rb +24 -0
  104. data/lib/flipper/exporter.rb +17 -0
  105. data/lib/flipper/exporters/json/export.rb +32 -0
  106. data/lib/flipper/exporters/json/v1.rb +33 -0
  107. data/lib/flipper/expression/builder.rb +73 -0
  108. data/lib/flipper/expression/constant.rb +25 -0
  109. data/lib/flipper/expression.rb +71 -0
  110. data/lib/flipper/expressions/all.rb +9 -0
  111. data/lib/flipper/expressions/any.rb +9 -0
  112. data/lib/flipper/expressions/boolean.rb +9 -0
  113. data/lib/flipper/expressions/comparable.rb +13 -0
  114. data/lib/flipper/expressions/equal.rb +9 -0
  115. data/lib/flipper/expressions/feature_enabled.rb +34 -0
  116. data/lib/flipper/expressions/greater_than.rb +9 -0
  117. data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
  118. data/lib/flipper/expressions/less_than.rb +9 -0
  119. data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
  120. data/lib/flipper/expressions/not_equal.rb +9 -0
  121. data/lib/flipper/expressions/now.rb +9 -0
  122. data/lib/flipper/expressions/number.rb +9 -0
  123. data/lib/flipper/expressions/percentage.rb +9 -0
  124. data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
  125. data/lib/flipper/expressions/property.rb +9 -0
  126. data/lib/flipper/expressions/random.rb +9 -0
  127. data/lib/flipper/expressions/string.rb +9 -0
  128. data/lib/flipper/expressions/time.rb +16 -0
  129. data/lib/flipper/feature.rb +95 -28
  130. data/lib/flipper/feature_check_context.rb +10 -6
  131. data/lib/flipper/gate.rb +13 -11
  132. data/lib/flipper/gate_values.rb +5 -18
  133. data/lib/flipper/gates/actor.rb +10 -17
  134. data/lib/flipper/gates/boolean.rb +1 -1
  135. data/lib/flipper/gates/expression.rb +75 -0
  136. data/lib/flipper/gates/group.rb +5 -7
  137. data/lib/flipper/gates/percentage_of_actors.rb +10 -13
  138. data/lib/flipper/gates/percentage_of_time.rb +1 -2
  139. data/lib/flipper/identifier.rb +17 -0
  140. data/lib/flipper/instrumentation/log_subscriber.rb +35 -8
  141. data/lib/flipper/instrumentation/statsd.rb +4 -2
  142. data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
  143. data/lib/flipper/instrumentation/subscriber.rb +8 -5
  144. data/lib/flipper/instrumenters/memory.rb +6 -2
  145. data/lib/flipper/metadata.rb +8 -1
  146. data/lib/flipper/middleware/memoizer.rb +46 -27
  147. data/lib/flipper/middleware/setup_env.rb +13 -3
  148. data/lib/flipper/model/active_record.rb +23 -0
  149. data/lib/flipper/poller.rb +157 -0
  150. data/lib/flipper/serializers/gzip.rb +22 -0
  151. data/lib/flipper/serializers/json.rb +17 -0
  152. data/lib/flipper/spec/shared_adapter_specs.rb +122 -56
  153. data/lib/flipper/test/shared_adapter_test.rb +120 -52
  154. data/lib/flipper/test_help.rb +43 -0
  155. data/lib/flipper/typecast.rb +59 -18
  156. data/lib/flipper/types/actor.rb +19 -13
  157. data/lib/flipper/types/group.rb +12 -5
  158. data/lib/flipper/types/percentage.rb +1 -1
  159. data/lib/flipper/version.rb +11 -1
  160. data/lib/flipper.rb +71 -12
  161. data/lib/generators/flipper/setup_generator.rb +68 -0
  162. data/lib/generators/flipper/templates/initializer.rb +45 -0
  163. data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
  164. data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
  165. data/lib/generators/flipper/update_generator.rb +35 -0
  166. data/package-lock.json +41 -0
  167. data/package.json +10 -0
  168. data/spec/fixtures/environment.rb +1 -0
  169. data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
  170. data/spec/flipper/actor_spec.rb +10 -2
  171. data/spec/flipper/adapter_builder_spec.rb +72 -0
  172. data/spec/flipper/adapter_spec.rb +52 -6
  173. data/spec/flipper/adapters/actor_limit_spec.rb +75 -0
  174. data/spec/flipper/adapters/dual_write_spec.rb +82 -0
  175. data/spec/flipper/adapters/failover_spec.rb +141 -0
  176. data/spec/flipper/adapters/failsafe_spec.rb +58 -0
  177. data/spec/flipper/adapters/http/client_spec.rb +61 -0
  178. data/spec/flipper/adapters/http_spec.rb +402 -65
  179. data/spec/flipper/adapters/instrumented_spec.rb +31 -13
  180. data/spec/flipper/adapters/memoizable_spec.rb +51 -33
  181. data/spec/flipper/adapters/memory_spec.rb +33 -5
  182. data/spec/flipper/adapters/operation_logger_spec.rb +38 -12
  183. data/spec/flipper/adapters/poll_spec.rb +41 -0
  184. data/spec/flipper/adapters/pstore_spec.rb +0 -2
  185. data/spec/flipper/adapters/read_only_spec.rb +32 -18
  186. data/spec/flipper/adapters/strict_spec.rb +64 -0
  187. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +39 -1
  188. data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +4 -5
  189. data/spec/flipper/adapters/sync/synchronizer_spec.rb +87 -1
  190. data/spec/flipper/adapters/sync_spec.rb +17 -6
  191. data/spec/flipper/cli_spec.rb +217 -0
  192. data/spec/flipper/cloud/configuration_spec.rb +257 -0
  193. data/spec/flipper/cloud/dsl_spec.rb +90 -0
  194. data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
  195. data/spec/flipper/cloud/middleware_spec.rb +307 -0
  196. data/spec/flipper/cloud/migrate_spec.rb +160 -0
  197. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
  198. data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
  199. data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
  200. data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
  201. data/spec/flipper/cloud/telemetry_spec.rb +208 -0
  202. data/spec/flipper/cloud_spec.rb +186 -0
  203. data/spec/flipper/configuration_spec.rb +37 -3
  204. data/spec/flipper/dsl_spec.rb +67 -80
  205. data/spec/flipper/engine_spec.rb +374 -0
  206. data/spec/flipper/export_spec.rb +13 -0
  207. data/spec/flipper/exporter_spec.rb +16 -0
  208. data/spec/flipper/exporters/json/export_spec.rb +60 -0
  209. data/spec/flipper/exporters/json/v1_spec.rb +33 -0
  210. data/spec/flipper/expression/builder_spec.rb +248 -0
  211. data/spec/flipper/expression_spec.rb +188 -0
  212. data/spec/flipper/expressions/all_spec.rb +15 -0
  213. data/spec/flipper/expressions/any_spec.rb +15 -0
  214. data/spec/flipper/expressions/boolean_spec.rb +15 -0
  215. data/spec/flipper/expressions/equal_spec.rb +24 -0
  216. data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
  217. data/spec/flipper/expressions/greater_than_spec.rb +28 -0
  218. data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
  219. data/spec/flipper/expressions/less_than_spec.rb +32 -0
  220. data/spec/flipper/expressions/not_equal_spec.rb +15 -0
  221. data/spec/flipper/expressions/now_spec.rb +11 -0
  222. data/spec/flipper/expressions/number_spec.rb +21 -0
  223. data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
  224. data/spec/flipper/expressions/percentage_spec.rb +15 -0
  225. data/spec/flipper/expressions/property_spec.rb +13 -0
  226. data/spec/flipper/expressions/random_spec.rb +9 -0
  227. data/spec/flipper/expressions/string_spec.rb +11 -0
  228. data/spec/flipper/expressions/time_spec.rb +29 -0
  229. data/spec/flipper/feature_check_context_spec.rb +18 -20
  230. data/spec/flipper/feature_spec.rb +461 -48
  231. data/spec/flipper/gate_spec.rb +0 -2
  232. data/spec/flipper/gate_values_spec.rb +2 -34
  233. data/spec/flipper/gates/actor_spec.rb +0 -2
  234. data/spec/flipper/gates/boolean_spec.rb +1 -3
  235. data/spec/flipper/gates/expression_spec.rb +190 -0
  236. data/spec/flipper/gates/group_spec.rb +2 -5
  237. data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -7
  238. data/spec/flipper/gates/percentage_of_time_spec.rb +2 -4
  239. data/spec/flipper/identifier_spec.rb +12 -0
  240. data/spec/flipper/instrumentation/log_subscriber_spec.rb +24 -7
  241. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -3
  242. data/spec/flipper/instrumenters/memory_spec.rb +18 -1
  243. data/spec/flipper/instrumenters/noop_spec.rb +14 -8
  244. data/spec/flipper/middleware/memoizer_spec.rb +199 -62
  245. data/spec/flipper/middleware/setup_env_spec.rb +23 -5
  246. data/spec/flipper/model/active_record_spec.rb +72 -0
  247. data/spec/flipper/poller_spec.rb +390 -0
  248. data/spec/flipper/registry_spec.rb +0 -1
  249. data/spec/flipper/serializers/gzip_spec.rb +13 -0
  250. data/spec/flipper/serializers/json_spec.rb +13 -0
  251. data/spec/flipper/typecast_spec.rb +121 -7
  252. data/spec/flipper/types/actor_spec.rb +63 -47
  253. data/spec/flipper/types/boolean_spec.rb +0 -1
  254. data/spec/flipper/types/group_spec.rb +24 -3
  255. data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
  256. data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
  257. data/spec/flipper/types/percentage_spec.rb +0 -1
  258. data/spec/{integration_spec.rb → flipper_integration_spec.rb} +301 -59
  259. data/spec/flipper_spec.rb +123 -29
  260. data/spec/{helper.rb → spec_helper.rb} +23 -21
  261. data/spec/support/actor_names.yml +1 -0
  262. data/spec/support/descriptions.yml +1 -0
  263. data/spec/support/fail_on_output.rb +8 -0
  264. data/spec/support/fake_backoff_policy.rb +15 -0
  265. data/spec/support/skippable.rb +18 -0
  266. data/spec/support/spec_helpers.rb +53 -6
  267. data/test/adapters/actor_limit_test.rb +20 -0
  268. data/test/test_helper.rb +2 -1
  269. data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
  270. data/test_rails/generators/flipper/update_generator_test.rb +96 -0
  271. data/test_rails/helper.rb +31 -0
  272. data/test_rails/system/test_help_test.rb +52 -0
  273. metadata +200 -82
  274. data/.rubocop.yml +0 -54
  275. data/.rubocop_todo.yml +0 -199
  276. data/docs/Adapters.md +0 -124
  277. data/docs/Caveats.md +0 -4
  278. data/docs/Gates.md +0 -167
  279. data/docs/Instrumentation.md +0 -27
  280. data/docs/Optimization.md +0 -114
  281. data/docs/api/README.md +0 -849
  282. data/docs/http/README.md +0 -35
  283. data/docs/read-only/README.md +0 -21
  284. data/examples/example_setup.rb +0 -8
  285. data/test/helper.rb +0 -11
@@ -1,4 +1,3 @@
1
- require 'helper'
2
1
  require 'flipper/feature'
3
2
 
4
3
  RSpec.describe Flipper do
@@ -8,25 +7,32 @@ RSpec.describe Flipper do
8
7
  let(:admin_group) { flipper.group(:admins) }
9
8
  let(:dev_group) { flipper.group(:devs) }
10
9
 
11
- let(:admin_thing) do
12
- double 'Non Flipper Thing', flipper_id: 1, admin?: true, dev?: false
10
+ let(:admin_actor) do
11
+ double 'Non Flipper Thing', flipper_id: 1, admin?: true, dev?: false, flipper_properties: {"admin" => true, "dev" => false}
13
12
  end
14
- let(:dev_thing) do
15
- double 'Non Flipper Thing', flipper_id: 10, admin?: false, dev?: true
13
+ let(:dev_actor) do
14
+ double 'Non Flipper Thing', flipper_id: 10, admin?: false, dev?: true, flipper_properties: {"admin" => false, "dev" => true}
16
15
  end
17
16
 
18
- let(:admin_truthy_thing) do
19
- double 'Non Flipper Thing', flipper_id: 1, admin?: 'true-ish', dev?: false
17
+ let(:admin_truthy_actor) do
18
+ double 'Non Flipper Thing', flipper_id: 1, admin?: 'true-ish', dev?: false, flipper_properties: {"admin" => "true-ish", "dev" => false}
20
19
  end
21
- let(:admin_falsey_thing) do
22
- double 'Non Flipper Thing', flipper_id: 1, admin?: nil, dev?: false
20
+ let(:admin_falsey_actor) do
21
+ double 'Non Flipper Thing', flipper_id: 1, admin?: nil, dev?: false, flipper_properties: {"admin" => nil, "dev" => false}
22
+ end
23
+
24
+ let(:basic_plan_actor) do
25
+ double 'Non Flipper Thing', flipper_id: 1, flipper_properties: {"plan" => "basic"}
26
+ end
27
+ let(:premium_plan_actor) do
28
+ double 'Non Flipper Thing', flipper_id: 10, flipper_properties: {"plan" => "premium"}
23
29
  end
24
30
 
25
31
  let(:pitt) { Flipper::Actor.new(1) }
26
32
  let(:clooney) { Flipper::Actor.new(10) }
27
33
 
28
- let(:five_percent_of_actors) { flipper.actors(5) }
29
- let(:five_percent_of_time) { flipper.time(5) }
34
+ let(:five_percent_of_actors) { Flipper::Types::PercentageOfActors.new(5) }
35
+ let(:five_percent_of_time) { Flipper::Types::PercentageOfTime.new(5) }
30
36
 
31
37
  before do
32
38
  described_class.register(:admins, &:admin?)
@@ -61,20 +67,22 @@ RSpec.describe Flipper do
61
67
  expect(@result).to eq(true)
62
68
  end
63
69
 
64
- it 'enables feature for non flipper thing in group' do
65
- expect(feature.enabled?(admin_thing)).to eq(true)
70
+ it 'enables feature for non flipper actor in group' do
71
+ expect(feature.enabled?(admin_actor)).to eq(true)
66
72
  end
67
73
 
68
- it 'does not enable feature for non flipper thing in other group' do
69
- expect(feature.enabled?(dev_thing)).to eq(false)
74
+ it 'does not enable feature for non flipper actor in other group' do
75
+ expect(feature.enabled?(dev_actor)).to eq(false)
70
76
  end
71
77
 
72
78
  it 'enables feature for flipper actor in group' do
73
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true)
79
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(true)
80
+ expect(feature.enabled?(admin_actor)).to eq(true)
74
81
  end
75
82
 
76
83
  it 'does not enable for flipper actor not in group' do
77
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false)
84
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(false)
85
+ expect(feature.enabled?(dev_actor)).to eq(false)
78
86
  end
79
87
 
80
88
  it 'does not enable feature for all' do
@@ -119,8 +127,8 @@ RSpec.describe Flipper do
119
127
 
120
128
  it 'enables feature for actor within percentage' do
121
129
  enabled = (1..100).select do |i|
122
- thing = Flipper::Actor.new(i)
123
- feature.enabled?(thing)
130
+ actor = Flipper::Actor.new(i)
131
+ feature.enabled?(actor)
124
132
  end.size
125
133
 
126
134
  expect(enabled).to be_within(2).of(5)
@@ -142,8 +150,8 @@ RSpec.describe Flipper do
142
150
 
143
151
  it 'enables feature for actor within percentage' do
144
152
  enabled = (1..100).select do |i|
145
- thing = Flipper::Actor.new(i)
146
- feature.enabled?(thing)
153
+ actor = Flipper::Actor.new(i)
154
+ feature.enabled?(actor)
147
155
  end.size
148
156
 
149
157
  expect(enabled).to be_within(2).of(5)
@@ -181,10 +189,10 @@ RSpec.describe Flipper do
181
189
 
182
190
  context 'with argument that has no gate' do
183
191
  it 'raises error' do
184
- thing = Object.new
192
+ actor = Object.new
185
193
  expect do
186
- feature.enable(thing)
187
- end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}")
194
+ feature.enable(actor)
195
+ end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{actor.inspect}")
188
196
  end
189
197
  end
190
198
  end
@@ -216,13 +224,13 @@ RSpec.describe Flipper do
216
224
  end
217
225
 
218
226
  it 'disables actor in group' do
219
- expect(feature.enabled?(admin_thing)).to eq(false)
227
+ expect(feature.enabled?(admin_actor)).to eq(false)
220
228
  end
221
229
 
222
230
  it 'disables actor in percentage of actors' do
223
231
  enabled = (1..100).select do |i|
224
- thing = Flipper::Actor.new(i)
225
- feature.enabled?(thing)
232
+ actor = Flipper::Actor.new(i)
233
+ feature.enabled?(actor)
226
234
  end.size
227
235
 
228
236
  expect(enabled).to be(0)
@@ -248,20 +256,22 @@ RSpec.describe Flipper do
248
256
  expect(@result).to eq(true)
249
257
  end
250
258
 
251
- it 'disables the feature for non flipper thing in the group' do
252
- expect(feature.enabled?(admin_thing)).to eq(false)
259
+ it 'disables the feature for non flipper actor in the group' do
260
+ expect(feature.enabled?(admin_actor)).to eq(false)
253
261
  end
254
262
 
255
- it 'does not disable feature for non flipper thing in other groups' do
256
- expect(feature.enabled?(dev_thing)).to eq(true)
263
+ it 'does not disable feature for non flipper actor in other groups' do
264
+ expect(feature.enabled?(dev_actor)).to eq(true)
257
265
  end
258
266
 
259
267
  it 'disables feature for flipper actor in group' do
260
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(false)
268
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(false)
269
+ expect(feature.enabled?(admin_actor)).to eq(false)
261
270
  end
262
271
 
263
272
  it 'does not disable feature for flipper actor in other groups' do
264
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(true)
273
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(true)
274
+ expect(feature.enabled?(dev_actor)).to eq(true)
265
275
  end
266
276
 
267
277
  it 'adds feature to set of features' do
@@ -295,7 +305,7 @@ RSpec.describe Flipper do
295
305
 
296
306
  context 'with a percentage of actors' do
297
307
  before do
298
- @result = feature.disable(flipper.actors(0))
308
+ @result = feature.disable(Flipper::Types::PercentageOfActors.new(0))
299
309
  end
300
310
 
301
311
  it 'returns true' do
@@ -304,8 +314,8 @@ RSpec.describe Flipper do
304
314
 
305
315
  it 'disables feature' do
306
316
  enabled = (1..100).select do |i|
307
- thing = Flipper::Actor.new(i)
308
- feature.enabled?(thing)
317
+ actor = Flipper::Actor.new(i)
318
+ feature.enabled?(actor)
309
319
  end.size
310
320
 
311
321
  expect(enabled).to be(0)
@@ -319,7 +329,7 @@ RSpec.describe Flipper do
319
329
  context 'with a percentage of time' do
320
330
  before do
321
331
  @gate = feature.gate(:percentage_of_time)
322
- @result = feature.disable(flipper.time(0))
332
+ @result = feature.disable(Flipper::Types::PercentageOfTime.new(0))
323
333
  end
324
334
 
325
335
  it 'returns true' do
@@ -343,10 +353,10 @@ RSpec.describe Flipper do
343
353
 
344
354
  context 'with argument that has no gate' do
345
355
  it 'raises error' do
346
- thing = Object.new
356
+ actor = Object.new
347
357
  expect do
348
- feature.disable(thing)
349
- end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}")
358
+ feature.disable(actor)
359
+ end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{actor.inspect}")
350
360
  end
351
361
  end
352
362
  end
@@ -374,23 +384,29 @@ RSpec.describe Flipper do
374
384
  end
375
385
 
376
386
  it 'returns true' do
377
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true)
378
- expect(feature.enabled?(admin_thing)).to eq(true)
387
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(true)
388
+ expect(feature.enabled?(admin_actor)).to eq(true)
379
389
  end
380
390
 
381
391
  it 'returns true for truthy block values' do
382
- expect(feature.enabled?(flipper.actor(admin_truthy_thing))).to eq(true)
392
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_truthy_actor))).to eq(true)
393
+ expect(feature.enabled?(admin_truthy_actor)).to eq(true)
394
+ end
395
+
396
+ it 'returns true if any actor is in enabled group' do
397
+ expect(feature.enabled?(dev_actor, admin_actor)).to be(true)
383
398
  end
384
399
  end
385
400
 
386
401
  context 'for actor in disabled group' do
387
402
  it 'returns false' do
388
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false)
389
- expect(feature.enabled?(dev_thing)).to eq(false)
403
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(false)
404
+ expect(feature.enabled?(dev_actor)).to eq(false)
390
405
  end
391
406
 
392
407
  it 'returns false for falsey block values' do
393
- expect(feature.enabled?(flipper.actor(admin_falsey_thing))).to eq(false)
408
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_falsey_actor))).to eq(false)
409
+ expect(feature.enabled?(admin_falsey_actor)).to eq(false)
394
410
  end
395
411
  end
396
412
 
@@ -409,6 +425,10 @@ RSpec.describe Flipper do
409
425
  expect(feature.enabled?(clooney)).to eq(false)
410
426
  end
411
427
 
428
+ it 'returns false if all actors are disabled' do
429
+ expect(feature.enabled?(clooney, pitt)).to be(false)
430
+ end
431
+
412
432
  it 'returns true if boolean enabled' do
413
433
  feature.enable
414
434
  expect(feature.enabled?(clooney)).to eq(true)
@@ -429,7 +449,7 @@ RSpec.describe Flipper do
429
449
  expect(feature.enabled?).to eq(true)
430
450
  expect(feature.enabled?(nil)).to eq(true)
431
451
  expect(feature.enabled?(pitt)).to eq(true)
432
- expect(feature.enabled?(admin_thing)).to eq(true)
452
+ expect(feature.enabled?(admin_actor)).to eq(true)
433
453
  end
434
454
  end
435
455
 
@@ -447,7 +467,7 @@ RSpec.describe Flipper do
447
467
  expect(feature.enabled?).to eq(true)
448
468
  expect(feature.enabled?(nil)).to eq(true)
449
469
  expect(feature.enabled?(pitt)).to eq(true)
450
- expect(feature.enabled?(admin_thing)).to eq(true)
470
+ expect(feature.enabled?(admin_actor)).to eq(true)
451
471
  end
452
472
  end
453
473
 
@@ -464,7 +484,7 @@ RSpec.describe Flipper do
464
484
  expect(feature.enabled?).to eq(false)
465
485
  expect(feature.enabled?(nil)).to eq(false)
466
486
  expect(feature.enabled?(pitt)).to eq(false)
467
- expect(feature.enabled?(admin_thing)).to eq(false)
487
+ expect(feature.enabled?(admin_actor)).to eq(false)
468
488
  end
469
489
 
470
490
  it 'returns true if boolean enabled' do
@@ -472,7 +492,7 @@ RSpec.describe Flipper do
472
492
  expect(feature.enabled?).to eq(true)
473
493
  expect(feature.enabled?(nil)).to eq(true)
474
494
  expect(feature.enabled?(pitt)).to eq(true)
475
- expect(feature.enabled?(admin_thing)).to eq(true)
495
+ expect(feature.enabled?(admin_actor)).to eq(true)
476
496
  end
477
497
  end
478
498
 
@@ -489,7 +509,7 @@ RSpec.describe Flipper do
489
509
  expect(feature.enabled?).to eq(false)
490
510
  expect(feature.enabled?(nil)).to eq(false)
491
511
  expect(feature.enabled?(pitt)).to eq(false)
492
- expect(feature.enabled?(admin_thing)).to eq(false)
512
+ expect(feature.enabled?(admin_actor)).to eq(false)
493
513
  end
494
514
 
495
515
  it 'returns true if boolean enabled' do
@@ -497,27 +517,31 @@ RSpec.describe Flipper do
497
517
  expect(feature.enabled?).to eq(true)
498
518
  expect(feature.enabled?(nil)).to eq(true)
499
519
  expect(feature.enabled?(pitt)).to eq(true)
500
- expect(feature.enabled?(admin_thing)).to eq(true)
520
+ expect(feature.enabled?(admin_actor)).to eq(true)
501
521
  end
502
522
  end
503
523
 
504
- context 'for a non flipper thing' do
524
+ context 'for a non flipper actor' do
505
525
  before do
506
526
  feature.enable admin_group
507
527
  end
508
528
 
509
529
  it 'returns true if in enabled group' do
510
- expect(feature.enabled?(admin_thing)).to eq(true)
530
+ expect(feature.enabled?(admin_actor)).to eq(true)
511
531
  end
512
532
 
513
533
  it 'returns false if not in enabled group' do
514
- expect(feature.enabled?(dev_thing)).to eq(false)
534
+ expect(feature.enabled?(dev_actor)).to eq(false)
535
+ end
536
+
537
+ it 'retruns true if any actor is true' do
538
+ expect(feature.enabled?(admin_actor, dev_actor)).to eq(true)
515
539
  end
516
540
 
517
541
  it 'returns true if boolean enabled' do
518
542
  feature.enable
519
- expect(feature.enabled?(admin_thing)).to eq(true)
520
- expect(feature.enabled?(dev_thing)).to eq(true)
543
+ expect(feature.enabled?(admin_actor)).to eq(true)
544
+ expect(feature.enabled?(dev_actor)).to eq(true)
521
545
  end
522
546
  end
523
547
  end
@@ -531,11 +555,229 @@ RSpec.describe Flipper do
531
555
  end
532
556
 
533
557
  it 'enables feature for object in enabled group' do
534
- expect(feature.enabled?(admin_thing)).to eq(true)
558
+ expect(feature.enabled?(admin_actor)).to eq(true)
535
559
  end
536
560
 
537
561
  it 'does not enable feature for object in not enabled group' do
538
- expect(feature.enabled?(dev_thing)).to eq(false)
562
+ expect(feature.enabled?(dev_actor)).to eq(false)
563
+ end
564
+ end
565
+
566
+ context "for expression" do
567
+ it "works" do
568
+ feature.enable Flipper.property(:plan).eq("basic")
569
+
570
+ expect(feature.enabled?).to be(false)
571
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
572
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
573
+ expect(feature.enabled?(admin_actor)).to be(false)
574
+ end
575
+
576
+ it "works for true expression with no actor" do
577
+ feature.enable Flipper.boolean(true)
578
+ expect(feature.enabled?).to be(true)
579
+ end
580
+
581
+ it "works for multiple actors" do
582
+ feature.enable Flipper.property(:plan).eq("basic")
583
+
584
+ expect(feature.enabled?(basic_plan_actor, premium_plan_actor)).to be(true)
585
+ expect(feature.enabled?(premium_plan_actor, basic_plan_actor)).to be(true)
586
+ expect(feature.enabled?(premium_plan_actor, admin_actor)).to be(false)
587
+ end
588
+ end
589
+
590
+ context "for Any" do
591
+ it "works" do
592
+ expression = Flipper.any(
593
+ Flipper.property(:plan).eq("basic"),
594
+ Flipper.property(:plan).eq("plus"),
595
+ )
596
+ feature.enable expression
597
+
598
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
599
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
600
+ end
601
+ end
602
+
603
+ context "for FeatureEnabled" do
604
+ before do
605
+ @original_instance = Flipper.instance
606
+ Flipper.instance = flipper
607
+ end
608
+
609
+ after do
610
+ Flipper.instance = @original_instance
611
+ Thread.current[:flipper_evaluating_features] = nil
612
+ end
613
+
614
+ it "returns true when referenced feature is enabled" do
615
+ flipper.enable(:search_beta)
616
+ feature.enable Flipper.feature_enabled(:search_beta)
617
+
618
+ expect(feature.enabled?).to be(true)
619
+ end
620
+
621
+ it "returns false when referenced feature is disabled" do
622
+ feature.enable Flipper.feature_enabled(:search_beta)
623
+
624
+ expect(feature.enabled?).to be(false)
625
+ end
626
+
627
+ it "passes actor through to referenced feature" do
628
+ flipper.enable_actor(:search_beta, pitt)
629
+ feature.enable Flipper.feature_enabled(:search_beta)
630
+
631
+ expect(feature.enabled?(pitt)).to be(true)
632
+ expect(feature.enabled?(clooney)).to be(false)
633
+ end
634
+
635
+ it "returns false on circular dependency" do
636
+ feature.enable Flipper.feature_enabled(:other)
637
+ flipper[:other].enable Flipper.feature_enabled(:search)
638
+
639
+ expect(feature.enabled?).to be(false)
640
+ end
641
+
642
+ it "returns false on self-reference" do
643
+ feature.enable Flipper.feature_enabled(:search)
644
+
645
+ expect(feature.enabled?).to be(false)
646
+ end
647
+
648
+ it "cleans up thread-local state after evaluation" do
649
+ flipper.enable(:search_beta)
650
+ feature.enable Flipper.feature_enabled(:search_beta)
651
+
652
+ feature.enabled?
653
+ evaluating = Thread.current[:flipper_evaluating_features]
654
+ expect(evaluating.nil? || evaluating.empty?).to be(true)
655
+ end
656
+
657
+ it "cleans up thread-local state after self-reference" do
658
+ feature.enable Flipper.feature_enabled(:search)
659
+
660
+ feature.enabled?
661
+ evaluating = Thread.current[:flipper_evaluating_features]
662
+ expect(evaluating.nil? || evaluating.empty?).to be(true)
663
+ end
664
+
665
+ it "cleans up thread-local state after circular dependency" do
666
+ feature.enable Flipper.feature_enabled(:other)
667
+ flipper[:other].enable Flipper.feature_enabled(:search)
668
+
669
+ feature.enabled?
670
+ evaluating = Thread.current[:flipper_evaluating_features]
671
+ expect(evaluating.nil? || evaluating.empty?).to be(true)
672
+ end
673
+ end
674
+
675
+ context "for FeatureDisabled" do
676
+ before do
677
+ @original_instance = Flipper.instance
678
+ Flipper.instance = flipper
679
+ end
680
+
681
+ after do
682
+ Flipper.instance = @original_instance
683
+ Thread.current[:flipper_evaluating_features] = nil
684
+ end
685
+
686
+ it "returns true when referenced feature is disabled" do
687
+ feature.enable Flipper.feature_disabled(:old_checkout)
688
+
689
+ expect(feature.enabled?).to be(true)
690
+ end
691
+
692
+ it "returns false when referenced feature is enabled" do
693
+ flipper.enable(:old_checkout)
694
+ feature.enable Flipper.feature_disabled(:old_checkout)
695
+
696
+ expect(feature.enabled?).to be(false)
697
+ end
698
+ end
699
+
700
+ context "for FeatureEnabled composed with other expressions" do
701
+ before do
702
+ @original_instance = Flipper.instance
703
+ Flipper.instance = flipper
704
+ end
705
+
706
+ after do
707
+ Flipper.instance = @original_instance
708
+ Thread.current[:flipper_evaluating_features] = nil
709
+ end
710
+
711
+ it "works with Any" do
712
+ feature.enable Flipper.any(
713
+ Flipper.feature_enabled(:beta_program),
714
+ Flipper.property(:plan).eq("basic")
715
+ )
716
+
717
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
718
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
719
+
720
+ flipper.enable(:beta_program)
721
+ expect(feature.enabled?(premium_plan_actor)).to be(true)
722
+ end
723
+
724
+ it "works with All" do
725
+ flipper.enable(:basic_search)
726
+ feature.enable Flipper.all(
727
+ Flipper.feature_enabled(:basic_search),
728
+ Flipper.property(:plan).eq("basic")
729
+ )
730
+
731
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
732
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
733
+ end
734
+ end
735
+
736
+ context "for All" do
737
+ it "works" do
738
+ true_actor = Flipper::Actor.new("User;1", {
739
+ "plan" => "basic",
740
+ "age" => 21,
741
+ })
742
+ false_actor = Flipper::Actor.new("User;1", {
743
+ "plan" => "basic",
744
+ "age" => 20,
745
+ })
746
+ expression = Flipper.all(
747
+ Flipper.property(:plan).eq("basic"),
748
+ Flipper.property(:age).eq(21)
749
+ )
750
+ feature.enable expression
751
+
752
+ expect(feature.enabled?(true_actor)).to be(true)
753
+ expect(feature.enabled?(false_actor)).to be(false)
754
+ end
755
+
756
+ it "works when nested" do
757
+ admin_actor = Flipper::Actor.new("User;1", {
758
+ "admin" => true,
759
+ })
760
+ true_actor = Flipper::Actor.new("User;1", {
761
+ "plan" => "basic",
762
+ "age" => 21,
763
+ })
764
+ false_actor = Flipper::Actor.new("User;1", {
765
+ "plan" => "basic",
766
+ "age" => 20,
767
+ })
768
+ expression = Flipper.any(
769
+ Flipper.property(:admin).eq(true),
770
+ Flipper.all(
771
+ Flipper.property(:plan).eq("basic"),
772
+ Flipper.property(:age).eq(21)
773
+ )
774
+ )
775
+
776
+ feature.enable expression
777
+
778
+ expect(feature.enabled?(admin_actor)).to be(true)
779
+ expect(feature.enabled?(true_actor)).to be(true)
780
+ expect(feature.enabled?(false_actor)).to be(false)
539
781
  end
540
782
  end
541
783
  end