flagsmith 3.0.0 → 3.1.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 +4 -4
- data/Gemfile.lock +5 -5
- data/README.md +1 -1
- data/example/Gemfile.lock +1 -1
- data/example/config/initializers/flagsmith.rb +1 -3
- data/flagsmith.gemspec +38 -0
- data/lib/flagsmith/engine/core.rb +1 -1
- data/lib/flagsmith/engine/segments/constants.rb +5 -1
- data/lib/flagsmith/engine/segments/evaluator.rb +14 -2
- data/lib/flagsmith/engine/segments/models.rb +10 -0
- data/lib/flagsmith/sdk/analytics_processor.rb +2 -2
- data/lib/flagsmith/sdk/models/{flags/collection.rb → flags.rb} +76 -4
- data/lib/flagsmith/sdk/models/segments.rb +14 -0
- data/lib/flagsmith/version.rb +1 -1
- data/lib/flagsmith.rb +150 -6
- metadata +9 -9
- data/lib/flagsmith/sdk/instance_methods.rb +0 -137
- data/lib/flagsmith/sdk/models/flag.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18c4f2e2e68aaffe9c18d2b1e2af5f0018695ecc17c5af3a94051c8bf4cde1b8
|
4
|
+
data.tar.gz: 53e41364ca7a0347f0358d80616c4781f81835bff43b8f2aedc15d5fa53bb77d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3da8964610c7fa4dbd31b94fdbc74b323711b49a4b9be7ad6f92738fc4c9b38e9a69f6a97c1875b78d6ccb86283325cc579b3f16d8b7d97e3b23eab89e8f8c1d
|
7
|
+
data.tar.gz: c464168ac8cfaff3611e95503b291b4e136e9bc944549b8ad66689232e0ef7411c2074781fbb8f7fd92bdb80775b3317400eb101f8c95bd83bb4d94206891033
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
flagsmith (3.
|
4
|
+
flagsmith (3.1.0)
|
5
5
|
faraday
|
6
6
|
faraday-retry
|
7
7
|
faraday_middleware
|
@@ -29,8 +29,8 @@ GEM
|
|
29
29
|
faraday-em_synchrony (1.0.0)
|
30
30
|
faraday-excon (1.1.0)
|
31
31
|
faraday-httpclient (1.0.1)
|
32
|
-
faraday-multipart (1.0.
|
33
|
-
multipart-post (
|
32
|
+
faraday-multipart (1.0.4)
|
33
|
+
multipart-post (~> 2)
|
34
34
|
faraday-net_http (1.0.1)
|
35
35
|
faraday-net_http_persistent (1.2.0)
|
36
36
|
faraday-patron (1.0.0)
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
faraday (~> 1.0)
|
41
41
|
gem-release (2.2.0)
|
42
42
|
method_source (1.0.0)
|
43
|
-
multipart-post (2.
|
43
|
+
multipart-post (2.2.3)
|
44
44
|
parallel (1.20.1)
|
45
45
|
parser (3.0.0.0)
|
46
46
|
ast (~> 2.4.1)
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
rainbow (3.0.0)
|
51
51
|
rake (13.0.3)
|
52
52
|
regexp_parser (2.0.3)
|
53
|
-
rexml (3.2.
|
53
|
+
rexml (3.2.5)
|
54
54
|
rspec (3.10.0)
|
55
55
|
rspec-core (~> 3.10.0)
|
56
56
|
rspec-expectations (~> 3.10.0)
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ The SDK clients for Ruby [https://www.flagsmith.com/](https://www.flagsmith.com/
|
|
6
6
|
|
7
7
|
## Adding to your project
|
8
8
|
|
9
|
-
For full documentation visit [https://docs.flagsmith.com/clients/
|
9
|
+
For full documentation visit [https://docs.flagsmith.com/clients/server-side](https://docs.flagsmith.com/clients/server-side).
|
10
10
|
|
11
11
|
## Contributing
|
12
12
|
|
data/example/Gemfile.lock
CHANGED
@@ -2,8 +2,6 @@ $flagsmith = Flagsmith::Client.new(
|
|
2
2
|
enable_local_evaluation: true,
|
3
3
|
environment_refresh_interval_seconds: 60,
|
4
4
|
default_flag_handler: lambda { |feature_name|
|
5
|
-
Flagsmith::
|
6
|
-
feature_name: feature_name, enabled: false, value: {}.to_json, feature_id: nil
|
7
|
-
)
|
5
|
+
Flagsmith::Flags::DefaultFlag.new(enabled: false, value: {}.to_json)
|
8
6
|
}
|
9
7
|
)
|
data/flagsmith.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('lib/flagsmith/version', __dir__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.required_ruby_version = '>= 2.4.0'
|
7
|
+
spec.name = 'flagsmith'
|
8
|
+
spec.version = Flagsmith::VERSION
|
9
|
+
spec.authors = ['Tom Stuart', 'Brian Moelk']
|
10
|
+
spec.email = ['tom@solidstategroup.com', 'bmoelk@gmail.com']
|
11
|
+
# Specify which files should be added to the gem when it is released.
|
12
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
13
|
+
spec.files = Dir.chdir(__dir__) do
|
14
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
15
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
spec.summary = 'Flagsmith - Ship features with confidence'
|
20
|
+
spec.description = 'Ruby Client for Flagsmith. Ship features with confidence using feature flags and remote config. Host yourself or use our hosted version at https://flagsmith.com'
|
21
|
+
spec.homepage = 'https://flagsmith.com'
|
22
|
+
|
23
|
+
spec.bindir = 'exe'
|
24
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler'
|
28
|
+
spec.add_development_dependency 'gem-release'
|
29
|
+
spec.add_development_dependency 'pry'
|
30
|
+
spec.add_development_dependency 'rake'
|
31
|
+
spec.add_development_dependency 'rspec'
|
32
|
+
spec.add_development_dependency 'rubocop'
|
33
|
+
|
34
|
+
spec.add_dependency 'faraday'
|
35
|
+
spec.add_dependency 'faraday_middleware'
|
36
|
+
spec.add_dependency 'faraday-retry'
|
37
|
+
spec.add_dependency 'semantic'
|
38
|
+
end
|
@@ -15,7 +15,7 @@ require_relative 'utils/hash_func'
|
|
15
15
|
module Flagsmith
|
16
16
|
module Engine
|
17
17
|
# Flags engine methods
|
18
|
-
|
18
|
+
class Engine
|
19
19
|
include Flagsmith::Engine::Segments::Evaluator
|
20
20
|
|
21
21
|
def get_identity_feature_state(environment, identity, feature_name, override_traits = nil)
|
@@ -22,6 +22,9 @@ module Flagsmith
|
|
22
22
|
NOT_EQUAL = 'NOT_EQUAL'
|
23
23
|
REGEX = 'REGEX'
|
24
24
|
PERCENTAGE_SPLIT = 'PERCENTAGE_SPLIT'
|
25
|
+
IS_SET = 'IS_SET'
|
26
|
+
IS_NOT_SET = 'IS_NOT_SET'
|
27
|
+
MODULO = 'MODULO'
|
25
28
|
|
26
29
|
CONDITION_OPERATORS = [
|
27
30
|
EQUAL,
|
@@ -33,7 +36,8 @@ module Flagsmith
|
|
33
36
|
NOT_CONTAINS,
|
34
37
|
NOT_EQUAL,
|
35
38
|
REGEX,
|
36
|
-
PERCENTAGE_SPLIT
|
39
|
+
PERCENTAGE_SPLIT,
|
40
|
+
MODULO
|
37
41
|
].freeze
|
38
42
|
end
|
39
43
|
end
|
@@ -56,12 +56,24 @@ module Flagsmith
|
|
56
56
|
return hashed_percentage_for_object_ids([segment_id, identity_id]) <= condition.value.to_f
|
57
57
|
end
|
58
58
|
|
59
|
-
trait = identity_traits.find { |t| t.key == condition.property }
|
59
|
+
trait = identity_traits.find { |t| t.key.to_s == condition.property }
|
60
60
|
|
61
|
-
|
61
|
+
if [IS_SET, IS_NOT_SET].include?(condition.operator)
|
62
|
+
return handle_trait_existence_conditions(trait, condition.operator)
|
63
|
+
end
|
64
|
+
|
65
|
+
return condition.match_trait_value?(trait.trait_value) if trait
|
62
66
|
|
63
67
|
false
|
64
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def handle_trait_existence_conditions(matching_trait, operator)
|
73
|
+
return operator == IS_NOT_SET if matching_trait.nil?
|
74
|
+
|
75
|
+
operator == IS_SET
|
76
|
+
end
|
65
77
|
end
|
66
78
|
end
|
67
79
|
end
|
@@ -55,10 +55,13 @@ module Flagsmith
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def match_trait_value?(trait_value)
|
58
|
+
# handle some exceptions
|
58
59
|
if @value.is_a?(String) && @value.match?(/:semver$/)
|
59
60
|
trait_value = Semantic::Version.new(trait_value.gsub(/:semver$/, ''))
|
60
61
|
end
|
61
62
|
|
63
|
+
return match_modulo_value(trait_value) if @operator == MODULO
|
64
|
+
|
62
65
|
type_as_trait_value = format_to_type_of(trait_value)
|
63
66
|
formatted_value = type_as_trait_value ? type_as_trait_value.call(@value) : @value
|
64
67
|
|
@@ -78,6 +81,13 @@ module Flagsmith
|
|
78
81
|
end
|
79
82
|
# rubocop:enable Metrics/AbcSize
|
80
83
|
|
84
|
+
def match_modulo_value(trait_value)
|
85
|
+
divisor, remainder = @value.split('|')
|
86
|
+
trait_value.is_a?(Numeric) && trait_value % divisor.to_f == remainder.to_f
|
87
|
+
rescue StandardError
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
81
91
|
class << self
|
82
92
|
def build(json)
|
83
93
|
new(**json.slice(:operator, :value).merge(property: json[:property_]))
|
@@ -31,8 +31,8 @@ module Flagsmith
|
|
31
31
|
@last_flushed = Time.now
|
32
32
|
end
|
33
33
|
|
34
|
-
def track_feature(
|
35
|
-
@analytics_data[
|
34
|
+
def track_feature(feature_name)
|
35
|
+
@analytics_data[feature_name] = @analytics_data.fetch(feature_name, 0) + 1
|
36
36
|
flush if (Time.now - @last_flushed) > TIMER * 1000
|
37
37
|
end
|
38
38
|
end
|
@@ -4,7 +4,79 @@ module Flagsmith
|
|
4
4
|
module Flags
|
5
5
|
class NotFound < StandardError; end
|
6
6
|
|
7
|
-
|
7
|
+
class BaseFlag
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_reader :enabled, :value, :default
|
11
|
+
|
12
|
+
def initialize(enabled:, value:, default:)
|
13
|
+
@enabled = enabled
|
14
|
+
@value = value
|
15
|
+
@default = default
|
16
|
+
end
|
17
|
+
|
18
|
+
def enabled?
|
19
|
+
enabled
|
20
|
+
end
|
21
|
+
|
22
|
+
alias is_default default
|
23
|
+
end
|
24
|
+
|
25
|
+
class DefaultFlag < BaseFlag
|
26
|
+
def initialize(enabled:, value:)
|
27
|
+
super(enabled: enabled, value: value, default: true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Flag < BaseFlag
|
32
|
+
|
33
|
+
attr_reader :feature_name, :feature_id
|
34
|
+
|
35
|
+
def initialize(feature_name:, enabled:, value:, feature_id:)
|
36
|
+
super(enabled: enabled, value: value, default: false)
|
37
|
+
@feature_name = feature_name
|
38
|
+
@feature_id = feature_id
|
39
|
+
end
|
40
|
+
|
41
|
+
def <=>(other)
|
42
|
+
feature_name <=> other.feature_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](key)
|
46
|
+
to_h[key]
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
{
|
51
|
+
feature_id: feature_id,
|
52
|
+
feature_name: feature_name,
|
53
|
+
value: value,
|
54
|
+
enabled: enabled,
|
55
|
+
default: default
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def from_feature_state_model(feature_state_model, identity_id)
|
61
|
+
new(
|
62
|
+
enabled: feature_state_model.enabled,
|
63
|
+
value: feature_state_model.get_value(identity_id),
|
64
|
+
feature_name: feature_state_model.feature.name,
|
65
|
+
feature_id: feature_state_model.feature.id
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def from_api(json_flag_data)
|
70
|
+
new(
|
71
|
+
enabled: json_flag_data[:enabled],
|
72
|
+
value: json_flag_data[:feature_state_value] || json_flag_data[:value],
|
73
|
+
feature_name: json_flag_data.dig(:feature, :name),
|
74
|
+
feature_id: json_flag_data.dig(:feature, :id)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
8
80
|
class Collection
|
9
81
|
include Enumerable
|
10
82
|
|
@@ -50,7 +122,7 @@ module Flagsmith
|
|
50
122
|
def get_flag(feature_name)
|
51
123
|
key = Flagsmith::Flags::Collection.normalize_key(feature_name)
|
52
124
|
flag = flags.fetch(key)
|
53
|
-
@analytics_processor.track_feature(flag.
|
125
|
+
@analytics_processor.track_feature(flag.feature_name) if @analytics_processor && flag.feature_id
|
54
126
|
flag
|
55
127
|
rescue KeyError
|
56
128
|
return @default_flag_handler.call(feature_name) if @default_flag_handler
|
@@ -75,7 +147,7 @@ module Flagsmith
|
|
75
147
|
def from_api(json_data, **args)
|
76
148
|
to_flag_object = lambda { |json_flag, acc|
|
77
149
|
acc[normalize_key(json_flag.dig(:feature, :name))] =
|
78
|
-
Flagsmith::Flag.from_api(json_flag)
|
150
|
+
Flagsmith::Flags::Flag.from_api(json_flag)
|
79
151
|
}
|
80
152
|
|
81
153
|
new(
|
@@ -87,7 +159,7 @@ module Flagsmith
|
|
87
159
|
def from_feature_state_models(feature_states, identity_id: nil, **args)
|
88
160
|
to_flag_object = lambda { |feature_state, acc|
|
89
161
|
acc[normalize_key(feature_state.feature.name)] =
|
90
|
-
|
162
|
+
Flagsmith::Flags::Flag.from_feature_state_model(feature_state, identity_id)
|
91
163
|
}
|
92
164
|
|
93
165
|
new(
|
data/lib/flagsmith/version.rb
CHANGED
data/lib/flagsmith.rb
CHANGED
@@ -14,9 +14,8 @@ require 'flagsmith/sdk/config'
|
|
14
14
|
require 'flagsmith/sdk/errors'
|
15
15
|
require 'flagsmith/sdk/intervals'
|
16
16
|
require 'flagsmith/sdk/pooling_manager'
|
17
|
-
require 'flagsmith/sdk/models/
|
18
|
-
require 'flagsmith/sdk/models/
|
19
|
-
require 'flagsmith/sdk/instance_methods'
|
17
|
+
require 'flagsmith/sdk/models/flags'
|
18
|
+
require 'flagsmith/sdk/models/segments'
|
20
19
|
|
21
20
|
require 'flagsmith/engine/core'
|
22
21
|
|
@@ -25,8 +24,6 @@ module Flagsmith
|
|
25
24
|
# Ruby client for flagsmith.com
|
26
25
|
class Client
|
27
26
|
extend Forwardable
|
28
|
-
include Flagsmith::SDK::InstanceMethods
|
29
|
-
include Flagsmith::Engine::Core
|
30
27
|
# A Flagsmith client.
|
31
28
|
#
|
32
29
|
# Provides an interface for interacting with the Flagsmith http API.
|
@@ -38,9 +35,11 @@ module Flagsmith
|
|
38
35
|
# feature_enabled = environment_flags.is_feature_enabled('foo')
|
39
36
|
# feature_value = identity_flags.get_feature_value('foo')
|
40
37
|
#
|
41
|
-
# identity_flags = flagsmith.get_identity_flags('identifier', 'foo': 'bar')
|
38
|
+
# identity_flags = flagsmith.get_identity_flags('identifier', {'foo': 'bar'})
|
42
39
|
# feature_enabled_for_identity = identity_flags.is_feature_enabled('foo')
|
43
40
|
# feature_value_for_identity = identity_flags.get_feature_value('foo')
|
41
|
+
#
|
42
|
+
# identity_segments = flagsmith.get_identity_segments('identifier', {'foo': 'bar'})
|
44
43
|
|
45
44
|
# Available Configs.
|
46
45
|
#
|
@@ -59,12 +58,17 @@ module Flagsmith
|
|
59
58
|
api_client
|
60
59
|
analytics_processor
|
61
60
|
environment_data_polling_manager
|
61
|
+
engine
|
62
62
|
end
|
63
63
|
|
64
64
|
def api_client
|
65
65
|
@api_client ||= Flagsmith::ApiClient.new(@config)
|
66
66
|
end
|
67
67
|
|
68
|
+
def engine
|
69
|
+
@engine ||= Flagsmith::Engine::Engine.new
|
70
|
+
end
|
71
|
+
|
68
72
|
def analytics_processor
|
69
73
|
return nil unless @config.enable_analytics?
|
70
74
|
|
@@ -95,5 +99,145 @@ module Flagsmith
|
|
95
99
|
environment_data = api_client.get(@config.environment_url).body
|
96
100
|
Flagsmith::Engine::Environment.build(environment_data)
|
97
101
|
end
|
102
|
+
|
103
|
+
# Get all the default for flags for the current environment.
|
104
|
+
# @returns Flags object holding all the flags for the current environment.
|
105
|
+
def get_environment_flags # rubocop:disable Naming/AccessorMethodName
|
106
|
+
return environment_flags_from_document if @config.local_evaluation?
|
107
|
+
|
108
|
+
environment_flags_from_api
|
109
|
+
end
|
110
|
+
|
111
|
+
# Get all the flags for the current environment for a given identity. Will also
|
112
|
+
# upsert all traits to the Flagsmith API for future evaluations. Providing a
|
113
|
+
# trait with a value of None will remove the trait from the identity if it exists.
|
114
|
+
#
|
115
|
+
# identifier a unique identifier for the identity in the current
|
116
|
+
# environment, e.g. email address, username, uuid
|
117
|
+
# traits { key => value } is a dictionary of traits to add / update on the identity in
|
118
|
+
# Flagsmith, e.g. { "num_orders": 10 }
|
119
|
+
# returns Flags object holding all the flags for the given identity.
|
120
|
+
def get_identity_flags(identifier, **traits)
|
121
|
+
return get_identity_flags_from_document(identifier, traits) if environment
|
122
|
+
|
123
|
+
get_identity_flags_from_api(identifier, traits)
|
124
|
+
end
|
125
|
+
|
126
|
+
def feature_enabled?(feature_name, default: false)
|
127
|
+
flag = get_environment_flags[feature_name]
|
128
|
+
return default if flag.nil?
|
129
|
+
|
130
|
+
flag.enabled?
|
131
|
+
end
|
132
|
+
|
133
|
+
def feature_enabled_for_identity?(feature_name, user_id, default: false)
|
134
|
+
flag = get_identity_flags(user_id)[feature_name]
|
135
|
+
return default if flag.nil?
|
136
|
+
|
137
|
+
flag.enabled?
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_value(feature_name, default: nil)
|
141
|
+
flag = get_environment_flags[feature_name]
|
142
|
+
return default if flag.nil?
|
143
|
+
|
144
|
+
flag.value
|
145
|
+
end
|
146
|
+
|
147
|
+
def get_value_for_identity(feature_name, user_id = nil, default: nil)
|
148
|
+
flag = get_identity_flags(user_id)[feature_name]
|
149
|
+
return default if flag.nil?
|
150
|
+
|
151
|
+
flag.value
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_identity_segments(identifier, traits = {})
|
155
|
+
unless environment
|
156
|
+
raise Flagsmith::ClientError,
|
157
|
+
'Local evaluation required to obtain identity segments.'
|
158
|
+
end
|
159
|
+
|
160
|
+
identity_model = build_identity_model(identifier, traits)
|
161
|
+
segment_models = engine.get_identity_segments(environment, identity_model)
|
162
|
+
return segment_models.map { |sm| Flagsmith::Segments::Segment.new(id: sm.id, name: sm.name) }.compact
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def environment_flags_from_document
|
168
|
+
Flagsmith::Flags::Collection.from_feature_state_models(
|
169
|
+
engine.get_environment_feature_states(environment),
|
170
|
+
analytics_processor: analytics_processor,
|
171
|
+
default_flag_handler: default_flag_handler
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_identity_flags_from_document(identifier, traits = {})
|
176
|
+
identity_model = build_identity_model(identifier, traits)
|
177
|
+
|
178
|
+
Flagsmith::Flags::Collection.from_feature_state_models(
|
179
|
+
engine.get_identity_feature_states(environment, identity_model),
|
180
|
+
analytics_processor: analytics_processor,
|
181
|
+
default_flag_handler: default_flag_handler
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
def environment_flags_from_api
|
186
|
+
rescue_with_default_handler do
|
187
|
+
api_flags = api_client.get(@config.environment_flags_url).body
|
188
|
+
api_flags = api_flags.select { |flag| flag[:feature_segment].nil? }
|
189
|
+
Flagsmith::Flags::Collection.from_api(
|
190
|
+
api_flags,
|
191
|
+
analytics_processor: analytics_processor,
|
192
|
+
default_flag_handler: default_flag_handler
|
193
|
+
)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_identity_flags_from_api(identifier, traits = {})
|
198
|
+
rescue_with_default_handler do
|
199
|
+
data = generate_identities_data(identifier, traits)
|
200
|
+
json_response = api_client.post(@config.identities_url, data.to_json).body
|
201
|
+
|
202
|
+
Flagsmith::Flags::Collection.from_api(
|
203
|
+
json_response[:flags],
|
204
|
+
analytics_processor: analytics_processor,
|
205
|
+
default_flag_handler: default_flag_handler
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def rescue_with_default_handler
|
211
|
+
yield
|
212
|
+
rescue StandardError
|
213
|
+
if default_flag_handler
|
214
|
+
return Flagsmith::Flags::Collection.new(
|
215
|
+
{},
|
216
|
+
default_flag_handler: default_flag_handler
|
217
|
+
)
|
218
|
+
end
|
219
|
+
raise
|
220
|
+
end
|
221
|
+
|
222
|
+
def build_identity_model(identifier, traits = {})
|
223
|
+
unless environment
|
224
|
+
raise Flagsmith::ClientError,
|
225
|
+
'Unable to build identity model when no local environment present.'
|
226
|
+
end
|
227
|
+
|
228
|
+
trait_models = traits.map do |key, value|
|
229
|
+
Flagsmith::Engine::Identities::Trait.new(trait_key: key, trait_value: value)
|
230
|
+
end
|
231
|
+
Flagsmith::Engine::Identity.new(
|
232
|
+
identity_traits: trait_models, environment_api_key: environment_key, identifier: identifier
|
233
|
+
)
|
234
|
+
end
|
235
|
+
|
236
|
+
def generate_identities_data(identifier, traits = {})
|
237
|
+
{
|
238
|
+
identifier: identifier,
|
239
|
+
traits: traits.map { |key, value| { trait_key: key, trait_value: value } }
|
240
|
+
}
|
241
|
+
end
|
98
242
|
end
|
99
243
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flagsmith
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Stuart
|
8
8
|
- Brian Moelk
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-11-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- example/spec/web/features/.gitkeep
|
205
205
|
- example/spec/web/views/application_layout_spec.rb
|
206
206
|
- example/spec/web/views/home/index_spec.rb
|
207
|
+
- flagsmith.gemspec
|
207
208
|
- lib/flagsmith.rb
|
208
209
|
- lib/flagsmith/engine/core.rb
|
209
210
|
- lib/flagsmith/engine/environments/models.rb
|
@@ -220,16 +221,15 @@ files:
|
|
220
221
|
- lib/flagsmith/sdk/api_client.rb
|
221
222
|
- lib/flagsmith/sdk/config.rb
|
222
223
|
- lib/flagsmith/sdk/errors.rb
|
223
|
-
- lib/flagsmith/sdk/instance_methods.rb
|
224
224
|
- lib/flagsmith/sdk/intervals.rb
|
225
|
-
- lib/flagsmith/sdk/models/
|
226
|
-
- lib/flagsmith/sdk/models/
|
225
|
+
- lib/flagsmith/sdk/models/flags.rb
|
226
|
+
- lib/flagsmith/sdk/models/segments.rb
|
227
227
|
- lib/flagsmith/sdk/pooling_manager.rb
|
228
228
|
- lib/flagsmith/version.rb
|
229
229
|
homepage: https://flagsmith.com
|
230
230
|
licenses: []
|
231
231
|
metadata: {}
|
232
|
-
post_install_message:
|
232
|
+
post_install_message:
|
233
233
|
rdoc_options: []
|
234
234
|
require_paths:
|
235
235
|
- lib
|
@@ -244,8 +244,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
244
244
|
- !ruby/object:Gem::Version
|
245
245
|
version: '0'
|
246
246
|
requirements: []
|
247
|
-
rubygems_version: 3.
|
248
|
-
signing_key:
|
247
|
+
rubygems_version: 3.3.7
|
248
|
+
signing_key:
|
249
249
|
specification_version: 4
|
250
250
|
summary: Flagsmith - Ship features with confidence
|
251
251
|
test_files: []
|
@@ -1,137 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Flagsmith
|
4
|
-
module SDK
|
5
|
-
# Available Flagsmith Functions
|
6
|
-
module InstanceMethods
|
7
|
-
# Get all the default for flags for the current environment.
|
8
|
-
# @returns Flags object holding all the flags for the current environment.
|
9
|
-
def get_environment_flags # rubocop:disable Naming/AccessorMethodName
|
10
|
-
return environment_flags_from_document if @config.local_evaluation?
|
11
|
-
|
12
|
-
environment_flags_from_api
|
13
|
-
end
|
14
|
-
|
15
|
-
# Get all the flags for the current environment for a given identity. Will also
|
16
|
-
# upsert all traits to the Flagsmith API for future evaluations. Providing a
|
17
|
-
# trait with a value of None will remove the trait from the identity if it exists.
|
18
|
-
#
|
19
|
-
# identifier a unique identifier for the identity in the current
|
20
|
-
# environment, e.g. email address, username, uuid
|
21
|
-
# traits { key => value } is a dictionary of traits to add / update on the identity in
|
22
|
-
# Flagsmith, e.g. { "num_orders": 10 }
|
23
|
-
# returns Flags object holding all the flags for the given identity.
|
24
|
-
def get_identity_flags(identifier, **traits)
|
25
|
-
return get_identity_flags_from_document(identifier, traits) if environment
|
26
|
-
|
27
|
-
get_identity_flags_from_api(identifier, traits)
|
28
|
-
end
|
29
|
-
|
30
|
-
def feature_enabled?(feature_name, default: false)
|
31
|
-
flag = get_environment_flags[feature_name]
|
32
|
-
return default if flag.nil?
|
33
|
-
|
34
|
-
flag.enabled?
|
35
|
-
end
|
36
|
-
|
37
|
-
def feature_enabled_for_identity?(feature_name, user_id, default: false)
|
38
|
-
flag = get_identity_flags(user_id)[feature_name]
|
39
|
-
return default if flag.nil?
|
40
|
-
|
41
|
-
flag.enabled?
|
42
|
-
end
|
43
|
-
|
44
|
-
def get_value(feature_name, default: nil)
|
45
|
-
flag = get_environment_flags[feature_name]
|
46
|
-
return default if flag.nil?
|
47
|
-
|
48
|
-
flag.value
|
49
|
-
end
|
50
|
-
|
51
|
-
def get_value_for_identity(feature_name, user_id = nil, default: nil)
|
52
|
-
flag = get_identity_flags(user_id)[feature_name]
|
53
|
-
return default if flag.nil?
|
54
|
-
|
55
|
-
flag.value
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def environment_flags_from_document
|
61
|
-
Flagsmith::Flags::Collection.from_feature_state_models(
|
62
|
-
get_environment_feature_states(environment),
|
63
|
-
analytics_processor: analytics_processor,
|
64
|
-
default_flag_handler: default_flag_handler
|
65
|
-
)
|
66
|
-
end
|
67
|
-
|
68
|
-
def get_identity_flags_from_document(identifier, traits = {})
|
69
|
-
identity_model = build_identity_model(identifier, traits)
|
70
|
-
|
71
|
-
Flagsmith::Flags::Collection.from_feature_state_models(
|
72
|
-
get_identity_feature_states(environment, identity_model),
|
73
|
-
analytics_processor: analytics_processor,
|
74
|
-
default_flag_handler: default_flag_handler
|
75
|
-
)
|
76
|
-
end
|
77
|
-
|
78
|
-
def environment_flags_from_api
|
79
|
-
rescue_with_default_handler do
|
80
|
-
api_flags = api_client.get(@config.environment_flags_url).body
|
81
|
-
api_flags = api_flags.select { |flag| flag[:feature_segment].nil? }
|
82
|
-
Flagsmith::Flags::Collection.from_api(
|
83
|
-
api_flags,
|
84
|
-
analytics_processor: analytics_processor,
|
85
|
-
default_flag_handler: default_flag_handler
|
86
|
-
)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def get_identity_flags_from_api(identifier, traits = {})
|
91
|
-
rescue_with_default_handler do
|
92
|
-
data = generate_identities_data(identifier, traits)
|
93
|
-
json_response = api_client.post(@config.identities_url, data.to_json).body
|
94
|
-
|
95
|
-
Flagsmith::Flags::Collection.from_api(
|
96
|
-
json_response[:flags],
|
97
|
-
analytics_processor: analytics_processor,
|
98
|
-
default_flag_handler: default_flag_handler
|
99
|
-
)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def rescue_with_default_handler
|
104
|
-
yield
|
105
|
-
rescue StandardError
|
106
|
-
if default_flag_handler
|
107
|
-
return Flagsmith::Flags::Collection.new(
|
108
|
-
{},
|
109
|
-
default_flag_handler: default_flag_handler
|
110
|
-
)
|
111
|
-
end
|
112
|
-
raise
|
113
|
-
end
|
114
|
-
|
115
|
-
def build_identity_model(identifier, traits = {})
|
116
|
-
unless environment
|
117
|
-
raise Flagsmith::ClientError,
|
118
|
-
'Unable to build identity model when no local environment present.'
|
119
|
-
end
|
120
|
-
|
121
|
-
trait_models = traits.map do |key, value|
|
122
|
-
Flagsmith::Engine::Identities::Trait.new(trait_key: key, trait_value: value)
|
123
|
-
end
|
124
|
-
Flagsmith::Engine::Identity.new(
|
125
|
-
identity_traits: trait_models, environment_api_key: environment_key, identifier: identifier
|
126
|
-
)
|
127
|
-
end
|
128
|
-
|
129
|
-
def generate_identities_data(identifier, traits = {})
|
130
|
-
{
|
131
|
-
identifier: identifier,
|
132
|
-
traits: traits.map { |key, value| { trait_key: key, trait_value: value } }
|
133
|
-
}
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Flagsmith
|
4
|
-
# Flag object
|
5
|
-
class Flag
|
6
|
-
include Comparable
|
7
|
-
|
8
|
-
attr_reader :enabled, :value, :default, :feature_name, :feature_id
|
9
|
-
|
10
|
-
def initialize(feature_name:, enabled:, value:, feature_id:, default: false)
|
11
|
-
@feature_name = feature_name
|
12
|
-
@feature_id = feature_id
|
13
|
-
@enabled = enabled
|
14
|
-
@value = value
|
15
|
-
@default = default
|
16
|
-
end
|
17
|
-
|
18
|
-
def enabled?
|
19
|
-
@enabled
|
20
|
-
end
|
21
|
-
|
22
|
-
alias is_default default
|
23
|
-
|
24
|
-
def <=>(other)
|
25
|
-
feature_name <=> other.feature_name
|
26
|
-
end
|
27
|
-
|
28
|
-
def [](key)
|
29
|
-
to_h[key]
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_h
|
33
|
-
{
|
34
|
-
feature_id: feature_id,
|
35
|
-
feature_name: feature_name,
|
36
|
-
value: value,
|
37
|
-
enabled: enabled,
|
38
|
-
default: default
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
class << self
|
43
|
-
def from_feature_state_model(feature_state_model, identity_id)
|
44
|
-
new(
|
45
|
-
enabled: feature_state_model.enabled,
|
46
|
-
value: feature_state_model.get_value(identity_id),
|
47
|
-
feature_name: feature_state_model.feature.name,
|
48
|
-
feature_id: feature_state_model.feature.id
|
49
|
-
)
|
50
|
-
end
|
51
|
-
|
52
|
-
def from_api(json_flag_data)
|
53
|
-
new(
|
54
|
-
enabled: json_flag_data[:enabled],
|
55
|
-
value: json_flag_data[:feature_state_value] || json_flag_data[:value],
|
56
|
-
feature_name: json_flag_data.dig(:feature, :name),
|
57
|
-
feature_id: json_flag_data.dig(:feature, :id)
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|