bandiera-client 2.2.2 → 3.0.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
  SHA1:
3
- metadata.gz: 748f5858d09ce6aec023da7987c3f4ab0fc0fa75
4
- data.tar.gz: e9cb4b7fa6ba2e12f04b24cb7bc8ca7505b5313a
3
+ metadata.gz: 170478dc6075aeda3c245c9ea8ef1f4fd357a556
4
+ data.tar.gz: a5bda6c22f5c52fceb82d665009a9c3323940948
5
5
  SHA512:
6
- metadata.gz: 75bbcece7e445f4420c8d4e8bb8091372705e435d4e5d81625ce8cc6d1c02ac71fee56846ded50c5b59360c5c41f5a55aa5f62adc41510914b847e62751f1d60
7
- data.tar.gz: e42c294b272343d9a495aa091c90dfcb72bfa7c7d1880f4a6645b8c08404c4befe0be8aeb2e3b4f3c5e26564ab01bdb6a6baddce4f1da8bdd74abdace2b4a6a6
6
+ metadata.gz: f274fff7ed02c4bcbc18324c7c1c0ea7eea83213b051f8c5ba859f89a1c21dbe25e6b310a95bd6448cd8b4aee0165dd0650b6a6a31d2e05fb4fa755866075034
7
+ data.tar.gz: 85585452a8af8119f8d12fd3d2a610ae0d4ecb10eaae61ccac7fd7782ac17a24744d641b0339e725053f03280a867e000d3d5567812cab8b48431655288ed72e
data/.hound.yml CHANGED
@@ -34,3 +34,6 @@ Style/CollectionMethods:
34
34
 
35
35
  Style/DotPosition:
36
36
  EnforcedStyle: leading
37
+
38
+ Style/DoubleNegation:
39
+ Enabled: false
data/.rubocop.yml CHANGED
@@ -34,3 +34,6 @@ Style/CollectionMethods:
34
34
 
35
35
  Style/DotPosition:
36
36
  EnforcedStyle: leading
37
+
38
+ Style/DoubleNegation:
39
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ sudo: false
2
+ cache: bundler
3
+ language: ruby
4
+
5
+ # Ruby build matrix
6
+ rvm:
7
+ - 2.0
8
+ - 2.1
9
+ - 2.2
10
+ - ruby
11
+ - ruby-head
12
+ - jruby
13
+
14
+ # Ensure we don't build for *every* commit (doesn't apply to PR builds)
15
+ branches:
16
+ only:
17
+ - master
18
+
19
+ script:
20
+ - bundle exec rspec --format documentation
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Bandiera::Client (Ruby)
2
2
 
3
- This is a client for talking to the [Bandiera][bandiera] feature flagging
4
- service from a Ruby application.
3
+ This is a client for talking to the [Bandiera][bandiera] feature flagging service from a Ruby application.
5
4
 
6
5
  This client is compatible with the [v2 Bandiera API][bandiera-api].
7
6
 
8
- **Current Version:** 2.2.1
9
- **License:** [MIT][mit]
10
- **Build Status:** [![Build Status][travis-img]][travis]
7
+ [![Gem version][shield-gem]][info-gem]
8
+ [![Build status][shield-build]][info-build]
9
+ [![Dependencies][shield-dependencies]][info-dependencies]
10
+ [![MIT licensed][shield-license]][info-license]
11
11
 
12
12
  ## Ruby Support:
13
13
 
@@ -26,72 +26,76 @@ Then interact with a Bandiera server like so:
26
26
  ```ruby
27
27
  require 'bandiera/client'
28
28
 
29
- $bandiera = Bandiera::Client.new('http://bandiera.example.com')
29
+ client = Bandiera::Client.new('http://bandiera-demo.herokuapp.com')
30
+ params = {}
30
31
 
31
- if $bandiera.enabled?('pubserv', 'show-new-search')
32
+ if client.enabled?('pubserv', 'show-new-search', params)
32
33
  # show the new experimental search function
33
34
  end
34
35
  ```
35
36
 
36
- The `$bandiera.enabled?` command takes two arguments - the 'feature group',
37
- and the 'feature name'. This is because in Bandiera, features are organised
38
- in groups as it is intented as a service for multiple applications to use at
39
- the same time - this organisation allows separation of feature flags that are
40
- intended for different audiences.
37
+ The `client.enabled?` command takes two main arguments - the 'feature group', and the 'feature name'. This is because in Bandiera, features are organised into groups as it is intented as a service for multiple applications to use at the same time - this organisation allows separation of feature flags that are intended for different audiences.
41
38
 
42
- ## Caching
39
+ `client.enabled?` also takes an optional `params` hash, this is for use with some of the more advanced features in Bandiera - user group and percentage based flags. It is in this params hash you pass in your `user_group` and `user_id`, i.e.:
43
40
 
44
- Bandiera::Client has a small layer of caching built into it in order to:
41
+ ```ruby
42
+ client.enabled?('pubserv', 'show-new-search',
43
+ { user_id: '1234567', user_group: 'Administrators' })
44
+ ```
45
45
 
46
- 1. Reduce the amount of HTTP requests made to the Bandiera server
47
- 2. Make things faster
46
+ For more information on these advanced features, please see the Bandiera wiki:
48
47
 
49
- ### Strategies
48
+ https://github.com/nature/bandiera/wiki/How-Feature-Flags-Work#feature-flags-in-bandiera
50
49
 
51
- There are three request/caching strategies you can use with Bandiera::Client.
50
+ # Performance
52
51
 
53
- **:single_feature**
52
+ Using the `client.enabled?` method all over your codebase isn't the most efficient way of working with Bandiera as every time you call `enabled?` you will make a HTTP request to the Bandiera server.
54
53
 
55
- This strategy calls the Bandiera API for each new .enabled? request, and stores
56
- the response in it's local cache. Subsequent `.enabled?` requests (using the
57
- same arguments) will read from the cache until it has expired. This is the
58
- **least** efficient strategy in terms of reducing HTTP requests and speed.
54
+ One way of working more efficiently is using the [Direct API Access](#direct-api-access) methods, to fetch all the feature flags for a given group (or even **all** of the feature flags in the Bandiera server) in one request. You can then hold these as you please in your application and call on the values when needed.
59
55
 
60
- **:group**
56
+ Another approach is to use the Bandiera::Middleware class supplied in this gem. This can be used in conjunction with other middlewares for identifying your currently logged in user and assigning them a UUID to enable all of the most advanced features in Bandiera very simply.
61
57
 
62
- This strategy calls the Bandiera API **once** for each feature flag group
63
- requested, and then stores the resulting feature flag values in the cache.
64
- This means that all subsequent calls to `.enabled?` for the same group will not
65
- perform a HTTP request and instead read from the cache until it has expired.
66
- This is a good compromise in terms of speed and number of HTTP requests, **and
67
- is the default caching strategy**.
58
+ [See the blog post on cruft.io for more information on how Bandiera::Client is used at Nature.](http://cruft.io)
68
59
 
69
- **:all**
60
+ <a name="direct-api-access"></a># Direct API Access
70
61
 
71
- This strategy calls the Bandiera API **once** and fetches/caches all feature
72
- flag values locally. All subsequent calls to `.enabled?` read from the cache
73
- until it has expired. This strategy is obviously the most efficient in terms of
74
- the number of HTTP requests if you are requesting flags from across multiple
75
- groups, but might not be the fastest if there are **lots** of flags in your
76
- Bandiera instance.
62
+ If you'd prefer not to use the `enabled?` method for featching feature flag values, the following methods are available...
77
63
 
78
- #### Changing the Cache Strategy
64
+ Get features for all groups:
79
65
 
80
66
  ```ruby
81
- $bandiera = Bandiera::Client.new('http://bandiera.example.com')
82
- $bandiera.cache_strategy = :all
67
+ client.get_all(params)
68
+ # gives:
69
+ # {
70
+ # 'group1' => {
71
+ # 'feature1' => true,
72
+ # 'feature2' => false
73
+ # },
74
+ # 'group2' => {
75
+ # 'feature1' => false
76
+ # },
77
+ # }
83
78
  ```
84
79
 
85
- ### Cache Expiration
80
+ Get features for a group:
81
+
82
+ ```ruby
83
+ client.get_features_for_group('pubserv', params)
84
+ # gives:
85
+ # {
86
+ # 'feature1' => true,
87
+ # 'feature2' => false
88
+ # }
89
+ ```
86
90
 
87
- The default cache lifetime is 5 seconds. If you would like to alter this you
88
- can do so as follows:
91
+ Get an individual feature:
89
92
 
90
93
  ```ruby
91
- $bandiera = Bandiera::Client.new('http://bandiera.example.com')
92
- $bandiera.cache_ttl = 10 # 10 seconds
94
+ client.get_feature('pubserv', 'show-article-metrics', params)
95
+ # gives: true/false
93
96
  ```
94
97
 
98
+ As with the `enabled?` method the `params` hash is for passing in your `user_group` and `user_id` values if you are using some of the more advanced features in Bandiera.
95
99
 
96
100
  # Development
97
101
 
@@ -107,5 +111,11 @@ Bandiera::Client (Ruby) is licensed under the [MIT License][mit].
107
111
  [mit]: http://opensource.org/licenses/mit-license.php
108
112
  [bandiera]: https://github.com/nature/bandiera
109
113
  [bandiera-api]: https://github.com/nature/bandiera/wiki/API-Documentation
110
- [travis]: https://travis-ci.org/nature/bandiera-client-ruby
111
- [travis-img]: https://travis-ci.org/nature/bandiera-client-ruby.svg?branch=master
114
+ [info-dependencies]: https://gemnasium.com/nature/bandiera-client-ruby
115
+ [info-license]: LICENSE
116
+ [info-gem]: https://rubygems.org/gems/bandiera-client
117
+ [info-build]: https://travis-ci.org/nature/bandiera-client-ruby
118
+ [shield-dependencies]: https://img.shields.io/gemnasium/nature/bandiera-client-ruby.svg
119
+ [shield-license]: https://img.shields.io/badge/license-MIT-blue.svg
120
+ [shield-gem]: https://img.shields.io/gem/v/bandiera-client.svg
121
+ [shield-build]: https://img.shields.io/travis/nature/bandiera-client-ruby/master.svg
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.description = 'Bandiera is a simple, stand-alone feature flagging service that is not tied to any existing web framework or language. This is a client for talking to the web service.'
14
14
  spec.summary = 'Simple feature flagging API client.'
15
15
  spec.homepage = 'https://github.com/nature/bandiera-client-ruby'
16
- spec.license = 'GPL-3'
16
+ spec.license = 'MIT'
17
17
 
18
18
  spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
19
19
  spec.test_files = Dir.glob('spec/*_spec.rb')
@@ -22,10 +22,10 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'bundler'
23
23
  spec.add_development_dependency 'rspec'
24
24
  spec.add_development_dependency 'webmock'
25
+ spec.add_development_dependency 'rack-test'
25
26
  spec.add_development_dependency 'rake'
26
27
  spec.add_development_dependency 'pry'
27
28
 
28
29
  spec.add_dependency 'rest-client'
29
- spec.add_dependency 'moneta'
30
30
  spec.add_dependency 'macmillan-utils'
31
31
  end
@@ -1,5 +1,5 @@
1
1
  module Bandiera
2
2
  class Client
3
- VERSION = '2.2.2'
3
+ VERSION = '3.0.0'
4
4
  end
5
5
  end
@@ -1,49 +1,54 @@
1
1
  require 'rest_client'
2
2
  require 'json'
3
3
  require 'logger'
4
- require 'moneta'
5
4
 
6
5
  module Bandiera
6
+ ##
7
+ # Client class for communicating with a Bandiera server.
8
+ #
9
+ # @since 1.0.0
10
+ # @attr [Float] timeout The HTTP timeout value (seconds) for requests
11
+ # @attr [String] client_name The client name passed along with HTTP requests
12
+ # @attr_reader [Logger] logger The logger object in use
13
+ #
7
14
  class Client
8
15
  autoload :VERSION, 'bandiera/client/version'
9
16
 
10
- CACHE_STRATEGIES = [:single_feature, :group, :all]
11
-
12
- attr_accessor :timeout, :client_name, :cache_ttl
13
- attr_reader :logger, :cache, :cache_strategy
17
+ attr_accessor :timeout, :client_name
18
+ attr_reader :logger
14
19
 
20
+ ##
21
+ # Builds a new instance of Bandiera::Client
22
+ #
23
+ # @param [String] base_uri The URI of the Bandiera server
24
+ # @param [Logger] logger A logger object
25
+ # @param [String] client_name A client name to pass through along with the HTTP requests
26
+ #
15
27
  def initialize(base_uri = 'http://localhost', logger = Logger.new($stdout), client_name = nil)
16
28
  @base_uri = base_uri
17
29
  @base_uri << '/api' unless @base_uri.match(/\/api$/)
18
30
  @logger = logger
19
31
  @timeout = 0.2 # 0.4s (0.2 + 0.2) default timeout
20
32
  @client_name = client_name
21
- @cache = Moneta.new(:LRUHash, expires: true)
22
- @cache_ttl = 5 # 5 seconds
23
- @cache_strategy = :group
24
33
  end
25
34
 
26
- def cache_strategy=(strategy)
27
- unless CACHE_STRATEGIES.include?(strategy)
28
- raise ArgumentError, "cache_strategy can only be #{CACHE_STRATEGIES}"
29
- end
30
- @cache_strategy = strategy
31
- end
32
-
33
- def enabled?(group, feature, params = {}, http_opts = {})
34
- cache_key = build_cache_key(group, feature, params)
35
-
36
- unless cache.key?(cache_key)
37
- case cache_strategy
38
- when :single_feature then get_feature(group, feature, params, http_opts)
39
- when :group then get_features_for_group(group, params, http_opts)
40
- when :all then get_all
41
- end
42
- end
43
-
44
- cache.fetch(cache_key)
35
+ # @deprecated This functionality was deprecated/removed in 3.0.0
36
+ def cache_strategy=(_)
37
+ warn 'The caching features in Bandiera::Client have been removed as of v3.0.0, please consider using using ' \
38
+ 'the Bandiera::Middleware class shipped as part of the "bandiera-client" gem.'
45
39
  end
46
40
 
41
+ ##
42
+ # Get the active/inactive state for a single feature flag
43
+ #
44
+ # @param [String] group The group of feature flags we're interested in
45
+ # @param [String] feature The feature flag we want to retrieve
46
+ # @param [Hash] params Additional parameters to pass through to the Bandiera request
47
+ # @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
48
+ # @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
49
+ #
50
+ # @return [Boolean] True/False - depending on if the feature is on or off
51
+ #
47
52
  def get_feature(group, feature, params = {}, http_opts = {})
48
53
  path = "/v2/groups/#{group}/features/#{feature}"
49
54
  default_response = false
@@ -51,11 +56,21 @@ module Bandiera
51
56
 
52
57
  logger.debug "[Bandiera::Client#get_feature] calling #{path} with params: #{params}"
53
58
 
54
- get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix) do |value|
55
- store_value_in_cache(group, feature, params, value)
56
- end
59
+ get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
57
60
  end
58
61
 
62
+ alias_method :enabled?, :get_feature
63
+
64
+ ##
65
+ # Get the active/inactive state for all feature flags in a group
66
+ #
67
+ # @param [String] group The group of feature flags we're interested in
68
+ # @param [Hash] params Additional parameters to pass through to the Bandiera request
69
+ # @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
70
+ # @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
71
+ #
72
+ # @return [Hash] A hash of feature flag pairs. Keys are the feature flag names, values are the active/inactive states.
73
+ #
59
74
  def get_features_for_group(group, params = {}, http_opts = {})
60
75
  path = "/v2/groups/#{group}/features"
61
76
  default_response = {}
@@ -63,11 +78,17 @@ module Bandiera
63
78
 
64
79
  logger.debug "[Bandiera::Client#get_features_for_group] calling #{path} with params: #{params}"
65
80
 
66
- get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix) do |feature_hash|
67
- store_feature_hash_in_cache(group, params, feature_hash)
68
- end
81
+ get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
69
82
  end
70
83
 
84
+ ##
85
+ # Get the active/inactive state for all feature flags known on the Bandiera server
86
+ #
87
+ # @param [Hash] params Additional parameters to pass through to the Bandiera request
88
+ # @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
89
+ # @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
90
+ #
91
+ # @return [Hash] A hash of hashes containing the feature flag active/inactive states grouped by 'group'
71
92
  def get_all(params = {}, http_opts = {})
72
93
  path = '/v2/all'
73
94
  default_response = {}
@@ -75,30 +96,11 @@ module Bandiera
75
96
 
76
97
  logger.debug "[Bandiera::Client#get_all] calling #{path} with params: #{params}"
77
98
 
78
- get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix) do |group_hash|
79
- group_hash.each do |group, feature_hash|
80
- store_feature_hash_in_cache(group, params, feature_hash)
81
- end
82
- end
99
+ get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
83
100
  end
84
101
 
85
102
  private
86
103
 
87
- def store_feature_hash_in_cache(group, params, feature_hash)
88
- feature_hash.each do |feature, value|
89
- store_value_in_cache(group, feature, params, value)
90
- end
91
- end
92
-
93
- def store_value_in_cache(group, feature, params, value)
94
- cache_key = build_cache_key(group, feature, params)
95
- cache.store(cache_key, value, expires: cache_ttl)
96
- end
97
-
98
- def build_cache_key(group, feature, params)
99
- "#{group} / #{feature} / #{params}"
100
- end
101
-
102
104
  def headers
103
105
  headers = { 'User-Agent' => "Bandiera Ruby Client / #{Bandiera::Client::VERSION}" }
104
106
  headers.merge!('Bandiera-Client' => client_name) unless client_name.nil?
@@ -0,0 +1,66 @@
1
+ require 'macmillan/utils'
2
+
3
+ module Bandiera
4
+ ##
5
+ # Rack middleware for talking to a Bandiera feature flagging service.
6
+ #
7
+ # The benefit of using this middleware is two-fold when working with a Bandiera server:
8
+ #
9
+ # 1. Performance - Using this middleware, you will only call the Bandiera server once per-request (in contrast to using the `enabled?` method where you will make one request per-use).
10
+ # 2. Better Quality Code - If you use the more advanced features in Bandiera (user groups and percentages) you will no longer need to pass around user objects and UUIDs in your code. This does assume the use of other middlewares to supply user objects and UUIDs though.
11
+ #
12
+ # This middleware can be used in conjunction with the {Macmillan::Utils::Middleware::Uuid} (or one of your own design) to automatically generate UUIDs for your users. See https://github.com/nature/bandiera/wiki for more information on this approach.
13
+ #
14
+ # @since 3.0.0
15
+ #
16
+ class Middleware
17
+ ##
18
+ # Builds a new instance of Bandiera::Middleware
19
+ #
20
+ # @param app The rack application
21
+ # @param [Hash] opts Additional options for the middleware
22
+ # @option opts [Bandiera::Client] :client The Bandiera::Client class to use
23
+ # @option opts [Array] :groups ([]) The feature flag groups to request and store in the rack env
24
+ # @option opts [String] :uuid_env_key (Macmillan::Utils::Middleware::Uuid.env_key) The rack env key containing a UUID for your current user (to pass to `user_id` in Bandiera)
25
+ # @option opts [String] :user_env_key ('current_user') The rack env key containing your currently logged in user
26
+ # @option opts [Symbol] :user_group_method (:email) The method to call on the current user to pass to `user_group` in Bandiera
27
+ #
28
+ # @see Bandiera::Client
29
+ # @see Macmillan::Utils::Middleware::Uuid
30
+ #
31
+ def initialize(app, opts = {})
32
+ @app = app
33
+ @client = opts[:client]
34
+ @groups = opts[:groups] || []
35
+ @uuid_env_key = opts[:uuid_env_key] || Macmillan::Utils::Middleware::Uuid.env_key
36
+ @user_env_key = opts[:user_env_key] || 'current_user'
37
+ @user_group_method = opts[:user_group_method] || :email
38
+
39
+ fail ArgumentError, 'You must supply a Bandiera::Client' unless @client
40
+ end
41
+
42
+ def call(env)
43
+ dup.process(env)
44
+ end
45
+
46
+ def process(env)
47
+ request = Rack::Request.new(env)
48
+
49
+ user = request.env[@user_env_key]
50
+ user_group = user ? user.public_send(@user_group_method) : nil
51
+ uuid = request.env[@uuid_env_key]
52
+
53
+ if @groups.empty?
54
+ @client.get_all(user_group: user_group, user_id: uuid).each do |group, flags|
55
+ request.env["bandiera.#{group}"] = flags
56
+ end
57
+ else
58
+ @groups.each do |group|
59
+ request.env["bandiera.#{group}"] = @client.get_features_for_group(group, user_group: user_group, user_id: uuid)
60
+ end
61
+ end
62
+
63
+ @app.call(request.env)
64
+ end
65
+ end
66
+ end
@@ -9,10 +9,10 @@ describe Bandiera::Client do
9
9
  context 'when a client name is provided' do
10
10
  let(:group) { 'pubserv' }
11
11
  let(:feature) { 'log-stats' }
12
- let(:url) { "#{api_uri}/v2/groups/#{group}/features" }
12
+ let(:url) { "#{api_uri}/v2/groups/#{group}/features/#{feature}" }
13
13
 
14
14
  it 'sends it as part of the headers' do
15
- stub = stub_api_request(url, { 'response' => {} }, { 'Bandiera-Client' => 'asdf' })
15
+ stub = stub_api_request(url, { 'response' => {} }, { 'Bandiera-Client' => 'asdf' })
16
16
  client = Bandiera::Client.new(api_uri, logger, 'asdf')
17
17
  client.enabled?(group, feature)
18
18
  expect(stub).to have_been_requested
@@ -36,83 +36,6 @@ describe Bandiera::Client do
36
36
  end
37
37
  end
38
38
 
39
- describe '#enabled?' do
40
- context 'with cache strategy :single_feature' do
41
- before do
42
- subject.cache_strategy = :single_feature
43
- end
44
-
45
- context 'and an empty cache' do
46
- it 'calls #get_feature' do
47
- expect(subject).to receive(:get_feature).once
48
- subject.enabled?('foo', 'bar')
49
- end
50
- end
51
-
52
- context 'and a primed cache' do
53
- before do
54
- cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
55
- subject.cache.store(cache_key, false)
56
- end
57
-
58
- it 'does not call #get_feature' do
59
- expect(subject).to_not receive(:get_feature)
60
- subject.enabled?('foo', 'bar')
61
- end
62
- end
63
- end
64
-
65
- context 'with cache strategy :group' do
66
- before do
67
- subject.cache_strategy = :group
68
- end
69
-
70
- context 'and an empty cache' do
71
- it 'calls #get_features_for_group' do
72
- expect(subject).to receive(:get_features_for_group).once
73
- subject.enabled?('foo', 'bar')
74
- end
75
- end
76
-
77
- context 'and a primed cache' do
78
- before do
79
- cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
80
- subject.cache.store(cache_key, false)
81
- end
82
-
83
- it 'does not call #get_features_for_group' do
84
- expect(subject).to_not receive(:get_features_for_group)
85
- subject.enabled?('foo', 'bar')
86
- end
87
- end
88
- end
89
-
90
- context 'with cache strategy :all' do
91
- before do
92
- subject.cache_strategy = :all
93
- end
94
-
95
- context 'and an empty cache' do
96
- it 'calls #get_all' do
97
- expect(subject).to receive(:get_all).once
98
- subject.enabled?('foo', 'bar')
99
- end
100
- end
101
-
102
- context 'and a primed cache' do
103
- before do
104
- cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
105
- subject.cache.store(cache_key, false)
106
- end
107
-
108
- it 'does not call #get_all' do
109
- expect(subject).to_not receive(:get_all)
110
- subject.enabled?('foo', 'bar')
111
- end
112
- end
113
- end
114
- end
115
-
116
39
  describe '#get_feature' do
117
40
  let(:group) { 'pubserv' }
118
41
  let(:feature) { 'log-stats' }
@@ -127,14 +50,6 @@ describe Bandiera::Client do
127
50
  expect(stub).to have_been_requested
128
51
  end
129
52
 
130
- it 'stores the feature value in the cache' do
131
- stub_api_request(url, 'response' => true)
132
- cache_key = subject.send(:build_cache_key, group, feature, {})
133
-
134
- subject.get_feature(group, feature)
135
- expect(subject.cache.key?(cache_key)).to be true
136
- end
137
-
138
53
  context 'and the user has passed through some extra params' do
139
54
  it 'passes them through to the API' do
140
55
  stub = stub_request(:get, url)
@@ -174,14 +89,6 @@ describe Bandiera::Client do
174
89
 
175
90
  expect(response).to be false
176
91
  end
177
-
178
- it 'does not store anything in the cache' do
179
- stub_request(:get, url).to_return(status: [0, ''])
180
-
181
- expect(subject.cache).to_not receive(:store)
182
-
183
- subject.get_feature(group, feature)
184
- end
185
92
  end
186
93
 
187
94
  context 'bandiera is having some problems' do
@@ -194,14 +101,6 @@ describe Bandiera::Client do
194
101
 
195
102
  expect(response).to be false
196
103
  end
197
-
198
- it 'does not store anything in the cache' do
199
- stub_request(:get, url).to_return(status: 500, body: '')
200
-
201
- expect(subject.cache).to_not receive(:store)
202
-
203
- subject.get_feature(group, feature)
204
- end
205
104
  end
206
105
 
207
106
  context 'bandiera times out' do
@@ -214,14 +113,6 @@ describe Bandiera::Client do
214
113
 
215
114
  expect(response).to be false
216
115
  end
217
-
218
- it 'does not store anything in the cache' do
219
- stub_request(:get, url).to_timeout
220
-
221
- expect(subject.cache).to_not receive(:store)
222
-
223
- subject.get_feature(group, feature)
224
- end
225
116
  end
226
117
  end
227
118
 
@@ -239,19 +130,6 @@ describe Bandiera::Client do
239
130
  expect(stub).to have_been_requested
240
131
  end
241
132
 
242
- it 'stores the feature values in the cache' do
243
- feature_hash = { 'show-stuff' => true, 'show-other-stuff' => false }
244
- stub_api_request(url, 'response' => feature_hash)
245
-
246
- subject.get_features_for_group(group)
247
-
248
- cache_key = subject.send(:build_cache_key, group, 'show-stuff', {})
249
- expect(subject.cache.key?(cache_key)).to be true
250
-
251
- cache_key = subject.send(:build_cache_key, group, 'show-other-stuff', {})
252
- expect(subject.cache.key?(cache_key)).to be true
253
- end
254
-
255
133
  context 'and the user has passed through some extra params' do
256
134
  it 'passes them through to the API' do
257
135
  stub = stub_request(:get, url)
@@ -291,14 +169,6 @@ describe Bandiera::Client do
291
169
 
292
170
  expect(response).to be {}
293
171
  end
294
-
295
- it 'does not store anything in the cache' do
296
- stub_request(:get, url).to_return(status: [0, ''])
297
-
298
- expect(subject.cache).to_not receive(:store)
299
-
300
- subject.get_features_for_group(group)
301
- end
302
172
  end
303
173
 
304
174
  context 'bandiera is having some problems' do
@@ -311,14 +181,6 @@ describe Bandiera::Client do
311
181
 
312
182
  expect(response).to be {}
313
183
  end
314
-
315
- it 'does not store anything in the cache' do
316
- stub_request(:get, url).to_return(status: 500, body: '')
317
-
318
- expect(subject.cache).to_not receive(:store)
319
-
320
- subject.get_features_for_group(group)
321
- end
322
184
  end
323
185
 
324
186
  context 'bandiera times out' do
@@ -331,14 +193,6 @@ describe Bandiera::Client do
331
193
 
332
194
  expect(response).to be {}
333
195
  end
334
-
335
- it 'does not store anything in the cache' do
336
- stub_request(:get, url).to_timeout
337
-
338
- expect(subject.cache).to_not receive(:store)
339
-
340
- subject.get_features_for_group(group)
341
- end
342
196
  end
343
197
  end
344
198
 
@@ -355,19 +209,6 @@ describe Bandiera::Client do
355
209
  expect(stub).to have_been_requested
356
210
  end
357
211
 
358
- it 'stores the feature values in the cache' do
359
- feature_hash = { 'pubserv' => { 'show-stuff' => true, 'show-other-stuff' => false } }
360
- stub_api_request(url, 'response' => feature_hash)
361
-
362
- subject.get_all
363
-
364
- cache_key = subject.send(:build_cache_key, 'pubserv', 'show-stuff', {})
365
- expect(subject.cache.key?(cache_key)).to be true
366
-
367
- cache_key = subject.send(:build_cache_key, 'pubserv', 'show-other-stuff', {})
368
- expect(subject.cache.key?(cache_key)).to be true
369
- end
370
-
371
212
  context 'and the user has passed through some extra params' do
372
213
  it 'passes them through to the API' do
373
214
  stub = stub_request(:get, url)
@@ -407,14 +248,6 @@ describe Bandiera::Client do
407
248
 
408
249
  expect(response).to be {}
409
250
  end
410
-
411
- it 'does not store anything in the cache' do
412
- stub_request(:get, url).to_return(status: [0, ''])
413
-
414
- expect(subject.cache).to_not receive(:store)
415
-
416
- subject.get_all
417
- end
418
251
  end
419
252
 
420
253
  context 'bandiera is having some problems' do
@@ -427,14 +260,6 @@ describe Bandiera::Client do
427
260
 
428
261
  expect(response).to be {}
429
262
  end
430
-
431
- it 'does not store anything in the cache' do
432
- stub_request(:get, url).to_return(status: 500, body: '')
433
-
434
- expect(subject.cache).to_not receive(:store)
435
-
436
- subject.get_all
437
- end
438
263
  end
439
264
 
440
265
  context 'bandiera times out' do
@@ -447,14 +272,6 @@ describe Bandiera::Client do
447
272
 
448
273
  expect(response).to be {}
449
274
  end
450
-
451
- it 'does not store anything in the cache' do
452
- stub_request(:get, url).to_timeout
453
-
454
- expect(subject.cache).to_not receive(:store)
455
-
456
- subject.get_all
457
- end
458
275
  end
459
276
  end
460
277
 
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+ require 'macmillan/utils/rspec/rack_test_helper'
3
+
4
+ describe Bandiera::Middleware do
5
+ let(:app) { ->(env) { [200, env, 'app'] } }
6
+ let(:request) { req_for('http://example.com') }
7
+ let(:bandiera_client) { double(:bandiera_client) }
8
+ let(:bandiera_groups) { [] }
9
+ let(:user) { double(:user, email: 'bob.flemming@cough.com') }
10
+ let(:user_uuid) { '12345-12345-12345' }
11
+
12
+ subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups) }
13
+
14
+ context 'when a bandiera client has NOT been passed' do
15
+ it 'raises an error' do
16
+ expect { described_class.new(app) }.to raise_error(ArgumentError)
17
+ end
18
+ end
19
+
20
+ context 'feature flag "groups"' do
21
+ context 'when NO feature flag "groups" have been defined' do
22
+ it 'calls #get_all on the bandiera_client' do
23
+ expect(bandiera_client).to receive(:get_all).and_return({})
24
+
25
+ subject.call(request.env)
26
+ end
27
+
28
+ it 'stores the returned flags in the env' do
29
+ pubserv_flags = { 'show-stuff' => true }
30
+ search_flags = { 'show-search' => false }
31
+ bandiera_response = { 'pubserv' => pubserv_flags, 'search' => search_flags }
32
+
33
+ expect(bandiera_client).to receive(:get_all).and_return(bandiera_response)
34
+
35
+ _status, headers, _body = subject.call(request.env)
36
+
37
+ expect(headers['bandiera.pubserv']).to eq(pubserv_flags)
38
+ expect(headers['bandiera.search']).to eq(search_flags)
39
+ end
40
+ end
41
+
42
+ context 'when SOME feature flag "groups" have been defined' do
43
+ let(:bandiera_groups) { ['search'] }
44
+
45
+ it 'calls #get_features_for_group on the bandiera_client' do
46
+ expect(bandiera_client).to receive(:get_features_for_group).and_return([])
47
+
48
+ subject.call(request.env)
49
+ end
50
+
51
+ it 'stores the returned flags in the env' do
52
+ bandiera_response = { 'show-search' => false }
53
+
54
+ expect(bandiera_client).to receive(:get_features_for_group).and_return(bandiera_response)
55
+
56
+ _status, headers, _body = subject.call(request.env)
57
+
58
+ expect(headers['bandiera.search']).to eq(bandiera_response)
59
+ end
60
+ end
61
+ end
62
+
63
+ context '"uuid_env_key"' do
64
+ before do
65
+ request.env[Macmillan::Utils::Middleware::Uuid.env_key] = user_uuid
66
+ end
67
+
68
+ context 'HAS NOT been set' do
69
+ it 'uses the default key from Macmillan::Utils' do
70
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: user_uuid)).and_return({})
71
+
72
+ subject.call(request.env)
73
+ end
74
+
75
+ context "and the nothing has set the #{Macmillan::Utils::Middleware::Uuid.env_key} value" do
76
+ it 'passes nil to the #user_id param' do
77
+ request.env.delete(Macmillan::Utils::Middleware::Uuid.env_key)
78
+
79
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: nil)).and_return({})
80
+
81
+ subject.call(request.env)
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'HAS been set' do
87
+ let(:uuid_env_key) { 'wibble' }
88
+ let(:uuid) { 'qwerty' }
89
+
90
+ subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, uuid_env_key: uuid_env_key) }
91
+
92
+ it 'uses the key passed into it' do
93
+ request.env[uuid_env_key] = uuid
94
+
95
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: uuid)).and_return({})
96
+
97
+ subject.call(request.env)
98
+ end
99
+ end
100
+ end
101
+
102
+ context '"user_env_key"' do
103
+ before do
104
+ request.env['current_user'] = user
105
+ end
106
+
107
+ context 'when there is no current user' do
108
+ it 'does not cause errors' do
109
+ expect do
110
+ request.env['current_user'] = nil
111
+
112
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: nil)).and_return({})
113
+
114
+ subject.call(request.env)
115
+ end.to_not raise_error
116
+ end
117
+ end
118
+
119
+ context 'HAS NOT been set' do
120
+ it 'uses the default key - "current_user"' do
121
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: user.email)).and_return({})
122
+
123
+ subject.call(request.env)
124
+ end
125
+ end
126
+
127
+ context 'HAS been set' do
128
+ let(:user_env_key) { 'wibble' }
129
+ let(:user) { double(:user, email: 'robert.paulson@example.com') }
130
+
131
+ subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, user_env_key: user_env_key) }
132
+
133
+ it 'uses the key passed to it' do
134
+ request.env[user_env_key] = user
135
+
136
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: user.email)).and_return({})
137
+
138
+ subject.call(request.env)
139
+ end
140
+ end
141
+ end
142
+
143
+ context '"user_group_method"' do
144
+ before do
145
+ request.env['current_user'] = user
146
+ end
147
+
148
+ context 'when there is no current user' do
149
+ it 'does not cause errors' do
150
+ expect do
151
+ request.env['current_user'] = nil
152
+
153
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: nil)).and_return({})
154
+
155
+ subject.call(request.env)
156
+ end.to_not raise_error
157
+ end
158
+ end
159
+
160
+ context 'HAS NOT been set' do
161
+ it 'uses the default method - "email"' do
162
+ email = 'ron.manager@football.com'
163
+
164
+ expect(user).to receive(:public_send).with(:email).once.and_return(email)
165
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: email)).and_return({})
166
+
167
+ subject.call(request.env)
168
+ end
169
+ end
170
+
171
+ context 'HAS been set' do
172
+ subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, user_group_method: :role) }
173
+
174
+ it 'uses the method passed to it' do
175
+ role = 'Administrator'
176
+
177
+ expect(user).to receive(:public_send).with(:role).once.and_return(role)
178
+ expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: role)).and_return({})
179
+
180
+ subject.call(request.env)
181
+ end
182
+ end
183
+ end
184
+ end
data/spec/spec_helper.rb CHANGED
@@ -12,3 +12,4 @@ require 'macmillan/utils/test_helpers/simplecov_helper'
12
12
  require 'pry'
13
13
 
14
14
  require 'bandiera/client'
15
+ require 'bandiera/middleware'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bandiera-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Macmillan Science and Education (New Publsihing Platforms)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-07 00:00:00.000000000 Z
11
+ date: 2015-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: rack-test
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,13 +81,13 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rest-client
84
+ name: pry
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
- type: :runtime
90
+ type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: moneta
98
+ name: rest-client
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -135,6 +135,7 @@ files:
135
135
  - ".hound.yml"
136
136
  - ".rspec"
137
137
  - ".rubocop.yml"
138
+ - ".travis.yml"
138
139
  - Gemfile
139
140
  - HISTORY
140
141
  - LICENSE.txt
@@ -143,11 +144,13 @@ files:
143
144
  - bandiera-client.gemspec
144
145
  - lib/bandiera/client.rb
145
146
  - lib/bandiera/client/version.rb
147
+ - lib/bandiera/middleware.rb
146
148
  - spec/lib/bandiera/client_spec.rb
149
+ - spec/lib/bandiera/middleware_spec.rb
147
150
  - spec/spec_helper.rb
148
151
  homepage: https://github.com/nature/bandiera-client-ruby
149
152
  licenses:
150
- - GPL-3
153
+ - MIT
151
154
  metadata: {}
152
155
  post_install_message:
153
156
  rdoc_options: []
@@ -165,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
168
  version: '0'
166
169
  requirements: []
167
170
  rubyforge_project:
168
- rubygems_version: 2.4.5
171
+ rubygems_version: 2.4.8
169
172
  signing_key:
170
173
  specification_version: 4
171
174
  summary: Simple feature flagging API client.