fino 1.2.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fea3206069816874011b9d072929f796ad38c3c5645689fa75daf68638a0226
4
- data.tar.gz: e8f72e2779da8c910539001ff7c0f91b0d23d44a8b2f8975c984506d8023796e
3
+ metadata.gz: c5692873000111c2c668cdf3dd509fb4bdd9d2f3935b467158686739e8e9cc9f
4
+ data.tar.gz: 8416e04110ddf6ef6dd4e110e32806ca3b711dd2a13fa13c7bc18cbece91443d
5
5
  SHA512:
6
- metadata.gz: bb7428b18c55226ed0c21f5de8d6cbfc219424a6986fd4ab208ae8ee4cd33fcb961fa3c112872c06b9c35a21d1148aafe1ff005e963d6c68facbf4eb4a5a5129
7
- data.tar.gz: fbbce319a638b28bf322f65683e998f83090d159c56b62861f9a3d0df4fce3cead018864fec4bb2c81f0a8de3313fd75747cbc2bcfa8316c707266952ddb1975
6
+ metadata.gz: 99dd798252bd649378504426f0b7c13857a3258501642244b5b110428c0bcfa9abce3266c698be1993722f9643e5897e8e2548fe501d9694fc6f0c205321de12
7
+ data.tar.gz: 9c2f6594f6453f818121e5e273ecc9ed91ff2c85a37c7eedbe82fdd6e2646e36b27f2074b16c5759e6a523003c880aea394785d3c26b599e8178dfd04ad9a57b
data/lib/fino/library.rb CHANGED
@@ -34,21 +34,35 @@ class Fino::Library
34
34
  pipeline.read_multi(setting_definitions)
35
35
  end
36
36
 
37
- def set(**setting_names_to_values)
38
- at = setting_names_to_values.delete(:at)
37
+ def variant(setting_name, at: nil, for:)
38
+ setting(setting_name, at: at).variant(
39
+ for: binding.local_variable_get(:for)
40
+ )
41
+ end
39
42
 
40
- scope = setting_names_to_values.delete(:scope)
41
- context = { scope: scope }.compact
43
+ def set(**data)
44
+ at = data.delete(:at)
45
+ raw_overrides = data.delete(:overrides) || {}
46
+ raw_variants = data.delete(:variants) || {}
42
47
 
43
- setting_names_to_values.each do |setting_name, value|
44
- setting_definition = build_setting_definition(setting_name, at: at)
48
+ setting_name, raw_value = data.first
49
+ setting_definition = build_setting_definition(setting_name, at: at)
50
+ value = setting_definition.type_class.deserialize(raw_value)
45
51
 
46
- pipeline.write(
47
- setting_definition,
48
- setting_definition.type_class.deserialize(value),
49
- **context
50
- )
52
+ variants = raw_variants.map do |percentage, value|
53
+ Fino::Variant.new(percentage, setting_definition.type_class.deserialize(value))
51
54
  end
55
+
56
+ variants.prepend(
57
+ Fino::Variant.new(100.0 - variants.sum(&:percentage), Fino::Variant::CONTROL)
58
+ )
59
+
60
+ pipeline.write(
61
+ setting_definition,
62
+ value,
63
+ raw_overrides.transform_values { |v| setting_definition.type_class.deserialize(v) },
64
+ variants
65
+ )
52
66
  end
53
67
 
54
68
  def slice(**mapping)
@@ -61,6 +75,11 @@ class Fino::Library
61
75
  pipeline.read_multi(setting_definitions)
62
76
  end
63
77
 
78
+ def set_variants(setting_name, at: nil, variants:)
79
+ setting_definition = build_setting_definition(setting_name, at: at)
80
+ pipeline.write_variants(setting_definition, variants)
81
+ end
82
+
64
83
  private
65
84
 
66
85
  attr_reader :configuration
@@ -17,8 +17,8 @@ class Fino::Pipe::Storage
17
17
  end
18
18
  end
19
19
 
20
- def write(setting_definition, value, **context)
21
- adapter.write(setting_definition, value, **context)
20
+ def write(setting_definition, value, overrides, variants)
21
+ adapter.write(setting_definition, value, overrides, variants)
22
22
  end
23
23
 
24
24
  private
@@ -28,11 +28,13 @@ class Fino::Pipe::Storage
28
28
  def to_setting(setting_definition, raw_adapter_data)
29
29
  raw_value = adapter.fetch_value_from(raw_adapter_data)
30
30
  scoped_raw_values = adapter.fetch_scoped_values_from(raw_adapter_data)
31
+ raw_variants = adapter.fetch_raw_variants_from(raw_adapter_data)
31
32
 
32
33
  setting_definition.type_class.build(
33
34
  setting_definition,
34
35
  raw_value,
35
- scoped_raw_values
36
+ scoped_raw_values,
37
+ raw_variants
36
38
  )
37
39
  end
38
40
  end
data/lib/fino/pipe.rb CHANGED
@@ -13,7 +13,7 @@ module Fino::Pipe
13
13
  raise NotImplementedError
14
14
  end
15
15
 
16
- def write(setting_definition, value, **context)
16
+ def write(setting_definition, value, overrides = {}, variants = {})
17
17
  raise NotImplementedError
18
18
  end
19
19
 
data/lib/fino/setting.rb CHANGED
@@ -14,24 +14,37 @@ module Fino::Setting
14
14
  raise NotImplementedError
15
15
  end
16
16
 
17
- def build(setting_definition, raw_value, scoped_raw_values)
17
+ def build(setting_definition, raw_value, scoped_raw_values, raw_variants)
18
18
  value = raw_value.equal?(Fino::EMPTINESS) ? setting_definition.options[:default] : deserialize(raw_value)
19
19
  scoped_values = scoped_raw_values.transform_values { |v| deserialize(v) }
20
20
 
21
+ variants = raw_variants.map do |raw_variant|
22
+ Fino::Variant.new(
23
+ raw_variant.fetch(:percentage),
24
+ deserialize(raw_variant.fetch(:value))
25
+ )
26
+ end
27
+
28
+ variants.prepend(
29
+ Fino::Variant.new(percentage: 100.0 - variants.sum(&:percentage), value: Fino::Variant::CONTROL)
30
+ )
31
+
21
32
  new(
22
33
  setting_definition,
23
34
  value,
24
- scoped_values
35
+ scoped_values,
36
+ variants
25
37
  )
26
38
  end
27
39
  end
28
40
 
29
- attr_reader :definition
41
+ attr_reader :definition, :variants
30
42
 
31
- def initialize(definition, value, scoped_values = {})
43
+ def initialize(definition, value, scoped_values = {}, variants = [])
32
44
  @definition = definition
33
45
  @value = value
34
46
  @scoped_values = scoped_values
47
+ @variants = variants
35
48
  end
36
49
 
37
50
  def name
@@ -42,8 +55,31 @@ module Fino::Setting
42
55
  definition.key
43
56
  end
44
57
 
45
- def value(scope: nil)
46
- scope ? scoped_values.fetch(scope.to_s, @value) : @value
58
+ def value(**context)
59
+ return @value unless (scope = context[:for])
60
+
61
+ scoped_values.fetch(scope.to_s) do
62
+ return @value if variants.empty?
63
+
64
+ variant = variant(for: scope)
65
+ result = variant_id_to_value.fetch(variant.id, @value)
66
+
67
+ return @value if result == Fino::Variant::CONTROL
68
+
69
+ result
70
+ end
71
+ end
72
+
73
+ def variant(for:)
74
+ Fino::VariantPicker.new(self).call(
75
+ binding.local_variable_get(:for)
76
+ )
77
+ end
78
+
79
+ def variant_id_to_value
80
+ @variant_id_to_value ||= variants.each_with_object({}) do |variant, memo|
81
+ memo[variant.id] = variant.value
82
+ end
47
83
  end
48
84
 
49
85
  def overriden_scopes
@@ -83,6 +119,8 @@ module Fino::Setting
83
119
  "key=#{key.inspect}",
84
120
  "type=#{type_class.inspect}",
85
121
  "value=#{value.inspect}",
122
+ "overrides=#{@scoped_values.inspect}",
123
+ "variants=#{variants.inspect}",
86
124
  "default=#{default.inspect}"
87
125
  ]
88
126
 
@@ -0,0 +1,5 @@
1
+
2
+ Fino::Variant = Struct.new(:percentage, :value) do
3
+ def id = @id ||= SecureRandom.uuid
4
+ end
5
+ Fino::Variant::CONTROL = Object.new
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Fino::VariantPicker
4
+ SCALING_FACTOR = 1_000
5
+
6
+ attr_reader :setting
7
+
8
+ def initialize(setting)
9
+ @setting = setting
10
+ end
11
+
12
+ def call(scope)
13
+ return nil if setting.variants.empty?
14
+
15
+ random = Zlib.crc32("#{setting.key}#{scope}") % (100 * SCALING_FACTOR)
16
+ cumulative = 0
17
+
18
+ picked_variant = setting.variants.sort_by(&:percentage).find do |variant|
19
+ cumulative += variant.percentage * SCALING_FACTOR
20
+ random <= cumulative
21
+ end
22
+
23
+ Fino.logger.debug { "Variant picked: #{picked_variant}" }
24
+
25
+ picked_variant
26
+ end
27
+ end
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.2.0"
4
+ VERSION = "1.3.0"
5
5
  REQUIRED_RUBY_VERSION = ">= 3.0.0"
6
6
  end
data/lib/fino.rb CHANGED
@@ -25,7 +25,9 @@ module Fino
25
25
  :setting,
26
26
  :settings,
27
27
  :slice,
28
- :set
28
+ :set,
29
+ :define_variants,
30
+ :variant
29
31
 
30
32
  module_function
31
33
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fino
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Egor Iskrenkov
@@ -54,6 +54,8 @@ files:
54
54
  - lib/fino/settings/integer.rb
55
55
  - lib/fino/settings/section.rb
56
56
  - lib/fino/settings/string.rb
57
+ - lib/fino/variant.rb
58
+ - lib/fino/variant_picker.rb
57
59
  - lib/fino/version.rb
58
60
  homepage: https://github.com/eiskrenkov/fino
59
61
  licenses: