Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,54 +26,9 @@ jobs:
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- run: bundle install
- run: bundle exec rake

rubocop:
runs-on: ${{ matrix.os }}
name: Rubocop ${{ matrix.ruby }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
ruby:
- "3.1.2"
- "2.7.6"
- "3.0.4"
env:
BUNDLE_GEMFILE: Gemfile

steps:
- uses: actions/checkout@v3
- name: Set up Ruby ${{ matrix.ruby }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run style checks
run: bundle exec rubocop

test:
runs-on: ${{ matrix.os }}
name: RSpec ${{ matrix.ruby }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
ruby:
- "3.1.2"
- "2.7.6"
- "3.0.4"
env:
BUNDLE_GEMFILE: Gemfile

steps:
- uses: actions/checkout@v3
- name: Set up Ruby ${{ matrix.ruby }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rspec
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
-I lib
--format documentation
--color
--require spec_helper
7 changes: 7 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ Style/StringLiteralsInInterpolation:

Layout/LineLength:
Max: 120
Exclude:
- 'spec/**/*.rb'

Metrics/BlockLength:
Exclude:
- 'spec/**/*.rb'
- 'openfeature-sdk.gemspec'

Gemspec/RequireMFA:
Enabled: false
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ source "https://rubygems.org"

# Specify your gem's dependencies in openfeature-sdk.gemspec
gemspec

gem "concurrent-ruby", require: "concurrent"
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
PATH
remote: .
specs:
openfeature-sdk (0.0.2)
openfeature-sdk (0.0.3)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
concurrent-ruby (1.1.10)
diff-lcs (1.5.0)
json (2.6.2)
parallel (1.22.1)
Expand Down Expand Up @@ -55,6 +56,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
concurrent-ruby
openfeature-sdk!
rake (~> 13.0)
rspec (~> 3.12.0)
Expand Down
5 changes: 3 additions & 2 deletions lib/openfeature/sdk.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# frozen_string_literal: true

require_relative "sdk/version"
require_relative "sdk/api"

module OpenFeature
# TODO: Add documentation
#
module SDK
class Error < StandardError; end
# Your code goes here...
end
end
53 changes: 53 additions & 0 deletions lib/openfeature/sdk/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

require "forwardable"
require "singleton"

require_relative "configuration"
require_relative "client"
require_relative "metadata"
require_relative "provider/no_op_provider"

module OpenFeature
module SDK
# API Initialization and Configuration
#
# Represents the entry point to the API, including configuration of <tt>Provider</tt>,<tt>Hook</tt>,
# and building the <tt>Client</tt>
#
# To use the SDK, you can optionally configure a <tt>Provider</tt>, with <tt>Hook</tt>
#
# OpenFeature::SDK::API.instance.configure do |config|
# config.provider = NoOpProvider.new
# end
#
# If no provider is specified, the <tt>NoOpProvider</tt> is set as the default <tt>Provider</tt>.
# Once the SDK has been configured, a client can be built
#
# client = OpenFeature::SDK::API.instance.build_client(name: 'my-open-feature-client')
class API
include Singleton
extend Forwardable

def_delegator :@configuration, :provider
def_delegator :@configuration, :hooks
def_delegator :@configuration, :context

def configuration
@configuration ||= Configuration.new
end

def configure(&block)
return unless block_given?

block.call(configuration)
end

def build_client(name: nil, version: nil)
client_options = Metadata.new(name: name, version: version).freeze
provider = Provider::NoOpProvider.new if provider.nil?
Client.new(provider, client_options, context)
end
end
end
end
19 changes: 19 additions & 0 deletions lib/openfeature/sdk/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module OpenFeature
module SDK
# TODO: Write documentation
#
class Client
attr_reader :metadata

attr_accessor :hooks

def initialize(provider, client_options, context)
@provider = provider
@client_options = client_options
@context = context
end
end
end
end
25 changes: 25 additions & 0 deletions lib/openfeature/sdk/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

require "concurrent"

require_relative "api"

module OpenFeature
module SDK
# Represents the configuration object for the global API where <tt>Provider</tt>, <tt>Hook</tt>,
# and <tt>Context</tt> are configured.
# This class is not meant to be interacted with directly but instead through the <tt>OpenFeature::SDK.configure</tt>
# method
class Configuration
extend Forwardable

attr_accessor :context, :provider, :hooks

def_delegator :@provider, :metadata

def initialize
@hooks = Concurrent::Array.new([])
end
end
end
end
35 changes: 35 additions & 0 deletions lib/openfeature/sdk/metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module OpenFeature
module SDK
# Metadata structure that defines general metadata relating to a <tt>Provider</tt> or <tt>Client</tt>
#
# Within the Metadata structure, the following attribute readers are available:
#
# * <tt>name</tt> - Defines the name of the structure
#
# * <tt>version</tt> - Allows you to specify version of the Metadata structure
#
# Usage:
#
# metadata = Metadata.new(name: 'name-for-metadata', version: 'v1.1.3')
# metadata.name # 'name-for-metadata'
# metadata.version # version
# metadata_two = Metadata.new(name: 'name-for-metadata')
# metadata_two == metadata # true - equality based on values
class Metadata
attr_reader :name, :version

def initialize(name:, version: nil)
@name = name
@version = version
end

def ==(other)
raise ArgumentError("Expected comparison to be between Metadata object") unless other.is_a?(Metadata)

@name == other.name && @version == other.version
end
end
end
end
62 changes: 62 additions & 0 deletions lib/openfeature/sdk/provider/no_op_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require_relative "../metadata"

# rubocop:disable Lint/UnusedMethodArgument
module OpenFeature
module SDK
module Provider
# Defines the default provider that is set if no provider is specified.
#
# To use <tt>NoOpProvider</tt>, it can be set during the configuration of the SDK
#
# OpenFeature::SDK.configure do |config|
# config.provider = NoOpProvider.new
# end
#
# Within the <tt>NoOpProvider</tt>, the following methods exist
#
# * <tt>fetch_boolean_value</tt> - Retrieve feature flag boolean value
#
# * <tt>fetch_string_value</tt> - Retrieve feature flag string value
#
# * <tt>fetch_number_value</tt> - Retrieve feature flag number value
#
# * <tt>fetch_object_value</tt> - Retrieve feature flag object value
#
class NoOpProvider
REASON_NO_OP = "No-op"
NAME = "No-op Provider"

attr_reader :metadata

def initialize
@metadata = Metadata.new(name: NAME).freeze
end

def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
no_op(default_value)
end

def fetch_string_value(flag_key:, default_value:, evaluation_context: nil)
no_op(default_value)
end

def fetch_number_value(flag_key:, default_value:, evaluation_context: nil)
no_op(default_value)
end

def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
no_op(default_value)
end

private

def no_op(default_value)
Struct.new("ResolutionDetails", :value, :reason)
end
end
end
end
end
# rubocop:enable Lint/UnusedMethodArgument
Loading