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
@@ -0,0 +1,186 @@
1
+ require 'flipper/cloud'
2
+ require 'flipper/adapters/instrumented'
3
+ require 'flipper/instrumenters/memory'
4
+
5
+ RSpec.describe Flipper::Cloud do
6
+ before do
7
+ stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
8
+ end
9
+
10
+ context "initialize with token" do
11
+ let(:token) { 'asdf' }
12
+
13
+ before do
14
+ @instance = described_class.new(token: token)
15
+ end
16
+
17
+ it 'returns Flipper::DSL instance' do
18
+ expect(@instance).to be_instance_of(Flipper::Cloud::DSL)
19
+ end
20
+
21
+ it 'can read the cloud configuration' do
22
+ expect(@instance.cloud_configuration).to be_instance_of(Flipper::Cloud::Configuration)
23
+ end
24
+
25
+ it 'configures the correct adapter' do
26
+ # pardon the nesting...
27
+ memoized_adapter = @instance.adapter
28
+ dual_write_adapter = memoized_adapter.adapter
29
+ expect(dual_write_adapter).to be_instance_of(Flipper::Adapters::DualWrite)
30
+ poll_adapter = dual_write_adapter.local
31
+ expect(poll_adapter).to be_instance_of(Flipper::Adapters::Poll)
32
+
33
+ http_adapter = dual_write_adapter.remote
34
+ client = http_adapter.client
35
+ expect(client.uri.scheme).to eq('https')
36
+ expect(client.uri.host).to eq('www.flippercloud.io')
37
+ expect(client.uri.path).to eq('/adapter')
38
+ expect(client.headers["flipper-cloud-token"]).to eq(token)
39
+ expect(@instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
40
+ expect(@instance.instrumenter.instrumenter).to be(Flipper::Instrumenters::Noop)
41
+ end
42
+ end
43
+
44
+ context 'initialize with token and options' do
45
+ it 'sets correct url' do
46
+ stub_request(:any, %r{fakeflipper.com}).to_return(status: 200)
47
+ instance = described_class.new(token: 'asdf', url: 'https://www.fakeflipper.com/sadpanda')
48
+ # pardon the nesting...
49
+ memoized = instance.adapter
50
+ dual_write = memoized.adapter
51
+ remote = dual_write.remote
52
+ uri = remote.client.uri
53
+ expect(uri.scheme).to eq('https')
54
+ expect(uri.host).to eq('www.fakeflipper.com')
55
+ expect(uri.path).to eq('/sadpanda')
56
+ end
57
+ end
58
+
59
+ it 'can initialize with no token explicitly provided' do
60
+ ENV['FLIPPER_CLOUD_TOKEN'] = 'asdf'
61
+ expect(described_class.new).to be_instance_of(Flipper::Cloud::DSL)
62
+ end
63
+
64
+ it 'can set instrumenter' do
65
+ instrumenter = Flipper::Instrumenters::Memory.new
66
+ instance = described_class.new(token: 'asdf', instrumenter: instrumenter)
67
+ expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
68
+ expect(instance.instrumenter.instrumenter).to be(instrumenter)
69
+ end
70
+
71
+ it 'allows wrapping adapter with another adapter like the instrumenter' do
72
+ instance = described_class.new(token: 'asdf') do |config|
73
+ config.adapter do |adapter|
74
+ Flipper::Adapters::Instrumented.new(adapter)
75
+ end
76
+ end
77
+ # instance.adapter is memoizable adapter instance
78
+ expect(instance.adapter.adapter).to be_instance_of(Flipper::Adapters::Instrumented)
79
+ end
80
+
81
+ it 'can set debug_output' do
82
+ instance = Flipper::Adapters::Http::Client.new(token: 'asdf', url: 'https://www.flippercloud.io/adapter')
83
+ expect(Flipper::Adapters::Http::Client).to receive(:new)
84
+ .with(hash_including(debug_output: STDOUT)).at_least(:once).and_return(instance)
85
+ described_class.new(token: 'asdf', debug_output: STDOUT)
86
+ end
87
+
88
+ it 'can set read_timeout' do
89
+ instance = Flipper::Adapters::Http::Client.new(token: 'asdf', url: 'https://www.flippercloud.io/adapter')
90
+ expect(Flipper::Adapters::Http::Client).to receive(:new)
91
+ .with(hash_including(read_timeout: 1)).at_least(:once).and_return(instance)
92
+ described_class.new(token: 'asdf', read_timeout: 1)
93
+ end
94
+
95
+ it 'can set open_timeout' do
96
+ instance = Flipper::Adapters::Http::Client.new(token: 'asdf', url: 'https://www.flippercloud.io/adapter')
97
+ expect(Flipper::Adapters::Http::Client).to receive(:new)
98
+ .with(hash_including(open_timeout: 1)).at_least(:once).and_return(instance)
99
+ described_class.new(token: 'asdf', open_timeout: 1)
100
+ end
101
+
102
+ if RUBY_VERSION >= '2.6.0'
103
+ it 'can set write_timeout' do
104
+ instance = Flipper::Adapters::Http::Client.new(token: 'asdf', url: 'https://www.flippercloud.io/adapter')
105
+ expect(Flipper::Adapters::Http::Client).to receive(:new)
106
+ .with(hash_including(open_timeout: 1)).at_least(:once).and_return(instance)
107
+ described_class.new(token: 'asdf', open_timeout: 1)
108
+ end
109
+ end
110
+
111
+ it 'can import' do
112
+ stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
113
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_return(status: 200, body: "{}", headers: {})
114
+
115
+ flipper = Flipper.new(Flipper::Adapters::Memory.new)
116
+
117
+ flipper.enable(:test)
118
+ flipper.enable(:search)
119
+ flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
120
+ flipper.enable_percentage_of_time(:logging, 5)
121
+
122
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
123
+
124
+ get_all = {
125
+ "logging" => {actors: Set.new, boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: "5"},
126
+ "search" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
127
+ "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
128
+ "test" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
129
+ }
130
+
131
+ expect(flipper.adapter.get_all).to eq(get_all)
132
+ cloud_flipper.import(flipper)
133
+ expect(flipper.adapter.get_all).to eq(get_all)
134
+ expect(cloud_flipper.adapter.get_all).to eq(get_all)
135
+ end
136
+
137
+ it 'raises error for failure while importing' do
138
+ stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
139
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_return(status: 500, body: "{}")
140
+
141
+ flipper = Flipper.new(Flipper::Adapters::Memory.new)
142
+
143
+ flipper.enable(:test)
144
+ flipper.enable(:search)
145
+ flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
146
+ flipper.enable_percentage_of_time(:logging, 5)
147
+
148
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
149
+
150
+ get_all = {
151
+ "logging" => {actors: Set.new, boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: "5"},
152
+ "search" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
153
+ "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
154
+ "test" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
155
+ }
156
+
157
+ expect(flipper.adapter.get_all).to eq(get_all)
158
+ expect { cloud_flipper.import(flipper) }.to raise_error(Flipper::Adapters::Http::Error)
159
+ expect(flipper.adapter.get_all).to eq(get_all)
160
+ end
161
+
162
+ it 'raises error for timeout while importing' do
163
+ stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/).
164
+ with(headers: {'flipper-cloud-token'=>'asdf'}).to_timeout
165
+
166
+ flipper = Flipper.new(Flipper::Adapters::Memory.new)
167
+
168
+ flipper.enable(:test)
169
+ flipper.enable(:search)
170
+ flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
171
+ flipper.enable_percentage_of_time(:logging, 5)
172
+
173
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
174
+
175
+ get_all = {
176
+ "logging" => {actors: Set.new, boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: "5"},
177
+ "search" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
178
+ "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
179
+ "test" => {actors: Set.new, boolean: "true", groups: Set.new, expression: nil, percentage_of_actors: nil, percentage_of_time: nil},
180
+ }
181
+
182
+ expect(flipper.adapter.get_all).to eq(get_all)
183
+ expect { cloud_flipper.import(flipper) }.to raise_error(Net::OpenTimeout)
184
+ expect(flipper.adapter.get_all).to eq(get_all)
185
+ end
186
+ end
@@ -1,16 +1,50 @@
1
- require 'helper'
2
1
  require 'flipper/configuration'
3
2
 
4
3
  RSpec.describe Flipper::Configuration do
4
+ describe '#adapter' do
5
+ it 'returns instance using Memory adapter' do
6
+ expect(subject.adapter).to be_a(Flipper::Adapters::Memory)
7
+ end
8
+
9
+ it 'can be set' do
10
+ instance = Flipper::Adapters::Memory.new
11
+ expect(subject.adapter).not_to be(instance)
12
+ subject.adapter { instance }
13
+ expect(subject.adapter).to be(instance)
14
+ # All adapters are wrapped in Memoizable
15
+ expect(subject.default.adapter.adapter).to be(instance)
16
+ end
17
+ end
18
+
5
19
  describe '#default' do
6
- it 'raises if default not configured' do
7
- expect { subject.default }.to raise_error(Flipper::DefaultNotSet)
20
+ it 'returns instance using Memory adapter' do
21
+ expect(subject.default).to be_a(Flipper::DSL)
22
+ # All adapters are wrapped in Memoizable
23
+ expect(subject.default.adapter.adapter).to be_a(Flipper::Adapters::Memory)
8
24
  end
9
25
 
10
26
  it 'can be set default' do
11
27
  instance = Flipper.new(Flipper::Adapters::Memory.new)
28
+ expect(subject.default).not_to be(instance)
12
29
  subject.default { instance }
13
30
  expect(subject.default).to be(instance)
14
31
  end
15
32
  end
33
+
34
+ describe '#statsd' do
35
+ let(:statsd) { double(Statsd) }
36
+
37
+ after do
38
+ Flipper::Instrumentation::StatsdSubscriber.client = nil
39
+ end
40
+
41
+ it 'returns nil by default' do
42
+ expect(subject.statsd).to be_nil
43
+ end
44
+
45
+ it 'can be set' do
46
+ subject.statsd = statsd
47
+ expect(subject.statsd).to be(statsd)
48
+ end
49
+ end
16
50
  end
@@ -1,4 +1,3 @@
1
- require 'helper'
2
1
  require 'flipper/dsl'
3
2
 
4
3
  RSpec.describe Flipper::DSL do
@@ -7,9 +6,19 @@ RSpec.describe Flipper::DSL do
7
6
  let(:adapter) { Flipper::Adapters::Memory.new }
8
7
 
9
8
  describe '#initialize' do
10
- it 'sets adapter' do
11
- dsl = described_class.new(adapter)
12
- expect(dsl.adapter).not_to be_nil
9
+ context 'when using default memoize strategy' do
10
+ it 'wraps the given adapter with Flipper::Adapters::Memoizable' do
11
+ dsl = described_class.new(adapter)
12
+ expect(dsl.adapter.class).to be(Flipper::Adapters::Memoizable)
13
+ expect(dsl.adapter.adapter).to be(adapter)
14
+ end
15
+ end
16
+
17
+ context 'when disabling memoization' do
18
+ it 'uses the given adapter directly' do
19
+ dsl = described_class.new(adapter, memoize: false)
20
+ expect(dsl.adapter).to be(adapter)
21
+ end
13
22
  end
14
23
 
15
24
  it 'defaults instrumenter to noop' do
@@ -114,18 +123,6 @@ RSpec.describe Flipper::DSL do
114
123
  end
115
124
  end
116
125
 
117
- describe '#boolean' do
118
- it_should_behave_like 'a DSL boolean method' do
119
- let(:method_name) { :boolean }
120
- end
121
- end
122
-
123
- describe '#bool' do
124
- it_should_behave_like 'a DSL boolean method' do
125
- let(:method_name) { :bool }
126
- end
127
- end
128
-
129
126
  describe '#group' do
130
127
  context 'for registered group' do
131
128
  before do
@@ -139,66 +136,15 @@ RSpec.describe Flipper::DSL do
139
136
  end
140
137
  end
141
138
 
142
- describe '#actor' do
143
- context 'for a thing' do
144
- it 'returns actor instance' do
145
- thing = Flipper::Actor.new(33)
146
- actor = subject.actor(thing)
147
- expect(actor).to be_instance_of(Flipper::Types::Actor)
148
- expect(actor.value).to eq('33')
149
- end
150
- end
151
-
152
- context 'for nil' do
153
- it 'raises argument error' do
154
- expect do
155
- subject.actor(nil)
156
- end.to raise_error(ArgumentError)
157
- end
158
- end
159
-
160
- context 'for something that is not actor wrappable' do
161
- it 'raises argument error' do
162
- expect do
163
- subject.actor(Object.new)
164
- end.to raise_error(ArgumentError)
165
- end
166
- end
167
- end
168
-
169
- describe '#time' do
170
- before do
171
- @result = subject.time(5)
172
- end
173
-
174
- it 'returns percentage of time' do
175
- expect(@result).to be_instance_of(Flipper::Types::PercentageOfTime)
176
- end
177
-
178
- it 'sets value' do
179
- expect(@result.value).to eq(5)
180
- end
181
-
182
- it 'is aliased to percentage_of_time' do
183
- expect(@result).to eq(subject.percentage_of_time(@result.value))
184
- end
185
- end
186
-
187
- describe '#actors' do
188
- before do
189
- @result = subject.actors(17)
190
- end
191
-
192
- it 'returns percentage of actors' do
193
- expect(@result).to be_instance_of(Flipper::Types::PercentageOfActors)
194
- end
195
-
196
- it 'sets value' do
197
- expect(@result.value).to eq(17)
139
+ describe '#expression' do
140
+ it "returns nil if feature has no expression" do
141
+ expect(subject.expression(:stats)).to be(nil)
198
142
  end
199
143
 
200
- it 'is aliased to percentage_of_actors' do
201
- expect(@result).to eq(subject.percentage_of_actors(@result.value))
144
+ it "returns expression if feature has expression" do
145
+ expression = Flipper.property(:plan).eq("basic")
146
+ subject[:stats].enable_expression expression
147
+ expect(subject.expression(:stats)).to eq(expression)
202
148
  end
203
149
  end
204
150
 
@@ -237,6 +183,33 @@ RSpec.describe Flipper::DSL do
237
183
  end
238
184
  end
239
185
 
186
+ describe '#enable_expression/disable_expression' do
187
+ it 'enables and disables the feature for the expression' do
188
+ expression = Flipper.property(:plan).eq("basic")
189
+
190
+ expect(subject[:stats].expression).to be(nil)
191
+ subject.enable_expression(:stats, expression)
192
+ expect(subject[:stats].expression).to eq(expression)
193
+
194
+ subject.disable_expression(:stats)
195
+ expect(subject[:stats].expression).to be(nil)
196
+ end
197
+ end
198
+
199
+ describe '#add_expression/remove_expression' do
200
+ it 'enables and disables the feature for the expression' do
201
+ expression = Flipper.property(:plan).eq("basic")
202
+ any_expression = Flipper.any(expression)
203
+
204
+ expect(subject[:stats].expression).to be(nil)
205
+ subject.add_expression(:stats, any_expression)
206
+ expect(subject[:stats].expression).to eq(any_expression)
207
+
208
+ subject.remove_expression(:stats, expression)
209
+ expect(subject[:stats].expression).to eq(Flipper.any)
210
+ end
211
+ end
212
+
240
213
  describe '#enable_actor/disable_actor' do
241
214
  it 'enables and disables the feature for actor' do
242
215
  actor = Flipper::Actor.new(5)
@@ -252,9 +225,6 @@ RSpec.describe Flipper::DSL do
252
225
 
253
226
  describe '#enable_group/disable_group' do
254
227
  it 'enables and disables the feature for group' do
255
- actor = Flipper::Actor.new(5)
256
- group = Flipper.register(:fives) { |actor| actor.flipper_id == 5 }
257
-
258
228
  expect(subject[:stats].groups_value).to be_empty
259
229
  subject.enable_group(:stats, :fives)
260
230
  expect(subject[:stats].groups_value).to eq(Set['fives'])
@@ -333,10 +303,27 @@ RSpec.describe Flipper::DSL do
333
303
  end
334
304
 
335
305
  describe '#import' do
306
+ context "with flipper instance" do
307
+ it 'delegates to adapter' do
308
+ destination_flipper = build_flipper
309
+ expect(subject.adapter).to receive(:import).with(destination_flipper)
310
+ subject.import(destination_flipper)
311
+ end
312
+ end
313
+
314
+ context "with flipper adapter" do
315
+ it 'delegates to adapter' do
316
+ destination_flipper = build_flipper
317
+ expect(subject.adapter).to receive(:import).with(destination_flipper.adapter)
318
+ subject.import(destination_flipper.adapter)
319
+ end
320
+ end
321
+ end
322
+
323
+ describe "#export" do
336
324
  it 'delegates to adapter' do
337
- destination_flipper = build_flipper
338
- expect(subject.adapter).to receive(:import).with(destination_flipper.adapter)
339
- subject.import(destination_flipper)
325
+ expect(subject.export).to eq(subject.adapter.export)
326
+ expect(subject.export(format: :json)).to eq(subject.adapter.export(format: :json))
340
327
  end
341
328
  end
342
329