this_feature-adapters-flipper 0.3.0 → 0.4.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: 1a4a261fc36bd82e31aecd8f83fc399e50eca40fdf1b639b10ce3c5d819f6aa9
4
- data.tar.gz: 6053c41648e337f13a9817d0f5c6134755ecb7b8b4f3364d9b11ef761ef4aabf
3
+ metadata.gz: 22af6aafd342c298c31378889d21f2f762b5ae5b96c36068a5297be4bb91cc3c
4
+ data.tar.gz: 67bfd16d7027c4c9ff4fe302bc559e2f3fffc68c54ef8b5dc374d174487ab53d
5
5
  SHA512:
6
- metadata.gz: 24ad8ced3e7e4f13f878943f49cd4f2bba8db2a6e543805b9eb29d8e7bd4d6736d9fd73525c1bcfdc4c20e617b5156f5d6d9561f38fc6ba5add72d7d09367e28
7
- data.tar.gz: 4d75aa7090b18b05b0ced212f315c92bce510793959fd79c83107507dc285199f31db6c4e0481c62e4c797548de5ae165fa2c46b02fd8bb9535822744f83dd04
6
+ metadata.gz: 40d04ebd41d2861ce29170706d6aff6d8da1455d190c592af2a133bf9fa0eaef778308a8eb5c75127d48e54aef79a6a1a2d35e31ca9662ee8e16a8f51dc3cee5
7
+ data.tar.gz: b8896633916796b3a01dc395e8b34c16c0c0051009f5ef8d2d04c313b3d99875da0130f823603c1818792a08417293ff7d4a18d36c3640da3651e57c1e91978c
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec name: "this_feature"
4
4
  gemspec name: "this_feature-adapters-flipper"
5
+ gemspec name: "this_feature-adapters-split_io"
@@ -1,10 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- this_feature (0.3.0)
5
- this_feature-adapters-flipper (0.3.0)
4
+ this_feature (0.4.0)
5
+ this_feature-adapters-flipper (0.4.0)
6
6
  flipper (~> 0.16)
7
7
  flipper-active_record (~> 0.16)
8
+ this_feature
9
+ this_feature-adapters-split_io (0.4.0)
10
+ splitclient-rb
11
+ this_feature
8
12
 
9
13
  GEM
10
14
  remote: https://rubygems.org/
@@ -23,20 +27,30 @@ GEM
23
27
  byebug (11.1.2)
24
28
  coderay (1.1.2)
25
29
  concurrent-ruby (1.1.6)
30
+ connection_pool (2.2.3)
26
31
  database_cleaner (1.8.4)
27
32
  database_cleaner-active_record (1.8.0)
28
33
  activerecord
29
34
  database_cleaner (~> 1.8.0)
30
35
  diff-lcs (1.3)
36
+ faraday (1.0.1)
37
+ multipart-post (>= 1.2, < 3)
31
38
  flipper (0.18.0)
32
39
  flipper-active_record (0.18.0)
33
40
  activerecord (>= 5.0, < 7)
34
41
  flipper (~> 0.18.0)
35
42
  gem-release (2.1.1)
43
+ hitimes (1.3.1)
36
44
  i18n (1.8.5)
37
45
  concurrent-ruby (~> 1.0)
46
+ json (2.3.1)
47
+ jwt (2.2.2)
48
+ lru_redux (1.1.0)
38
49
  method_source (1.0.0)
39
50
  minitest (5.14.1)
51
+ multipart-post (2.1.1)
52
+ net-http-persistent (4.0.0)
53
+ connection_pool (~> 2.2)
40
54
  pry (0.13.1)
41
55
  coderay (~> 1.1)
42
56
  method_source (~> 1.0)
@@ -44,6 +58,7 @@ GEM
44
58
  byebug (~> 11.0)
45
59
  pry (~> 0.13.0)
46
60
  rake (13.0.1)
61
+ redis (4.2.1)
47
62
  rspec (3.9.0)
48
63
  rspec-core (~> 3.9.0)
49
64
  rspec-expectations (~> 3.9.0)
@@ -57,6 +72,18 @@ GEM
57
72
  diff-lcs (>= 1.2.0, < 2.0)
58
73
  rspec-support (~> 3.9.0)
59
74
  rspec-support (3.9.2)
75
+ socketry (0.5.1)
76
+ hitimes (~> 1.2)
77
+ splitclient-rb (7.1.3)
78
+ concurrent-ruby (~> 1.0)
79
+ faraday (>= 0.8)
80
+ json (>= 1.8)
81
+ jwt (>= 2.2.1)
82
+ lru_redux
83
+ net-http-persistent (>= 2.9)
84
+ redis (>= 3.2)
85
+ socketry (~> 0.5.1)
86
+ thread_safe (>= 0.3)
60
87
  sqlite3 (1.4.2)
61
88
  thread_safe (0.3.6)
62
89
  tzinfo (1.2.7)
@@ -76,6 +103,7 @@ DEPENDENCIES
76
103
  sqlite3
77
104
  this_feature!
78
105
  this_feature-adapters-flipper!
106
+ this_feature-adapters-split_io!
79
107
 
80
108
  BUNDLED WITH
81
109
  2.1.4
data/README.md CHANGED
@@ -19,42 +19,68 @@ bundle
19
19
  Or install it yourself as:
20
20
 
21
21
  ```sh
22
- gem install feature
22
+ gem install this_feature
23
23
  ```
24
24
 
25
- ## Usage
25
+ ## Configuration
26
+
27
+ ```ruby
28
+ # config/initializers/this_feature.rb
29
+ require 'this_feature'
26
30
 
27
- ### Currently
31
+ ThisFeature.configure do |config|
32
+ config.adapters = [ThisFeature::Adapters::Memory]
33
+ config.default_adapter = config.adapters.first
34
+ end
35
+ ```
28
36
 
29
- Currently, the only available adapter is `Flipper`.
30
- We will update this document when more are added.
37
+ **NOTE**: When searching for the presence of a flag, adapters are queried in order. The default adapter is the fallback adapter used when a flag isn't present in any of the adapters.
31
38
 
32
- To set it up, put this in an initializer file:
39
+
40
+ ### With Flipper
33
41
 
34
42
  ```ruby
35
- ThisFeature.set_adapters([ThisFeature::Adapters::FlipperAdapter])
43
+ # config/initializers/this_feature.rb
44
+ require 'this_feature/adapters/flipper'
45
+
46
+ ThisFeature.configure do |config|
47
+ config.adapters = [ThisFeature::Adapters::Flipper]
48
+ config.default_adapter = config.adapters.first
49
+ end
36
50
  ```
37
51
 
38
- This `set_adapters` will internally call the `.setup` method on the `FlipperAdapter`, which performs the Flipper initialization.
39
52
 
40
- Then you can call `ThisFeature.enabled?("flag name")`.
41
53
 
42
- It will iterate through the adapters until one of them returns `true`/`false`.
54
+ ## Usage
55
+
56
+ ### Flags
57
+ ```ruby
58
+ ThisFeature.flag('flag_name').on? # check if flag is turned on
59
+ ThisFeature.flag('flag_name').off? # check if flag is turned off
60
+ ThisFeature.flag('flag_name').control? # see if the adapter is using the control
61
+ ThisFeature.flag('flag_name').on! # turn on the flag
62
+ ThisFeature.flag('flag_name').off! # turn off the flag
63
+ ```
43
64
 
44
- A context (`User` or `Org`) can be passed in the arguments to `enabled?` as well. `ThisFeature.enabled?(:flag_name, Current.user)`
65
+ ### Context
45
66
 
46
- ### Planned
67
+ You can also pass a context to the flag, many feature flagging systems support this.
47
68
 
48
- Create an initializer file in your Rails app:
69
+ ```ruby
70
+ ThisFeature.flag('flag_name', context: current_user).on?
71
+ ```
49
72
 
50
- `/config/initializers/this_feature.rb`
73
+ ### Data
51
74
 
52
- And set your list of adapters, _ordered by priority_. For example:
75
+ In case context is not sufficient, you can also pass a data hash.
53
76
 
54
77
  ```ruby
55
- ThisFeature.adapters = [SplitIO Flipper]
78
+ ThisFeature.flag('flag_name', context: context, data: { org_id: 1 }).on?
56
79
  ```
57
80
 
81
+ ## TODO: Write documentation for the adapters (creating adapters, using memory adapter, using flipper adapter)
82
+
83
+
58
84
  ## Development
59
85
 
60
86
  The tests are a good reflection of the current development state.
@@ -13,19 +13,19 @@ class ThisFeature
13
13
 
14
14
  def self.adapter_for(flag_name, context: nil, data: {})
15
15
  matching_adapter = adapters.find do |adapter|
16
- adapter.present?(flag_name, context: context, data: data)
16
+ adapter.present?(flag_name)
17
17
  end
18
18
 
19
19
  matching_adapter || configuration.default_adapter
20
20
  end
21
21
 
22
- # Configuration
23
-
24
22
  def self.configuration
25
23
  @configuration ||= Configuration.new
26
24
  end
27
25
 
28
26
  def self.configure
27
+ @configuration = Configuration.new
28
+
29
29
  yield(configuration)
30
30
 
31
31
  configuration.init
@@ -34,5 +34,4 @@ class ThisFeature
34
34
  def self.adapters
35
35
  configuration.adapters
36
36
  end
37
-
38
37
  end
@@ -2,33 +2,25 @@ class ThisFeature
2
2
  module Adapters
3
3
  class Base
4
4
 
5
- def self.setup
5
+ def setup
6
6
  raise UnimplementedError.new(self, __method__)
7
7
  end
8
8
 
9
- def self.present?(flag_name)
9
+ def present?(flag_name)
10
10
  raise UnimplementedError.new(self, __method__)
11
11
  end
12
12
 
13
- def self.on?(flag_name, context: nil, data: {})
13
+ def on?(flag_name, context: nil, data: {})
14
14
  raise UnimplementedError.new(self, __method__)
15
15
  end
16
16
 
17
- def self.off?(flag_name, context: nil, data: {})
18
- raise UnimplementedError.new(self, __method__)
19
- end
20
-
21
- def self.on!(flag_name, context: nil, data: {})
22
- raise UnimplementedError.new(self, __method__)
23
- end
24
-
25
- def self.off!(flag_name, context: nil, data: {})
17
+ def off?(flag_name, context: nil, data: {})
26
18
  raise UnimplementedError.new(self, __method__)
27
19
  end
28
20
 
29
21
  # OPTIONAL method
30
22
  # check to see if a control is being used
31
- def self.control?(flag_name, context: nil, data: {})
23
+ def control?(flag_name, context: nil, data: {})
32
24
  false
33
25
  end
34
26
  end
@@ -4,31 +4,27 @@ require "flipper/adapters/active_record"
4
4
  class ThisFeature
5
5
  module Adapters
6
6
  class Flipper < Base
7
+ attr_reader :client
7
8
 
8
- def self.setup(flipper = nil)
9
- return @flipper = flipper unless flipper.nil?
10
-
11
- @flipper = ::Flipper
9
+ def initialize(client: nil)
10
+ @client = client || default_flipper_adapter
11
+ end
12
12
 
13
- ::Flipper.configure do |config|
14
- config.default do
15
- adapter = ::Flipper::Adapters::ActiveRecord.new
16
- ::Flipper.new(adapter)
17
- end
18
- end
13
+ def present?(flag_name)
14
+ client[flag_name].exist?
19
15
  end
20
16
 
21
- def self.present?(flag_name)
22
- flipper[flag_name].exist?
17
+ def control?(flag_name, **kwargs)
18
+ !present?(flag_name)
23
19
  end
24
20
 
25
- def self.on?(flag_name, context: nil, data: {})
21
+ def on?(flag_name, context: nil, data: {})
26
22
  return unless present?(flag_name)
27
23
 
28
- flipper[flag_name].enabled?(*[context].compact)
24
+ client[flag_name].enabled?(*[context].compact)
29
25
  end
30
26
 
31
- def self.off?(flag_name, context: nil, data: {})
27
+ def off?(flag_name, context: nil, data: {})
32
28
  on_result = on?(flag_name, context: context)
33
29
 
34
30
  return if on_result.nil?
@@ -36,16 +32,16 @@ class ThisFeature
36
32
  !on_result
37
33
  end
38
34
 
39
- def self.on!(flag_name, context: nil, data: {})
40
- flipper[flag_name].enable(*[context].compact)
41
- end
35
+ private
42
36
 
43
- def self.off!(flag_name, context: nil, data: {})
44
- flipper[flag_name].disable(*[context].compact)
45
- end
46
-
47
- def self.flipper
48
- @flipper
37
+ def default_flipper_adapter
38
+ ::Flipper.configure do |config|
39
+ config.default do
40
+ adapter = ::Flipper::Adapters::ActiveRecord.new
41
+ ::Flipper.new(adapter)
42
+ end
43
+ end
44
+ ::Flipper
49
45
  end
50
46
  end
51
47
  end
@@ -2,20 +2,19 @@ class ThisFeature
2
2
  module Adapters
3
3
  class Memory < Base
4
4
 
5
- def self.setup(context_id_method: :id)
6
- @context_id_method = context_id_method
5
+ def initialize(context_key_method: nil)
6
+ @context_key_method = context_key_method
7
7
  end
8
8
 
9
- def self.clear
9
+ def clear
10
10
  storage.clear
11
11
  end
12
12
 
13
- def self.present?(flag_name)
13
+ def present?(flag_name)
14
14
  !storage[flag_name].nil?
15
15
  end
16
16
 
17
- def self.on?(flag_name, context: nil, data: {})
18
- # binding.pry
17
+ def on?(flag_name, context: nil, data: {})
19
18
  return unless present?(flag_name)
20
19
 
21
20
  flag_data = storage[flag_name]
@@ -25,10 +24,10 @@ class ThisFeature
25
24
 
26
25
  flag_data[:contexts] ||= {}
27
26
 
28
- !!flag_data[:contexts][context.send(@context_id_method)]
27
+ !!flag_data[:contexts][context_key(context)]
29
28
  end
30
29
 
31
- def self.off?(flag_name, context: nil, data: {})
30
+ def off?(flag_name, context: nil, data: {})
32
31
  on_result = on?(flag_name, context: context)
33
32
 
34
33
  return if on_result.nil?
@@ -36,27 +35,37 @@ class ThisFeature
36
35
  !on_result
37
36
  end
38
37
 
39
- def self.on!(flag_name, context: nil, data: {})
38
+ def on!(flag_name, context: nil, data: {})
40
39
  storage[flag_name] ||= {}
41
40
 
42
41
  return storage[flag_name][:global] = true if context.nil?
43
42
 
44
43
  storage[flag_name][:contexts] ||= {}
45
- storage[flag_name][:contexts][context.send(@context_id_method)] = true
44
+ storage[flag_name][:contexts][context_key(context)] = true
46
45
  end
47
46
 
48
- def self.off!(flag_name, context: nil, data: {})
47
+ def off!(flag_name, context: nil, data: {})
49
48
  storage[flag_name] ||= {}
50
49
 
51
50
  return storage[flag_name][:global] = false if context.nil?
52
51
 
53
52
  storage[flag_name][:contexts] ||= {}
54
- storage[flag_name][:contexts][context.send(@context_id_method)] = false
53
+ storage[flag_name][:contexts][context_key(context)] = false
55
54
  end
56
55
 
57
- def self.storage
56
+ def storage
58
57
  @storage ||= {}
59
58
  end
59
+
60
+ private
61
+
62
+ attr_reader :context_key_method
63
+
64
+ def context_key(context)
65
+ return context if context_key_method.nil?
66
+
67
+ context.send(context_key_method)
68
+ end
60
69
  end
61
70
  end
62
71
  end
@@ -0,0 +1,52 @@
1
+ require 'splitclient-rb'
2
+
3
+ class ThisFeature
4
+ module Adapters
5
+ class SplitIo < Base
6
+ # used as treatment key when none is given, it's required by split
7
+ UNDEFINED_KEY = 'undefined_key'
8
+
9
+ def initialize(client: nil)
10
+ @client = client || default_split_client
11
+
12
+ @client.block_until_ready
13
+ end
14
+
15
+ def present?(flag_name)
16
+ !control?(flag_name)
17
+ end
18
+
19
+ def control?(flag_name, context: UNDEFINED_KEY, data: {})
20
+ treatment(flag_name, context: context, data: data).eql?('control_treatment')
21
+ end
22
+
23
+ def on?(flag_name, context: UNDEFINED_KEY, data: {})
24
+ treatment(flag_name, context: context, data: data).eql?('on')
25
+ end
26
+
27
+ def off?(flag_name, context: UNDEFINED_KEY, data: {})
28
+ treatment(flag_name, context: context, data: data).eql?('off')
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :client
34
+
35
+ def treatment(flag_name, context: UNDEFINED_KEY, data: {})
36
+ key = if context.nil?
37
+ UNDEFINED_KEY
38
+ elsif context.respond_to?(:to_s)
39
+ context.to_s
40
+ else
41
+ context
42
+ end
43
+
44
+ client.get_treatment(key, flag_name, data)
45
+ end
46
+
47
+ def default_split_client
48
+ SplitIoClient::SplitFactory.new(ENV.fetch('SPLIT_IO_AUTH_KEY')).client
49
+ end
50
+ end
51
+ end
52
+ end
@@ -5,13 +5,11 @@ class ThisFeature
5
5
 
6
6
  def init
7
7
  validate_adapters!
8
-
9
- adapters.each(&:setup)
10
8
  end
11
9
 
12
10
  def validate_adapters!
13
11
  adapters.each do |adapter|
14
- raise BadAdapterError.new(adapter) unless adapter < Adapters::Base
12
+ raise BadAdapterError.new(adapter) unless adapter.class < Adapters::Base
15
13
  end
16
14
  end
17
15
 
@@ -3,14 +3,14 @@ class ThisFeature
3
3
  class Error < StandardError; end
4
4
 
5
5
  class UnimplementedError < Error
6
- def initialize(klass, fn_name)
7
- super("class #{klass.name} doesnt implement method .#{fn_name}")
6
+ def initialize(adapter_instance, fn_name)
7
+ super("class #{adapter_instance.class.name} doesnt implement method .#{fn_name}")
8
8
  end
9
9
  end
10
10
 
11
11
  class BadAdapterError < Error
12
- def initialize(adapter)
13
- super("adapter #{adapter.name} doesn't inherit from ThisFeature::Adapters::Base")
12
+ def initialize(adapter_instance)
13
+ super("adapter #{adapter_instance.class.name} doesn't inherit from #{ThisFeature::Adapters::Base.name}")
14
14
  end
15
15
  end
16
16
 
@@ -20,13 +20,5 @@ class ThisFeature
20
20
  def control?
21
21
  adapter.control?(flag_name, context: context, data: data)
22
22
  end
23
-
24
- def on!
25
- adapter.on!(flag_name, context: context, data: data)
26
- end
27
-
28
- def off!
29
- adapter.off!(flag_name, context: context, data: data)
30
- end
31
23
  end
32
24
  end
@@ -1,3 +1,3 @@
1
1
  class ThisFeature
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/memory CHANGED
Binary file
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.platform = Gem::Platform::RUBY
19
19
  s.require_paths = ['lib']
20
20
 
21
+ s.add_runtime_dependency "this_feature"
21
22
  s.add_runtime_dependency "flipper", "~> 0.16"
22
23
  s.add_runtime_dependency "flipper-active_record", "~> 0.16"
23
24
  end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift File.expand_path('../lib', __FILE__)
4
+
5
+ require 'this_feature/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'this_feature-adapters-split_io'
9
+ s.version = ThisFeature::VERSION
10
+ s.authors = ['Max Pleaner', 'Matt Fong']
11
+ s.email = ['maxpleaner@gmail.com', 'matthewjf@gmail.com']
12
+ s.homepage = 'http://hover.to'
13
+ s.licenses = ['MIT']
14
+ s.summary = '[summary]'
15
+ s.description = '[description]'
16
+
17
+ s.files = Dir.glob('{bin/*,lib/**/*,[A-Z]*}')
18
+ s.platform = Gem::Platform::RUBY
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_runtime_dependency "this_feature"
22
+ s.add_runtime_dependency "splitclient-rb"
23
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: this_feature-adapters-flipper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Pleaner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2020-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: this_feature
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: flipper
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,18 +72,20 @@ files:
58
72
  - lib/this_feature/adapters/base.rb
59
73
  - lib/this_feature/adapters/flipper.rb
60
74
  - lib/this_feature/adapters/memory.rb
75
+ - lib/this_feature/adapters/split_io.rb
61
76
  - lib/this_feature/configuration.rb
62
77
  - lib/this_feature/errors.rb
63
78
  - lib/this_feature/flag.rb
64
79
  - lib/this_feature/version.rb
65
80
  - memory
66
81
  - this_feature-adapters-flipper.gemspec
82
+ - this_feature-adapters-split_io.gemspec
67
83
  - this_feature.gemspec
68
84
  homepage: http://hover.to
69
85
  licenses:
70
86
  - MIT
71
87
  metadata: {}
72
- post_install_message:
88
+ post_install_message:
73
89
  rdoc_options: []
74
90
  require_paths:
75
91
  - lib
@@ -85,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
101
  version: '0'
86
102
  requirements: []
87
103
  rubygems_version: 3.1.2
88
- signing_key:
104
+ signing_key:
89
105
  specification_version: 4
90
106
  summary: "[summary]"
91
107
  test_files: []