flagsmith 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f75b8801ce9af235343975f0e7ded1e3d6fa79544ffccc94e9052a1293143cfb
4
- data.tar.gz: 5fb178bb71c4fa72207e5843fcbc6d2778285e85be2f4138e19f9822038d68c8
3
+ metadata.gz: 18c4f2e2e68aaffe9c18d2b1e2af5f0018695ecc17c5af3a94051c8bf4cde1b8
4
+ data.tar.gz: 53e41364ca7a0347f0358d80616c4781f81835bff43b8f2aedc15d5fa53bb77d
5
5
  SHA512:
6
- metadata.gz: 17306e034862b4b66996edb1bed6f3744a15e74dcc283a10630b3c25dd6dde1a8ed80259d24169d167030af6e264f5da8df9141c5c8790668596e29441819ab1
7
- data.tar.gz: d46975cd909e97f08c0f81f15b4d265fe11c9762a8f4558701af380874e338ee3d101d5a05be68264ca5f53861290250046ea8aa3e6ae3b374421b6c7c7d35a2
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.0.0)
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.3)
33
- multipart-post (>= 1.2, < 3)
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.1.1)
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.4)
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/ruby/](https://docs.flagsmith.com/clients/ruby/)
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
@@ -172,7 +172,7 @@ GEM
172
172
  public_suffix (4.0.7)
173
173
  puma (5.6.4)
174
174
  nio4r (~> 2.0)
175
- rack (2.2.3)
175
+ rack (2.2.3.1)
176
176
  rack-test (1.1.0)
177
177
  rack (>= 1.0, < 3)
178
178
  rainbow (3.1.1)
@@ -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::Flag.new(
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
- module Core
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
- return condition.match_trait_value?(trait.value) if trait
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(feature_id)
35
- @analytics_data[feature_id] = @analytics_data.fetch(feature_id, 0) + 1
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
- # Flag Collection
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.feature_id) if @analytics_processor && flag.feature_id
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
- Flagsmith::Flag.from_feature_state_model(feature_state, identity_id)
162
+ Flagsmith::Flags::Flag.from_feature_state_model(feature_state, identity_id)
91
163
  }
92
164
 
93
165
  new(
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flagsmith
4
+ module Segments
5
+ class Segment
6
+ attr_reader :id, :name
7
+
8
+ def initialize(id:, name:)
9
+ @id = id
10
+ @name = name
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Flagsmith
4
- VERSION = '3.0.0'
4
+ VERSION = '3.1.0'
5
5
  end
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/flag'
18
- require 'flagsmith/sdk/models/flags/collection'
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.0.0
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-06-07 00:00:00.000000000 Z
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/flag.rb
226
- - lib/flagsmith/sdk/models/flags/collection.rb
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.0.3.1
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