flagsmith 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +4 -1
  4. data/.rubocop_todo.yml +0 -1
  5. data/.ruby-version +1 -0
  6. data/Gemfile.lock +35 -6
  7. data/LICENCE +4 -5
  8. data/README.md +8 -64
  9. data/Rakefile +5 -1
  10. data/example/.env.development +5 -0
  11. data/example/.env.test +4 -0
  12. data/example/.gitignore +5 -0
  13. data/example/.hanamirc +3 -0
  14. data/example/.rspec +2 -0
  15. data/example/Gemfile +25 -0
  16. data/example/Gemfile.lock +269 -0
  17. data/example/README.md +29 -0
  18. data/example/Rakefile +9 -0
  19. data/example/apps/web/application.rb +162 -0
  20. data/example/apps/web/config/routes.rb +1 -0
  21. data/example/apps/web/controllers/.gitkeep +0 -0
  22. data/example/apps/web/controllers/home/index.rb +32 -0
  23. data/example/apps/web/templates/application.html.slim +7 -0
  24. data/example/apps/web/templates/home/index.html.slim +10 -0
  25. data/example/apps/web/views/application_layout.rb +7 -0
  26. data/example/apps/web/views/home/index.rb +29 -0
  27. data/example/config/boot.rb +2 -0
  28. data/example/config/environment.rb +17 -0
  29. data/example/config/initializers/.gitkeep +0 -0
  30. data/example/config/initializers/flagsmith.rb +9 -0
  31. data/example/config/puma.rb +15 -0
  32. data/example/config.ru +3 -0
  33. data/example/spec/example/entities/.gitkeep +0 -0
  34. data/example/spec/example/mailers/.gitkeep +0 -0
  35. data/example/spec/example/repositories/.gitkeep +0 -0
  36. data/example/spec/features_helper.rb +12 -0
  37. data/example/spec/spec_helper.rb +103 -0
  38. data/example/spec/support/.gitkeep +0 -0
  39. data/example/spec/support/capybara.rb +8 -0
  40. data/example/spec/web/controllers/.gitkeep +0 -0
  41. data/example/spec/web/controllers/home/index_spec.rb +9 -0
  42. data/example/spec/web/features/.gitkeep +0 -0
  43. data/example/spec/web/views/application_layout_spec.rb +10 -0
  44. data/example/spec/web/views/home/index_spec.rb +10 -0
  45. data/lib/flagsmith/engine/core.rb +88 -0
  46. data/lib/flagsmith/engine/environments/models.rb +61 -0
  47. data/lib/flagsmith/engine/features/models.rb +173 -0
  48. data/lib/flagsmith/engine/identities/models.rb +115 -0
  49. data/lib/flagsmith/engine/organisations/models.rb +28 -0
  50. data/lib/flagsmith/engine/projects/models.rb +31 -0
  51. data/lib/flagsmith/engine/segments/constants.rb +41 -0
  52. data/lib/flagsmith/engine/segments/evaluator.rb +68 -0
  53. data/lib/flagsmith/engine/segments/models.rb +121 -0
  54. data/lib/flagsmith/engine/utils/hash_func.rb +34 -0
  55. data/lib/flagsmith/hash_slice.rb +12 -0
  56. data/lib/flagsmith/sdk/analytics_processor.rb +39 -0
  57. data/lib/flagsmith/sdk/api_client.rb +47 -0
  58. data/lib/flagsmith/sdk/config.rb +91 -0
  59. data/lib/flagsmith/sdk/errors.rb +9 -0
  60. data/lib/flagsmith/sdk/instance_methods.rb +137 -0
  61. data/lib/flagsmith/sdk/intervals.rb +24 -0
  62. data/lib/flagsmith/sdk/models/flag.rb +62 -0
  63. data/lib/flagsmith/sdk/models/flags/collection.rb +105 -0
  64. data/lib/flagsmith/sdk/pooling_manager.rb +31 -0
  65. data/lib/flagsmith/version.rb +5 -0
  66. data/lib/flagsmith.rb +79 -101
  67. metadata +104 -6
  68. data/.gitignore +0 -57
  69. data/flagsmith.gemspec +0 -22
data/lib/flagsmith.rb CHANGED
@@ -1,121 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday'
4
+ require 'faraday/retry'
4
5
  require 'faraday_middleware'
5
6
 
6
- # Ruby client for flagsmith.com
7
- class Flagsmith
8
- attr_reader :flagsmith_api
9
-
10
- def initialize(opts = {})
11
- @opts = determine_opts(opts)
12
-
13
- @flagsmith_api = Faraday.new(url: @opts[:url]) do |faraday|
14
- faraday.headers['Accept'] = 'application/json'
15
- faraday.headers['Content-Type'] = 'application/json'
16
- faraday.headers['x-environment-key'] = @opts[:api_key]
17
- faraday.response :json
18
- # TODO: add timeout adjustment here
19
- faraday.adapter Faraday.default_adapter
7
+ # Hash#slice was added in ruby version 2.5
8
+ # This is the patch to use slice in earler versions
9
+ require 'flagsmith/hash_slice'
10
+
11
+ require 'flagsmith/sdk/analytics_processor'
12
+ require 'flagsmith/sdk/api_client'
13
+ require 'flagsmith/sdk/config'
14
+ require 'flagsmith/sdk/errors'
15
+ require 'flagsmith/sdk/intervals'
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'
20
+
21
+ require 'flagsmith/engine/core'
22
+
23
+ # no-doc
24
+ module Flagsmith
25
+ # Ruby client for flagsmith.com
26
+ class Client
27
+ extend Forwardable
28
+ include Flagsmith::SDK::InstanceMethods
29
+ include Flagsmith::Engine::Core
30
+ # A Flagsmith client.
31
+ #
32
+ # Provides an interface for interacting with the Flagsmith http API.
33
+ # Basic Usage::
34
+ #
35
+ # flagsmith = Flagsmith::Client.new(environment_key: '<your API key>')
36
+ #
37
+ # environment_flags = flagsmith.get_environment_flags
38
+ # feature_enabled = environment_flags.is_feature_enabled('foo')
39
+ # feature_value = identity_flags.get_feature_value('foo')
40
+ #
41
+ # identity_flags = flagsmith.get_identity_flags('identifier', 'foo': 'bar')
42
+ # feature_enabled_for_identity = identity_flags.is_feature_enabled('foo')
43
+ # feature_value_for_identity = identity_flags.get_feature_value('foo')
44
+
45
+ # Available Configs.
46
+ #
47
+ # :environment_key, :api_url, :custom_headers, :request_timeout_seconds, :enable_local_evaluation,
48
+ # :environment_refresh_interval_seconds, :retries, :enable_analytics, :default_flag_handler
49
+ # You can see full description in the Flagsmith::Config
50
+
51
+ attr_reader :config, :environment
52
+
53
+ delegate Flagsmith::Config::OPTIONS => :@config
54
+
55
+ def initialize(config)
56
+ @_mutex = Mutex.new
57
+ @config = Flagsmith::Config.new(config)
58
+
59
+ api_client
60
+ analytics_processor
61
+ environment_data_polling_manager
20
62
  end
21
- end
22
63
 
23
- def get_flags(user_id = nil)
24
- if user_id.nil?
25
- res = @flagsmith_api.get('flags/')
26
- flags = transform_flags(res.body).select { |flag| flag[:segment].nil? }
27
- flags_to_hash(flags)
28
- else
29
- res = @flagsmith_api.get("identities/?identifier=#{user_id}")
30
- flags_to_hash(transform_flags(res.body['flags']))
64
+ def api_client
65
+ @api_client ||= Flagsmith::ApiClient.new(@config)
31
66
  end
32
- end
33
-
34
- def feature_enabled?(feature, user_id = nil, default = false)
35
- flag = get_flags(user_id)[normalize_key(feature)]
36
- return default if flag.nil?
37
-
38
- flag[:enabled]
39
- end
40
-
41
- def get_value(key, user_id = nil, default = nil)
42
- flag = get_flags(user_id)[normalize_key(key)]
43
- return default if flag.nil?
44
-
45
- flag[:value]
46
- end
47
67
 
48
- def set_trait(user_id, trait, value)
49
- raise StandardError, 'user_id cannot be nil' if user_id.nil?
68
+ def analytics_processor
69
+ return nil unless @config.enable_analytics?
50
70
 
51
- trait = {
52
- identity: { identifier: user_id },
53
- trait_key: normalize_key(trait),
54
- trait_value: value
55
- }
56
- res = @flagsmith_api.post('traits/', trait.to_json)
57
- res.body
58
- end
71
+ @analytics_processor ||=
72
+ Flagsmith::AnalyticsProcessor.new(
73
+ api_client: api_client,
74
+ timeout: request_timeout_seconds
75
+ )
76
+ end
59
77
 
60
- def get_traits(user_id)
61
- return {} if user_id.nil?
78
+ def environment_data_polling_manager
79
+ return nil unless @config.local_evaluation?
62
80
 
63
- res = @flagsmith_api.get("identities/?identifier=#{user_id}")
64
- traits_to_hash(res.body)
65
- end
81
+ update_environment
66
82
 
67
- # def remove_trait(user_id, trait_id)
68
- # # Request URL: https://api.flagsmith.com/api/v1/environments/API_KEY/identities/12345/traits/54321/
69
- # # Request Method: DELETE
70
- # end
71
-
72
- def transform_flags(flags)
73
- flags.map do |flag|
74
- {
75
- name: flag['feature']['name'],
76
- enabled: flag['enabled'],
77
- value: flag['feature_state_value'],
78
- segment: flag['feature_segment']
79
- }
83
+ @environment_data_polling_manager ||= Flagsmith::EnvironmentDataPollingManager.new(
84
+ self, environment_refresh_interval_seconds
85
+ ).tap(&:start)
80
86
  end
81
- end
82
87
 
83
- def flags_to_hash(flags)
84
- result = {}
85
- flags.each do |flag|
86
- key = normalize_key(flag.delete(:name))
87
- result[key] = flag
88
+ # Updates the environment state for local flag evaluation.
89
+ # You only need to call this if you wish to bypass environment_refresh_interval_seconds.
90
+ def update_environment
91
+ @_mutex.synchronize { @environment = environment_from_api }
88
92
  end
89
- result
90
- end
91
93
 
92
- def traits_to_hash(user_flags)
93
- result = {}
94
- user_flags['traits']&.each do |t|
95
- key = normalize_key(t['trait_key'])
96
- result[key] = t['trait_value']
94
+ def environment_from_api
95
+ environment_data = api_client.get(@config.environment_url).body
96
+ Flagsmith::Engine::Environment.build(environment_data)
97
97
  end
98
- result
99
- end
100
-
101
- def normalize_key(key)
102
- key.to_s.downcase
103
- end
104
-
105
- def determine_opts(opts)
106
- opts = { api_key: opts } if opts.is_a? String
107
-
108
- {
109
- api_key: opts[:api_key] || self.class.api_key,
110
- url: opts[:url] || self.class.api_url
111
- }
112
- end
113
-
114
- def self.api_key
115
- ENV['FLAGSMITH_API_KEY']
116
- end
117
-
118
- def self.api_url
119
- ENV.fetch('FLAGSMITH_URL', 'https://api.flagsmith.com/api/v1/')
120
98
  end
121
99
  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: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Stuart
8
8
  - Brian Moelk
9
9
  autorequire:
10
- bindir: bin
10
+ bindir: exe
11
11
  cert_chain: []
12
- date: 2021-01-04 00:00:00.000000000 Z
12
+ date: 2022-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: pry
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: rake
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +123,34 @@ dependencies:
109
123
  - - ">="
110
124
  - !ruby/object:Gem::Version
111
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: faraday-retry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :runtime
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: semantic
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :runtime
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
112
154
  description: Ruby Client for Flagsmith. Ship features with confidence using feature
113
155
  flags and remote config. Host yourself or use our hosted version at https://flagsmith.com
114
156
  email:
@@ -118,16 +160,72 @@ executables: []
118
160
  extensions: []
119
161
  extra_rdoc_files: []
120
162
  files:
121
- - ".gitignore"
163
+ - ".rspec"
122
164
  - ".rubocop.yml"
123
165
  - ".rubocop_todo.yml"
166
+ - ".ruby-version"
124
167
  - Gemfile
125
168
  - Gemfile.lock
126
169
  - LICENCE
127
170
  - README.md
128
171
  - Rakefile
129
- - flagsmith.gemspec
172
+ - example/.env.development
173
+ - example/.env.test
174
+ - example/.gitignore
175
+ - example/.hanamirc
176
+ - example/.rspec
177
+ - example/Gemfile
178
+ - example/Gemfile.lock
179
+ - example/README.md
180
+ - example/Rakefile
181
+ - example/apps/web/application.rb
182
+ - example/apps/web/config/routes.rb
183
+ - example/apps/web/controllers/.gitkeep
184
+ - example/apps/web/controllers/home/index.rb
185
+ - example/apps/web/templates/application.html.slim
186
+ - example/apps/web/templates/home/index.html.slim
187
+ - example/apps/web/views/application_layout.rb
188
+ - example/apps/web/views/home/index.rb
189
+ - example/config.ru
190
+ - example/config/boot.rb
191
+ - example/config/environment.rb
192
+ - example/config/initializers/.gitkeep
193
+ - example/config/initializers/flagsmith.rb
194
+ - example/config/puma.rb
195
+ - example/spec/example/entities/.gitkeep
196
+ - example/spec/example/mailers/.gitkeep
197
+ - example/spec/example/repositories/.gitkeep
198
+ - example/spec/features_helper.rb
199
+ - example/spec/spec_helper.rb
200
+ - example/spec/support/.gitkeep
201
+ - example/spec/support/capybara.rb
202
+ - example/spec/web/controllers/.gitkeep
203
+ - example/spec/web/controllers/home/index_spec.rb
204
+ - example/spec/web/features/.gitkeep
205
+ - example/spec/web/views/application_layout_spec.rb
206
+ - example/spec/web/views/home/index_spec.rb
130
207
  - lib/flagsmith.rb
208
+ - lib/flagsmith/engine/core.rb
209
+ - lib/flagsmith/engine/environments/models.rb
210
+ - lib/flagsmith/engine/features/models.rb
211
+ - lib/flagsmith/engine/identities/models.rb
212
+ - lib/flagsmith/engine/organisations/models.rb
213
+ - lib/flagsmith/engine/projects/models.rb
214
+ - lib/flagsmith/engine/segments/constants.rb
215
+ - lib/flagsmith/engine/segments/evaluator.rb
216
+ - lib/flagsmith/engine/segments/models.rb
217
+ - lib/flagsmith/engine/utils/hash_func.rb
218
+ - lib/flagsmith/hash_slice.rb
219
+ - lib/flagsmith/sdk/analytics_processor.rb
220
+ - lib/flagsmith/sdk/api_client.rb
221
+ - lib/flagsmith/sdk/config.rb
222
+ - lib/flagsmith/sdk/errors.rb
223
+ - lib/flagsmith/sdk/instance_methods.rb
224
+ - lib/flagsmith/sdk/intervals.rb
225
+ - lib/flagsmith/sdk/models/flag.rb
226
+ - lib/flagsmith/sdk/models/flags/collection.rb
227
+ - lib/flagsmith/sdk/pooling_manager.rb
228
+ - lib/flagsmith/version.rb
131
229
  homepage: https://flagsmith.com
132
230
  licenses: []
133
231
  metadata: {}
@@ -146,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
244
  - !ruby/object:Gem::Version
147
245
  version: '0'
148
246
  requirements: []
149
- rubygems_version: 3.0.3
247
+ rubygems_version: 3.0.3.1
150
248
  signing_key:
151
249
  specification_version: 4
152
250
  summary: Flagsmith - Ship features with confidence
data/.gitignore DELETED
@@ -1,57 +0,0 @@
1
- .vscode/*
2
- !.vscode/settings.json
3
- !.vscode/tasks.json
4
- !.vscode/launch.json
5
- !.vscode/extensions.json
6
-
7
-
8
- *.gem
9
- *.rbc
10
- /.config
11
- /coverage/
12
- /InstalledFiles
13
- /pkg/
14
- /spec/reports/
15
- /spec/examples.txt
16
- /test/tmp/
17
- /test/version_tmp/
18
- /tmp/
19
-
20
- # Used by dotenv library to load environment variables.
21
- # .env
22
-
23
- ## Specific to RubyMotion:
24
- .dat*
25
- .repl_history
26
- build/
27
- *.bridgesupport
28
- build-iPhoneOS/
29
- build-iPhoneSimulator/
30
-
31
- ## Specific to RubyMotion (use of CocoaPods):
32
- #
33
- # We recommend against adding the Pods directory to your .gitignore. However
34
- # you should judge for yourself, the pros and cons are mentioned at:
35
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36
- #
37
- # vendor/Pods/
38
-
39
- ## Documentation cache and generated files:
40
- /.yardoc/
41
- /_yardoc/
42
- /doc/
43
- /rdoc/
44
-
45
- ## Environment normalization:
46
- /.bundle/
47
- /vendor/bundle
48
- /lib/bundler/man/
49
-
50
- # for a library or gem, you might want to ignore these files since the code is
51
- # intended to run in multiple environments; otherwise, check them in:
52
- # Gemfile.lock
53
- # .ruby-version
54
- # .ruby-gemset
55
-
56
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
57
- .rvmrc
data/flagsmith.gemspec DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Gem::Specification.new do |spec|
4
- spec.required_ruby_version = '>= 2.4.0'
5
- spec.name = 'flagsmith'
6
- spec.version = '2.0.0'
7
- spec.authors = ['Tom Stuart', 'Brian Moelk']
8
- spec.email = ['tom@solidstategroup.com', 'bmoelk@gmail.com']
9
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
10
-
11
- spec.summary = 'Flagsmith - Ship features with confidence'
12
- 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'
13
- spec.homepage = 'https://flagsmith.com'
14
-
15
- spec.add_development_dependency 'bundler'
16
- spec.add_development_dependency 'gem-release'
17
- spec.add_development_dependency 'rake'
18
- spec.add_development_dependency 'rspec'
19
- spec.add_development_dependency 'rubocop'
20
- spec.add_dependency 'faraday'
21
- spec.add_dependency 'faraday_middleware'
22
- end