Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement Requirement 1.1.2 #78

Merged
merged 13 commits into from
Feb 22, 2024
Merged
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,30 @@ object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON

For complete documentation, visit: https://openfeature.dev/docs/category/concepts

### Providers

Providers are the abstraction layer between OpenFeature and different flag management systems.

The `NoOpProvider` is an example of a minimalist provider. For complete documentation on the Provider interface, visit: https://openfeature.dev/specification/sections/providers.

In addition to the `fetch_*` methods, providers can optionally implement lifecycle methods that are invoked when the underlying provider is switched out. For example:

```ruby
class MyProvider
def init
# Perform any initialization steps with flag management system here
# Return value is ignored
end

def shutdown
# Perform any shutdown/reclamation steps with flag management system here
# Return value is ignored
end
maxveldink marked this conversation as resolved.
Show resolved Hide resolved
end
```

**Note** The OpenFeature spec defines a lifecycle method called `initialize` to be called when a new provider is set. To avoid conflicting with the Ruby `initialize` method, this method should be named `init` when creating a provider.
maxveldink marked this conversation as resolved.
Show resolved Hide resolved

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the OpenFeature project.
Expand Down
4 changes: 2 additions & 2 deletions lib/open_feature/sdk/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ module SDK
#
# client = OpenFeature::SDK::API.instance.build_client(name: 'my-open-feature-client')
class API
include Singleton
include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1
extend Forwardable

def_delegators :configuration, :provider, :hooks, :context
def_delegators :configuration, :provider, :provider=, :hooks, :context

def configuration
@configuration ||= Configuration.new
Expand Down
15 changes: 14 additions & 1 deletion lib/open_feature/sdk/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@ module SDK
class Configuration
extend Forwardable

attr_accessor :context, :provider, :hooks
attr_accessor :context, :hooks
attr_reader :provider

def_delegator :@provider, :metadata

def initialize
@hooks = []
end

# When switching providers, there are a few lifecycle methods that need to be taken care of.
# 1. If a provider is already set, we need to call `shutdown` on it.
# 2. On the new provider, call `init`.
# 3. Finally, set the internal provider to the new provider
def provider=(provider)
@provider.shutdown if @provider.respond_to?(:shutdown)

provider.init if provider.respond_to?(:init)

@provider = provider
end
end
end
end
20 changes: 0 additions & 20 deletions spec/open_feature/sdk/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,6 @@
RSpec.describe OpenFeature::SDK::API do
subject(:api) { described_class.instance }

context "with Requirement 1.1.2" do
before do
api.configure do |config|
config.provider = OpenFeature::SDK::Provider::NoOpProvider.new
end
end

it do
expect(api).to respond_to(:provider)
end

it do
expect(api.provider).not_to be_nil
end

it do
expect(api.provider).is_a?(OpenFeature::SDK::Provider)
end
end

context "with Requirement 1.1.3" do
before do
api.configure do |config|
Expand Down
31 changes: 31 additions & 0 deletions spec/open_feature/sdk/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe OpenFeature::SDK::Configuration do
subject(:configuration) { described_class.new }

describe "#provider=" do
context "when provider has an init method" do
let(:provider) { TestProvider.new }

it "inits and sets the provider" do
expect(provider).to receive(:init)

configuration.provider = provider

expect(configuration.provider).to be(provider)
end
end

context "when provider does not have an init method" do
it "sets the provider" do
provider = OpenFeature::SDK::Provider::NoOpProvider.new

configuration.provider = provider

expect(configuration.provider).to be(provider)
end
end
end
end
33 changes: 33 additions & 0 deletions spec/specification/flag_evaluation_api_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "spec_helper"
require_relative "../support/test_provider"

RSpec.describe "Flag Evaluation API" do
context "1.1 - API Initialization and Configuration" do
Expand All @@ -9,5 +10,37 @@
expect(OpenFeature::SDK::API).to include(Singleton)
end
end

context "Requirement 1.1.2.1" do
specify "the API must define a provider mutator" do
provider = OpenFeature::SDK::Provider::NoOpProvider.new

OpenFeature::SDK.provider = provider

expect(OpenFeature::SDK.provider).to be(provider)
end
end

context "Requirement 1.1.2.2" do
specify "the provider mutator must invoke an initialize function on the provider" do
provider = TestProvider.new
expect(provider).to receive(:init)

OpenFeature::SDK.provider = provider
end
end

context "Requirement 1.1.2.3" do
specify "the provider mutator must invoke a shutdown function on previously registered provider" do
previous_provider = TestProvider.new
new_provider = TestProvider.new

expect(previous_provider).to receive(:shutdown)
expect(new_provider).not_to receive(:shutdown)

OpenFeature::SDK.provider = previous_provider
OpenFeature::SDK.provider = new_provider
end
maxveldink marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
9 changes: 9 additions & 0 deletions spec/support/test_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class TestProvider
def init
end

def shutdown
end
end