onni 0.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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +30 -0
  3. data/CODE_OF_CONDUCT.md +26 -0
  4. data/CONTRIBUTING.md +23 -0
  5. data/GOVERNANCE.md +25 -0
  6. data/LICENSE.md +21 -0
  7. data/README.md +301 -0
  8. data/SECURITY.md +25 -0
  9. data/app/admin/features.rb +97 -0
  10. data/app/models/onni/feature.rb +14 -0
  11. data/app/models/onni/gate.rb +12 -0
  12. data/app/models/onni/hit_chart.rb +82 -0
  13. data/app/models/onni/stat_bucket.rb +12 -0
  14. data/app/views/admin/features/_chart.html.erb +24 -0
  15. data/app/views/admin/features/_stats.html.erb +49 -0
  16. data/app/views/admin/features/show.html.erb +60 -0
  17. data/config/locales/en.yml +6 -0
  18. data/config/polyrun_coverage.yml +8 -0
  19. data/lib/generators/onni/install_generator.rb +37 -0
  20. data/lib/generators/onni/stats_cleanup_generator.rb +50 -0
  21. data/lib/generators/onni/templates/add_onni_stats_period_start_index.rb.tt +9 -0
  22. data/lib/generators/onni/templates/create_onni_features.rb.tt +34 -0
  23. data/lib/generators/onni/templates/features.yml.tt +9 -0
  24. data/lib/generators/onni/templates/initializer.rb.tt +41 -0
  25. data/lib/generators/onni/templates/purge_stats_job.rb.tt +26 -0
  26. data/lib/onni/active_admin.rb +13 -0
  27. data/lib/onni/actor.rb +31 -0
  28. data/lib/onni/adapters/active_record.rb +128 -0
  29. data/lib/onni/adapters/memory.rb +161 -0
  30. data/lib/onni/conditions.rb +30 -0
  31. data/lib/onni/configuration.rb +54 -0
  32. data/lib/onni/engine.rb +50 -0
  33. data/lib/onni/flag.rb +53 -0
  34. data/lib/onni/group.rb +36 -0
  35. data/lib/onni/instrumenter.rb +27 -0
  36. data/lib/onni/migrator/importer.rb +55 -0
  37. data/lib/onni/migrator/snapshot.rb +15 -0
  38. data/lib/onni/registry.rb +78 -0
  39. data/lib/onni/request_store.rb +33 -0
  40. data/lib/onni/stats/buffer.rb +35 -0
  41. data/lib/onni/stats/overview.rb +34 -0
  42. data/lib/onni/stats/purge.rb +77 -0
  43. data/lib/onni/stats/sampler.rb +62 -0
  44. data/lib/onni/stats.rb +45 -0
  45. data/lib/onni/store.rb +155 -0
  46. data/lib/onni/sync.rb +43 -0
  47. data/lib/onni/version.rb +5 -0
  48. data/lib/onni.rb +193 -0
  49. data/onni.gemspec +72 -0
  50. data/sig/onni.rbs +210 -0
  51. metadata +373 -0
data/sig/onni.rbs ADDED
@@ -0,0 +1,210 @@
1
+ module Onni
2
+ VERSION: String
3
+
4
+ def self.configuration: () -> Configuration
5
+ def self.configure: () { (Configuration) -> void } -> void
6
+ def self.adapter: () -> untyped
7
+ def self.adapter=: (untyped) -> untyped
8
+ def self.reset_adapter!: () -> void
9
+ def self.enabled?: (untyped name, ?untyped? actor, ?record_stats: bool) -> bool
10
+ def self.disabled?: (untyped name, ?untyped? actor, ?record_stats: bool) -> bool
11
+ def self.variant: (untyped name, ?untyped? actor, ?record_stats: bool) -> untyped
12
+ def self.enable: (untyped name, ?untyped? actor) -> true
13
+ def self.disable: (untyped name, ?untyped? actor) -> true
14
+ def self.enable_group: (untyped name, untyped group_name) -> true
15
+ def self.disable_group: (untyped name, untyped group_name) -> true
16
+ def self.enable_percentage_of_actors: (untyped name, untyped percentage) -> true
17
+ def self.set_variants: (untyped name, untyped variants) -> true
18
+ def self.add: (untyped name) -> untyped
19
+ def self.exist?: (untyped name) -> bool
20
+ def self.features: () -> Array[Flag]
21
+ def self.[]: (untyped name) -> Flag
22
+ def self.register_group: (untyped name) { (untyped, untyped) -> bool } -> void
23
+ def self.migrate!: (untyped source) -> Hash[Symbol, Integer]
24
+ def self.sync_registry!: () -> void
25
+ def self.purge_stats!: (**untyped) -> Hash[Symbol, untyped]
26
+ def self.stats_overview: () -> Hash[Symbol, untyped]
27
+ def self.preload!: (*untyped) -> Integer
28
+ def self.flush_stats!: () -> Integer
29
+ def self.reset_request!: () -> void
30
+
31
+ class Configuration
32
+ attr_accessor features_path: untyped
33
+ attr_accessor cache: untyped
34
+ attr_accessor cache_ttl: untyped
35
+ attr_accessor stats_enabled: bool
36
+ attr_reader stats_sample_rate: Float
37
+ def stats_sample_rate=: (untyped) -> Float
38
+ attr_accessor stats_retention: untyped
39
+ attr_accessor stats_sampler: untyped
40
+ attr_accessor stats_ignore: untyped
41
+ attr_accessor stats_scale_sampled_hits: bool
42
+ attr_accessor stats_buffer: bool
43
+ attr_accessor adapter: untyped
44
+ attr_accessor admin_authorized: untyped
45
+ attr_reader concurrency: Symbol
46
+ def concurrency=: (untyped) -> Symbol
47
+ end
48
+
49
+ module RequestStore
50
+ def self.gates: () -> untyped
51
+ def self.gates=: (untyped) -> untyped
52
+ def self.stats_buffer: () -> untyped
53
+ def self.stats_buffer=: (untyped) -> untyped
54
+ def self.clear!: () -> void
55
+ end
56
+
57
+ module Sync
58
+ class NullLock
59
+ def synchronize: () { () -> untyped } -> untyped
60
+ end
61
+
62
+ def self.new_lock: () -> untyped
63
+ def self.synchronize: () { () -> untyped } -> untyped
64
+ def self.reset!: () -> void
65
+ end
66
+
67
+ module Instrumenter
68
+ def self.instrument: (untyped name, ?Hash[Symbol, untyped] payload) ?{ (Hash[Symbol, untyped]) -> untyped } -> untyped
69
+ end
70
+
71
+ class Flag
72
+ attr_reader name: String
73
+ def initialize: (untyped name) -> void
74
+ def exist?: () -> bool
75
+ def enabled?: (?untyped? actor, ?record_stats: bool) -> bool
76
+ def gates: () -> Array[Hash[String, untyped]]
77
+ def definition: () -> Hash[String, untyped]
78
+ def description: () -> untyped
79
+ def expires_at: () -> untyped
80
+ def variants: () -> Hash[String, Integer]
81
+ end
82
+
83
+ class HitChart
84
+ WINDOWS: Hash[String, Integer]
85
+ attr_reader range: String
86
+ attr_reader from: untyped
87
+ attr_reader to: untyped
88
+ attr_reader rows: Array[Hash[String, untyped]]
89
+ attr_reader enabled_hits: Integer
90
+ attr_reader disabled_hits: Integer
91
+ def initialize: (untyped name, ?range: String) -> void
92
+ def hourly?: () -> bool
93
+ def enabled_percentage: () -> Float?
94
+ def maximum: () -> Integer
95
+ def points: (String column) -> String
96
+ end
97
+
98
+ module Store
99
+ def self.enabled?: (untyped name, ?untyped? actor, ?record_stats: bool) -> bool
100
+ def self.variant: (untyped name, ?untyped? actor, ?record_stats: bool) -> untyped
101
+ def self.bust_cache!: (untyped name) -> untyped
102
+ end
103
+
104
+ module Registry
105
+ def self.config: () -> Hash[String, untyped]
106
+ def self.reload!: () -> Hash[String, untyped]
107
+ def self.known?: (untyped name) -> bool
108
+ def self.definition: (untyped name) -> untyped
109
+ def self.require_definition!: (untyped name) -> untyped
110
+ end
111
+
112
+ module Group
113
+ def self.register: (untyped name) { (untyped, untyped) -> bool } -> void
114
+ def self.clear!: () -> void
115
+ def self.names: () -> Array[Symbol]
116
+ def self.known?: (untyped name) -> bool
117
+ def self.member?: (untyped name, untyped actor) -> bool
118
+ end
119
+
120
+ module Actor
121
+ def self.id: (untyped? actor) -> untyped
122
+ def self.percentage_value: (untyped actor, untyped feature_key) -> Integer
123
+ def self.variant_for: (untyped? actor, untyped feature_key, untyped variants) -> untyped
124
+ end
125
+
126
+ module Conditions
127
+ def self.met?: (untyped name, untyped? actor, ?definition: untyped) -> bool
128
+ end
129
+
130
+ module Stats
131
+ def self.record: (untyped name, enabled: bool) -> untyped
132
+ def self.for: (untyped name, ?period: String, ?from: untyped, ?to: untyped) -> Array[Hash[String, untyped]]
133
+ def self.purge!: (**untyped) -> Hash[Symbol, untyped]
134
+ def self.overview: () -> Hash[Symbol, untyped]
135
+
136
+ class Purge
137
+ def self.call: (**untyped) -> Hash[Symbol, untyped]
138
+ def initialize: (**untyped) -> void
139
+ def call: () -> Hash[Symbol, untyped]
140
+ end
141
+
142
+ module Sampler
143
+ MAX_WEIGHT: Integer
144
+ def self.decide: (name: untyped, enabled: bool) -> (:skip | Hash[Symbol, untyped])
145
+ def self.coerce_rate: (untyped) -> Float
146
+ def self.weight_for: (Float) -> Integer
147
+ end
148
+
149
+ module Buffer
150
+ def self.add: (untyped name, enabled: bool, weight: untyped) -> void
151
+ def self.flush!: () -> Integer
152
+ end
153
+
154
+ module Overview
155
+ ROW_WARNING: Integer
156
+ def self.call: () -> Hash[Symbol, untyped]
157
+ end
158
+ end
159
+
160
+ module Adapters
161
+ class Memory
162
+ def initialize: (?lock: untyped) -> void
163
+ def clear!: () -> void
164
+ def add: (untyped key) -> untyped
165
+ def exist?: (untyped key) -> bool
166
+ def feature_keys: () -> Array[String]
167
+ def gates_for: (untyped key) -> Array[Hash[String, untyped]]
168
+ def gates_for_many: (untyped keys) -> Hash[String, Array[Hash[String, untyped]]]
169
+ def replace_gates: (untyped key, untyped gates) -> untyped
170
+ def add_gate: (untyped key, untyped gate_key, untyped value) -> untyped
171
+ def remove_gate: (untyped key, untyped gate_key, ?untyped? value) -> untyped
172
+ def clear_gates: (untyped key) -> untyped
173
+ def record_stat: (untyped key, enabled: bool, ?weight: untyped) -> untyped
174
+ def record_stats_batch: (untyped buckets) -> Integer
175
+ def stats_for: (untyped key, ?period: String, ?from: untyped, ?to: untyped) -> Array[Hash[String, untyped]]
176
+ def purge_stats_batch: (older_than: untyped, batch_size: untyped, after_id: untyped) -> Hash[Symbol, untyped]
177
+ def stats_overview: () -> Hash[Symbol, untyped]
178
+ end
179
+
180
+ class ActiveRecord
181
+ def add: (untyped key) -> untyped
182
+ def exist?: (untyped key) -> bool
183
+ def feature_keys: () -> Array[String]
184
+ def gates_for: (untyped key) -> Array[Hash[String, untyped]]
185
+ def gates_for_many: (untyped keys) -> Hash[String, Array[Hash[String, untyped]]]
186
+ def replace_gates: (untyped key, untyped gates) -> untyped
187
+ def add_gate: (untyped key, untyped gate_key, untyped value) -> untyped
188
+ def remove_gate: (untyped key, untyped gate_key, ?untyped? value) -> untyped
189
+ def clear_gates: (untyped key) -> untyped
190
+ def record_stat: (untyped key, enabled: bool, ?weight: untyped) -> untyped
191
+ def record_stats_batch: (untyped buckets) -> Integer
192
+ def stats_for: (untyped key, ?period: String, ?from: untyped, ?to: untyped) -> Array[Hash[String, untyped]]
193
+ def purge_stats_batch: (older_than: untyped, batch_size: untyped, after_id: untyped) -> Hash[Symbol, untyped]
194
+ def stats_overview: () -> Hash[Symbol, untyped]
195
+ end
196
+ end
197
+
198
+ module Migrator
199
+ class Importer
200
+ def initialize: (untyped source) -> void
201
+ def call: () -> Hash[Symbol, Integer]
202
+ end
203
+
204
+ class Snapshot
205
+ attr_reader feature_keys: Array[untyped]
206
+ attr_reader gates: Array[untyped]
207
+ def initialize: (feature_keys: untyped, gates: untyped) -> void
208
+ end
209
+ end
210
+ end
metadata ADDED
@@ -0,0 +1,373 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: onni
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrei Makarov
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 7.2.3.2
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 7.2.3.2
26
+ - !ruby/object:Gem::Dependency
27
+ name: activeadmin
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 4.0.0.beta23
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '5'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 4.0.0.beta23
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: appraisal
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '2'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2'
60
+ - !ruby/object:Gem::Dependency
61
+ name: bundler
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '2'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '2'
74
+ - !ruby/object:Gem::Dependency
75
+ name: bundler-audit
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.9'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.9'
88
+ - !ruby/object:Gem::Dependency
89
+ name: importmap-rails
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ - !ruby/object:Gem::Dependency
103
+ name: polyrun
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 3.0.0
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 3.0.0
116
+ - !ruby/object:Gem::Dependency
117
+ name: rails
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 7.2.3.2
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 7.2.3.2
130
+ - !ruby/object:Gem::Dependency
131
+ name: rake
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '13'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '13'
144
+ - !ruby/object:Gem::Dependency
145
+ name: rbs
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '4'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '4'
158
+ - !ruby/object:Gem::Dependency
159
+ name: rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '3'
165
+ type: :development
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '3'
172
+ - !ruby/object:Gem::Dependency
173
+ name: rspec-rails
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '6'
179
+ type: :development
180
+ prerelease: false
181
+ version_requirements: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '6'
186
+ - !ruby/object:Gem::Dependency
187
+ name: sqlite3
188
+ requirement: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 2.10.2
193
+ type: :development
194
+ prerelease: false
195
+ version_requirements: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: 2.10.2
200
+ - !ruby/object:Gem::Dependency
201
+ name: rubocop-rspec
202
+ requirement: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - "~>"
205
+ - !ruby/object:Gem::Version
206
+ version: '3.8'
207
+ type: :development
208
+ prerelease: false
209
+ version_requirements: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - "~>"
212
+ - !ruby/object:Gem::Version
213
+ version: '3.8'
214
+ - !ruby/object:Gem::Dependency
215
+ name: rubocop-thread_safety
216
+ requirement: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - "~>"
219
+ - !ruby/object:Gem::Version
220
+ version: '0.7'
221
+ type: :development
222
+ prerelease: false
223
+ version_requirements: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - "~>"
226
+ - !ruby/object:Gem::Version
227
+ version: '0.7'
228
+ - !ruby/object:Gem::Dependency
229
+ name: standard
230
+ requirement: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - "~>"
233
+ - !ruby/object:Gem::Version
234
+ version: '1.52'
235
+ type: :development
236
+ prerelease: false
237
+ version_requirements: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - "~>"
240
+ - !ruby/object:Gem::Version
241
+ version: '1.52'
242
+ - !ruby/object:Gem::Dependency
243
+ name: standard-custom
244
+ requirement: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - "~>"
247
+ - !ruby/object:Gem::Version
248
+ version: '1.0'
249
+ type: :development
250
+ prerelease: false
251
+ version_requirements: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - "~>"
254
+ - !ruby/object:Gem::Version
255
+ version: '1.0'
256
+ - !ruby/object:Gem::Dependency
257
+ name: standard-performance
258
+ requirement: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - "~>"
261
+ - !ruby/object:Gem::Version
262
+ version: '1.8'
263
+ type: :development
264
+ prerelease: false
265
+ version_requirements: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - "~>"
268
+ - !ruby/object:Gem::Version
269
+ version: '1.8'
270
+ - !ruby/object:Gem::Dependency
271
+ name: standard-rspec
272
+ requirement: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - "~>"
275
+ - !ruby/object:Gem::Version
276
+ version: '0.3'
277
+ type: :development
278
+ prerelease: false
279
+ version_requirements: !ruby/object:Gem::Requirement
280
+ requirements:
281
+ - - "~>"
282
+ - !ruby/object:Gem::Version
283
+ version: '0.3'
284
+ description: |
285
+ Feature-flag and experiment core for any Ruby app: YAML registry, runtime hot
286
+ switches, actor and audience-group targeting, percentage / A/B assignment, and
287
+ hit stats for analysis. Not an authorization system. Optional ActiveRecord
288
+ adapter and pluggable ActiveAdmin dashboard. Includes a pluggable migrator
289
+ for importing gates from another feature-flag library.
290
+ email:
291
+ - contact@kiskolabs.com
292
+ executables: []
293
+ extensions: []
294
+ extra_rdoc_files: []
295
+ files:
296
+ - CHANGELOG.md
297
+ - CODE_OF_CONDUCT.md
298
+ - CONTRIBUTING.md
299
+ - GOVERNANCE.md
300
+ - LICENSE.md
301
+ - README.md
302
+ - SECURITY.md
303
+ - app/admin/features.rb
304
+ - app/models/onni/feature.rb
305
+ - app/models/onni/gate.rb
306
+ - app/models/onni/hit_chart.rb
307
+ - app/models/onni/stat_bucket.rb
308
+ - app/views/admin/features/_chart.html.erb
309
+ - app/views/admin/features/_stats.html.erb
310
+ - app/views/admin/features/show.html.erb
311
+ - config/locales/en.yml
312
+ - config/polyrun_coverage.yml
313
+ - lib/generators/onni/install_generator.rb
314
+ - lib/generators/onni/stats_cleanup_generator.rb
315
+ - lib/generators/onni/templates/add_onni_stats_period_start_index.rb.tt
316
+ - lib/generators/onni/templates/create_onni_features.rb.tt
317
+ - lib/generators/onni/templates/features.yml.tt
318
+ - lib/generators/onni/templates/initializer.rb.tt
319
+ - lib/generators/onni/templates/purge_stats_job.rb.tt
320
+ - lib/onni.rb
321
+ - lib/onni/active_admin.rb
322
+ - lib/onni/actor.rb
323
+ - lib/onni/adapters/active_record.rb
324
+ - lib/onni/adapters/memory.rb
325
+ - lib/onni/conditions.rb
326
+ - lib/onni/configuration.rb
327
+ - lib/onni/engine.rb
328
+ - lib/onni/flag.rb
329
+ - lib/onni/group.rb
330
+ - lib/onni/instrumenter.rb
331
+ - lib/onni/migrator/importer.rb
332
+ - lib/onni/migrator/snapshot.rb
333
+ - lib/onni/registry.rb
334
+ - lib/onni/request_store.rb
335
+ - lib/onni/stats.rb
336
+ - lib/onni/stats/buffer.rb
337
+ - lib/onni/stats/overview.rb
338
+ - lib/onni/stats/purge.rb
339
+ - lib/onni/stats/sampler.rb
340
+ - lib/onni/store.rb
341
+ - lib/onni/sync.rb
342
+ - lib/onni/version.rb
343
+ - onni.gemspec
344
+ - sig/onni.rbs
345
+ homepage: https://github.com/amkisko/onni.rb
346
+ licenses:
347
+ - MIT
348
+ metadata:
349
+ homepage_uri: https://github.com/amkisko/onni.rb
350
+ source_code_uri: https://github.com/amkisko/onni.rb/tree/main
351
+ changelog_uri: https://github.com/amkisko/onni.rb/blob/main/CHANGELOG.md
352
+ bug_tracker_uri: https://github.com/amkisko/onni.rb/issues
353
+ documentation_uri: https://github.com/amkisko/onni.rb#readme
354
+ allowed_push_host: https://rubygems.org
355
+ rubygems_mfa_required: 'true'
356
+ rdoc_options: []
357
+ require_paths:
358
+ - lib
359
+ required_ruby_version: !ruby/object:Gem::Requirement
360
+ requirements:
361
+ - - ">="
362
+ - !ruby/object:Gem::Version
363
+ version: 3.4.0
364
+ required_rubygems_version: !ruby/object:Gem::Requirement
365
+ requirements:
366
+ - - ">="
367
+ - !ruby/object:Gem::Version
368
+ version: '0'
369
+ requirements: []
370
+ rubygems_version: 4.0.6
371
+ specification_version: 4
372
+ summary: Feature flags, A/B variants, and hit stats for Ruby apps
373
+ test_files: []