fino-redis 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9974c2bc5e434114c5184d48f9c7d2654437c669e27b016fedd4694fb9404201
4
- data.tar.gz: 1f74ae02ccc9e4ff3c23a25a23f37f34b2a4f3eec45bd7b3f12bcaefe6e9121f
3
+ metadata.gz: 7f208678be6c8dc8bdf5fde1a3c8458e8cd908c59f870f432b4136e4f8e38bf5
4
+ data.tar.gz: 134c3275f900f0dcd9ace76124f6a3b2167316d2b2b7845ddc2597c64c3647ed
5
5
  SHA512:
6
- metadata.gz: 1892e8b1672e070d5c608a21f2350f510fd5555434603c82296863bd8344eee9793da4102d017050246e131575dffdc83cb146386f139489b5b4dc753f8e9e44
7
- data.tar.gz: 281ed65e05cf646c8672ccd85be6f85ee53ee1a3d7b74b7d78ea0ff86a4d1ce7151f014b127d1d49e4817e351cbcb456f9945c2b5f9511e30f1bdce245fed7b8
6
+ metadata.gz: 4ea485e204970f5f571f8993cfe3431cfa7a031f9c70167214656646b1538af6a3d568cd260faa2de51db66b7c54f793ed64c6b5d11a7c6cb7819a035b46e0dc
7
+ data.tar.gz: a65a7aaa5a427741f39f56daa5e2913c80b3347c3806dc47f5c58eed6a9ddbeab341816954ce724477702c92790c93df41f6a7e62e57b822fac0e1f20c665446
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Fino
2
2
 
3
- ⚠️ Fino is under active development. API changes are possible ⚠️
3
+ ⚠️ Fino in active development phase at wasn't properly battle tested in production just yet. Give us a star and stay tuned for Production test results and new features
4
4
 
5
5
  Fino is a dynamic settings engine for Ruby and Rails
6
6
 
@@ -23,10 +23,15 @@ Fino.configure do
23
23
  settings do
24
24
  setting :maintenance_mode, :boolean, default: false
25
25
 
26
+ setting :api_rate_limit,
27
+ :integer,
28
+ default: 1000,
29
+ description: "Maximum API requests per minute per user to prevent abuse"
30
+
26
31
  section :openai, label: "OpenAI" do
27
32
  setting :model,
28
33
  :string,
29
- default: "gpt-4o",
34
+ default: "gpt-5",
30
35
  description: "OpenAI model"
31
36
 
32
37
  setting :temperature,
@@ -51,7 +56,7 @@ end
51
56
  ### Work with settings
52
57
 
53
58
  ```ruby
54
- Fino.value(:model, at: :openai) #=> "gpt-4o"
59
+ Fino.value(:model, at: :openai) #=> "gpt-5"
55
60
  Fino.value(:temperature, at: :openai) #=> 0.7
56
61
 
57
62
  Fino.values(:model, :temperature, at: :openai) #=> ["gpt-4", 0.7]
@@ -60,12 +65,46 @@ Fino.set(model: "gpt-5", at: :openai)
60
65
  Fino.value(:model, at: :openai) #=> "gpt-5"
61
66
  ```
62
67
 
63
- ### Manage settings via UI
68
+ ### Overrides
64
69
 
65
70
  ```ruby
66
- gem "fino-rails"
71
+ Fino.value(:model, at: :openai) #=> "gpt-5"
72
+
73
+ Fino.set(model: "gpt-5", at: :openai, overrides: { "qa" => "our_local_model_not_to_pay_to_sam_altman" })
74
+
75
+ Fino.value(:model, at: :openai) #=> "gpt-5"
76
+ Fino.value(:model, at: :openai, for: "qa") #=> "our_local_model_not_to_pay_to_sam_altman"
77
+ ```
78
+
79
+ ### A/B testing
80
+
81
+ ```ruby
82
+ Fino.value(:model, at: :openai) #=> "gpt-5"
83
+
84
+ # "gpt-5" becomes the control variant value and a 20.0% variant is created with value "gpt-6"
85
+ Fino.set(model: "gpt-5", at: :openai, variants: { 20.0 => "gpt-6" })
86
+
87
+ Fino.setting(:model, at: :openai).experiment.variant(for: "user_1") #=> #<Fino::AbTesting::Variant percentage: 20.0, value: "gpt-6">
88
+
89
+ # Picked variant is sticked to the user
90
+ Fino.value(:model, at: :openai, for: "user_1") #=> "gpt-6"
91
+ Fino.value(:model, at: :openai, for: "user_1") #=> "gpt-6"
92
+
93
+ Fino.value(:model, at: :openai, for: "user_2") #=> "gpt-5"
67
94
  ```
68
95
 
96
+ ## Rails integration
97
+
98
+ Fino easily integrates with Rails. Just add the gem to your Gemfile:
99
+
100
+ ```
101
+ gem "fino-rails", require: false
102
+ ```
103
+
104
+ to get built-in UI engine for your settings!
105
+
106
+ ### UI engine
107
+
69
108
  Mount Fino Rails engine in your `config/routes.rb`:
70
109
 
71
110
  ```ruby
@@ -74,4 +113,84 @@ Rails.application.routes.draw do
74
113
  end
75
114
  ```
76
115
 
77
- <img width="1229" height="641" alt="Screenshot 2025-09-04 at 16 01 51" src="https://github.com/user-attachments/assets/646df84c-c25b-4890-9637-c481e18c9bd4" />
116
+ ### Configuration
117
+
118
+ ```ruby
119
+ Rails.application.configure do
120
+ config.fino.instrument = true
121
+ config.fino.log = true
122
+ config.fino.cache_within_request = false
123
+ config.fino.preload_before_request = true
124
+ end
125
+ ```
126
+
127
+ <img width="1493" height="676" alt="Screenshot 2025-09-19 at 13 09 06" src="https://github.com/user-attachments/assets/19b6147a-e18c-41cf-aac7-99111efcc9d5" />
128
+
129
+ <img width="1775" height="845" alt="Screenshot 2025-09-19 at 13 09 33" src="https://github.com/user-attachments/assets/c0010abd-285d-43d0-ae5d-ce0edb781309" />
130
+
131
+ ## Performance tweaks
132
+
133
+ 1. In Memory cache
134
+
135
+ Fino provides in-memory settings caching functionality which will store settings received from adaper in memory for
136
+ a very quick access. As this kind of cache is not distributed between machines, but belongs to each process
137
+ separately, it's impossible to invalidate all at once, so be aware that setting update application time will depend
138
+ on cache TTL you configure
139
+
140
+ ```ruby
141
+ Fino.configure do
142
+ # ...
143
+ cache { Fino::Cache::Memory.new(expires_in: 3.seconds) }
144
+ # ...
145
+ end
146
+ ```
147
+
148
+ 2. Request scoped cache
149
+
150
+ When using Fino in Rails context it's possible to cache settings within request, in current thread storage. This is
151
+ safe way to cache settings as it's lifetime is limited, thus it is enabled by default
152
+
153
+ ```ruby
154
+ Rails.application.configure do
155
+ config.fino.cache_within_request = true
156
+ end
157
+ ```
158
+
159
+ 3. Preloading
160
+
161
+ In Rails context it is possible to tell Fino to preload multiple settings before processing request in a single
162
+ adapter call. Preloading is recommended for requests that use multiple different settings in their logic
163
+
164
+ ```ruby
165
+ # Preload all settings
166
+ Rails.application.configure do
167
+ config.fino.preload_before_request = true
168
+ end
169
+
170
+ # Preload specific subset of settings depending on request
171
+ Rails.application.configure do
172
+ config.fino.preload_before_request = ->(request) {
173
+ case request.path
174
+ when "request/using/all/settings"
175
+ true
176
+ when "request/not/using/settings"
177
+ false
178
+ when "request/using/specific/settings"
179
+ [
180
+ :api_rate_limit,
181
+ openai: [:model, :temperature]
182
+ ]
183
+ end
184
+ }
185
+ end
186
+ ```
187
+
188
+ ## Releasing
189
+
190
+ `rake release`
191
+
192
+ ## Contributing
193
+
194
+ 1. Fork it
195
+ 2. Do contribution
196
+ 6. Create Pull Request into this repo
@@ -19,6 +19,14 @@ class Fino::Redis::Adapter
19
19
  redis.hgetall(redis_key_for(setting_definition))
20
20
  end
21
21
 
22
+ def read_multi(setting_definitions)
23
+ keys = setting_definitions.map { |definition| redis_key_for(definition) }
24
+
25
+ redis.pipelined do |pipeline|
26
+ keys.each { |key| pipeline.hgetall(key) }
27
+ end
28
+ end
29
+
22
30
  def write(setting_definition, value, overrides, variants)
23
31
  serialize_value = ->(raw_value) { setting_definition.type_class.serialize(raw_value) }
24
32
 
@@ -29,7 +37,7 @@ class Fino::Redis::Adapter
29
37
  end
30
38
 
31
39
  variants.each do |variant|
32
- next if variant.value == Fino::Variant::CONTROL
40
+ next if variant.value == Fino::AbTesting::Variant::CONTROL_VALUE
33
41
 
34
42
  hash["#{VARIANT_PREFIX}/#{variant.percentage}/#{VALUE_KEY}"] = serialize_value.call(variant.value)
35
43
  end
@@ -37,19 +45,11 @@ class Fino::Redis::Adapter
37
45
  redis.mapped_hreplace(redis_key_for(setting_definition), hash)
38
46
  end
39
47
 
40
- def read_multi(setting_definitions)
41
- keys = setting_definitions.map { |definition| redis_key_for(definition) }
42
-
43
- redis.pipelined do |pipeline|
44
- keys.each { |key| pipeline.hgetall(key) }
45
- end
46
- end
47
-
48
48
  def fetch_value_from(raw_adapter_data)
49
49
  raw_adapter_data.key?(VALUE_KEY) ? raw_adapter_data.delete(VALUE_KEY) : Fino::EMPTINESS
50
50
  end
51
51
 
52
- def fetch_scoped_values_from(raw_adapter_data)
52
+ def fetch_raw_overrides_from(raw_adapter_data)
53
53
  raw_adapter_data.each_with_object({}) do |(key, value), memo|
54
54
  next unless key.start_with?("#{SCOPE_PREFIX}/")
55
55
 
data/lib/fino/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fino
4
- VERSION = "1.3.0"
4
+ VERSION = "1.3.1"
5
5
  REQUIRED_RUBY_VERSION = ">= 3.0.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fino-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Egor Iskrenkov
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 1.3.0
18
+ version: 1.3.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 1.3.0
25
+ version: 1.3.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: redis
28
28
  requirement: !ruby/object:Gem::Requirement