Skip to content

Commit

Permalink
feat: Implement Requirement 1.1.2 (#78)
Browse files Browse the repository at this point in the history
Signed-off-by: Max VelDink <[email protected]>
  • Loading branch information
maxveldink authored Feb 22, 2024
1 parent 2d89570 commit 8cea7d0
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 23 deletions.
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
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.

## 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
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

0 comments on commit 8cea7d0

Please sign in to comment.