flipper 1.0.0 → 1.3.6

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/ci.yml +50 -7
  4. data/.github/workflows/examples.yml +50 -8
  5. data/CLAUDE.md +74 -0
  6. data/Changelog.md +1 -584
  7. data/Gemfile +15 -8
  8. data/README.md +31 -27
  9. data/Rakefile +2 -2
  10. data/benchmark/typecast_ips.rb +8 -0
  11. data/docs/images/banner.jpg +0 -0
  12. data/docs/images/flipper_cloud.png +0 -0
  13. data/examples/cloud/backoff_policy.rb +13 -0
  14. data/examples/cloud/cloud_setup.rb +16 -0
  15. data/examples/cloud/forked.rb +7 -2
  16. data/examples/cloud/threaded.rb +15 -18
  17. data/examples/expressions.rb +213 -0
  18. data/examples/strict.rb +18 -0
  19. data/exe/flipper +5 -0
  20. data/flipper.gemspec +6 -3
  21. data/lib/flipper/actor.rb +6 -3
  22. data/lib/flipper/adapter.rb +10 -0
  23. data/lib/flipper/adapter_builder.rb +44 -0
  24. data/lib/flipper/adapters/actor_limit.rb +28 -0
  25. data/lib/flipper/adapters/cache_base.rb +143 -0
  26. data/lib/flipper/adapters/dual_write.rb +1 -3
  27. data/lib/flipper/adapters/failover.rb +0 -4
  28. data/lib/flipper/adapters/failsafe.rb +0 -4
  29. data/lib/flipper/adapters/http/client.rb +40 -12
  30. data/lib/flipper/adapters/http/error.rb +2 -2
  31. data/lib/flipper/adapters/http.rb +19 -14
  32. data/lib/flipper/adapters/instrumented.rb +0 -4
  33. data/lib/flipper/adapters/memoizable.rb +14 -19
  34. data/lib/flipper/adapters/memory.rb +4 -6
  35. data/lib/flipper/adapters/operation_logger.rb +18 -92
  36. data/lib/flipper/adapters/poll.rb +16 -3
  37. data/lib/flipper/adapters/pstore.rb +17 -11
  38. data/lib/flipper/adapters/read_only.rb +8 -41
  39. data/lib/flipper/adapters/strict.rb +45 -0
  40. data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
  41. data/lib/flipper/adapters/sync.rb +0 -4
  42. data/lib/flipper/adapters/wrapper.rb +54 -0
  43. data/lib/flipper/cli.rb +263 -0
  44. data/lib/flipper/cloud/configuration.rb +131 -54
  45. data/lib/flipper/cloud/middleware.rb +5 -5
  46. data/lib/flipper/cloud/telemetry/backoff_policy.rb +96 -0
  47. data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
  48. data/lib/flipper/cloud/telemetry/metric.rb +39 -0
  49. data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
  50. data/lib/flipper/cloud/telemetry/submitter.rb +100 -0
  51. data/lib/flipper/cloud/telemetry.rb +191 -0
  52. data/lib/flipper/cloud.rb +1 -1
  53. data/lib/flipper/configuration.rb +25 -4
  54. data/lib/flipper/dsl.rb +51 -0
  55. data/lib/flipper/engine.rb +42 -3
  56. data/lib/flipper/export.rb +0 -2
  57. data/lib/flipper/exporters/json/export.rb +1 -1
  58. data/lib/flipper/exporters/json/v1.rb +1 -1
  59. data/lib/flipper/expression/builder.rb +73 -0
  60. data/lib/flipper/expression/constant.rb +25 -0
  61. data/lib/flipper/expression.rb +71 -0
  62. data/lib/flipper/expressions/all.rb +9 -0
  63. data/lib/flipper/expressions/any.rb +9 -0
  64. data/lib/flipper/expressions/boolean.rb +9 -0
  65. data/lib/flipper/expressions/comparable.rb +13 -0
  66. data/lib/flipper/expressions/duration.rb +28 -0
  67. data/lib/flipper/expressions/equal.rb +9 -0
  68. data/lib/flipper/expressions/greater_than.rb +9 -0
  69. data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
  70. data/lib/flipper/expressions/less_than.rb +9 -0
  71. data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
  72. data/lib/flipper/expressions/not_equal.rb +9 -0
  73. data/lib/flipper/expressions/now.rb +9 -0
  74. data/lib/flipper/expressions/number.rb +9 -0
  75. data/lib/flipper/expressions/percentage.rb +9 -0
  76. data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
  77. data/lib/flipper/expressions/property.rb +9 -0
  78. data/lib/flipper/expressions/random.rb +9 -0
  79. data/lib/flipper/expressions/string.rb +9 -0
  80. data/lib/flipper/expressions/time.rb +9 -0
  81. data/lib/flipper/feature.rb +63 -1
  82. data/lib/flipper/gate.rb +2 -1
  83. data/lib/flipper/gate_values.rb +5 -2
  84. data/lib/flipper/gates/expression.rb +75 -0
  85. data/lib/flipper/instrumentation/log_subscriber.rb +13 -5
  86. data/lib/flipper/instrumentation/statsd.rb +4 -2
  87. data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
  88. data/lib/flipper/instrumentation/subscriber.rb +0 -4
  89. data/lib/flipper/metadata.rb +4 -1
  90. data/lib/flipper/middleware/memoizer.rb +29 -13
  91. data/lib/flipper/model/active_record.rb +23 -0
  92. data/lib/flipper/poller.rb +9 -8
  93. data/lib/flipper/serializers/gzip.rb +22 -0
  94. data/lib/flipper/serializers/json.rb +17 -0
  95. data/lib/flipper/spec/shared_adapter_specs.rb +46 -27
  96. data/lib/flipper/test/shared_adapter_test.rb +41 -22
  97. data/lib/flipper/test_help.rb +43 -0
  98. data/lib/flipper/typecast.rb +37 -9
  99. data/lib/flipper/types/percentage.rb +1 -1
  100. data/lib/flipper/version.rb +11 -1
  101. data/lib/flipper.rb +41 -2
  102. data/lib/generators/flipper/setup_generator.rb +68 -0
  103. data/lib/generators/flipper/templates/initializer.rb +45 -0
  104. data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
  105. data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
  106. data/lib/generators/flipper/update_generator.rb +35 -0
  107. data/package-lock.json +41 -0
  108. data/package.json +10 -0
  109. data/spec/fixtures/environment.rb +1 -0
  110. data/spec/flipper/adapter_builder_spec.rb +72 -0
  111. data/spec/flipper/adapter_spec.rb +1 -0
  112. data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
  113. data/spec/flipper/adapters/http/client_spec.rb +61 -0
  114. data/spec/flipper/adapters/http_spec.rb +135 -74
  115. data/spec/flipper/adapters/memoizable_spec.rb +15 -15
  116. data/spec/flipper/adapters/poll_spec.rb +41 -0
  117. data/spec/flipper/adapters/read_only_spec.rb +26 -11
  118. data/spec/flipper/adapters/strict_spec.rb +64 -0
  119. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
  120. data/spec/flipper/cli_spec.rb +166 -0
  121. data/spec/flipper/cloud/configuration_spec.rb +39 -57
  122. data/spec/flipper/cloud/dsl_spec.rb +6 -6
  123. data/spec/flipper/cloud/middleware_spec.rb +8 -8
  124. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
  125. data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
  126. data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
  127. data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
  128. data/spec/flipper/cloud/telemetry_spec.rb +208 -0
  129. data/spec/flipper/cloud_spec.rb +31 -25
  130. data/spec/flipper/configuration_spec.rb +17 -0
  131. data/spec/flipper/dsl_spec.rb +39 -3
  132. data/spec/flipper/engine_spec.rb +226 -42
  133. data/spec/flipper/exporters/json/v1_spec.rb +3 -3
  134. data/spec/flipper/expression/builder_spec.rb +248 -0
  135. data/spec/flipper/expression_spec.rb +188 -0
  136. data/spec/flipper/expressions/all_spec.rb +15 -0
  137. data/spec/flipper/expressions/any_spec.rb +15 -0
  138. data/spec/flipper/expressions/boolean_spec.rb +15 -0
  139. data/spec/flipper/expressions/duration_spec.rb +43 -0
  140. data/spec/flipper/expressions/equal_spec.rb +24 -0
  141. data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
  142. data/spec/flipper/expressions/greater_than_spec.rb +28 -0
  143. data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
  144. data/spec/flipper/expressions/less_than_spec.rb +32 -0
  145. data/spec/flipper/expressions/not_equal_spec.rb +15 -0
  146. data/spec/flipper/expressions/now_spec.rb +11 -0
  147. data/spec/flipper/expressions/number_spec.rb +21 -0
  148. data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
  149. data/spec/flipper/expressions/percentage_spec.rb +15 -0
  150. data/spec/flipper/expressions/property_spec.rb +13 -0
  151. data/spec/flipper/expressions/random_spec.rb +9 -0
  152. data/spec/flipper/expressions/string_spec.rb +11 -0
  153. data/spec/flipper/expressions/time_spec.rb +13 -0
  154. data/spec/flipper/feature_spec.rb +380 -10
  155. data/spec/flipper/gate_values_spec.rb +2 -2
  156. data/spec/flipper/gates/expression_spec.rb +108 -0
  157. data/spec/flipper/identifier_spec.rb +4 -5
  158. data/spec/flipper/instrumentation/log_subscriber_spec.rb +10 -2
  159. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +16 -2
  160. data/spec/flipper/middleware/memoizer_spec.rb +79 -10
  161. data/spec/flipper/model/active_record_spec.rb +72 -0
  162. data/spec/flipper/serializers/gzip_spec.rb +13 -0
  163. data/spec/flipper/serializers/json_spec.rb +13 -0
  164. data/spec/flipper/typecast_spec.rb +43 -7
  165. data/spec/flipper/types/actor_spec.rb +18 -1
  166. data/spec/flipper_integration_spec.rb +102 -4
  167. data/spec/flipper_spec.rb +91 -3
  168. data/spec/spec_helper.rb +17 -5
  169. data/spec/support/actor_names.yml +1 -0
  170. data/spec/support/fail_on_output.rb +8 -0
  171. data/spec/support/fake_backoff_policy.rb +15 -0
  172. data/spec/support/spec_helpers.rb +34 -8
  173. data/test/adapters/actor_limit_test.rb +20 -0
  174. data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
  175. data/test_rails/generators/flipper/update_generator_test.rb +96 -0
  176. data/test_rails/helper.rb +22 -2
  177. data/test_rails/system/test_help_test.rb +52 -0
  178. metadata +145 -29
  179. data/lib/flipper/cloud/instrumenter.rb +0 -48
  180. data/spec/support/climate_control.rb +0 -7
@@ -5,115 +5,34 @@ module Flipper
5
5
  # Public: Adapter that wraps another adapter and stores the operations.
6
6
  #
7
7
  # Useful in tests to verify calls and such. Never use outside of testing.
8
- class OperationLogger
9
- include Flipper::Adapter
8
+ class OperationLogger < Wrapper
10
9
 
11
10
  class Operation
12
- attr_reader :type, :args
11
+ attr_reader :type, :args, :kwargs
13
12
 
14
- def initialize(type, args)
13
+ def initialize(type, args, kwargs = {})
15
14
  @type = type
16
15
  @args = args
16
+ @kwargs = kwargs
17
17
  end
18
18
  end
19
19
 
20
- OperationTypes = [
21
- :import,
22
- :export,
23
- :features,
24
- :add,
25
- :remove,
26
- :clear,
27
- :get,
28
- :get_multi,
29
- :get_all,
30
- :enable,
31
- :disable,
32
- ].freeze
33
-
34
20
  # Internal: An array of the operations that have happened.
35
21
  attr_reader :operations
36
22
 
37
- # Internal: The name of the adapter.
38
- attr_reader :name
39
-
40
23
  # Public
41
24
  def initialize(adapter, operations = nil)
42
- @adapter = adapter
43
- @name = :operation_logger
25
+ super(adapter)
44
26
  @operations = operations || []
45
27
  end
46
28
 
47
- # Public: The set of known features.
48
- def features
49
- @operations << Operation.new(:features, [])
50
- @adapter.features
51
- end
52
-
53
- # Public: Adds a feature to the set of known features.
54
- def add(feature)
55
- @operations << Operation.new(:add, [feature])
56
- @adapter.add(feature)
57
- end
58
-
59
- # Public: Removes a feature from the set of known features and clears
60
- # all the values for the feature.
61
- def remove(feature)
62
- @operations << Operation.new(:remove, [feature])
63
- @adapter.remove(feature)
64
- end
65
-
66
- # Public: Clears all the gate values for a feature.
67
- def clear(feature)
68
- @operations << Operation.new(:clear, [feature])
69
- @adapter.clear(feature)
70
- end
71
-
72
- # Public
73
- def get(feature)
74
- @operations << Operation.new(:get, [feature])
75
- @adapter.get(feature)
76
- end
77
-
78
- # Public
79
- def get_multi(features)
80
- @operations << Operation.new(:get_multi, [features])
81
- @adapter.get_multi(features)
82
- end
83
-
84
- # Public
85
- def get_all
86
- @operations << Operation.new(:get_all, [])
87
- @adapter.get_all
88
- end
89
-
90
- # Public
91
- def enable(feature, gate, thing)
92
- @operations << Operation.new(:enable, [feature, gate, thing])
93
- @adapter.enable(feature, gate, thing)
94
- end
95
-
96
- # Public
97
- def disable(feature, gate, thing)
98
- @operations << Operation.new(:disable, [feature, gate, thing])
99
- @adapter.disable(feature, gate, thing)
100
- end
101
-
102
- # Public
103
- def import(source)
104
- @operations << Operation.new(:import, [source])
105
- @adapter.import(source)
106
- end
107
-
108
- # Public
109
- def export(format: :json, version: 1)
110
- @operations << Operation.new(:export, [format, version])
111
- @adapter.export(format: format, version: version)
112
- end
113
-
114
29
  # Public: Count the number of times a certain operation happened.
115
- def count(type)
116
- type(type).size
30
+ def count(type = nil)
31
+ if type
32
+ type(type).size
33
+ else
34
+ @operations.size
35
+ end
117
36
  end
118
37
 
119
38
  # Public: Get all operations of a certain type.
@@ -135,6 +54,13 @@ module Flipper
135
54
  inspect_id = ::Kernel::format "%x", (object_id * 2)
136
55
  %(#<#{self.class}:0x#{inspect_id} @name=#{name.inspect}, @operations=#{@operations.inspect}, @adapter=#{@adapter.inspect}>)
137
56
  end
57
+
58
+ private
59
+
60
+ def wrap(method, *args, **kwargs, &block)
61
+ @operations << Operation.new(method, args, kwargs)
62
+ block.call
63
+ end
138
64
  end
139
65
  end
140
66
  end
@@ -10,16 +10,29 @@ module Flipper
10
10
  # Deprecated
11
11
  Poller = ::Flipper::Poller
12
12
 
13
- # Public: The name of the adapter.
14
- attr_reader :name, :adapter, :poller
13
+ attr_reader :adapter, :poller
15
14
 
16
15
  def_delegators :synced_adapter, :features, :get, :get_multi, :get_all, :add, :remove, :clear, :enable, :disable
17
16
 
18
17
  def initialize(poller, adapter)
19
- @name = :poll
20
18
  @adapter = adapter
21
19
  @poller = poller
22
20
  @last_synced_at = 0
21
+
22
+ # If the adapter is empty, we need to sync before starting the poller.
23
+ # Yes, this will block the main thread, but that's better than thinking
24
+ # nothing is enabled.
25
+ if adapter.features.empty?
26
+ begin
27
+ @poller.sync
28
+ rescue
29
+ # TODO: Warn here that it's possible that no data has been synced
30
+ # and flags are being evaluated without flag data being present
31
+ # until a sync completes. We rescue to avoid flipper being down
32
+ # causing your processes to crash.
33
+ end
34
+ end
35
+
23
36
  @poller.start
24
37
  end
25
38
 
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  require 'pstore'
2
3
  require 'set'
3
4
  require 'flipper'
@@ -9,19 +10,14 @@ module Flipper
9
10
  class PStore
10
11
  include ::Flipper::Adapter
11
12
 
12
- FeaturesKey = :flipper_features
13
-
14
- # Public: The name of the adapter.
15
- attr_reader :name
16
-
17
13
  # Public: The path to where the file is stored.
18
14
  attr_reader :path
19
15
 
20
16
  # Public
21
17
  def initialize(path = 'flipper.pstore', thread_safe = true)
22
- @name = :pstore
23
18
  @path = path
24
19
  @store = ::PStore.new(path, thread_safe)
20
+ @features_key = :flipper_features
25
21
  end
26
22
 
27
23
  # Public: The set of known features.
@@ -34,7 +30,7 @@ module Flipper
34
30
  # Public: Adds a feature to the set of known features.
35
31
  def add(feature)
36
32
  @store.transaction do
37
- set_add FeaturesKey, feature.key
33
+ set_add @features_key, feature.key
38
34
  end
39
35
  true
40
36
  end
@@ -43,7 +39,7 @@ module Flipper
43
39
  # all the values for the feature.
44
40
  def remove(feature)
45
41
  @store.transaction do
46
- set_delete FeaturesKey, feature.key
42
+ set_delete @features_key, feature.key
47
43
  clear_gates(feature)
48
44
  end
49
45
  true
@@ -88,6 +84,8 @@ module Flipper
88
84
  write key(feature, gate), thing.value.to_s
89
85
  when :set
90
86
  set_add key(feature, gate), thing.value.to_s
87
+ when :json
88
+ write key(feature, gate), Typecast.to_json(thing.value)
91
89
  else
92
90
  raise "#{gate} is not supported by this adapter yet"
93
91
  end
@@ -109,6 +107,10 @@ module Flipper
109
107
  @store.transaction do
110
108
  set_delete key(feature, gate), thing.value.to_s
111
109
  end
110
+ when :json
111
+ @store.transaction do
112
+ delete key(feature, gate)
113
+ end
112
114
  else
113
115
  raise "#{gate} is not supported by this adapter yet"
114
116
  end
@@ -135,7 +137,7 @@ module Flipper
135
137
  end
136
138
 
137
139
  def read_feature_keys
138
- set_members FeaturesKey
140
+ set_members @features_key
139
141
  end
140
142
 
141
143
  def read_many_features(features)
@@ -150,12 +152,16 @@ module Flipper
150
152
  result = {}
151
153
 
152
154
  feature.gates.each do |gate|
155
+ key = key(feature, gate)
153
156
  result[gate.key] =
154
157
  case gate.data_type
155
158
  when :boolean, :integer
156
- read key(feature, gate)
159
+ read key
157
160
  when :set
158
- set_members key(feature, gate)
161
+ set_members key
162
+ when :json
163
+ value = read(key)
164
+ Typecast.from_json(value)
159
165
  else
160
166
  raise "#{gate} is not supported by this adapter yet"
161
167
  end
@@ -3,8 +3,8 @@ require 'flipper'
3
3
  module Flipper
4
4
  module Adapters
5
5
  # Public: Adapter that wraps another adapter and raises for any writes.
6
- class ReadOnly
7
- include ::Flipper::Adapter
6
+ class ReadOnly < Wrapper
7
+ WRITE_METHODS = %i[add remove clear enable disable]
8
8
 
9
9
  class WriteAttempted < Error
10
10
  def initialize(message = nil)
@@ -12,49 +12,16 @@ module Flipper
12
12
  end
13
13
  end
14
14
 
15
- # Internal: The name of the adapter.
16
- attr_reader :name
17
-
18
- # Public
19
- def initialize(adapter)
20
- @adapter = adapter
21
- @name = :read_only
22
- end
23
-
24
- def features
25
- @adapter.features
26
- end
27
-
28
- def get(feature)
29
- @adapter.get(feature)
30
- end
31
-
32
- def get_multi(features)
33
- @adapter.get_multi(features)
15
+ def read_only?
16
+ true
34
17
  end
35
18
 
36
- def get_all
37
- @adapter.get_all
38
- end
19
+ private
39
20
 
40
- def add(_feature)
41
- raise WriteAttempted
42
- end
43
-
44
- def remove(_feature)
45
- raise WriteAttempted
46
- end
47
-
48
- def clear(_feature)
49
- raise WriteAttempted
50
- end
51
-
52
- def enable(_feature, _gate, _thing)
53
- raise WriteAttempted
54
- end
21
+ def wrap(method, *args, **kwargs)
22
+ raise WriteAttempted if WRITE_METHODS.include?(method)
55
23
 
56
- def disable(_feature, _gate, _thing)
57
- raise WriteAttempted
24
+ yield
58
25
  end
59
26
  end
60
27
  end
@@ -0,0 +1,45 @@
1
+ module Flipper
2
+ module Adapters
3
+ # An adapter that ensures a feature exists before checking it.
4
+ class Strict < Wrapper
5
+ attr_reader :handler
6
+
7
+ class NotFound < ::Flipper::Error
8
+ def initialize(name)
9
+ super "Could not find feature #{name.inspect}. Call `Flipper.add(#{name.inspect})` to create it."
10
+ end
11
+ end
12
+
13
+ def initialize(adapter, handler = nil, &block)
14
+ super(adapter)
15
+ @handler = block || handler
16
+ end
17
+
18
+ def get(feature)
19
+ assert_feature_exists(feature)
20
+ super
21
+ end
22
+
23
+ def get_multi(features)
24
+ features.each { |feature| assert_feature_exists(feature) }
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ def assert_feature_exists(feature)
31
+ return if @adapter.features.include?(feature.key)
32
+
33
+ case handler
34
+ when Proc then handler.call(feature)
35
+ when :warn then warn NotFound.new(feature.key).message
36
+ when :noop, false, nil
37
+ # noop
38
+ else # truthy or :raise
39
+ raise NotFound.new(feature.key)
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -9,6 +9,7 @@ module Flipper
9
9
  class FeatureSynchronizer
10
10
  extend Forwardable
11
11
 
12
+ def_delegator :@local_gate_values, :expression, :local_expression
12
13
  def_delegator :@local_gate_values, :boolean, :local_boolean
13
14
  def_delegator :@local_gate_values, :actors, :local_actors
14
15
  def_delegator :@local_gate_values, :groups, :local_groups
@@ -17,6 +18,7 @@ module Flipper
17
18
  def_delegator :@local_gate_values, :percentage_of_time,
18
19
  :local_percentage_of_time
19
20
 
21
+ def_delegator :@remote_gate_values, :expression, :remote_expression
20
22
  def_delegator :@remote_gate_values, :boolean, :remote_boolean
21
23
  def_delegator :@remote_gate_values, :actors, :remote_actors
22
24
  def_delegator :@remote_gate_values, :groups, :remote_groups
@@ -40,8 +42,9 @@ module Flipper
40
42
  @feature.enable
41
43
  else
42
44
  @feature.disable if local_boolean_enabled?
43
- sync_actors
44
45
  sync_groups
46
+ sync_actors
47
+ sync_expression
45
48
  sync_percentage_of_actors
46
49
  sync_percentage_of_time
47
50
  end
@@ -49,6 +52,12 @@ module Flipper
49
52
 
50
53
  private
51
54
 
55
+ def sync_expression
56
+ return if local_expression == remote_expression
57
+
58
+ @feature.enable_expression remote_expression
59
+ end
60
+
52
61
  def sync_actors
53
62
  remote_actors_added = remote_actors - local_actors
54
63
  remote_actors_added.each do |flipper_id|
@@ -8,9 +8,6 @@ module Flipper
8
8
  class Sync
9
9
  include ::Flipper::Adapter
10
10
 
11
- # Public: The name of the adapter.
12
- attr_reader :name
13
-
14
11
  # Public: The synchronizer that will keep the local and remote in sync.
15
12
  attr_reader :synchronizer
16
13
 
@@ -22,7 +19,6 @@ module Flipper
22
19
  # interval - The Float or Integer number of seconds between syncs from
23
20
  # remote to local. Default value is set in IntervalSynchronizer.
24
21
  def initialize(local, remote, options = {})
25
- @name = :sync
26
22
  @local = local
27
23
  @remote = remote
28
24
  @synchronizer = options.fetch(:synchronizer) do
@@ -0,0 +1,54 @@
1
+ module Flipper
2
+ module Adapters
3
+ # A base class for any adapter that wraps another adapter. By default, all methods
4
+ # delegate to the wrapped adapter. Implement `#wrap` to customize the behavior of
5
+ # all delegated methods, or override individual methods as needed.
6
+ class Wrapper
7
+ include Flipper::Adapter
8
+
9
+ METHODS = [
10
+ :import,
11
+ :export,
12
+ :features,
13
+ :add,
14
+ :remove,
15
+ :clear,
16
+ :get,
17
+ :get_multi,
18
+ :get_all,
19
+ :enable,
20
+ :disable,
21
+ ].freeze
22
+
23
+ attr_reader :adapter
24
+
25
+ def initialize(adapter)
26
+ @adapter = adapter
27
+ end
28
+
29
+ METHODS.each do |method|
30
+ if RUBY_VERSION >= '3.0'
31
+ define_method(method) do |*args, **kwargs|
32
+ wrap(method, *args, **kwargs) { @adapter.public_send(method, *args, **kwargs) }
33
+ end
34
+ else
35
+ define_method(method) do |*args|
36
+ wrap(method, *args) { @adapter.public_send(method, *args) }
37
+ end
38
+ end
39
+ end
40
+
41
+ # Override this method to customize the behavior of all delegated methods, and just yield to
42
+ # the block to call the wrapped adapter.
43
+ if RUBY_VERSION >= '3.0'
44
+ def wrap(method, *args, **kwargs, &block)
45
+ block.call
46
+ end
47
+ else
48
+ def wrap(method, *args, &block)
49
+ block.call
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end