launchdarkly-server-sdk 5.7.4 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +28 -122
- data/.gitignore +1 -1
- data/.ldrelease/build-docs.sh +18 -0
- data/.ldrelease/circleci/linux/execute.sh +18 -0
- data/.ldrelease/circleci/mac/execute.sh +18 -0
- data/.ldrelease/circleci/template/build.sh +29 -0
- data/.ldrelease/circleci/template/publish.sh +23 -0
- data/.ldrelease/circleci/template/set-gem-home.sh +7 -0
- data/.ldrelease/circleci/template/test.sh +10 -0
- data/.ldrelease/circleci/template/update-version.sh +8 -0
- data/.ldrelease/circleci/windows/execute.ps1 +19 -0
- data/.ldrelease/config.yml +14 -2
- data/CHANGELOG.md +28 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +92 -76
- data/README.md +4 -3
- data/azure-pipelines.yml +1 -1
- data/docs/Makefile +26 -0
- data/docs/index.md +9 -0
- data/launchdarkly-server-sdk.gemspec +20 -13
- data/lib/ldclient-rb.rb +0 -1
- data/lib/ldclient-rb/config.rb +15 -3
- data/lib/ldclient-rb/evaluation_detail.rb +293 -0
- data/lib/ldclient-rb/events.rb +3 -4
- data/lib/ldclient-rb/file_data_source.rb +1 -1
- data/lib/ldclient-rb/impl/evaluator.rb +225 -0
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +74 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +160 -0
- data/lib/ldclient-rb/impl/event_factory.rb +22 -0
- data/lib/ldclient-rb/impl/event_sender.rb +56 -40
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +5 -5
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +5 -5
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +8 -7
- data/lib/ldclient-rb/impl/model/serialization.rb +62 -0
- data/lib/ldclient-rb/impl/unbounded_pool.rb +34 -0
- data/lib/ldclient-rb/integrations/redis.rb +3 -0
- data/lib/ldclient-rb/ldclient.rb +33 -11
- data/lib/ldclient-rb/polling.rb +1 -4
- data/lib/ldclient-rb/redis_store.rb +1 -0
- data/lib/ldclient-rb/requestor.rb +25 -23
- data/lib/ldclient-rb/stream.rb +10 -30
- data/lib/ldclient-rb/util.rb +12 -8
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/evaluation_detail_spec.rb +135 -0
- data/spec/event_sender_spec.rb +20 -2
- data/spec/events_spec.rb +10 -0
- data/spec/http_util.rb +11 -1
- data/spec/impl/evaluator_bucketing_spec.rb +111 -0
- data/spec/impl/evaluator_clause_spec.rb +55 -0
- data/spec/impl/evaluator_operators_spec.rb +141 -0
- data/spec/impl/evaluator_rule_spec.rb +96 -0
- data/spec/impl/evaluator_segment_spec.rb +125 -0
- data/spec/impl/evaluator_spec.rb +305 -0
- data/spec/impl/evaluator_spec_base.rb +75 -0
- data/spec/impl/model/serialization_spec.rb +41 -0
- data/spec/launchdarkly-server-sdk_spec.rb +1 -1
- data/spec/ldclient_end_to_end_spec.rb +34 -0
- data/spec/ldclient_spec.rb +60 -8
- data/spec/polling_spec.rb +2 -2
- data/spec/redis_feature_store_spec.rb +32 -3
- data/spec/requestor_spec.rb +11 -45
- data/spec/spec_helper.rb +0 -3
- data/spec/stream_spec.rb +1 -16
- metadata +110 -60
- data/.yardopts +0 -9
- data/lib/ldclient-rb/evaluation.rb +0 -462
- data/scripts/gendocs.sh +0 -11
- data/scripts/release.sh +0 -27
- data/spec/evaluation_spec.rb +0 -789
data/README.md
CHANGED
@@ -5,6 +5,8 @@ LaunchDarkly Server-side SDK for Ruby
|
|
5
5
|
|
6
6
|
[![Circle CI](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master)
|
7
7
|
[![Security](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master.svg)](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master)
|
8
|
+
[![RubyDoc](https://img.shields.io/static/v1?label=docs+-+all+versions&message=reference&color=00add8)](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
|
9
|
+
[![GitHub Pages](https://img.shields.io/static/v1?label=docs+-+latest&message=reference&color=00add8)](https://launchdarkly.github.io/ruby-server-sdk)
|
8
10
|
|
9
11
|
LaunchDarkly overview
|
10
12
|
-------------------------
|
@@ -15,7 +17,7 @@ LaunchDarkly overview
|
|
15
17
|
Supported Ruby versions
|
16
18
|
-----------------------
|
17
19
|
|
18
|
-
This version of the LaunchDarkly SDK has a minimum Ruby version of 2.
|
20
|
+
This version of the LaunchDarkly SDK has a minimum Ruby version of 2.5.0, or 9.2.0 for JRuby.
|
19
21
|
|
20
22
|
Getting started
|
21
23
|
-----------
|
@@ -27,7 +29,7 @@ Learn more
|
|
27
29
|
|
28
30
|
Check out our [documentation](http://docs.launchdarkly.com) for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the [reference guide for this SDK](http://docs.launchdarkly.com/docs/ruby-sdk-reference).
|
29
31
|
|
30
|
-
Generated API documentation is on [RubyDoc.info](https://www.rubydoc.info/gems/launchdarkly-server-sdk).
|
32
|
+
Generated API documentation for all versions of the SDK is on [RubyDoc.info](https://www.rubydoc.info/gems/launchdarkly-server-sdk). The API documentation for the latest version is also on [GitHub Pages](https://launchdarkly.github.io/ruby-server-sdk).
|
31
33
|
|
32
34
|
Testing
|
33
35
|
-------
|
@@ -53,4 +55,3 @@ About LaunchDarkly
|
|
53
55
|
* [docs.launchdarkly.com](https://docs.launchdarkly.com/ "LaunchDarkly Documentation") for our documentation and SDK reference guides
|
54
56
|
* [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/ "LaunchDarkly API Documentation") for our API documentation
|
55
57
|
* [blog.launchdarkly.com](https://blog.launchdarkly.com/ "LaunchDarkly Blog Documentation") for the latest product updates
|
56
|
-
* [Feature Flagging Guide](https://github.com/launchdarkly/featureflags/ "Feature Flagging Guide") for best practices and strategies
|
data/azure-pipelines.yml
CHANGED
@@ -45,7 +45,7 @@ jobs:
|
|
45
45
|
workingDirectory: $(System.DefaultWorkingDirectory)
|
46
46
|
script: |
|
47
47
|
ruby -v
|
48
|
-
gem install bundler
|
48
|
+
gem install bundler:2.2.7
|
49
49
|
bundle install
|
50
50
|
mkdir rspec
|
51
51
|
bundle exec rspec --format progress --format RspecJunitFormatter -o ./rspec/rspec.xml spec
|
data/docs/Makefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
ifeq ($(LD_RELEASE_VERSION),)
|
3
|
+
TITLE=LaunchDarkly Ruby SDK
|
4
|
+
else
|
5
|
+
TITLE=LaunchDarkly Ruby SDK ($(LD_RELEASE_VERSION))
|
6
|
+
endif
|
7
|
+
|
8
|
+
.PHONY: dependencies html
|
9
|
+
|
10
|
+
html: dependencies
|
11
|
+
rm -rf ./build
|
12
|
+
cd .. && yard doc \
|
13
|
+
-o docs/build/html \
|
14
|
+
--title "$(TITLE)" \
|
15
|
+
--no-private \
|
16
|
+
--markup markdown \
|
17
|
+
--embed-mixins \
|
18
|
+
-r docs/index.md \
|
19
|
+
lib/*.rb \
|
20
|
+
lib/**/*.rb \
|
21
|
+
lib/**/**/*.rb \
|
22
|
+
lib/**/**/**/*.rb
|
23
|
+
|
24
|
+
dependencies:
|
25
|
+
gem install --conservative yard
|
26
|
+
gem install --conservative redcarpet # provides Markdown formatting
|
data/docs/index.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# LaunchDarkly Server-side SDK for Ruby
|
2
|
+
|
3
|
+
This generated API documentation lists all types and methods in the SDK.
|
4
|
+
|
5
|
+
The API documentation for the most recent SDK release is hosted on [GitHub Pages](https://launchdarkly.github.io/ruby-server-sdk). API documentation for current and past releases is hosted on [RubyDoc.info](https://www.rubydoc.info/gems/launchdarkly-server-sdk).
|
6
|
+
|
7
|
+
Source code and readme: [GitHub](https://github.com/launchdarkly/ruby-server-sdk)
|
8
|
+
|
9
|
+
SDK reference guide: [docs.launchdarkly.com](https://docs.launchdarkly.com/sdk/server-side/ruby)
|
@@ -19,20 +19,27 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
21
|
spec.require_paths = ["lib"]
|
22
|
+
spec.required_ruby_version = ">= 2.5.0"
|
22
23
|
|
23
|
-
spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.
|
24
|
-
spec.add_development_dependency "bundler", "~> 1
|
25
|
-
spec.add_development_dependency "rspec", "~> 3.
|
26
|
-
spec.add_development_dependency "
|
27
|
-
spec.add_development_dependency "
|
28
|
-
spec.add_development_dependency "
|
29
|
-
spec.add_development_dependency "
|
30
|
-
spec.add_development_dependency "
|
31
|
-
spec.add_development_dependency "
|
32
|
-
spec.add_development_dependency "
|
24
|
+
spec.add_development_dependency "aws-sdk-dynamodb", "~> 1.57"
|
25
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.10"
|
27
|
+
spec.add_development_dependency "diplomat", "~> 2.4.2"
|
28
|
+
spec.add_development_dependency "redis", "~> 4.2"
|
29
|
+
spec.add_development_dependency "connection_pool", "~> 2.2.3"
|
30
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
|
31
|
+
spec.add_development_dependency "timecop", "~> 0.9"
|
32
|
+
spec.add_development_dependency "listen", "~> 3.3" # see file_data_source.rb
|
33
|
+
spec.add_development_dependency "webrick", "~> 1.7"
|
34
|
+
# required by dynamodb
|
35
|
+
spec.add_development_dependency "oga", "~> 2.2"
|
33
36
|
|
34
|
-
spec.add_runtime_dependency "json", [">= 1.8", "< 3"]
|
35
37
|
spec.add_runtime_dependency "semantic", "~> 1.6"
|
36
|
-
spec.add_runtime_dependency "concurrent-ruby", "~> 1.
|
37
|
-
spec.add_runtime_dependency "ld-eventsource", "
|
38
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 1.1"
|
39
|
+
spec.add_runtime_dependency "ld-eventsource", "~> 2.0"
|
40
|
+
|
41
|
+
# lock json to 2.3.x as ruby libraries often remove
|
42
|
+
# support for older ruby versions in minor releases
|
43
|
+
spec.add_runtime_dependency "json", "~> 2.3.1"
|
44
|
+
spec.add_runtime_dependency "http", "~> 4.4.1"
|
38
45
|
end
|
data/lib/ldclient-rb.rb
CHANGED
data/lib/ldclient-rb/config.rb
CHANGED
@@ -15,7 +15,7 @@ module LaunchDarkly
|
|
15
15
|
#
|
16
16
|
# @param opts [Hash] the configuration options
|
17
17
|
# @option opts [Logger] :logger See {#logger}.
|
18
|
-
# @option opts [String] :base_uri ("https://
|
18
|
+
# @option opts [String] :base_uri ("https://sdk.launchdarkly.com") See {#base_uri}.
|
19
19
|
# @option opts [String] :stream_uri ("https://stream.launchdarkly.com") See {#stream_uri}.
|
20
20
|
# @option opts [String] :events_uri ("https://events.launchdarkly.com") See {#events_uri}.
|
21
21
|
# @option opts [Integer] :capacity (10000) See {#capacity}.
|
@@ -41,6 +41,7 @@ module LaunchDarkly
|
|
41
41
|
# @option opts [Float] :diagnostic_recording_interval (900) See {#diagnostic_recording_interval}.
|
42
42
|
# @option opts [String] :wrapper_name See {#wrapper_name}.
|
43
43
|
# @option opts [String] :wrapper_version See {#wrapper_version}.
|
44
|
+
# @option opts [#open] :socket_factory See {#socket_factory}.
|
44
45
|
#
|
45
46
|
def initialize(opts = {})
|
46
47
|
@base_uri = (opts[:base_uri] || Config.default_base_uri).chomp("/")
|
@@ -71,6 +72,7 @@ module LaunchDarkly
|
|
71
72
|
opts[:diagnostic_recording_interval] : Config.default_diagnostic_recording_interval
|
72
73
|
@wrapper_name = opts[:wrapper_name]
|
73
74
|
@wrapper_version = opts[:wrapper_version]
|
75
|
+
@socket_factory = opts[:socket_factory]
|
74
76
|
end
|
75
77
|
|
76
78
|
#
|
@@ -305,6 +307,16 @@ module LaunchDarkly
|
|
305
307
|
#
|
306
308
|
attr_reader :wrapper_version
|
307
309
|
|
310
|
+
#
|
311
|
+
# The factory used to construct sockets for HTTP operations. The factory must
|
312
|
+
# provide the method `open(uri, timeout)`. The `open` method must return a
|
313
|
+
# connected stream that implements the `IO` class, such as a `TCPSocket`.
|
314
|
+
#
|
315
|
+
# Defaults to nil.
|
316
|
+
# @return [#open]
|
317
|
+
#
|
318
|
+
attr_reader :socket_factory
|
319
|
+
|
308
320
|
#
|
309
321
|
# The default LaunchDarkly client configuration. This configuration sets
|
310
322
|
# reasonable defaults for most users.
|
@@ -324,10 +336,10 @@ module LaunchDarkly
|
|
324
336
|
|
325
337
|
#
|
326
338
|
# The default value for {#base_uri}.
|
327
|
-
# @return [String] "https://
|
339
|
+
# @return [String] "https://sdk.launchdarkly.com"
|
328
340
|
#
|
329
341
|
def self.default_base_uri
|
330
|
-
"https://
|
342
|
+
"https://sdk.launchdarkly.com"
|
331
343
|
end
|
332
344
|
|
333
345
|
#
|
@@ -0,0 +1,293 @@
|
|
1
|
+
|
2
|
+
module LaunchDarkly
|
3
|
+
# An object returned by {LDClient#variation_detail}, combining the result of a flag evaluation with
|
4
|
+
# an explanation of how it was calculated.
|
5
|
+
class EvaluationDetail
|
6
|
+
# Creates a new instance.
|
7
|
+
#
|
8
|
+
# @param value the result value of the flag evaluation; may be of any type
|
9
|
+
# @param variation_index [int|nil] the index of the value within the flag's list of variations, or
|
10
|
+
# `nil` if the application default value was returned
|
11
|
+
# @param reason [EvaluationReason] an object describing the main factor that influenced the result
|
12
|
+
# @raise [ArgumentError] if `variation_index` or `reason` is not of the correct type
|
13
|
+
def initialize(value, variation_index, reason)
|
14
|
+
raise ArgumentError.new("variation_index must be a number") if !variation_index.nil? && !(variation_index.is_a? Numeric)
|
15
|
+
raise ArgumentError.new("reason must be an EvaluationReason") if !(reason.is_a? EvaluationReason)
|
16
|
+
@value = value
|
17
|
+
@variation_index = variation_index
|
18
|
+
@reason = reason
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# The result of the flag evaluation. This will be either one of the flag's variations, or the
|
23
|
+
# default value that was passed to {LDClient#variation_detail}. It is the same as the return
|
24
|
+
# value of {LDClient#variation}.
|
25
|
+
#
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
attr_reader :value
|
29
|
+
|
30
|
+
#
|
31
|
+
# The index of the returned value within the flag's list of variations. The first variation is
|
32
|
+
# 0, the second is 1, etc. This is `nil` if the default value was returned.
|
33
|
+
#
|
34
|
+
# @return [int|nil]
|
35
|
+
#
|
36
|
+
attr_reader :variation_index
|
37
|
+
|
38
|
+
#
|
39
|
+
# An object describing the main factor that influenced the flag evaluation value.
|
40
|
+
#
|
41
|
+
# @return [EvaluationReason]
|
42
|
+
#
|
43
|
+
attr_reader :reason
|
44
|
+
|
45
|
+
#
|
46
|
+
# Tests whether the flag evaluation returned a default value. This is the same as checking
|
47
|
+
# whether {#variation_index} is nil.
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
#
|
51
|
+
def default_value?
|
52
|
+
variation_index.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def ==(other)
|
56
|
+
@value == other.value && @variation_index == other.variation_index && @reason == other.reason
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Describes the reason that a flag evaluation produced a particular value. This is returned by
|
61
|
+
# methods such as {LDClient#variation_detail} as the `reason` property of an {EvaluationDetail}.
|
62
|
+
#
|
63
|
+
# The `kind` property is always defined, but other properties will have non-nil values only for
|
64
|
+
# certain values of `kind`. All properties are immutable.
|
65
|
+
#
|
66
|
+
# There is a standard JSON representation of evaluation reasons when they appear in analytics events.
|
67
|
+
# Use `as_json` or `to_json` to convert to this representation.
|
68
|
+
#
|
69
|
+
# Use factory methods such as {EvaluationReason#off} to obtain instances of this class.
|
70
|
+
class EvaluationReason
|
71
|
+
# Value for {#kind} indicating that the flag was off and therefore returned its configured off value.
|
72
|
+
OFF = :OFF
|
73
|
+
|
74
|
+
# Value for {#kind} indicating that the flag was on but the user did not match any targets or rules.
|
75
|
+
FALLTHROUGH = :FALLTHROUGH
|
76
|
+
|
77
|
+
# Value for {#kind} indicating that the user key was specifically targeted for this flag.
|
78
|
+
TARGET_MATCH = :TARGET_MATCH
|
79
|
+
|
80
|
+
# Value for {#kind} indicating that the user matched one of the flag's rules.
|
81
|
+
RULE_MATCH = :RULE_MATCH
|
82
|
+
|
83
|
+
# Value for {#kind} indicating that the flag was considered off because it had at least one
|
84
|
+
# prerequisite flag that either was off or did not return the desired variation.
|
85
|
+
PREREQUISITE_FAILED = :PREREQUISITE_FAILED
|
86
|
+
|
87
|
+
# Value for {#kind} indicating that the flag could not be evaluated, e.g. because it does not exist
|
88
|
+
# or due to an unexpected error. In this case the result value will be the application default value
|
89
|
+
# that the caller passed to the client. Check {#error_kind} for more details on the problem.
|
90
|
+
ERROR = :ERROR
|
91
|
+
|
92
|
+
# Value for {#error_kind} indicating that the caller tried to evaluate a flag before the client had
|
93
|
+
# successfully initialized.
|
94
|
+
ERROR_CLIENT_NOT_READY = :CLIENT_NOT_READY
|
95
|
+
|
96
|
+
# Value for {#error_kind} indicating that the caller provided a flag key that did not match any known flag.
|
97
|
+
ERROR_FLAG_NOT_FOUND = :FLAG_NOT_FOUND
|
98
|
+
|
99
|
+
# Value for {#error_kind} indicating that there was an internal inconsistency in the flag data, e.g.
|
100
|
+
# a rule specified a nonexistent variation. An error message will always be logged in this case.
|
101
|
+
ERROR_MALFORMED_FLAG = :MALFORMED_FLAG
|
102
|
+
|
103
|
+
# Value for {#error_kind} indicating that the caller passed `nil` for the user parameter, or the
|
104
|
+
# user lacked a key.
|
105
|
+
ERROR_USER_NOT_SPECIFIED = :USER_NOT_SPECIFIED
|
106
|
+
|
107
|
+
# Value for {#error_kind} indicating that an unexpected exception stopped flag evaluation. An error
|
108
|
+
# message will always be logged in this case.
|
109
|
+
ERROR_EXCEPTION = :EXCEPTION
|
110
|
+
|
111
|
+
# Indicates the general category of the reason. Will always be one of the class constants such
|
112
|
+
# as {#OFF}.
|
113
|
+
attr_reader :kind
|
114
|
+
|
115
|
+
# The index of the rule that was matched (0 for the first rule in the feature flag). If
|
116
|
+
# {#kind} is not {#RULE_MATCH}, this will be `nil`.
|
117
|
+
attr_reader :rule_index
|
118
|
+
|
119
|
+
# A unique string identifier for the matched rule, which will not change if other rules are added
|
120
|
+
# or deleted. If {#kind} is not {#RULE_MATCH}, this will be `nil`.
|
121
|
+
attr_reader :rule_id
|
122
|
+
|
123
|
+
# The key of the prerequisite flag that did not return the desired variation. If {#kind} is not
|
124
|
+
# {#PREREQUISITE_FAILED}, this will be `nil`.
|
125
|
+
attr_reader :prerequisite_key
|
126
|
+
|
127
|
+
# A value indicating the general category of error. This should be one of the class constants such
|
128
|
+
# as {#ERROR_FLAG_NOT_FOUND}. If {#kind} is not {#ERROR}, it will be `nil`.
|
129
|
+
attr_reader :error_kind
|
130
|
+
|
131
|
+
# Returns an instance whose {#kind} is {#OFF}.
|
132
|
+
# @return [EvaluationReason]
|
133
|
+
def self.off
|
134
|
+
@@off
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns an instance whose {#kind} is {#FALLTHROUGH}.
|
138
|
+
# @return [EvaluationReason]
|
139
|
+
def self.fallthrough
|
140
|
+
@@fallthrough
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns an instance whose {#kind} is {#TARGET_MATCH}.
|
144
|
+
# @return [EvaluationReason]
|
145
|
+
def self.target_match
|
146
|
+
@@target_match
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns an instance whose {#kind} is {#RULE_MATCH}.
|
150
|
+
#
|
151
|
+
# @param rule_index [Number] the index of the rule that was matched (0 for the first rule in
|
152
|
+
# the feature flag)
|
153
|
+
# @param rule_id [String] unique string identifier for the matched rule
|
154
|
+
# @return [EvaluationReason]
|
155
|
+
# @raise [ArgumentError] if `rule_index` is not a number or `rule_id` is not a string
|
156
|
+
def self.rule_match(rule_index, rule_id)
|
157
|
+
raise ArgumentError.new("rule_index must be a number") if !(rule_index.is_a? Numeric)
|
158
|
+
raise ArgumentError.new("rule_id must be a string") if !rule_id.nil? && !(rule_id.is_a? String) # in test data, ID could be nil
|
159
|
+
new(:RULE_MATCH, rule_index, rule_id, nil, nil)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns an instance whose {#kind} is {#PREREQUISITE_FAILED}.
|
163
|
+
#
|
164
|
+
# @param prerequisite_key [String] key of the prerequisite flag that did not return the desired variation
|
165
|
+
# @return [EvaluationReason]
|
166
|
+
# @raise [ArgumentError] if `prerequisite_key` is nil or not a string
|
167
|
+
def self.prerequisite_failed(prerequisite_key)
|
168
|
+
raise ArgumentError.new("prerequisite_key must be a string") if !(prerequisite_key.is_a? String)
|
169
|
+
new(:PREREQUISITE_FAILED, nil, nil, prerequisite_key, nil)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns an instance whose {#kind} is {#ERROR}.
|
173
|
+
#
|
174
|
+
# @param error_kind [Symbol] value indicating the general category of error
|
175
|
+
# @return [EvaluationReason]
|
176
|
+
# @raise [ArgumentError] if `error_kind` is not a symbol
|
177
|
+
def self.error(error_kind)
|
178
|
+
raise ArgumentError.new("error_kind must be a symbol") if !(error_kind.is_a? Symbol)
|
179
|
+
e = @@error_instances[error_kind]
|
180
|
+
e.nil? ? make_error(error_kind) : e
|
181
|
+
end
|
182
|
+
|
183
|
+
def ==(other)
|
184
|
+
if other.is_a? EvaluationReason
|
185
|
+
@kind == other.kind && @rule_index == other.rule_index && @rule_id == other.rule_id &&
|
186
|
+
@prerequisite_key == other.prerequisite_key && @error_kind == other.error_kind
|
187
|
+
elsif other.is_a? Hash
|
188
|
+
@kind.to_s == other[:kind] && @rule_index == other[:ruleIndex] && @rule_id == other[:ruleId] &&
|
189
|
+
@prerequisite_key == other[:prerequisiteKey] &&
|
190
|
+
(other[:errorKind] == @error_kind.nil? ? nil : @error_kind.to_s)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Equivalent to {#inspect}.
|
195
|
+
# @return [String]
|
196
|
+
def to_s
|
197
|
+
inspect
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns a concise string representation of the reason. Examples: `"FALLTHROUGH"`,
|
201
|
+
# `"ERROR(FLAG_NOT_FOUND)"`. The exact syntax is not guaranteed to remain the same; this is meant
|
202
|
+
# for debugging.
|
203
|
+
# @return [String]
|
204
|
+
def inspect
|
205
|
+
case @kind
|
206
|
+
when :RULE_MATCH
|
207
|
+
"RULE_MATCH(#{@rule_index},#{@rule_id})"
|
208
|
+
when :PREREQUISITE_FAILED
|
209
|
+
"PREREQUISITE_FAILED(#{@prerequisite_key})"
|
210
|
+
when :ERROR
|
211
|
+
"ERROR(#{@error_kind})"
|
212
|
+
else
|
213
|
+
@kind.to_s
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Returns a hash that can be used as a JSON representation of the reason, in the format used
|
218
|
+
# in LaunchDarkly analytics events.
|
219
|
+
# @return [Hash]
|
220
|
+
def as_json(*) # parameter is unused, but may be passed if we're using the json gem
|
221
|
+
# Note that this implementation is somewhat inefficient; it allocates a new hash every time.
|
222
|
+
# However, in normal usage the SDK only serializes reasons if 1. full event tracking is
|
223
|
+
# enabled for a flag and the application called variation_detail, or 2. experimentation is
|
224
|
+
# enabled for an evaluation. We can't reuse these hashes because an application could call
|
225
|
+
# as_json and then modify the result.
|
226
|
+
case @kind
|
227
|
+
when :RULE_MATCH
|
228
|
+
{ kind: @kind, ruleIndex: @rule_index, ruleId: @rule_id }
|
229
|
+
when :PREREQUISITE_FAILED
|
230
|
+
{ kind: @kind, prerequisiteKey: @prerequisite_key }
|
231
|
+
when :ERROR
|
232
|
+
{ kind: @kind, errorKind: @error_kind }
|
233
|
+
else
|
234
|
+
{ kind: @kind }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Same as {#as_json}, but converts the JSON structure into a string.
|
239
|
+
# @return [String]
|
240
|
+
def to_json(*a)
|
241
|
+
as_json.to_json(a)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Allows this object to be treated as a hash corresponding to its JSON representation. For
|
245
|
+
# instance, if `reason.kind` is {#RULE_MATCH}, then `reason[:kind]` will be `"RULE_MATCH"` and
|
246
|
+
# `reason[:ruleIndex]` will be equal to `reason.rule_index`.
|
247
|
+
def [](key)
|
248
|
+
case key
|
249
|
+
when :kind
|
250
|
+
@kind.to_s
|
251
|
+
when :ruleIndex
|
252
|
+
@rule_index
|
253
|
+
when :ruleId
|
254
|
+
@rule_id
|
255
|
+
when :prerequisiteKey
|
256
|
+
@prerequisite_key
|
257
|
+
when :errorKind
|
258
|
+
@error_kind.nil? ? nil : @error_kind.to_s
|
259
|
+
else
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
|
266
|
+
def initialize(kind, rule_index, rule_id, prerequisite_key, error_kind)
|
267
|
+
@kind = kind.to_sym
|
268
|
+
@rule_index = rule_index
|
269
|
+
@rule_id = rule_id
|
270
|
+
@rule_id.freeze if !rule_id.nil?
|
271
|
+
@prerequisite_key = prerequisite_key
|
272
|
+
@prerequisite_key.freeze if !prerequisite_key.nil?
|
273
|
+
@error_kind = error_kind
|
274
|
+
end
|
275
|
+
|
276
|
+
private_class_method :new
|
277
|
+
|
278
|
+
def self.make_error(error_kind)
|
279
|
+
new(:ERROR, nil, nil, nil, error_kind)
|
280
|
+
end
|
281
|
+
|
282
|
+
@@fallthrough = new(:FALLTHROUGH, nil, nil, nil, nil)
|
283
|
+
@@off = new(:OFF, nil, nil, nil, nil)
|
284
|
+
@@target_match = new(:TARGET_MATCH, nil, nil, nil, nil)
|
285
|
+
@@error_instances = {
|
286
|
+
ERROR_CLIENT_NOT_READY => make_error(ERROR_CLIENT_NOT_READY),
|
287
|
+
ERROR_FLAG_NOT_FOUND => make_error(ERROR_FLAG_NOT_FOUND),
|
288
|
+
ERROR_MALFORMED_FLAG => make_error(ERROR_MALFORMED_FLAG),
|
289
|
+
ERROR_USER_NOT_SPECIFIED => make_error(ERROR_USER_NOT_SPECIFIED),
|
290
|
+
ERROR_EXCEPTION => make_error(ERROR_EXCEPTION)
|
291
|
+
}
|
292
|
+
end
|
293
|
+
end
|