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 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