convert_sdk 2.0.0 → 2.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.
- checksums.yaml +4 -4
- data/RELEASE.md +6 -4
- data/lib/convert_sdk/context.rb +101 -25
- data/lib/convert_sdk/feature_manager.rb +18 -10
- data/lib/convert_sdk/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01f414eebe47b8979033ef84e6d1f6c8c36147fce4075ca95258ddd3f8158e75
|
|
4
|
+
data.tar.gz: '01794d5e51f41191f2402fc0327e6d979f140b9fd105db70b140fb277dc3e513'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce9300fe59f844d33189e40c6c9532a9f71bcff6f73ab087d41839a9572b543ddfc1a1ebdfabf9bc868a29ab810ad80a88672f1ca923bc2f4c8aa221727826af
|
|
7
|
+
data.tar.gz: b2e48b3667a3f4597af73c09e143b1f6912561cadacd4aba6ca24f11092ad85493c7f24fd948221da356e1ec0e36285a8d8e2762f9aafdce726137c2602cd55c
|
data/RELEASE.md
CHANGED
|
@@ -202,13 +202,15 @@ Releases are fully automatic. The process:
|
|
|
202
202
|
|
|
203
203
|
semantic-release checks the current branch against the `branches` entry in
|
|
204
204
|
`release.config.mjs` (currently `['main']`). On `main`, the dry-run prints the
|
|
205
|
-
next-version plan. On any other branch it
|
|
205
|
+
next-version plan. On any other branch it stops with:
|
|
206
206
|
|
|
207
207
|
```
|
|
208
|
-
This test run was
|
|
208
|
+
This test run was triggered on the branch <your-branch>, while semantic-release
|
|
209
|
+
is configured to only publish from main, therefore a new version won’t be published.
|
|
209
210
|
```
|
|
210
211
|
|
|
211
|
-
That message is **expected** — it confirms the config parses
|
|
212
|
+
That message is **expected** — it confirms the config parses and every plugin in
|
|
213
|
+
the chain loaded. To exercise a full
|
|
212
214
|
dry-run on a feature branch, temporarily add the branch name to
|
|
213
215
|
`release.config.mjs`'s `branches` array, run the dry-run, then discard the
|
|
214
216
|
temporary edit before committing:
|
|
@@ -308,6 +310,6 @@ through:
|
|
|
308
310
|
| Release ran but published nothing | No release-worthy commit since the last tag (only `chore`/`docs`/`ci`/`test`/`style`/`perf`). | Expected — semantic-release succeeds silently with no version. Land a `feat:`/`fix:` to publish. |
|
|
309
311
|
| `gem push` failed / no RubyGems credential | The RubyGems Trusted Publisher is not registered (or the repo/workflow filename in the registration doesn't match `convertcom/ruby-sdk` ↔ `release.yml`). | Re-check the trusted-publisher entry on rubygems.org (One-Time Setup step 1). The workflow needs `id-token: write` (it has it) and the OIDC exchange step must run before semantic-release. |
|
|
310
312
|
| GitHub Release/tag created but gem missing | Should not happen — publish runs before the Release (publish-before-Release). If you see it, a manual tag was likely pushed out of band. | Do not hand-create `v*` tags. Let the pipeline own tagging. |
|
|
311
|
-
| `yarn release:dry-run`
|
|
313
|
+
| `yarn release:dry-run` stops with "This test run was triggered on the branch …, while semantic-release is configured to only publish from `main`" | Expected on any branch except `main`. | To force a full dry-run on a feature branch, temporarily add the branch to `release.config.mjs`'s `branches` array (discard before committing). On `main`, this means the local branch isn't pushed to `origin` — push first. |
|
|
312
314
|
| `Cannot find module '<preset>'` from a semantic-release plugin | The yarn node linker isn't producing a `node_modules/` tree the dynamic preset import can walk. | Confirm `.yarnrc.yml` selects the `node-modules` linker and re-run `yarn install --immutable`. |
|
|
313
315
|
| Forbidden release mechanism reintroduced (lint job fails) | A `@semantic-release/git`/`@semantic-release/changelog` plugin, a `rake release` task, `bundler/gem_tasks`, or `rubygems/release-gem` was added. | These are blocked by the release-safety step in the `Lint (RuboCop)` job. Remove the forbidden mechanism — publishing happens only via OIDC `release.yml`. |
|
data/lib/convert_sdk/context.rb
CHANGED
|
@@ -46,6 +46,29 @@ module ConvertSdk
|
|
|
46
46
|
# (+nil+ for lookups, +self+ for the chainable mutator). A raising collaborator
|
|
47
47
|
# degrades the call; it never crashes the host request.
|
|
48
48
|
class Context
|
|
49
|
+
# The reserved keys the public per-call hash accepts (CAP-3): a +:merged_map+ row
|
|
50
|
+
# lands in the engine envelope at +destination+, a +:raw_per_call+ row in that reader.
|
|
51
|
+
RESERVED_KEYS = {
|
|
52
|
+
"location_properties" => { source: :merged_map, destination: :location_properties,
|
|
53
|
+
scope: "every decision entry point" },
|
|
54
|
+
"environment" => { source: :merged_map, destination: :environment,
|
|
55
|
+
scope: "every decision entry point" },
|
|
56
|
+
"enable_tracking" => { source: :raw_per_call, destination: :tracking_enabled_for_call,
|
|
57
|
+
scope: "honoured on run_experience(s), accepted inert on run_feature(s)" },
|
|
58
|
+
"experience_keys" => { source: :raw_per_call, destination: :experiences,
|
|
59
|
+
scope: "run_feature(s) only; narrows the experiences decided (CAP-1)" },
|
|
60
|
+
"type_casting" => { source: :raw_per_call, destination: :type_casting,
|
|
61
|
+
scope: "run_feature(s) only; false returns the config-stored variables (CAP-2)" },
|
|
62
|
+
"ruleData" => { source: :raw_per_call, destination: :visitor_properties,
|
|
63
|
+
scope: "run_custom_segments only; camelCase on a snake_case surface (SD-4)" }
|
|
64
|
+
}.freeze
|
|
65
|
+
|
|
66
|
+
# Engine-readable keys the seam never lifts, each with its reason (CAP-3's negative list).
|
|
67
|
+
NOT_LIFTED = {
|
|
68
|
+
"enable_storage" => "preview owns the persistence gate (D-4)",
|
|
69
|
+
"update_visitor_properties" => "documented Ruby divergence (D-5)"
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
49
72
|
# @param visitor_id [String] the resolved visitor id (validated non-blank by
|
|
50
73
|
# {Client#create_context} before construction).
|
|
51
74
|
# @param attributes [Hash, nil] the per-visitor attributes; deep-stringified
|
|
@@ -357,9 +380,12 @@ module ConvertSdk
|
|
|
357
380
|
# {SystemEvents::BUCKETING} event, exactly as {#run_experience}'s forced
|
|
358
381
|
# branch returns before {#fire_bucketing}. Every OTHER decided variation is
|
|
359
382
|
# zero-trace like {#run_experience}'s other-experience branch:
|
|
360
|
-
# {#decision_attributes} suppresses the sticky persist
|
|
361
|
-
#
|
|
362
|
-
#
|
|
383
|
+
# {#decision_attributes} suppresses the sticky persist, the tracking verdict
|
|
384
|
+
# is forced +false+ so the outbound enqueue is skipped, and
|
|
385
|
+
# {#fire_bucketing}'s +@preview.nil?+ guard suppresses the
|
|
386
|
+
# {SystemEvents::BUCKETING} event as well — NO event fires for ANY
|
|
387
|
+
# experience on a preview-active context (JS parity — +context.ts:260+:
|
|
388
|
+
# +if (!this._preview) { fire BUCKETING }+).
|
|
363
389
|
#
|
|
364
390
|
# @param attributes [Hash, nil] optional per-call visitor properties merged
|
|
365
391
|
# over the context attributes (deep-stringified). May carry +:enable_tracking+.
|
|
@@ -402,23 +428,30 @@ module ConvertSdk
|
|
|
402
428
|
# render_legacy_checkout
|
|
403
429
|
# end
|
|
404
430
|
#
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
431
|
+
# A per-call +experience_keys+ Array narrows which experiences are decided,
|
|
432
|
+
# and so which sticky assignments the read commits (CAP-1); absent, nil or
|
|
433
|
+
# empty decides every configured experience (D-6).
|
|
434
|
+
# A per-call +type_casting+ of +false+ returns variables as config stores them (CAP-2).
|
|
435
|
+
#
|
|
436
|
+
# NOTE (accepted parity break): +type_casting: nil+ leaves casting ON here,
|
|
437
|
+
# where the JS presence-based rule would disable it (D-8).
|
|
408
438
|
#
|
|
409
439
|
# Never raises into the host: an internal failure degrades to a DISABLED
|
|
410
440
|
# {BucketedFeature} (carrying the requested key) + an +error+ log (NFR9).
|
|
411
441
|
#
|
|
412
442
|
# @param key [String] the feature +key+ to evaluate.
|
|
413
|
-
# @param attributes [Hash, nil] optional per-call visitor properties merged
|
|
414
|
-
#
|
|
443
|
+
# @param attributes [Hash, nil] optional per-call visitor properties merged over
|
|
444
|
+
# the context attributes (deep-stringified). May carry +:experience_keys+
|
|
445
|
+
# (CAP-1) and +:type_casting+ (CAP-2); only a boolean +false+ disables casting (D-8).
|
|
415
446
|
# @return [BucketedFeature, Array<BucketedFeature>] the resolved feature(s).
|
|
416
447
|
def run_feature(key, attributes = nil)
|
|
417
448
|
manager = @feature_manager
|
|
418
449
|
return disabled_feature(key) if manager.nil?
|
|
419
450
|
|
|
420
451
|
@data_manager.ensure_fresh_config!
|
|
421
|
-
manager.run_feature(@visitor_id, key, decision_attributes(attributes)
|
|
452
|
+
manager.run_feature(@visitor_id, key, decision_attributes(attributes),
|
|
453
|
+
experiences: experience_keys_for_call(attributes),
|
|
454
|
+
type_casting: type_casting_for_call?(attributes))
|
|
422
455
|
rescue StandardError => e
|
|
423
456
|
@log_manager.error("Context#run_feature: #{e.class}: #{e.message}")
|
|
424
457
|
disabled_feature(key)
|
|
@@ -437,15 +470,18 @@ module ConvertSdk
|
|
|
437
470
|
# Never raises into the host: an internal failure degrades to +[]+ + an
|
|
438
471
|
# +error+ log (NFR9).
|
|
439
472
|
#
|
|
440
|
-
# @param attributes [Hash, nil] optional per-call visitor properties merged
|
|
441
|
-
#
|
|
473
|
+
# @param attributes [Hash, nil] optional per-call visitor properties merged over
|
|
474
|
+
# the context attributes (deep-stringified). May carry +:experience_keys+
|
|
475
|
+
# (CAP-1) and +:type_casting+ (CAP-2); only a boolean +false+ disables casting (D-8).
|
|
442
476
|
# @return [Array<BucketedFeature>] the resolved features (enabled + disabled).
|
|
443
477
|
def run_features(attributes = nil)
|
|
444
478
|
manager = @feature_manager
|
|
445
479
|
return [] if manager.nil?
|
|
446
480
|
|
|
447
481
|
@data_manager.ensure_fresh_config!
|
|
448
|
-
manager.run_features(@visitor_id, decision_attributes(attributes)
|
|
482
|
+
manager.run_features(@visitor_id, decision_attributes(attributes),
|
|
483
|
+
experiences: experience_keys_for_call(attributes),
|
|
484
|
+
type_casting: type_casting_for_call?(attributes))
|
|
449
485
|
rescue StandardError => e
|
|
450
486
|
@log_manager.error("Context#run_features: #{e.class}: #{e.message}")
|
|
451
487
|
[]
|
|
@@ -623,11 +659,16 @@ module ConvertSdk
|
|
|
623
659
|
|
|
624
660
|
# {#run_experiences}'s preview branch, extracted to keep #run_experiences
|
|
625
661
|
# within RuboCop's ABC/complexity budget: drop the previewed experience's
|
|
626
|
-
# normally-decided entry (
|
|
627
|
-
#
|
|
628
|
-
#
|
|
629
|
-
# {#
|
|
630
|
-
#
|
|
662
|
+
# normally-decided entry (the forced decision replaces it), route every
|
|
663
|
+
# OTHER experience through {#fire_bucketing} with +track: false+, then
|
|
664
|
+
# append the forced variation. On a preview-active context that
|
|
665
|
+
# {#fire_bucketing} call is inert BY DESIGN — its +@preview.nil?+ guard
|
|
666
|
+
# suppresses the {SystemEvents::BUCKETING} event and +track: false+
|
|
667
|
+
# suppresses the enqueue, so all it emits is the +debug+ suppression line.
|
|
668
|
+
# The seam is kept rather than skipped so the zero-trace verdict stays
|
|
669
|
+
# enforced at the SINGLE bucketing site instead of being duplicated here.
|
|
670
|
+
# A defensive Sentinel from {#forced_preview_variation} (set_preview
|
|
671
|
+
# pre-validates, so not expected) appends nothing.
|
|
631
672
|
def force_preview_in_run_all(variations, preview)
|
|
632
673
|
others = variations.reject { |variation| variation.experience_key == preview[:experience_key] }
|
|
633
674
|
others.each { |variation| fire_bucketing(variation.experience_key, variation, track: false) }
|
|
@@ -706,7 +747,8 @@ module ConvertSdk
|
|
|
706
747
|
# per-call +ruleData+ (and context attributes) win over stored segments. All
|
|
707
748
|
# deep-stringified to string keys (the rule engine reads string keys).
|
|
708
749
|
def visitor_properties(attributes)
|
|
709
|
-
|
|
750
|
+
key = reserved_key_name(:visitor_properties)&.to_s
|
|
751
|
+
rule_data = key && attributes.is_a?(Hash) ? attributes[key.to_sym] || attributes[key] : nil
|
|
710
752
|
empty = {} #: Hash[String, untyped]
|
|
711
753
|
merged = @attributes.merge(deep_stringify(rule_data || empty))
|
|
712
754
|
stored = get_visitor_data["segments"]
|
|
@@ -738,12 +780,43 @@ module ConvertSdk
|
|
|
738
780
|
# byte-identical to the pre-qs-03 behavior.
|
|
739
781
|
def decision_attributes(per_call)
|
|
740
782
|
merged = @attributes.merge(deep_stringify(per_call || {}))
|
|
741
|
-
{
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
783
|
+
envelope = { visitor_properties: merged } #: Hash[Symbol, untyped]
|
|
784
|
+
reserved_rows(:merged_map).each { |name, fields| envelope[fields[:destination]] = merged[name.to_s] }
|
|
785
|
+
envelope[:enable_storage] = @preview.nil?
|
|
786
|
+
envelope
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
def reserved_rows(source)
|
|
790
|
+
RESERVED_KEYS.reject { |name, fields| NOT_LIFTED.key?(name.to_s) || fields[:source] != source }
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
# The per-call key name the enumeration routes to +destination+, nil when none does.
|
|
794
|
+
def reserved_key_name(destination)
|
|
795
|
+
row = reserved_rows(:raw_per_call).find { |_, fields| fields[:destination] == destination }
|
|
796
|
+
row&.first
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
# The per-call experience-key filter (CAP-1). A non-Array value degrades to
|
|
800
|
+
# no filter with a +warn+ (SD-2); nil is absence and never warns.
|
|
801
|
+
def experience_keys_for_call(attributes)
|
|
802
|
+
key = reserved_key_name(:experiences)&.to_s
|
|
803
|
+
return nil unless key && attributes.is_a?(Hash)
|
|
804
|
+
|
|
805
|
+
value = attributes.fetch(key.to_sym) { attributes.fetch(key, nil) }
|
|
806
|
+
return value if value.nil? || value.is_a?(Array)
|
|
807
|
+
|
|
808
|
+
@log_manager.warn("Context#run_feature: #{key} must be an Array, got #{value.class} — ignoring it")
|
|
809
|
+
nil
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
# The per-call casting switch (CAP-2): only an explicit +false+ turns it off (D-8).
|
|
813
|
+
def type_casting_for_call?(attributes)
|
|
814
|
+
return true unless attributes.is_a?(Hash)
|
|
815
|
+
|
|
816
|
+
key = reserved_key_name(:type_casting)&.to_s
|
|
817
|
+
return true if key.nil?
|
|
818
|
+
|
|
819
|
+
attributes.fetch(key.to_sym) { attributes.fetch(key, true) } != false
|
|
747
820
|
end
|
|
748
821
|
|
|
749
822
|
# The single named seam fired once per fresh/decided variation. It does TWO
|
|
@@ -809,7 +882,10 @@ module ConvertSdk
|
|
|
809
882
|
def tracking_enabled_for_call?(attributes)
|
|
810
883
|
return true unless attributes.is_a?(Hash)
|
|
811
884
|
|
|
812
|
-
|
|
885
|
+
key = reserved_key_name(:tracking_enabled_for_call)&.to_s
|
|
886
|
+
return true if key.nil?
|
|
887
|
+
|
|
888
|
+
value = attributes.fetch(key.to_sym) { attributes.fetch(key, true) }
|
|
813
889
|
value != false
|
|
814
890
|
end
|
|
815
891
|
|
|
@@ -84,16 +84,20 @@ module ConvertSdk
|
|
|
84
84
|
# @param feature_key [String] the feature +key+ to resolve.
|
|
85
85
|
# @param attributes [Hash] bucketing attributes (+:visitor_properties+,
|
|
86
86
|
# +:location_properties+, +:environment+) — see {DataManager#get_bucketing}.
|
|
87
|
+
# @param experiences [Array<String>, nil] optional experience-key filter
|
|
88
|
+
# narrowing which experiences are decided (CAP-1); nil/empty means all.
|
|
89
|
+
# @param type_casting [Boolean] +false+ returns variables as config stores them (CAP-2).
|
|
87
90
|
# @return [BucketedFeature, Array<BucketedFeature>] enabled feature(s) or a
|
|
88
91
|
# frozen DISABLED {BucketedFeature} on a miss.
|
|
89
|
-
def run_feature(visitor_id, feature_key, attributes = {})
|
|
92
|
+
def run_feature(visitor_id, feature_key, attributes = {}, experiences: nil, type_casting: true)
|
|
90
93
|
declared = @data_manager.feature_by_key(feature_key)
|
|
91
94
|
unless declared
|
|
92
95
|
@log_manager&.debug("FeatureManager#run_feature: feature not declared key=#{feature_key}")
|
|
93
96
|
return disabled_feature(key: feature_key)
|
|
94
97
|
end
|
|
95
98
|
|
|
96
|
-
enabled = run_features(visitor_id, attributes,
|
|
99
|
+
enabled = run_features(visitor_id, attributes,
|
|
100
|
+
experiences: experiences, features: [feature_key], type_casting: type_casting)
|
|
97
101
|
if enabled.empty?
|
|
98
102
|
@log_manager&.debug("FeatureManager#run_feature: not bucketed into a carrying variation key=#{feature_key}")
|
|
99
103
|
return disabled_from_declared(declared)
|
|
@@ -119,12 +123,13 @@ module ConvertSdk
|
|
|
119
123
|
# @param experiences [Array<String>, nil] optional experience-key filter.
|
|
120
124
|
# @param features [Array<String>, nil] optional feature-key filter (suppresses
|
|
121
125
|
# the DISABLED padding).
|
|
126
|
+
# @param type_casting [Boolean] +false+ skips the per-variable conversion (CAP-2).
|
|
122
127
|
# @return [Array<BucketedFeature>] the resolved features.
|
|
123
|
-
def run_features(visitor_id, attributes = {}, experiences: nil, features: nil)
|
|
128
|
+
def run_features(visitor_id, attributes = {}, experiences: nil, features: nil, type_casting: true)
|
|
124
129
|
declared_by_id = features_by_id
|
|
125
130
|
variations = bucketed_variations(visitor_id, attributes, experiences)
|
|
126
131
|
|
|
127
|
-
bucketed = collect_enabled(variations, declared_by_id, features)
|
|
132
|
+
bucketed = collect_enabled(variations, declared_by_id, features, type_casting: type_casting)
|
|
128
133
|
|
|
129
134
|
# Pad with DISABLED features ONLY when no feature filter is supplied.
|
|
130
135
|
append_disabled(bucketed, declared_by_id) if features.nil?
|
|
@@ -179,11 +184,12 @@ module ConvertSdk
|
|
|
179
184
|
# Walk every bucketed variation's +fullStackFeature+ changes, mapping each to
|
|
180
185
|
# its declared feature (by id), casting the variables, and building an ENABLED
|
|
181
186
|
# {BucketedFeature}. Honours the optional +feature_keys+ filter.
|
|
182
|
-
def collect_enabled(variations, declared_by_id, feature_keys)
|
|
187
|
+
def collect_enabled(variations, declared_by_id, feature_keys, type_casting: true)
|
|
183
188
|
bucketed = [] #: Array[BucketedFeature]
|
|
184
189
|
variations.each do |variation|
|
|
185
190
|
feature_changes(variation).each do |change|
|
|
186
|
-
feature = enabled_feature_from_change(variation, change, declared_by_id, feature_keys
|
|
191
|
+
feature = enabled_feature_from_change(variation, change, declared_by_id, feature_keys,
|
|
192
|
+
type_casting: type_casting)
|
|
187
193
|
bucketed << feature if feature
|
|
188
194
|
end
|
|
189
195
|
end
|
|
@@ -208,13 +214,14 @@ module ConvertSdk
|
|
|
208
214
|
|
|
209
215
|
# Build the ENABLED {BucketedFeature} for one feature change, or nil when the
|
|
210
216
|
# change has no feature_id, the feature is undeclared, or it is filtered out.
|
|
211
|
-
def enabled_feature_from_change(variation, change, declared_by_id, feature_keys)
|
|
217
|
+
def enabled_feature_from_change(variation, change, declared_by_id, feature_keys, type_casting: true)
|
|
212
218
|
data = change["data"]
|
|
213
219
|
declared = declared_for_change(data, declared_by_id)
|
|
214
220
|
return nil if declared.nil?
|
|
215
221
|
return nil if filtered_out?(declared, feature_keys)
|
|
216
222
|
|
|
217
|
-
|
|
223
|
+
variables = cast_variables(declared, data["variables_data"], type_casting: type_casting)
|
|
224
|
+
build_enabled(variation, declared, variables)
|
|
218
225
|
end
|
|
219
226
|
|
|
220
227
|
# The declared feature a feature-change maps to (by data.feature_id), or nil
|
|
@@ -239,7 +246,8 @@ module ConvertSdk
|
|
|
239
246
|
# Cast every supplied raw variable per its declared type (data-driven). A
|
|
240
247
|
# variable with no declared type passes through uncast (JS warns
|
|
241
248
|
# FEATURE_VARIABLES_TYPE_NOT_FOUND). Returns a fresh string-keyed Hash.
|
|
242
|
-
|
|
249
|
+
# +type_casting+ +false+ skips the conversion only, not the walk or the warn (CAP-2, D-8).
|
|
250
|
+
def cast_variables(declared, raw, type_casting: true)
|
|
243
251
|
unless raw.is_a?(Hash)
|
|
244
252
|
@log_manager&.warn("FeatureManager#run_features: feature variables not found")
|
|
245
253
|
return {}
|
|
@@ -250,7 +258,7 @@ module ConvertSdk
|
|
|
250
258
|
raw.each do |name, value|
|
|
251
259
|
type = variable_type(definitions, name)
|
|
252
260
|
if type
|
|
253
|
-
cast[name.to_s] = cast_type(value, type)
|
|
261
|
+
cast[name.to_s] = type_casting ? cast_type(value, type) : value
|
|
254
262
|
else
|
|
255
263
|
@log_manager&.warn("FeatureManager#run_features: variable type not found name=#{name}")
|
|
256
264
|
cast[name.to_s] = value
|
data/lib/convert_sdk/version.rb
CHANGED