launchdarkly-server-sdk 5.7.3 → 6.0.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 +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 +36 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +92 -76
- data/README.md +5 -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 +1 -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_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 +16 -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/user_filter.rb +3 -2
- 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 +11 -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 +10 -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
|
[](https://circleci.com/gh/launchdarkly/ruby-server-sdk/tree/master)
|
7
7
|
[](https://hakiri.io/github/launchdarkly/ruby-server-sdk/master)
|
8
|
+
[](https://www.rubydoc.info/gems/launchdarkly-server-sdk)
|
9
|
+
[](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,4 @@ 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
|
58
|
+
* [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
|
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
|