openfeature-sdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef370095e429959ee1d1c0089e9a9e321cc3e4a2345f059aae26b8d965adf1b3
4
- data.tar.gz: c00d676c31455f90592bd1ff604816f468750576e5a181d3b9f02d81c27dd878
3
+ metadata.gz: d79e98004b058036248ba05928ee5debe398a607a0d079f3d8d366fcde9bce07
4
+ data.tar.gz: 76c896c762a1e5a7828f7785864c0bbb2034214c5ffcc12988af044ad23d2806
5
5
  SHA512:
6
- metadata.gz: 7d7d24acecc2f9de37b64d037316f17c58138ab78b4ef71923ec8e403fabb94d739b9bc66611a31e50012774f88792b8b9823049202a519da6b13f188a2ec389
7
- data.tar.gz: d9830c29d198b3a4b7f0aef3da391d08ac4a30042eb0bbc47e9b30602ee806e5bb76794243dfa2ea1ed235ab0a8d17ea9b64184a6de6d900a4d7d1b82f0c06b1
6
+ metadata.gz: 62d7a0aa1d5110d346afd5d39317064ef5f5722138bdd90cf31f96aa3587cd3fc1083cfade3b7d8f9b02560075078010e82104fcba9939621e301dfa12e3cf85
7
+ data.tar.gz: 10540bcff19a5d57113d4b298f2f0ed9ddf13f3e1b2bdcff116ef27e6ac55c233a8b2ee4aba3468c40a31c698e9f3cbeb8208c2964f9df67551e632dd0190564
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.2.0"
2
+ ".": "0.3.0"
3
3
  }
data/.standard.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  parallel: true
2
- formatter: progress
2
+ format: progress
3
3
  ruby_version: 3.1
4
4
  plugins:
5
5
  - standard-performance
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/open-feature/ruby-sdk/compare/v0.2.1...v0.3.0) (2024-04-05)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * Add `EvaluationContext` helpers and context merging to flag evaluation ([#119](https://github.com/open-feature/ruby-sdk/issues/119))
9
+ * Separate `Client` and `Provider` metadata, add client creation tests ([#116](https://github.com/open-feature/ruby-sdk/issues/116))
10
+
11
+ ### Features
12
+
13
+ * Add `EvaluationContext` helpers and context merging to flag evaluation ([#119](https://github.com/open-feature/ruby-sdk/issues/119)) ([34e4795](https://github.com/open-feature/ruby-sdk/commit/34e47956d66e0c6763f58c818461aa52f628bd21))
14
+ * Add evaluation context based on requirement 3.1 ([#114](https://github.com/open-feature/ruby-sdk/issues/114)) ([f8e016f](https://github.com/open-feature/ruby-sdk/commit/f8e016f1cf7bf1ca7fddce7a41efdeb4d3d522c1))
15
+ * Flag Evaluation Requirement 1.1.4 and 1.1.5 and Provider Requirement 2.1.1 ([#112](https://github.com/open-feature/ruby-sdk/issues/112)) ([aac74b1](https://github.com/open-feature/ruby-sdk/commit/aac74b1e80a4b3e69983e55cf5c75b9cee37b71b))
16
+
17
+
18
+ ### Code Refactoring
19
+
20
+ * Separate `Client` and `Provider` metadata, add client creation tests ([#116](https://github.com/open-feature/ruby-sdk/issues/116)) ([f028c39](https://github.com/open-feature/ruby-sdk/commit/f028c398db3e2317847fe7e7bcbe6bbe96bb0b1c))
21
+
22
+ ## [0.2.1](https://github.com/open-feature/ruby-sdk/compare/v0.2.0...v0.2.1) (2024-03-29)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * Add domain to build_client ([#109](https://github.com/open-feature/ruby-sdk/issues/109)) ([56ccf17](https://github.com/open-feature/ruby-sdk/commit/56ccf17ec340df0ea14a72ea7379c51dbb9d7b13))
28
+
3
29
  ## [0.2.0](https://github.com/open-feature/ruby-sdk/compare/v0.1.1...v0.2.0) (2024-03-09)
4
30
 
5
31
 
data/Gemfile CHANGED
@@ -4,5 +4,3 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in openfeature-sdk.gemspec
6
6
  gemspec
7
-
8
- gem "concurrent-ruby", require: "concurrent"
data/Gemfile.lock CHANGED
@@ -1,72 +1,69 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openfeature-sdk (0.2.0)
4
+ openfeature-sdk (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.2)
10
- base64 (0.1.1)
11
- concurrent-ruby (1.2.3)
12
- debug (1.9.1)
10
+ debug (1.9.2)
13
11
  irb (~> 1.10)
14
12
  reline (>= 0.3.8)
15
- diff-lcs (1.5.0)
13
+ diff-lcs (1.5.1)
16
14
  docile (1.4.0)
17
15
  io-console (0.7.2)
18
- irb (1.11.2)
16
+ irb (1.12.0)
19
17
  rdoc
20
18
  reline (>= 0.4.2)
21
- json (2.6.3)
19
+ json (2.7.1)
22
20
  language_server-protocol (3.17.0.3)
23
21
  lint_roller (1.1.0)
24
22
  markly (0.10.0)
25
- parallel (1.23.0)
26
- parser (3.2.2.3)
23
+ parallel (1.24.0)
24
+ parser (3.3.0.5)
27
25
  ast (~> 2.4.1)
28
26
  racc
29
27
  psych (5.1.2)
30
28
  stringio
31
- racc (1.7.1)
29
+ racc (1.7.3)
32
30
  rainbow (3.1.1)
33
- rake (13.0.6)
34
- rdoc (6.6.2)
31
+ rake (13.1.0)
32
+ rdoc (6.6.3.1)
35
33
  psych (>= 4.0.0)
36
- regexp_parser (2.8.1)
37
- reline (0.4.3)
34
+ regexp_parser (2.9.0)
35
+ reline (0.5.0)
38
36
  io-console (~> 0.5)
39
37
  rexml (3.2.6)
40
38
  rspec (3.12.0)
41
39
  rspec-core (~> 3.12.0)
42
40
  rspec-expectations (~> 3.12.0)
43
41
  rspec-mocks (~> 3.12.0)
44
- rspec-core (3.12.2)
42
+ rspec-core (3.12.3)
45
43
  rspec-support (~> 3.12.0)
46
- rspec-expectations (3.12.3)
44
+ rspec-expectations (3.12.4)
47
45
  diff-lcs (>= 1.2.0, < 2.0)
48
46
  rspec-support (~> 3.12.0)
49
- rspec-mocks (3.12.6)
47
+ rspec-mocks (3.12.7)
50
48
  diff-lcs (>= 1.2.0, < 2.0)
51
49
  rspec-support (~> 3.12.0)
52
- rspec-support (3.12.1)
53
- rubocop (1.56.3)
54
- base64 (~> 0.1.1)
50
+ rspec-support (3.12.2)
51
+ rubocop (1.62.1)
55
52
  json (~> 2.3)
56
53
  language_server-protocol (>= 3.17.0)
57
54
  parallel (~> 1.10)
58
- parser (>= 3.2.2.3)
55
+ parser (>= 3.3.0.2)
59
56
  rainbow (>= 2.2.2, < 4.0)
60
57
  regexp_parser (>= 1.8, < 3.0)
61
58
  rexml (>= 3.2.5, < 4.0)
62
- rubocop-ast (>= 1.28.1, < 2.0)
59
+ rubocop-ast (>= 1.31.1, < 2.0)
63
60
  ruby-progressbar (~> 1.7)
64
61
  unicode-display_width (>= 2.4.0, < 3.0)
65
- rubocop-ast (1.29.0)
66
- parser (>= 3.2.1.0)
67
- rubocop-performance (1.19.0)
68
- rubocop (>= 1.7.0, < 2.0)
69
- rubocop-ast (>= 0.4.0)
62
+ rubocop-ast (1.31.2)
63
+ parser (>= 3.3.0.4)
64
+ rubocop-performance (1.20.2)
65
+ rubocop (>= 1.48.1, < 2.0)
66
+ rubocop-ast (>= 1.30.0, < 2.0)
70
67
  ruby-progressbar (1.13.0)
71
68
  simplecov (0.22.0)
72
69
  docile (~> 1.1)
@@ -77,20 +74,20 @@ GEM
77
74
  simplecov (~> 0.19)
78
75
  simplecov-html (0.12.3)
79
76
  simplecov_json_formatter (0.1.4)
80
- standard (1.31.1)
77
+ standard (1.35.1)
81
78
  language_server-protocol (~> 3.17.0.2)
82
79
  lint_roller (~> 1.0)
83
- rubocop (~> 1.56.2)
80
+ rubocop (~> 1.62.0)
84
81
  standard-custom (~> 1.0.0)
85
- standard-performance (~> 1.2)
82
+ standard-performance (~> 1.3)
86
83
  standard-custom (1.0.2)
87
84
  lint_roller (~> 1.0)
88
85
  rubocop (~> 1.50)
89
- standard-performance (1.2.0)
86
+ standard-performance (1.3.1)
90
87
  lint_roller (~> 1.1)
91
- rubocop-performance (~> 1.19.0)
88
+ rubocop-performance (~> 1.20.2)
92
89
  stringio (3.1.0)
93
- unicode-display_width (2.4.2)
90
+ unicode-display_width (2.5.0)
94
91
 
95
92
  PLATFORMS
96
93
  arm64-darwin-21
@@ -104,7 +101,6 @@ PLATFORMS
104
101
  x86_64-linux
105
102
 
106
103
  DEPENDENCIES
107
- concurrent-ruby
108
104
  debug
109
105
  markly
110
106
  openfeature-sdk!
data/README.md CHANGED
@@ -12,12 +12,11 @@ We support multiple data types for flags (numbers, strings, booleans, objects) a
12
12
 
13
13
  ## Support Matrix
14
14
 
15
- | Ruby Version | OS |
16
- | ----------- | ----------- |
17
- | Ruby 3.1.4 | Windows, MacOS, Linux |
18
- | Ruby 3.2.3 | Windows, MacOS, Linux |
19
- | Ruby 3.3.0 | Windows, MacOS, Linux |
20
-
15
+ | Ruby Version | OS |
16
+ | ------------ | --------------------- |
17
+ | Ruby 3.1.4 | Windows, MacOS, Linux |
18
+ | Ruby 3.2.3 | Windows, MacOS, Linux |
19
+ | Ruby 3.3.0 | Windows, MacOS, Linux |
21
20
 
22
21
  ## Installation
23
22
 
@@ -51,10 +50,18 @@ OpenFeature::SDK.configure do |config|
51
50
  ))
52
51
  # alternatively, you can bind multiple providers to different domains
53
52
  config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new, domain: "legacy_flags")
53
+ # you can set a global evaluation context here
54
+ config.evaluation_context = OpenFeature::SDK::EvaluationContext.new("host" => "myhost.com")
54
55
  end
55
56
 
56
57
  # Create a client
57
- client = OpenFeature::SDK.build_client(name: "my-app")
58
+ client = OpenFeature::SDK.build_client
59
+ # Create a client for a different domain, this will use the provider assigned to that domain
60
+ legacy_flag_client = OpenFeature::SDK.build_client(domain: "legacy_flags")
61
+ # Evaluation context can be set on a client as well
62
+ client_with_context = OpenFeature::SDK.build_client(
63
+ evaluation_context: OpenFeature::SDK::EvaluationContext.new("controller_name" => "admin")
64
+ )
58
65
 
59
66
  # fetching boolean value feature flag
60
67
  bool_value = client.fetch_boolean_value(flag_key: 'boolean_flag', default_value: false)
@@ -68,6 +75,15 @@ integer_value = client.fetch_number_value(flag_key: 'number_value', default_valu
68
75
 
69
76
  # get an object value
70
77
  object = client.fetch_object_value(flag_key: 'object_value', default_value: JSON.dump({ name: 'object'}))
78
+
79
+ # Invocation evaluation context can also be passed in during flag evaluation.
80
+ # During flag evaluation, invocation context takes precedence over client context
81
+ # which takes precedence over API (aka global) context.
82
+ bool_value = client.fetch_boolean_value(
83
+ flag_key: 'boolean_flag',
84
+ default_value: false,
85
+ evaluation_context: OpenFeature::SDK::EvaluationContext.new("is_friday" => true)
86
+ )
71
87
  ```
72
88
 
73
89
  For complete documentation, visit: https://openfeature.dev/docs/category/concepts
@@ -102,7 +118,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the O
102
118
 
103
119
  Our community meetings are held regularly and open to everyone. Check the [OpenFeature community calendar](https://calendar.google.com/calendar/u/0?cid=MHVhN2kxaGl2NWRoMThiMjd0b2FoNjM2NDRAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) for specific dates and for the Zoom meeting links.
104
120
 
105
-
106
121
  ## License
107
122
 
108
123
  [Apache License 2.0](LICENSE)
@@ -4,9 +4,11 @@ require "forwardable"
4
4
  require "singleton"
5
5
 
6
6
  require_relative "configuration"
7
+ require_relative "evaluation_context"
8
+ require_relative "evaluation_context_builder"
7
9
  require_relative "evaluation_details"
10
+ require_relative "client_metadata"
8
11
  require_relative "client"
9
- require_relative "metadata"
10
12
  require_relative "provider"
11
13
 
12
14
  module OpenFeature
@@ -30,7 +32,7 @@ module OpenFeature
30
32
  include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1
31
33
  extend Forwardable
32
34
 
33
- def_delegators :configuration, :provider, :set_provider, :hooks, :context
35
+ def_delegators :configuration, :provider, :set_provider, :hooks, :evaluation_context
34
36
 
35
37
  def configuration
36
38
  @configuration ||= Configuration.new
@@ -42,10 +44,12 @@ module OpenFeature
42
44
  block.call(configuration)
43
45
  end
44
46
 
45
- def build_client(name: nil, version: nil)
46
- client_options = Metadata.new(name: name, version: version).freeze
47
- provider = Provider::NoOpProvider.new if provider.nil?
48
- Client.new(provider: provider, client_options: client_options, context: context)
47
+ def build_client(domain: nil, evaluation_context: nil)
48
+ active_provider = provider(domain:).nil? ? Provider::NoOpProvider.new : provider(domain:)
49
+
50
+ Client.new(provider: active_provider, domain:, evaluation_context:)
51
+ rescue
52
+ Client.new(provider: Provider::NoOpProvider.new, evaluation_context:)
49
53
  end
50
54
  end
51
55
  end
@@ -8,14 +8,14 @@ module OpenFeature
8
8
  RESULT_TYPE = %i[boolean string number object].freeze
9
9
  SUFFIXES = %i[value details].freeze
10
10
 
11
- attr_reader :metadata
11
+ attr_reader :metadata, :evaluation_context
12
12
 
13
13
  attr_accessor :hooks
14
14
 
15
- def initialize(provider:, client_options: nil, context: nil)
15
+ def initialize(provider:, domain: nil, evaluation_context: nil)
16
16
  @provider = provider
17
- @metadata = client_options
18
- @context = context
17
+ @metadata = ClientMetadata.new(domain:)
18
+ @evaluation_context = evaluation_context
19
19
  @hooks = []
20
20
  end
21
21
 
@@ -26,7 +26,8 @@ module OpenFeature
26
26
  # result = @provider.fetch_boolean_value(flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
27
27
  # end
28
28
  def fetch_#{result_type}_#{suffix}(flag_key:, default_value:, evaluation_context: nil)
29
- resolution_details = @provider.fetch_#{result_type}_value(flag_key:, default_value:, evaluation_context:)
29
+ built_context = EvaluationContextBuilder.new.call(api_context: OpenFeature::SDK.evaluation_context, client_context: self.evaluation_context, invocation_context: evaluation_context)
30
+ resolution_details = @provider.fetch_#{result_type}_value(flag_key:, default_value:, evaluation_context: built_context)
30
31
  evaluation_details = EvaluationDetails.new(flag_key:, resolution_details:)
31
32
  #{"evaluation_details.value" if suffix == :value}
32
33
  end
@@ -0,0 +1,5 @@
1
+ module OpenFeature
2
+ module SDK
3
+ ClientMetadata = Struct.new(:domain, keyword_init: true)
4
+ end
5
+ end
@@ -1,21 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "concurrent"
4
-
5
3
  require_relative "api"
6
4
 
7
5
  module OpenFeature
8
6
  module SDK
9
7
  # Represents the configuration object for the global API where <tt>Provider</tt>, <tt>Hook</tt>,
10
- # and <tt>Context</tt> are configured.
8
+ # and <tt>EvaluationContext</tt> are configured.
11
9
  # This class is not meant to be interacted with directly but instead through the <tt>OpenFeature::SDK.configure</tt>
12
10
  # method
13
11
  class Configuration
14
12
  extend Forwardable
15
13
 
16
- attr_accessor :context, :hooks
17
-
18
- def_delegator :provider, :metadata
14
+ attr_accessor :evaluation_context, :hooks
19
15
 
20
16
  def initialize
21
17
  @hooks = []
@@ -23,7 +19,7 @@ module OpenFeature
23
19
  end
24
20
 
25
21
  def provider(domain: nil)
26
- @providers[domain]
22
+ @providers[domain] || @providers[nil]
27
23
  end
28
24
 
29
25
  # When switching providers, there are a few lifecycle methods that need to be taken care of.
@@ -0,0 +1,32 @@
1
+ module OpenFeature
2
+ module SDK
3
+ class EvaluationContext
4
+ TARGETING_KEY = "targeting_key"
5
+
6
+ attr_reader :fields
7
+
8
+ def initialize(**fields)
9
+ @fields = fields.transform_keys(&:to_s)
10
+ end
11
+
12
+ def targeting_key
13
+ fields[TARGETING_KEY]
14
+ end
15
+
16
+ def field(key)
17
+ fields[key]
18
+ end
19
+
20
+ def merge(overriding_context)
21
+ EvaluationContext.new(
22
+ targeting_key: overriding_context.targeting_key || targeting_key,
23
+ **fields.merge(overriding_context.fields)
24
+ )
25
+ end
26
+
27
+ def ==(other)
28
+ fields == other.fields
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module OpenFeature
2
+ module SDK
3
+ # Used to combine evaluation contexts from different sources
4
+ class EvaluationContextBuilder
5
+ def call(api_context:, client_context:, invocation_context:)
6
+ available_contexts = [api_context, client_context, invocation_context].compact
7
+
8
+ return nil if available_contexts.empty?
9
+
10
+ available_contexts.reduce(EvaluationContext.new) do |built_context, context|
11
+ built_context.merge(context)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,8 +5,10 @@ module OpenFeature
5
5
  class InMemoryProvider
6
6
  NAME = "In-memory Provider"
7
7
 
8
+ attr_reader :metadata
9
+
8
10
  def initialize(flags = {})
9
- @metadata = Metadata.new(name: NAME).freeze
11
+ @metadata = ProviderMetadata.new(name: NAME).freeze
10
12
  @flags = flags
11
13
  end
12
14
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../metadata"
4
-
5
3
  # rubocop:disable Lint/UnusedMethodArgument
6
4
  module OpenFeature
7
5
  module SDK
@@ -31,7 +29,7 @@ module OpenFeature
31
29
  attr_reader :metadata
32
30
 
33
31
  def initialize
34
- @metadata = Metadata.new(name: NAME).freeze
32
+ @metadata = ProviderMetadata.new(name: NAME).freeze
35
33
  end
36
34
 
37
35
  def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
@@ -0,0 +1,7 @@
1
+ module OpenFeature
2
+ module SDK
3
+ module Provider
4
+ ProviderMetadata = Struct.new(:name, keyword_init: true)
5
+ end
6
+ end
7
+ end
@@ -1,6 +1,7 @@
1
1
  require_relative "provider/error_code"
2
2
  require_relative "provider/reason"
3
3
  require_relative "provider/resolution_details"
4
+ require_relative "provider/provider_metadata"
4
5
  require_relative "provider/no_op_provider"
5
6
  require_relative "provider/in_memory_provider"
6
7
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OpenFeature
4
4
  module SDK
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openfeature-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenFeature Authors
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-11 00:00:00.000000000 Z
11
+ date: 2024-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: debug
@@ -147,13 +147,16 @@ files:
147
147
  - lib/open_feature/sdk.rb
148
148
  - lib/open_feature/sdk/api.rb
149
149
  - lib/open_feature/sdk/client.rb
150
+ - lib/open_feature/sdk/client_metadata.rb
150
151
  - lib/open_feature/sdk/configuration.rb
152
+ - lib/open_feature/sdk/evaluation_context.rb
153
+ - lib/open_feature/sdk/evaluation_context_builder.rb
151
154
  - lib/open_feature/sdk/evaluation_details.rb
152
- - lib/open_feature/sdk/metadata.rb
153
155
  - lib/open_feature/sdk/provider.rb
154
156
  - lib/open_feature/sdk/provider/error_code.rb
155
157
  - lib/open_feature/sdk/provider/in_memory_provider.rb
156
158
  - lib/open_feature/sdk/provider/no_op_provider.rb
159
+ - lib/open_feature/sdk/provider/provider_metadata.rb
157
160
  - lib/open_feature/sdk/provider/reason.rb
158
161
  - lib/open_feature/sdk/provider/resolution_details.rb
159
162
  - lib/open_feature/sdk/version.rb
@@ -1,35 +0,0 @@
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