Skip to content

Commit d6b0922

Browse files
authored
feat(spec): Add API implementation (#32)
Signed-off-by: Jose Colella <[email protected]>
1 parent 12db272 commit d6b0922

File tree

13 files changed

+304
-49
lines changed

13 files changed

+304
-49
lines changed

.github/workflows/main.yml

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -26,54 +26,9 @@ jobs:
2626
- uses: ruby/setup-ruby@v1
2727
with:
2828
ruby-version: ${{ matrix.ruby }}
29-
bundler-cache: true
3029
- run: bundle install
3130
- run: bundle exec rake
32-
33-
rubocop:
34-
runs-on: ${{ matrix.os }}
35-
name: Rubocop ${{ matrix.ruby }}
36-
strategy:
37-
fail-fast: false
38-
matrix:
39-
os: [ubuntu-latest, macos-latest, windows-latest]
40-
ruby:
41-
- "3.1.2"
42-
- "2.7.6"
43-
- "3.0.4"
44-
env:
45-
BUNDLE_GEMFILE: Gemfile
46-
47-
steps:
48-
- uses: actions/checkout@v3
49-
- name: Set up Ruby ${{ matrix.ruby }}
50-
uses: ruby/setup-ruby@v1
51-
with:
52-
ruby-version: ${{ matrix.ruby }}
53-
bundler-cache: true
5431
- name: Run style checks
5532
run: bundle exec rubocop
56-
57-
test:
58-
runs-on: ${{ matrix.os }}
59-
name: RSpec ${{ matrix.ruby }}
60-
strategy:
61-
fail-fast: false
62-
matrix:
63-
os: [ubuntu-latest, macos-latest, windows-latest]
64-
ruby:
65-
- "3.1.2"
66-
- "2.7.6"
67-
- "3.0.4"
68-
env:
69-
BUNDLE_GEMFILE: Gemfile
70-
71-
steps:
72-
- uses: actions/checkout@v3
73-
- name: Set up Ruby ${{ matrix.ruby }}
74-
uses: ruby/setup-ruby@v1
75-
with:
76-
ruby-version: ${{ matrix.ruby }}
77-
bundler-cache: true
7833
- name: Run tests
7934
run: bundle exec rspec

.rspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
-I lib
12
--format documentation
23
--color
34
--require spec_helper

.rubocop.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ Style/StringLiteralsInInterpolation:
1212

1313
Layout/LineLength:
1414
Max: 120
15+
Exclude:
16+
- 'spec/**/*.rb'
17+
18+
Metrics/BlockLength:
19+
Exclude:
20+
- 'spec/**/*.rb'
21+
- 'openfeature-sdk.gemspec'
1522

1623
Gemspec/RequireMFA:
1724
Enabled: false

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ source "https://rubygems.org"
44

55
# Specify your gem's dependencies in openfeature-sdk.gemspec
66
gemspec
7+
8+
gem "concurrent-ruby", require: "concurrent"

Gemfile.lock

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
PATH
22
remote: .
33
specs:
4-
openfeature-sdk (0.0.2)
4+
openfeature-sdk (0.0.3)
55

66
GEM
77
remote: https://rubygems.org/
88
specs:
99
ast (2.4.2)
10+
concurrent-ruby (1.1.10)
1011
diff-lcs (1.5.0)
1112
json (2.6.2)
1213
parallel (1.22.1)
@@ -55,6 +56,7 @@ PLATFORMS
5556
x86_64-linux
5657

5758
DEPENDENCIES
59+
concurrent-ruby
5860
openfeature-sdk!
5961
rake (~> 13.0)
6062
rspec (~> 3.12.0)

lib/openfeature/sdk.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# frozen_string_literal: true
22

33
require_relative "sdk/version"
4+
require_relative "sdk/api"
45

56
module OpenFeature
7+
# TODO: Add documentation
8+
#
69
module SDK
7-
class Error < StandardError; end
8-
# Your code goes here...
910
end
1011
end

lib/openfeature/sdk/api.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
require "forwardable"
4+
require "singleton"
5+
6+
require_relative "configuration"
7+
require_relative "client"
8+
require_relative "metadata"
9+
require_relative "provider/no_op_provider"
10+
11+
module OpenFeature
12+
module SDK
13+
# API Initialization and Configuration
14+
#
15+
# Represents the entry point to the API, including configuration of <tt>Provider</tt>,<tt>Hook</tt>,
16+
# and building the <tt>Client</tt>
17+
#
18+
# To use the SDK, you can optionally configure a <tt>Provider</tt>, with <tt>Hook</tt>
19+
#
20+
# OpenFeature::SDK::API.instance.configure do |config|
21+
# config.provider = NoOpProvider.new
22+
# end
23+
#
24+
# If no provider is specified, the <tt>NoOpProvider</tt> is set as the default <tt>Provider</tt>.
25+
# Once the SDK has been configured, a client can be built
26+
#
27+
# client = OpenFeature::SDK::API.instance.build_client(name: 'my-open-feature-client')
28+
class API
29+
include Singleton
30+
extend Forwardable
31+
32+
def_delegator :@configuration, :provider
33+
def_delegator :@configuration, :hooks
34+
def_delegator :@configuration, :context
35+
36+
def configuration
37+
@configuration ||= Configuration.new
38+
end
39+
40+
def configure(&block)
41+
return unless block_given?
42+
43+
block.call(configuration)
44+
end
45+
46+
def build_client(name: nil, version: nil)
47+
client_options = Metadata.new(name: name, version: version).freeze
48+
provider = Provider::NoOpProvider.new if provider.nil?
49+
Client.new(provider, client_options, context)
50+
end
51+
end
52+
end
53+
end

lib/openfeature/sdk/client.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
module OpenFeature
4+
module SDK
5+
# TODO: Write documentation
6+
#
7+
class Client
8+
attr_reader :metadata
9+
10+
attr_accessor :hooks
11+
12+
def initialize(provider, client_options, context)
13+
@provider = provider
14+
@client_options = client_options
15+
@context = context
16+
end
17+
end
18+
end
19+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
require "concurrent"
4+
5+
require_relative "api"
6+
7+
module OpenFeature
8+
module SDK
9+
# Represents the configuration object for the global API where <tt>Provider</tt>, <tt>Hook</tt>,
10+
# and <tt>Context</tt> are configured.
11+
# This class is not meant to be interacted with directly but instead through the <tt>OpenFeature::SDK.configure</tt>
12+
# method
13+
class Configuration
14+
extend Forwardable
15+
16+
attr_accessor :context, :provider, :hooks
17+
18+
def_delegator :@provider, :metadata
19+
20+
def initialize
21+
@hooks = Concurrent::Array.new([])
22+
end
23+
end
24+
end
25+
end

lib/openfeature/sdk/metadata.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
module OpenFeature
4+
module SDK
5+
# Metadata structure that defines general metadata relating to a <tt>Provider</tt> or <tt>Client</tt>
6+
#
7+
# Within the Metadata structure, the following attribute readers are available:
8+
#
9+
# * <tt>name</tt> - Defines the name of the structure
10+
#
11+
# * <tt>version</tt> - Allows you to specify version of the Metadata structure
12+
#
13+
# Usage:
14+
#
15+
# metadata = Metadata.new(name: 'name-for-metadata', version: 'v1.1.3')
16+
# metadata.name # 'name-for-metadata'
17+
# metadata.version # version
18+
# metadata_two = Metadata.new(name: 'name-for-metadata')
19+
# metadata_two == metadata # true - equality based on values
20+
class Metadata
21+
attr_reader :name, :version
22+
23+
def initialize(name:, version: nil)
24+
@name = name
25+
@version = version
26+
end
27+
28+
def ==(other)
29+
raise ArgumentError("Expected comparison to be between Metadata object") unless other.is_a?(Metadata)
30+
31+
@name == other.name && @version == other.version
32+
end
33+
end
34+
end
35+
end

0 commit comments

Comments
 (0)