convert_sdk 1.0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +191 -0
- data/.yardopts +16 -0
- data/CONTRIBUTING.md +131 -0
- data/LICENSE +201 -0
- data/README.md +183 -0
- data/RELEASE.md +313 -0
- data/Rakefile +16 -0
- data/convert_sdk.gemspec +50 -0
- data/lib/convert_sdk/api_manager.rb +288 -0
- data/lib/convert_sdk/background_timer.rb +129 -0
- data/lib/convert_sdk/bucketed_feature.rb +35 -0
- data/lib/convert_sdk/bucketed_variation.rb +43 -0
- data/lib/convert_sdk/bucketing_manager.rb +134 -0
- data/lib/convert_sdk/client.rb +417 -0
- data/lib/convert_sdk/comparisons.rb +257 -0
- data/lib/convert_sdk/config.rb +214 -0
- data/lib/convert_sdk/config_validator.rb +127 -0
- data/lib/convert_sdk/context.rb +618 -0
- data/lib/convert_sdk/data_manager.rb +897 -0
- data/lib/convert_sdk/data_store_manager.rb +185 -0
- data/lib/convert_sdk/enums/bucketing_error.rb +18 -0
- data/lib/convert_sdk/enums/feature_status.rb +13 -0
- data/lib/convert_sdk/enums/goal_data_key.rb +62 -0
- data/lib/convert_sdk/enums/log_level.rb +22 -0
- data/lib/convert_sdk/enums/rule_error.rb +19 -0
- data/lib/convert_sdk/enums/system_events.rb +29 -0
- data/lib/convert_sdk/event_manager.rb +125 -0
- data/lib/convert_sdk/experience_manager.rb +69 -0
- data/lib/convert_sdk/feature_manager.rb +367 -0
- data/lib/convert_sdk/fork_guard.rb +144 -0
- data/lib/convert_sdk/http_client.rb +198 -0
- data/lib/convert_sdk/log_manager.rb +168 -0
- data/lib/convert_sdk/murmur_hash3.rb +129 -0
- data/lib/convert_sdk/redactor.rb +93 -0
- data/lib/convert_sdk/rule_manager.rb +242 -0
- data/lib/convert_sdk/segments_manager.rb +241 -0
- data/lib/convert_sdk/sentinel.rb +57 -0
- data/lib/convert_sdk/stores/memory_store.rb +55 -0
- data/lib/convert_sdk/stores/redis_store.rb +126 -0
- data/lib/convert_sdk/version.rb +14 -0
- data/lib/convert_sdk/visitors_queue.rb +190 -0
- data/lib/convert_sdk.rb +218 -0
- data/scripts/check-generated-rbs-header.sh +41 -0
- data/steep/config_contract_probe.rb +154 -0
- metadata +93 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 16cc235aa36baea77098bf6d47a5d479e840ca95a416b36b87f8f178b95895c1
|
|
4
|
+
data.tar.gz: 1354d6d78384ebea2f6b187ae601da34e7090de152fc6ca4fa95480e406c04a0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5fc43abd0fbf174055ef97a00c3c983f232287f886151990931f2635a733fcd0a626e1b35a7f7ef564b2953bcc3cee32b6fe7cb96a9e9ba7dce40b5dfc2e7ae7
|
|
7
|
+
data.tar.gz: 74e704ae66077b55a6e92a008a44933190208f8b3caaacefbea041801b4e9bfe8e8498f082205bfa71b4901834ed9cca2ce5d3e80b5e317848e4fc4a2b925e6e
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-performance
|
|
3
|
+
|
|
4
|
+
AllCops:
|
|
5
|
+
TargetRubyVersion: 3.1
|
|
6
|
+
NewCops: enable
|
|
7
|
+
SuggestExtensions: false
|
|
8
|
+
Exclude:
|
|
9
|
+
- "bin/**/*"
|
|
10
|
+
- "vendor/**/*"
|
|
11
|
+
# RBS signature files are not Ruby; exclude from all cops.
|
|
12
|
+
- "**/*.rbs"
|
|
13
|
+
# The demo Rails app (Story 5.2) is an APP, not the gem's lib — it lives
|
|
14
|
+
# under demo/ purely to exercise the SDK end-to-end under a real Puma cluster.
|
|
15
|
+
# It must NOT enter the gem's quality gates (RuboCop here, SimpleCov coverage,
|
|
16
|
+
# RBS/Steep typing): its Rails-idiomatic style, its smoke script, and its
|
|
17
|
+
# boot files follow different conventions and would pollute the gem's offense
|
|
18
|
+
# count and coverage %. The gemspec already excludes every demo/ path from the
|
|
19
|
+
# package; this keeps the LINT gate equally clean. (SimpleCov tracks only
|
|
20
|
+
# lib/ — see spec/spec_helper.rb — so the demo never enters coverage; RBS/Steep
|
|
21
|
+
# only check sig/ against lib/, never demo/.)
|
|
22
|
+
- "demo/**/*"
|
|
23
|
+
|
|
24
|
+
# Every .rb file MUST start with the frozen-string-literal magic comment.
|
|
25
|
+
Style/FrozenStringLiteralComment:
|
|
26
|
+
Enabled: true
|
|
27
|
+
EnforcedStyle: always
|
|
28
|
+
|
|
29
|
+
Style/StringLiterals:
|
|
30
|
+
Enabled: true
|
|
31
|
+
EnforcedStyle: double_quotes
|
|
32
|
+
|
|
33
|
+
Style/StringLiteralsInInterpolation:
|
|
34
|
+
Enabled: true
|
|
35
|
+
EnforcedStyle: double_quotes
|
|
36
|
+
|
|
37
|
+
Layout/LineLength:
|
|
38
|
+
Max: 120
|
|
39
|
+
|
|
40
|
+
# RBS inline type annotations (`#: Type`) are deliberately written without a
|
|
41
|
+
# space after `#` — that exact form is what Steep parses. The hardened HTTP
|
|
42
|
+
# port (Story 1.5) uses them to type empty-literal seams (`{} #: Hash[...]`)
|
|
43
|
+
# at the single Net::HTTP site. Allow the RBS-inline form for this cop.
|
|
44
|
+
Layout/LeadingCommentSpace:
|
|
45
|
+
AllowRBSInlineAnnotation: true
|
|
46
|
+
|
|
47
|
+
# Specs use long descriptive blocks; the default 25-line block limit is noise
|
|
48
|
+
# for RSpec example groups and the SimpleCov config block in spec_helper.
|
|
49
|
+
Metrics/BlockLength:
|
|
50
|
+
Exclude:
|
|
51
|
+
- "spec/**/*"
|
|
52
|
+
- "*.gemspec"
|
|
53
|
+
- "Rakefile"
|
|
54
|
+
- "spec/spec_helper.rb"
|
|
55
|
+
|
|
56
|
+
Metrics/MethodLength:
|
|
57
|
+
Max: 20
|
|
58
|
+
|
|
59
|
+
# The Client and DataManager are the two files Story 2.7's config-caching /
|
|
60
|
+
# background-refresh / lazy-TTL feature extends — the architecture mandates "no
|
|
61
|
+
# new lib files" for that story (refresh logic in the Client, snapshot + TTL
|
|
62
|
+
# bookkeeping in the DataManager), so the cohesive feature surface lands inside
|
|
63
|
+
# these two existing classes rather than being split across new files. The
|
|
64
|
+
# resulting length is a deliberate architecture decision, not unmanaged growth.
|
|
65
|
+
# RuleManager (Story 2.10) is added on the same principle: the OR->AND->OR_WHEN
|
|
66
|
+
# walk levels + leaf evaluation + validation are one cohesive unit mirroring the
|
|
67
|
+
# JS rule-manager.ts class — length is the walk surface, not unmanaged growth.
|
|
68
|
+
# Context (Story 2.11) joins for the same reason: the per-visitor public API
|
|
69
|
+
# (create/attributes/lookups from 2.8 + run_experience(s) decision entry points)
|
|
70
|
+
# is one cohesive surface mirroring the JS context.ts class — the orchestration
|
|
71
|
+
# itself lives in DataManager/ExperienceManager, Context just wires the entry
|
|
72
|
+
# points, fires the lifecycle event, and holds the never-crash boundary.
|
|
73
|
+
Metrics/ClassLength:
|
|
74
|
+
Exclude:
|
|
75
|
+
- "lib/convert_sdk/client.rb"
|
|
76
|
+
- "lib/convert_sdk/data_manager.rb"
|
|
77
|
+
- "lib/convert_sdk/rule_manager.rb"
|
|
78
|
+
- "lib/convert_sdk/context.rb"
|
|
79
|
+
# FeatureManager (Story 3.1) ports feature-manager.ts (the run_feature /
|
|
80
|
+
# run_features resolution surface) PLUS the castType truth table
|
|
81
|
+
# (types-utils.ts). The two cohesive JS modules it mirrors are the unit; the
|
|
82
|
+
# length reflects the ported reference surface, not unmanaged growth (same
|
|
83
|
+
# rationale as the DataManager/RuleManager feature surfaces above).
|
|
84
|
+
- "lib/convert_sdk/feature_manager.rb"
|
|
85
|
+
|
|
86
|
+
# The Client (Story 2.5) is wired by explicit constructor injection of its six
|
|
87
|
+
# collaborator managers (config, log, http, data-store, event, data) — the
|
|
88
|
+
# architecture mandates constructor injection throughout with no globals or
|
|
89
|
+
# singletons, so each dependency is a named keyword argument rather than being
|
|
90
|
+
# hidden inside an options bag. The Context (Story 2.8) follows the same
|
|
91
|
+
# injection contract and adds the two per-visitor values it binds (visitor_id +
|
|
92
|
+
# attributes) on top of its five injected managers. Story 2.11 grows the
|
|
93
|
+
# injection surface again: the DataManager gains its decision-flow collaborators
|
|
94
|
+
# (bucketing_manager, rule_manager, account/project resolvers) and the Context
|
|
95
|
+
# gains the experience_manager. Because every one of these is a NAMED keyword
|
|
96
|
+
# argument (self-documenting at the call site — the readability problem the cop
|
|
97
|
+
# guards against applies to positional lists, not keyword injection), keyword
|
|
98
|
+
# args are excluded from the count; the positional limit stays at 7.
|
|
99
|
+
Metrics/ParameterLists:
|
|
100
|
+
Max: 7
|
|
101
|
+
CountKeywordArgs: false
|
|
102
|
+
|
|
103
|
+
# get_visitor_data / get_config_entity (Story 2.8) are FROZEN public-API parity
|
|
104
|
+
# names mirroring the JS SDK surface (getVisitorData/getConfigEntity,
|
|
105
|
+
# context.ts:495,549). The get_ prefix is mandated by the cross-SDK naming
|
|
106
|
+
# contract, not an accessor-naming slip, so the cop is disabled for the Context.
|
|
107
|
+
Naming/AccessorMethodName:
|
|
108
|
+
Exclude:
|
|
109
|
+
- "lib/convert_sdk/context.rb"
|
|
110
|
+
|
|
111
|
+
# MurmurHash3 uses the canonical algorithm variable names from Austin Appleby's
|
|
112
|
+
# reference implementation (h1 = hash state, k1 = block key). Renaming them would
|
|
113
|
+
# break the line-by-line auditability of the vendored implementation against the
|
|
114
|
+
# reference, so they are explicitly allowed as method parameter names.
|
|
115
|
+
Naming/MethodParameterName:
|
|
116
|
+
AllowedNames:
|
|
117
|
+
- h1
|
|
118
|
+
- k1
|
|
119
|
+
|
|
120
|
+
# The 13 rule operators (Story 2.10) port the JS SDK comparisons.ts signatures
|
|
121
|
+
# verbatim: each takes a trailing positional `negation` boolean so RuleManager
|
|
122
|
+
# can dispatch uniformly across all operators (processor[matching](value, rule
|
|
123
|
+
# value, negation), mirroring rule-manager.ts:299-319). Keyword-izing it would
|
|
124
|
+
# break the uniform positional dispatch and diverge from the JS contract.
|
|
125
|
+
Style/OptionalBooleanParameter:
|
|
126
|
+
Exclude:
|
|
127
|
+
- "lib/convert_sdk/comparisons.rb"
|
|
128
|
+
|
|
129
|
+
# is_in / does_not_exist are the snake_case mirrors of the FROZEN wire operator
|
|
130
|
+
# names isIn / doesNotExist (the two-worlds rule for operator dispatch — the
|
|
131
|
+
# dispatch-map keys stay byte-identical to the rule JSON match_type, the Ruby
|
|
132
|
+
# methods underneath are snake_case). is_rule_matched (RuleManager, Story 2.10)
|
|
133
|
+
# is the snake_case mirror of the FROZEN cross-SDK public API name isRuleMatched
|
|
134
|
+
# (rule-manager.ts:99). The predicate-prefix cop would force in?/not_exist?/
|
|
135
|
+
# rule_matched?, breaking those naming contracts.
|
|
136
|
+
Naming/PredicatePrefix:
|
|
137
|
+
Exclude:
|
|
138
|
+
- "lib/convert_sdk/comparisons.rb"
|
|
139
|
+
- "lib/convert_sdk/rule_manager.rb"
|
|
140
|
+
|
|
141
|
+
# custom_dimension_1 .. custom_dimension_5 (Story 4.3) are the snake_case mirrors
|
|
142
|
+
# of the FROZEN wire goal-data keys customDimension1 .. customDimension5
|
|
143
|
+
# (the two-worlds rule — the public track_conversion goal_data: kwarg surface is
|
|
144
|
+
# snake_case symbols, the wire identifiers stay camelCase via GoalDataKey). The
|
|
145
|
+
# trailing digit is part of the cross-SDK key name, not a variable-numbering
|
|
146
|
+
# slip; normalcasing them to custom_dimension1 would break the public contract
|
|
147
|
+
# and the snake↔camel mapping. Allowed as identifiers for this cop.
|
|
148
|
+
Naming/VariableNumber:
|
|
149
|
+
AllowedIdentifiers:
|
|
150
|
+
- custom_dimension_1
|
|
151
|
+
- custom_dimension_2
|
|
152
|
+
- custom_dimension_3
|
|
153
|
+
- custom_dimension_4
|
|
154
|
+
- custom_dimension_5
|
|
155
|
+
|
|
156
|
+
# cast_boolean is one branch of the symmetric cast_* helper family (cast_boolean
|
|
157
|
+
# / cast_integer / cast_float / cast_json) that mirrors the JS castType switch
|
|
158
|
+
# (types-utils.ts:13-54) one-to-one. Only the boolean branch happens to return a
|
|
159
|
+
# bool; renaming it cast_boolean? would break the deliberate one-name-per-cast-
|
|
160
|
+
# branch symmetry (the others return Integer/Float/parsed-JSON, not predicates).
|
|
161
|
+
Naming/PredicateMethod:
|
|
162
|
+
Exclude:
|
|
163
|
+
- "lib/convert_sdk/feature_manager.rb"
|
|
164
|
+
|
|
165
|
+
# Comparisons is a cohesive 13-operator + numeric-helper module; the operator
|
|
166
|
+
# set is the unit, not unmanaged growth. (Mirrors the ClassLength rationale for
|
|
167
|
+
# the Client/DataManager feature surfaces.)
|
|
168
|
+
Metrics/ModuleLength:
|
|
169
|
+
Exclude:
|
|
170
|
+
- "lib/convert_sdk/comparisons.rb"
|
|
171
|
+
|
|
172
|
+
# The frozen value objects (BucketedVariation, BucketedFeature) are declared as
|
|
173
|
+
# explicit `class X < Struct.new(...)` subclasses. This is the form the
|
|
174
|
+
# architecture mandates ("frozen Struct subclass") AND the only form Steep can
|
|
175
|
+
# statically resolve: methods defined in a `Struct.new(...) do ... end` block are
|
|
176
|
+
# mis-attributed by Steep to the enclosing module (Ruby::UndeclaredMethodDefinition),
|
|
177
|
+
# whereas the subclass form attributes #error?/#initialize to the class as the RBS
|
|
178
|
+
# declares. The Style/StructInheritance preference is overridden here for that reason.
|
|
179
|
+
Style/StructInheritance:
|
|
180
|
+
Exclude:
|
|
181
|
+
- "lib/convert_sdk/bucketed_variation.rb"
|
|
182
|
+
- "lib/convert_sdk/bucketed_feature.rb"
|
|
183
|
+
- "lib/convert_sdk/http_client.rb"
|
|
184
|
+
|
|
185
|
+
# The steep/ directory holds the build-time config-contract probe (qs-03). The
|
|
186
|
+
# probe re-opens `module ConvertSdk` as a CI-only utility file (OUTSIDE lib/,
|
|
187
|
+
# never required at runtime). The module is already documented in lib/
|
|
188
|
+
# convert_sdk.rb; the probe re-opening does not warrant a second top-level doc.
|
|
189
|
+
Style/Documentation:
|
|
190
|
+
Exclude:
|
|
191
|
+
- "steep/**/*"
|
data/.yardopts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
--output-dir doc
|
|
2
|
+
--markup markdown
|
|
3
|
+
--protected
|
|
4
|
+
--no-private
|
|
5
|
+
--readme README.md
|
|
6
|
+
--title "Convert Ruby SDK"
|
|
7
|
+
lib/**/*.rb
|
|
8
|
+
-
|
|
9
|
+
CONTRIBUTING.md
|
|
10
|
+
docs/quickstart-rails.md
|
|
11
|
+
docs/quickstart-sidekiq.md
|
|
12
|
+
docs/quickstart-lambda.md
|
|
13
|
+
docs/quickstart-cli.md
|
|
14
|
+
docs/troubleshooting.md
|
|
15
|
+
docs/migrate-from-kameleoon.md
|
|
16
|
+
LICENSE
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Contributing to the Convert Ruby SDK
|
|
2
|
+
|
|
3
|
+
Thanks for contributing. This guide covers the dev setup, the conventions CI
|
|
4
|
+
enforces, the test layout, and how releases happen.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
Requires Ruby ≥ 3.1 (the gem supports CRuby 3.1–3.4 and JRuby).
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
bundle install # install dev/test dependencies
|
|
12
|
+
bundle exec rake # the default task: RSpec + RuboCop
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Type checking (RBS + Steep) lives in a separate Gemfile group so the JRuby
|
|
16
|
+
matrix leg can skip the C-extension build:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
bundle exec rbs -r net-http -r uri -r json -I sig validate # validate RBS signatures
|
|
20
|
+
bundle exec steep check # static type check
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The gem has **zero runtime dependencies** by design (stdlib only). Adding a
|
|
24
|
+
runtime dependency is an architecture change, not a routine PR — discuss it
|
|
25
|
+
first. All dev/test gems live in the `Gemfile`.
|
|
26
|
+
|
|
27
|
+
## Conventions CI enforces
|
|
28
|
+
|
|
29
|
+
The **Quality Checks** workflow (`.github/workflows/qa.yml`) gates every PR:
|
|
30
|
+
|
|
31
|
+
- **RuboCop** — `bundle exec rubocop` must pass (config in `.rubocop.yml`).
|
|
32
|
+
Notably: frozen-string-literal magic comment on every file, double-quoted
|
|
33
|
+
strings, 120-char lines.
|
|
34
|
+
- **RBS + Steep** — signatures in `sig/` must validate and type-check against
|
|
35
|
+
`lib/`.
|
|
36
|
+
- **RSpec** — the full suite runs on the CRuby 3.1–3.4 + JRuby matrix, with
|
|
37
|
+
coverage gates (see below).
|
|
38
|
+
- **Cross-SDK parity** — the 75-vector MurmurHash3 parity suite must pass 100%
|
|
39
|
+
(a byte-identical-bucketing release gate).
|
|
40
|
+
- **Full-chain + gem-smoke + demo fork-smoke** — end-to-end release gates.
|
|
41
|
+
|
|
42
|
+
### Conventional Commits + squash merge
|
|
43
|
+
|
|
44
|
+
This repo is **squash-merge only**, so the **PR title** becomes the squash commit
|
|
45
|
+
subject — and CI validates it as a [Conventional Commit](https://www.conventionalcommits.org/):
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
<type>(<scope>)?!?: <description>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Allowed types: `feat`, `fix`, `docs`, `test`, `refactor`, `perf`, `build`, `ci`,
|
|
52
|
+
`chore`. A `feat` or `fix` (or a `!` breaking-change marker) drives the next
|
|
53
|
+
release version (see below), so title accuracy matters.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
feat(context): add run_features bulk evaluation
|
|
59
|
+
fix(api): retain queue on a failed flush POST
|
|
60
|
+
docs(readme): document the sentinel return contract
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Coverage gates
|
|
64
|
+
|
|
65
|
+
Coverage is single-sourced in `spec/spec_helper.rb` (do not declare thresholds
|
|
66
|
+
elsewhere). The global floor is 85% line coverage; the critical algorithm units
|
|
67
|
+
(Hashing, Bucketing, Rules) require ≥ 95% line **and** branch coverage. Coverage
|
|
68
|
+
runs on CRuby only — the JRuby leg runs the suite without the gate.
|
|
69
|
+
|
|
70
|
+
## Test layout
|
|
71
|
+
|
|
72
|
+
| Directory | What lives there |
|
|
73
|
+
|-----------|------------------|
|
|
74
|
+
| `spec/unit/` | Per-class unit specs (mock collaborators, isolated logic). |
|
|
75
|
+
| `spec/integration/` | End-to-end specs: `full_chain_spec.rb` (the release-blocking create→decide→track→flush loop), `runtime_recipes_spec.rb` (the runtime-lifecycle recipes the quickstarts are transcribed from), `fork_safety_spec.rb`, `factory_wiring_spec.rb`. |
|
|
76
|
+
| `spec/cross_sdk/` | The cross-SDK MurmurHash3 parity vectors (the byte-identical bucketing proof). |
|
|
77
|
+
| `spec/docs/` | Docs-snippet smoke specs — run the README/quickstart code samples against the real gem so documentation never drifts. |
|
|
78
|
+
| `spec/staging/` | The live-platform suite (runs on schedule/dispatch only, never in PR CI). |
|
|
79
|
+
| `spec/support/` | Shared helpers (e.g. `runtime_recipe_helpers.rb`, which co-locates the recipe wiring snippets the quickstarts ship). |
|
|
80
|
+
| `spec/fixtures/` | Vendored config fixtures (`test-config.json`). |
|
|
81
|
+
| `demo/rails/` | The living Rails example app + fork-safety smoke (an app, not the lib — excluded from gem/RuboCop/coverage gates). |
|
|
82
|
+
|
|
83
|
+
When adding a documented code sample, **copy it from a working spec or the demo**
|
|
84
|
+
and add it to the `spec/docs/` smoke spec — never ship doc-only code that can
|
|
85
|
+
drift from the real gem.
|
|
86
|
+
|
|
87
|
+
## Testing & verification
|
|
88
|
+
|
|
89
|
+
Run the default gate before opening a PR:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
bundle exec rake # RSpec + RuboCop — the same pair CI enforces
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
See the **[Testing wiki page](https://github.com/convertcom/ruby-sdk/wiki/Testing)**
|
|
96
|
+
for the full command reference: individual RSpec invocations, the RBS/Steep type-check
|
|
97
|
+
commands, the cross-SDK parity gate, the full-chain release gate, and the Puma-cluster
|
|
98
|
+
fork smoke. Do not change the coverage configuration — it is single-sourced in
|
|
99
|
+
`spec/spec_helper.rb` (see **Coverage gates** above).
|
|
100
|
+
|
|
101
|
+
## API documentation (YARD)
|
|
102
|
+
|
|
103
|
+
All public classes and methods are YARD-documented; `@api private` internals are
|
|
104
|
+
excluded from the published docs (`.yardopts` sets `--no-private`). Build the
|
|
105
|
+
docs locally with:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
bundle exec yard doc # output to doc/
|
|
109
|
+
bundle exec yard stats --list-undoc # audit the public surface
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The published docs deploy to [GitHub Pages](https://convertcom.github.io/ruby-sdk)
|
|
113
|
+
on every push to `main` (`.github/workflows/pages.yml`).
|
|
114
|
+
|
|
115
|
+
## Releases
|
|
116
|
+
|
|
117
|
+
Releases are **fully automated** and run only from `main` — there is **no**
|
|
118
|
+
`rake release` task (it is deliberately absent from the `Rakefile`). The release
|
|
119
|
+
pipeline (semantic-release in `.github/workflows/release.yml`) reads the merged
|
|
120
|
+
Conventional Commit subjects since the last release, computes the next semantic
|
|
121
|
+
version, tags it, and publishes the gem to RubyGems via OIDC Trusted Publishing.
|
|
122
|
+
The changelog is **GitHub Releases** (there is no `CHANGELOG.md` in the repo).
|
|
123
|
+
|
|
124
|
+
You never bump the version or publish manually — write an accurate PR title and
|
|
125
|
+
the pipeline does the rest on merge.
|
|
126
|
+
|
|
127
|
+
See **[RELEASE.md](RELEASE.md)** for the full release runbook: the release chain,
|
|
128
|
+
the Conventional-Commit → version map, one-time repo-admin setup (RubyGems
|
|
129
|
+
trusted-publisher registration, GitHub Pages, branch-protection required checks),
|
|
130
|
+
dry-runs, the first `v1.0.0` release, the fork-PR safeguard, rollback (`gem
|
|
131
|
+
yank`), and troubleshooting.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|