Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

openfeature-sdk-sorbet

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openfeature-sdk-sorbet

  • 0.5.0
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Sorbet-aware OpenFeature Ruby Implementation

NOTE This implementation has been deprecated in favor of the official Ruby SDK. We recommend switching to that. In the future, we hope to add RBS and Tapioca DSL generation support to the official library.

OpenFeature is an open standard for vendor-agnostic feature flagging. Sorbet is a type-checker for Ruby, built by Stripe. Sorbet provides powerful runtime utilities to achieve things traditionally not possible with Ruby, such as interfaces, immutable structures and enums. This makes it a very good option when defining specifications.

If an organization is not already using Sorbet, you probably don't want to introduce a dependency on sorbet-runtime, which this gem does. As such, this will always be a distinct implementation, separate from the official Ruby SDK.

Current OpenFeature specification target version: 0.5.2 Current supported Ruby versions: 3.1.X, 3.2.X Support for Evaluation Context and Hooks is not complete.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add openfeature-sdk-sorbet

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install openfeature-sdk-sorbet

Usage

require "open_feature_sorbet"

# Configure global API properties

OpenFeature.set_provider(OpenFeatureSorbet::NoOpProvider.new)
OpenFeature.set_evaluation_context(OpenFeatureSorbet::EvaluationContext.new(fields: { "globally" => "available" }))
OpenFeature.add_hooks([OpenFeatureSorbet::Hook.new]) # experimental, not fully supported

client = OpenFeature.create_client(evaluation_context: OpenFeatureSorbet::EvaluationContext.new(fields: { "client" => "available" }))

# Fetch boolean value
# Also methods available for String, Number, Integer, Float and Structure (Hash)
bool_value = client.fetch_boolean_value(flag_key: "my_toggle", default_value: false) # => (true or false)

# Sorbet sprinkles in type safety
bool_value = client.fetch_boolean_value(flag_key: "my_toggle", default_value: "bad!") # => raises TypeError from Sorbet, invalid default value

# Additional evaluation context can be provided during invocation
number_value = client.fetch_number_value(flag_key: "my_toggle", default_value: 1, context: OpenFeatureSorbet::EvaluationContext.new(fields: { "only_this_call_site" => 10 })) # => merges client and global context

# Fetch structure evaluation details
structure_evaluation_details = client.fetch_structure_details(flag_key: "my_structure", default_value: { "a" => "fallback" }) # => EvaluationDetails(value: Hash, flag_key: "my_structure", ...)

Note on Structure

The OpenFeature specification defines Structure as a potential return type. This is somewhat ambiguous in Ruby, further complicated by T::Struct that we get from Sorbet. For now, the type I've elected here is T.any(T::Array[T.untyped], T::Hash[T.untyped, T.untyped] (loosely, either an Array of untyped members or a Hash with untyped keys and untyped values) for flexibility but with a little more structure than a YML or JSON parsable string. This decision might change in the future upon further interpretation or new versions of the specification.

Evaluation Context

We support global evaluation context (set on the OpenFeature module), client evaluation context (set on client instances or during client initialization) and invocation evaluation context (passed in during flag evaluation). In compliance with the specification, the invocation context merges into the client context which merges into the global context. Fields in invocation context take precedence over fields in the client context which take precedence over fields in the global context.

Provider Abstract Class

By default, this implementation sets the provider to the OpenFeatureSorbet::NoOpProvider which always returns the default value. It's up to the individual teams to define their own providers based on their flag source (in the future, I'll release open-source providers based on various, common vendors).

This gem also provides OpenFeatureSorbet::MultipleSourceProvider to allow fetching flags from multiple sources. This is especially useful if your existing application has flags spread across bespoke and vendor solutions and you want to unify the evaluation sites. It can be instantiated and configured like so:

provider = OpenFeatureSorbet::MultipleSourceProvider.new(
  providers: [
    CustomProvider.new,
    OpenFeatureSorbet::NoOpProvider.new
  ]
)

OpenFeature.set_provider(provider)
Implementing Custom Providers

Thanks to Sorbet abstract classes, it's fairly straightforward to implement a new provider. Here is an example for a JSON-based flag format on disk:

class JsonFileFlagProvider < OpenFeatureSorbet::Provider
  extend T::Sig

  sig { void }
  def initialize
    super(OpenFeatureSorbet::ProviderStatus::NotReady)
  end

  def init(context)
    @file = File.open(context.file || "flags.json")
    @status = OpenFeatureSorbet::ProviderStatus::Ready
  end

  sig { overridable.void }
  def shutdown
    @file.close
  end

  sig { override.returns(OpenFeatureSorbet::ProviderMetadata) }
  def metadata
    OpenFeatureSorbet::ProviderMetadata.new(name: "Json File Flag Provider")
  end

  sig { override.returns(T::Array[Hook]) }
  def hooks
    []
  end

  sig do
    override
      .params(
        flag_key: String,
        default_value: T::Boolean,
        context: T.nilable(EvaluationContext)
      )
      .returns(OpenFeatureSorbet::ResolutionDetails[T::Boolean])
  end
  def resolve_boolean_value(flag_key:, default_value:, context: nil)
    file_input = JSON.parse(File.read("flags.rb"))
    value = file_input.fetch("flag_key", default_value)

    OpenFeatureSorbet::ResolutionDetails.new(
      value: value,
      # ... other optional fields
    )
  end

  # ... other resolver methods
end

By inheriting from the OpenFeatureSorbet::Provider class, Sorbet will indicate what methods it's expecting and what their type signatures should be.

A note on initialize versus init

The Ruby initialize method is the best place to do any direct construction logic for an object, such as setting configuration values. init is called by OpenFeature when setting a provider and is the best place to make any HTTP requests, establish persistent connections, or any other connection logic that could potentially fail. By the end of this method, @status must be set to either Ready or Error.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake to run Rubocop and the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/maxveldink/openfeature-sdk-sorbet. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in this project's codebase, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

Sponsorships

I love creating in the open. If you find this or any other maxveld.ink content useful, please consider sponsoring me on GitHub.

FAQs

Package last updated on 17 Apr 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc