Skip to content

Commit 67b9796

Browse files
josecolellaclaude
andauthored
chore: add frozen_string_literal to all Ruby files (#216)
Signed-off-by: Jose Colella <jose.colella@gusto.com> Signed-off-by: Jose Miguel Colella <josecolella@yahoo.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ee19450 commit 67b9796

14 files changed

Lines changed: 52 additions & 47 deletions

CLAUDE.md

Lines changed: 26 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,50 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5-
## Project Overview
5+
## Overview
66

7-
OpenFeature Ruby SDK — implements the [OpenFeature specification](https://openfeature.dev) (v0.8.0) for vendor-agnostic feature flag management. Published as the `openfeature-sdk` gem. Pure Ruby, no runtime dependencies. Requires Ruby >= 3.1.
7+
This is the official OpenFeature SDK for Ruby — an implementation of the [OpenFeature specification](https://openfeature.dev) providing a vendor-agnostic API for feature flag evaluation. Published as the `openfeature-sdk` gem. Requires Ruby >= 3.1.
88

99
## Commands
1010

11-
```bash
12-
# Install dependencies
13-
bundle install
11+
- **Run all tests:** `bundle exec rspec`
12+
- **Run a single test file:** `bundle exec rspec spec/open_feature/sdk/client_spec.rb`
13+
- **Run a specific test by line:** `bundle exec rspec spec/open_feature/sdk/client_spec.rb:43`
14+
- **Lint:** `bundle exec standardrb`
15+
- **Lint with autofix:** `bundle exec standardrb --fix`
16+
- **Default rake (tests + lint):** `bundle exec rake`
1417

15-
# Run full test suite + linting (default rake task)
16-
bundle exec rake
18+
Note: Linting uses [Standard Ruby](https://github.com/standardrb/standard) (configured via the `standard` gem), which enforces double-quoted strings and its own opinionated style. There is no `.rubocop.yml` — Standard manages RuboCop configuration internally. Do not use `bundle exec rubocop` directly as a stale RuboCop server may apply different rules; always use `bundle exec standardrb`.
1719

18-
# Run tests only
19-
bundle exec rspec
20-
21-
# Run a single test file
22-
bundle exec rspec spec/open_feature/sdk/client_spec.rb
23-
24-
# Run a specific test by line number
25-
bundle exec rspec spec/open_feature/sdk/client_spec.rb:40
26-
27-
# Lint (StandardRB with performance plugin)
28-
bundle exec rake standard
20+
## Architecture
2921

30-
# Auto-fix lint issues
31-
bundle exec standardrb --fix
32-
```
22+
### Entry point and API singleton
3323

34-
## Architecture
24+
`OpenFeature::SDK` (in `lib/open_feature/sdk.rb`) delegates all method calls to `API.instance` via `method_missing`. `API` is a Singleton that holds a `Configuration` object and builds `Client` instances.
3525

36-
Entry point: `require 'open_feature/sdk'` — the `OpenFeature::SDK` module delegates all method calls to `API.instance` (Singleton) via `method_missing`.
26+
### Provider duck type
3727

38-
### Core Components
28+
Providers are not subclasses — they follow a duck type interface. Any object implementing `fetch_boolean_value`, `fetch_string_value`, `fetch_number_value`, `fetch_integer_value`, `fetch_float_value`, and `fetch_object_value` (all accepting `flag_key:`, `default_value:`, `evaluation_context:`) works as a provider. Each method must return a `ResolutionDetails` struct. Two built-in providers exist: `NoOpProvider` (default) and `InMemoryProvider` (for testing). Providers may optionally implement `init(evaluation_context)`, `shutdown`, and `metadata`.
3929

40-
- **API** (`lib/open_feature/sdk/api.rb`) — Singleton orchestrator. Manages providers (global or domain-scoped), builds clients, stores API-level evaluation context, and registers event handlers.
41-
- **Configuration** (`lib/open_feature/sdk/configuration.rb`) — Thread-safe provider storage. Handles provider lifecycle (init/shutdown), domain-scoped provider mapping, and event dispatching. Uses Mutex for all shared state.
42-
- **Client** (`lib/open_feature/sdk/client.rb`) — Flag evaluation interface. Uses `class_eval` metaprogramming to generate 12 typed methods: `fetch_{boolean,string,number,integer,float,object}_value` and `fetch_*_details` variants. Merges evaluation contexts (API + client + invocation).
43-
- **EvaluationContext** (`lib/open_feature/sdk/evaluation_context.rb`) — Key-value targeting data with a special `targeting_key`. Supports merging with precedence: invocation > client > API.
30+
### Client dynamic method generation
4431

45-
### Provider System
32+
`Client` uses `class_eval` to metaprogram `fetch_<type>_value` and `fetch_<type>_details` methods from `RESULT_TYPE` and `SUFFIXES` arrays. This generates 12 public methods (6 types × 2 suffixes).
4633

47-
- **Provider interface** — Must implement 6 `fetch_*_value` methods, optional `init(evaluation_context)` and `shutdown`. Returns `ResolutionDetails`.
48-
- **EventEmitter** (`lib/open_feature/sdk/provider/event_emitter.rb`) — Mixin that providers include to emit lifecycle events.
49-
- **Built-in providers**: `NoOpProvider` (default), `InMemoryProvider` (testing/examples).
50-
- **Provider states**: `NOT_READY → READY`, with `ERROR`, `FATAL`, `STALE` transitions. Tracked per-instance via `ProviderStateRegistry` using `object_id`.
51-
- **Initialization modes**: `set_provider` (async, background thread) or `set_provider_and_wait` (sync, raises `ProviderInitializationError` on failure).
34+
### Evaluation context merging
5235

53-
### Event System
36+
`EvaluationContextBuilder` merges three layers of context with this precedence: invocation > client > API (global). Context is a hash-like object with a special `targeting_key` field.
5437

55-
- **EventDispatcher** (`lib/open_feature/sdk/event_dispatcher.rb`) — Thread-safe pub-sub. Handlers called outside mutex to prevent deadlocks. Supports API-level and client-level handlers.
56-
- **ProviderEvent** constants: `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_STALE`, `PROVIDER_CONFIGURATION_CHANGED`.
38+
### Provider eventing
5739

58-
## Test Structure
40+
`Configuration` manages provider lifecycle events (READY, ERROR, STALE, CONFIGURATION_CHANGED). Providers can emit spontaneous events by including `Provider::EventEmitter`. Event handlers can be registered at API level (global) or client level (domain-scoped). `ProviderStateRegistry` tracks provider states; `EventDispatcher` manages handler registration and invocation.
5941

60-
Tests in `spec/` split into two categories:
61-
- `spec/specification/` — OpenFeature spec compliance tests, organized by requirement number (e.g., "Requirement 1.1.1")
62-
- `spec/open_feature/` — Unit tests for individual components
42+
### Domain-based provider binding
6343

64-
Uses Timecop for time-sensitive tests (auto-reset after each test), SimpleCov for coverage.
44+
Providers can be registered for specific domains. `Configuration#provider(domain:)` resolves domain-specific providers, falling back to the default (nil-domain) provider. Clients are built with an optional `domain:` that binds them to a specific provider.
6545

6646
## Conventions
6747

68-
- **Linter**: StandardRB (Ruby Standard Style) with `standard-performance` plugin, targeting Ruby 3.1
69-
- **Commits**: Conventional Commits required for PR titles (enforced by CI)
70-
- **Releases**: Automated via release-please; changelog auto-generated
71-
- **Threading**: All shared mutable state must be Mutex-protected. Provider storage uses immutable reassignment (`@providers = @providers.dup.merge(...)`)
72-
- **Structs for DTOs**: `EvaluationDetails`, `ResolutionDetails`, `ClientMetadata`, `ProviderMetadata` are `Struct`-based
48+
- All `.rb` files must have `# frozen_string_literal: true` as the first line.
49+
- Tests live under `spec/` and mirror the `lib/` structure. `spec/specification/` contains tests mapped to OpenFeature spec requirements.
50+
- Always sign git commits using the `-S` flag.
51+
- Always include DCO sign-off in commits using the `-s` flag (i.e., `git commit -s -S`). This adds a `Signed-off-by` trailer required by the project's CI.

lib/open_feature/sdk/client_metadata.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
ClientMetadata = Struct.new(:domain, keyword_init: true)

lib/open_feature/sdk/evaluation_context.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
class EvaluationContext

lib/open_feature/sdk/evaluation_context_builder.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
# Used to combine evaluation contexts from different sources

lib/open_feature/sdk/evaluation_details.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
EvaluationDetails = Struct.new(:flag_key, :resolution_details, keyword_init: true) do

lib/open_feature/sdk/provider.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require_relative "provider/error_code"
24
require_relative "provider/reason"
35
require_relative "provider/resolution_details"

lib/open_feature/sdk/provider/error_code.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
module Provider

lib/open_feature/sdk/provider/in_memory_provider.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
module Provider

lib/open_feature/sdk/provider/provider_metadata.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
module Provider

lib/open_feature/sdk/provider/reason.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module OpenFeature
24
module SDK
35
module Provider

0 commit comments

Comments
 (0)