bandiera-client 2.2.2
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 +7 -0
- data/.gitignore +24 -0
- data/.hound.yml +36 -0
- data/.rspec +3 -0
- data/.rubocop.yml +36 -0
- data/Gemfile +3 -0
- data/HISTORY +39 -0
- data/LICENSE.txt +20 -0
- data/README.md +111 -0
- data/Rakefile +6 -0
- data/bandiera-client.gemspec +31 -0
- data/lib/bandiera/client/version.rb +5 -0
- data/lib/bandiera/client.rb +140 -0
- data/spec/lib/bandiera/client_spec.rb +472 -0
- data/spec/spec_helper.rb +14 -0
- metadata +173 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 748f5858d09ce6aec023da7987c3f4ab0fc0fa75
|
4
|
+
data.tar.gz: e9cb4b7fa6ba2e12f04b24cb7bc8ca7505b5313a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 75bbcece7e445f4420c8d4e8bb8091372705e435d4e5d81625ce8cc6d1c02ac71fee56846ded50c5b59360c5c41f5a55aa5f62adc41510914b847e62751f1d60
|
7
|
+
data.tar.gz: e42c294b272343d9a495aa091c90dfcb72bfa7c7d1880f4a6645b8c08404c4befe0be8aeb2e3b4f3c5e26564ab01bdb6a6baddce4f1da8bdd74abdace2b4a6a6
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
artifacts*
|
24
|
+
RBENV_VERSION*
|
data/.hound.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
Metrics/LineLength:
|
2
|
+
Description: 'Limit lines to 120 characters.'
|
3
|
+
Max: 120
|
4
|
+
|
5
|
+
Style/Documentation:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Style/SingleSpaceBeforeFirstArg:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/BracesAroundHashParameters:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Style/IndentHash:
|
15
|
+
EnforcedStyle: consistent
|
16
|
+
|
17
|
+
Style/AlignHash:
|
18
|
+
EnforcedHashRocketStyle: table
|
19
|
+
EnforcedColonStyle: table
|
20
|
+
|
21
|
+
Style/AlignParameters:
|
22
|
+
EnforcedStyle: with_fixed_indentation
|
23
|
+
|
24
|
+
Style/StringLiterals:
|
25
|
+
EnforcedStyle: single_quotes
|
26
|
+
|
27
|
+
Style/CollectionMethods:
|
28
|
+
PreferredMethods:
|
29
|
+
collect: 'map'
|
30
|
+
collect!: 'map!'
|
31
|
+
inject: 'reduce'
|
32
|
+
detect: 'find'
|
33
|
+
find_all: 'select'
|
34
|
+
|
35
|
+
Style/DotPosition:
|
36
|
+
EnforcedStyle: leading
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
Metrics/LineLength:
|
2
|
+
Description: 'Limit lines to 120 characters.'
|
3
|
+
Max: 120
|
4
|
+
|
5
|
+
Style/Documentation:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Style/SingleSpaceBeforeFirstArg:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/BracesAroundHashParameters:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Style/IndentHash:
|
15
|
+
EnforcedStyle: consistent
|
16
|
+
|
17
|
+
Style/AlignHash:
|
18
|
+
EnforcedHashRocketStyle: table
|
19
|
+
EnforcedColonStyle: table
|
20
|
+
|
21
|
+
Style/AlignParameters:
|
22
|
+
EnforcedStyle: with_fixed_indentation
|
23
|
+
|
24
|
+
Style/StringLiterals:
|
25
|
+
EnforcedStyle: single_quotes
|
26
|
+
|
27
|
+
Style/CollectionMethods:
|
28
|
+
PreferredMethods:
|
29
|
+
collect: 'map'
|
30
|
+
collect!: 'map!'
|
31
|
+
inject: 'reduce'
|
32
|
+
detect: 'find'
|
33
|
+
find_all: 'select'
|
34
|
+
|
35
|
+
Style/DotPosition:
|
36
|
+
EnforcedStyle: leading
|
data/Gemfile
ADDED
data/HISTORY
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= 2.2.2 / 02-March-2015
|
2
|
+
|
3
|
+
* Handle non-JSON responses correctly (these can happen due to proxy issues etc).
|
4
|
+
|
5
|
+
= 2.2.1 / 19-January-2015
|
6
|
+
|
7
|
+
* Small updates for Ruby 2.2.0 compatability.
|
8
|
+
* Documentation updates.
|
9
|
+
|
10
|
+
= 2.2.0 / 14-August-2014
|
11
|
+
|
12
|
+
* Add a configurable caching layer into the client.
|
13
|
+
* Expose methods to get raw responses from the Bandiera API
|
14
|
+
|
15
|
+
= 2.1.0 / 02-July-2014
|
16
|
+
|
17
|
+
* Switch license to MIT.
|
18
|
+
* Handle connection refused errors.
|
19
|
+
|
20
|
+
= 2.0.2 / 15-May-2014
|
21
|
+
|
22
|
+
* Switch the HTTP library used from Typhoeus to RestClient.
|
23
|
+
* Set the default HTTP timeout to 0.2 seconds.
|
24
|
+
* Allow per-request timeout (and other RestClient::Resource options) to be passed through.
|
25
|
+
|
26
|
+
= 2.0.1 / 07-May-2014
|
27
|
+
|
28
|
+
* Enable test suite to be run via Rake.
|
29
|
+
* Allow a client name to be passed through in the HTTP headers as 'Bandiera-Client'.
|
30
|
+
* Set the 'User-Agent' HTTP header to identify the requests as coming from this client.
|
31
|
+
|
32
|
+
= 2.0.0 / 01-May-2014
|
33
|
+
|
34
|
+
* Update to support Bandiera API v2 and user_groups feature.
|
35
|
+
|
36
|
+
= 1.0.0 / 24-Jan-2014
|
37
|
+
|
38
|
+
* Initial tagging of 1.0.0 codebase - support for Bandiera API v1.
|
39
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2014, Nature Publishing Group
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Bandiera::Client (Ruby)
|
2
|
+
|
3
|
+
This is a client for talking to the [Bandiera][bandiera] feature flagging
|
4
|
+
service from a Ruby application.
|
5
|
+
|
6
|
+
This client is compatible with the [v2 Bandiera API][bandiera-api].
|
7
|
+
|
8
|
+
**Current Version:** 2.2.1
|
9
|
+
**License:** [MIT][mit]
|
10
|
+
**Build Status:** [![Build Status][travis-img]][travis]
|
11
|
+
|
12
|
+
## Ruby Support:
|
13
|
+
|
14
|
+
This client has been tested against the latest MRI and JRuby builds.
|
15
|
+
|
16
|
+
# Usage
|
17
|
+
|
18
|
+
Add the following to your `Gemfile`:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'bandiera-client'
|
22
|
+
```
|
23
|
+
|
24
|
+
Then interact with a Bandiera server like so:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'bandiera/client'
|
28
|
+
|
29
|
+
$bandiera = Bandiera::Client.new('http://bandiera.example.com')
|
30
|
+
|
31
|
+
if $bandiera.enabled?('pubserv', 'show-new-search')
|
32
|
+
# show the new experimental search function
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
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.
|
41
|
+
|
42
|
+
## Caching
|
43
|
+
|
44
|
+
Bandiera::Client has a small layer of caching built into it in order to:
|
45
|
+
|
46
|
+
1. Reduce the amount of HTTP requests made to the Bandiera server
|
47
|
+
2. Make things faster
|
48
|
+
|
49
|
+
### Strategies
|
50
|
+
|
51
|
+
There are three request/caching strategies you can use with Bandiera::Client.
|
52
|
+
|
53
|
+
**:single_feature**
|
54
|
+
|
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.
|
59
|
+
|
60
|
+
**:group**
|
61
|
+
|
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**.
|
68
|
+
|
69
|
+
**:all**
|
70
|
+
|
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.
|
77
|
+
|
78
|
+
#### Changing the Cache Strategy
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
$bandiera = Bandiera::Client.new('http://bandiera.example.com')
|
82
|
+
$bandiera.cache_strategy = :all
|
83
|
+
```
|
84
|
+
|
85
|
+
### Cache Expiration
|
86
|
+
|
87
|
+
The default cache lifetime is 5 seconds. If you would like to alter this you
|
88
|
+
can do so as follows:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
$bandiera = Bandiera::Client.new('http://bandiera.example.com')
|
92
|
+
$bandiera.cache_ttl = 10 # 10 seconds
|
93
|
+
```
|
94
|
+
|
95
|
+
|
96
|
+
# Development
|
97
|
+
|
98
|
+
1. Fork this repo.
|
99
|
+
2. Run `bundle install`
|
100
|
+
|
101
|
+
# License
|
102
|
+
|
103
|
+
[© 2014 Nature Publishing Group](LICENSE.txt).
|
104
|
+
Bandiera::Client (Ruby) is licensed under the [MIT License][mit].
|
105
|
+
|
106
|
+
|
107
|
+
[mit]: http://opensource.org/licenses/mit-license.php
|
108
|
+
[bandiera]: https://github.com/nature/bandiera
|
109
|
+
[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
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'bandiera/client/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'bandiera-client'
|
10
|
+
spec.version = Bandiera::Client::VERSION
|
11
|
+
spec.authors = ['Macmillan Science and Education (New Publsihing Platforms)']
|
12
|
+
spec.email = ['npp-developers@macmillan.com']
|
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
|
+
spec.summary = 'Simple feature flagging API client.'
|
15
|
+
spec.homepage = 'https://github.com/nature/bandiera-client-ruby'
|
16
|
+
spec.license = 'GPL-3'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
spec.test_files = Dir.glob('spec/*_spec.rb')
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'webmock'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'pry'
|
27
|
+
|
28
|
+
spec.add_dependency 'rest-client'
|
29
|
+
spec.add_dependency 'moneta'
|
30
|
+
spec.add_dependency 'macmillan-utils'
|
31
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'json'
|
3
|
+
require 'logger'
|
4
|
+
require 'moneta'
|
5
|
+
|
6
|
+
module Bandiera
|
7
|
+
class Client
|
8
|
+
autoload :VERSION, 'bandiera/client/version'
|
9
|
+
|
10
|
+
CACHE_STRATEGIES = [:single_feature, :group, :all]
|
11
|
+
|
12
|
+
attr_accessor :timeout, :client_name, :cache_ttl
|
13
|
+
attr_reader :logger, :cache, :cache_strategy
|
14
|
+
|
15
|
+
def initialize(base_uri = 'http://localhost', logger = Logger.new($stdout), client_name = nil)
|
16
|
+
@base_uri = base_uri
|
17
|
+
@base_uri << '/api' unless @base_uri.match(/\/api$/)
|
18
|
+
@logger = logger
|
19
|
+
@timeout = 0.2 # 0.4s (0.2 + 0.2) default timeout
|
20
|
+
@client_name = client_name
|
21
|
+
@cache = Moneta.new(:LRUHash, expires: true)
|
22
|
+
@cache_ttl = 5 # 5 seconds
|
23
|
+
@cache_strategy = :group
|
24
|
+
end
|
25
|
+
|
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)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_feature(group, feature, params = {}, http_opts = {})
|
48
|
+
path = "/v2/groups/#{group}/features/#{feature}"
|
49
|
+
default_response = false
|
50
|
+
error_msg_prefix = "[Bandiera::Client#get_feature] '#{group} / #{feature} / #{params}'"
|
51
|
+
|
52
|
+
logger.debug "[Bandiera::Client#get_feature] calling #{path} with params: #{params}"
|
53
|
+
|
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
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_features_for_group(group, params = {}, http_opts = {})
|
60
|
+
path = "/v2/groups/#{group}/features"
|
61
|
+
default_response = {}
|
62
|
+
error_msg_prefix = "[Bandiera::Client#get_features_for_group] '#{group} / #{params}'"
|
63
|
+
|
64
|
+
logger.debug "[Bandiera::Client#get_features_for_group] calling #{path} with params: #{params}"
|
65
|
+
|
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
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_all(params = {}, http_opts = {})
|
72
|
+
path = '/v2/all'
|
73
|
+
default_response = {}
|
74
|
+
error_msg_prefix = "[Bandiera::Client#get_all] '#{params}'"
|
75
|
+
|
76
|
+
logger.debug "[Bandiera::Client#get_all] calling #{path} with params: #{params}"
|
77
|
+
|
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
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
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
|
+
def headers
|
103
|
+
headers = { 'User-Agent' => "Bandiera Ruby Client / #{Bandiera::Client::VERSION}" }
|
104
|
+
headers.merge!('Bandiera-Client' => client_name) unless client_name.nil?
|
105
|
+
headers
|
106
|
+
end
|
107
|
+
|
108
|
+
EXCEPTIONS_TO_HANDLE = (
|
109
|
+
Errno.constants.map { |cla| Errno.const_get(cla) } + [RestClient::Exception, JSON::ParserError]
|
110
|
+
).flatten
|
111
|
+
|
112
|
+
def get_and_handle_exceptions(path, params, http_opts, return_upon_error, error_msg_prefix, &block)
|
113
|
+
res = get(path, params, http_opts)
|
114
|
+
logger.warn "#{error_msg_prefix} - #{res['warning']}" if res['warning']
|
115
|
+
block.call(res['response']) if block
|
116
|
+
res['response']
|
117
|
+
rescue *EXCEPTIONS_TO_HANDLE => error
|
118
|
+
logger.warn("#{error_msg_prefix} - #{error.class} - #{error.message}")
|
119
|
+
return_upon_error
|
120
|
+
end
|
121
|
+
|
122
|
+
def get(path, params, passed_http_opts)
|
123
|
+
default_http_opts = { method: :get, timeout: timeout, open_timeout: timeout, headers: headers }
|
124
|
+
resource = RestClient::Resource.new(@base_uri, default_http_opts.merge(passed_http_opts))
|
125
|
+
response = resource[path].get(params: clean_params(params))
|
126
|
+
|
127
|
+
JSON.parse(response.body)
|
128
|
+
end
|
129
|
+
|
130
|
+
def clean_params(passed_params)
|
131
|
+
params = {}
|
132
|
+
|
133
|
+
passed_params.each do |key, val|
|
134
|
+
params[key] = val unless val.nil? || (val.respond_to?(:empty) && val.empty?)
|
135
|
+
end
|
136
|
+
|
137
|
+
params
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,472 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bandiera::Client do
|
4
|
+
let(:base_uri) { 'http://bandiera.com' }
|
5
|
+
let(:api_uri) { "#{base_uri}/api" }
|
6
|
+
let(:logger) { double.as_null_object }
|
7
|
+
subject { Bandiera::Client.new(api_uri, logger) }
|
8
|
+
|
9
|
+
context 'when a client name is provided' do
|
10
|
+
let(:group) { 'pubserv' }
|
11
|
+
let(:feature) { 'log-stats' }
|
12
|
+
let(:url) { "#{api_uri}/v2/groups/#{group}/features" }
|
13
|
+
|
14
|
+
it 'sends it as part of the headers' do
|
15
|
+
stub = stub_api_request(url, { 'response' => {} }, { 'Bandiera-Client' => 'asdf' })
|
16
|
+
client = Bandiera::Client.new(api_uri, logger, 'asdf')
|
17
|
+
client.enabled?(group, feature)
|
18
|
+
expect(stub).to have_been_requested
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when some RestClient::Resource options are passed' do
|
23
|
+
it 'passes them onto the RestClient::Resource' do
|
24
|
+
params = {}
|
25
|
+
options = { timeout: 24, open_timeout: 24 }
|
26
|
+
response = double(:response, body: JSON.generate({ response: {} }))
|
27
|
+
resource = double(:resource, '[]' => double(get: response))
|
28
|
+
|
29
|
+
expect(RestClient::Resource)
|
30
|
+
.to receive(:new)
|
31
|
+
.with(api_uri, method: :get, timeout: 24, open_timeout: 24, headers: anything)
|
32
|
+
.once
|
33
|
+
.and_return(resource)
|
34
|
+
|
35
|
+
subject.enabled?('foo', 'bar', params, options)
|
36
|
+
end
|
37
|
+
end
|
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
|
+
describe '#get_feature' do
|
117
|
+
let(:group) { 'pubserv' }
|
118
|
+
let(:feature) { 'log-stats' }
|
119
|
+
let(:url) { "#{api_uri}/v2/groups/#{group}/features/#{feature}" }
|
120
|
+
|
121
|
+
context 'all is ok' do
|
122
|
+
it 'returns the bandiera response' do
|
123
|
+
stub = stub_api_request(url, 'response' => true)
|
124
|
+
response = subject.get_feature(group, feature)
|
125
|
+
|
126
|
+
expect(response).to be true
|
127
|
+
expect(stub).to have_been_requested
|
128
|
+
end
|
129
|
+
|
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
|
+
context 'and the user has passed through some extra params' do
|
139
|
+
it 'passes them through to the API' do
|
140
|
+
stub = stub_request(:get, url)
|
141
|
+
.with(query: { user_group: 'admin', user_id: '12345' })
|
142
|
+
.to_return(
|
143
|
+
body: JSON.generate('response' => true),
|
144
|
+
headers: { 'Content-Type' => 'application/json' }
|
145
|
+
)
|
146
|
+
|
147
|
+
subject.get_feature(group, feature, { user_group: 'admin', user_id: 12345 })
|
148
|
+
|
149
|
+
expect(stub).to have_been_requested
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'but bandiera returns a warning along with the response' do
|
154
|
+
it 'logs the warning' do
|
155
|
+
stub = stub_api_request(url, 'response' => false, 'warning' => 'The group does not exist')
|
156
|
+
|
157
|
+
expect(logger).to receive(:warn).once
|
158
|
+
|
159
|
+
response = subject.get_feature(group, feature)
|
160
|
+
|
161
|
+
expect(response).to be false
|
162
|
+
expect(stub).to have_been_requested
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'bandiera is down' do
|
168
|
+
it 'returns a default response and logs a warning' do
|
169
|
+
stub_request(:get, url).to_return(status: [0, ''])
|
170
|
+
|
171
|
+
expect(logger).to receive(:warn).once
|
172
|
+
|
173
|
+
response = subject.get_feature(group, feature)
|
174
|
+
|
175
|
+
expect(response).to be false
|
176
|
+
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
|
+
end
|
186
|
+
|
187
|
+
context 'bandiera is having some problems' do
|
188
|
+
it 'returns a default response and logs a warning' do
|
189
|
+
stub_request(:get, url).to_return(status: 500, body: '')
|
190
|
+
|
191
|
+
expect(logger).to receive(:warn).once
|
192
|
+
|
193
|
+
response = subject.get_feature(group, feature)
|
194
|
+
|
195
|
+
expect(response).to be false
|
196
|
+
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
|
+
end
|
206
|
+
|
207
|
+
context 'bandiera times out' do
|
208
|
+
it 'returns a default response and logs a warning' do
|
209
|
+
stub_request(:get, url).to_timeout
|
210
|
+
|
211
|
+
expect(logger).to receive(:warn).once
|
212
|
+
|
213
|
+
response = subject.get_feature(group, feature)
|
214
|
+
|
215
|
+
expect(response).to be false
|
216
|
+
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
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '#get_features_for_group' do
|
229
|
+
let(:group) { 'pubserv' }
|
230
|
+
let(:url) { "#{api_uri}/v2/groups/#{group}/features" }
|
231
|
+
|
232
|
+
context 'all is ok' do
|
233
|
+
it 'returns the bandiera response' do
|
234
|
+
feature_hash = { 'show-stuff' => true, 'show-other-stuff' => false }
|
235
|
+
stub = stub_api_request(url, 'response' => feature_hash)
|
236
|
+
response = subject.get_features_for_group(group)
|
237
|
+
|
238
|
+
expect(response).to eq(feature_hash)
|
239
|
+
expect(stub).to have_been_requested
|
240
|
+
end
|
241
|
+
|
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
|
+
context 'and the user has passed through some extra params' do
|
256
|
+
it 'passes them through to the API' do
|
257
|
+
stub = stub_request(:get, url)
|
258
|
+
.with(query: { user_group: 'admin', user_id: '12345' })
|
259
|
+
.to_return(
|
260
|
+
body: JSON.generate('response' => {}),
|
261
|
+
headers: { 'Content-Type' => 'application/json' }
|
262
|
+
)
|
263
|
+
|
264
|
+
subject.get_features_for_group(group, { user_group: 'admin', user_id: 12345 })
|
265
|
+
|
266
|
+
expect(stub).to have_been_requested
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'but bandiera returns a warning along with the response' do
|
271
|
+
it 'logs the warning' do
|
272
|
+
stub = stub_api_request(url, 'response' => {}, 'warning' => 'The group does not exist')
|
273
|
+
|
274
|
+
expect(logger).to receive(:warn).once
|
275
|
+
|
276
|
+
response = subject.get_features_for_group(group)
|
277
|
+
|
278
|
+
expect(response).to be {}
|
279
|
+
expect(stub).to have_been_requested
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'bandiera is down' do
|
285
|
+
it 'returns a default response and logs a warning' do
|
286
|
+
stub_request(:get, url).to_return(status: [0, ''])
|
287
|
+
|
288
|
+
expect(logger).to receive(:warn).once
|
289
|
+
|
290
|
+
response = subject.get_features_for_group(group)
|
291
|
+
|
292
|
+
expect(response).to be {}
|
293
|
+
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
|
+
end
|
303
|
+
|
304
|
+
context 'bandiera is having some problems' do
|
305
|
+
it 'returns a default response and logs a warning' do
|
306
|
+
stub_request(:get, url).to_return(status: 500, body: '')
|
307
|
+
|
308
|
+
expect(logger).to receive(:warn).once
|
309
|
+
|
310
|
+
response = subject.get_features_for_group(group)
|
311
|
+
|
312
|
+
expect(response).to be {}
|
313
|
+
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
|
+
end
|
323
|
+
|
324
|
+
context 'bandiera times out' do
|
325
|
+
it 'returns a default response and logs a warning' do
|
326
|
+
stub_request(:get, url).to_timeout
|
327
|
+
|
328
|
+
expect(logger).to receive(:warn).once
|
329
|
+
|
330
|
+
response = subject.get_features_for_group(group)
|
331
|
+
|
332
|
+
expect(response).to be {}
|
333
|
+
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
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe '#get_all' do
|
346
|
+
let(:url) { "#{api_uri}/v2/all" }
|
347
|
+
|
348
|
+
context 'all is ok' do
|
349
|
+
it 'returns the bandiera response' do
|
350
|
+
feature_hash = { 'pubserv' => { 'show-stuff' => true, 'show-other-stuff' => false } }
|
351
|
+
stub = stub_api_request(url, 'response' => feature_hash)
|
352
|
+
response = subject.get_all
|
353
|
+
|
354
|
+
expect(response).to eq(feature_hash)
|
355
|
+
expect(stub).to have_been_requested
|
356
|
+
end
|
357
|
+
|
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
|
+
context 'and the user has passed through some extra params' do
|
372
|
+
it 'passes them through to the API' do
|
373
|
+
stub = stub_request(:get, url)
|
374
|
+
.with(query: { user_group: 'admin', user_id: '12345' })
|
375
|
+
.to_return(
|
376
|
+
body: JSON.generate('response' => {}),
|
377
|
+
headers: { 'Content-Type' => 'application/json' }
|
378
|
+
)
|
379
|
+
|
380
|
+
subject.get_all({ user_group: 'admin', user_id: 12345 })
|
381
|
+
|
382
|
+
expect(stub).to have_been_requested
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
context 'but bandiera returns a warning along with the response' do
|
387
|
+
it 'logs the warning' do
|
388
|
+
stub = stub_api_request(url, 'response' => {}, 'warning' => 'The group does not exist')
|
389
|
+
|
390
|
+
expect(logger).to receive(:warn).once
|
391
|
+
|
392
|
+
response = subject.get_all
|
393
|
+
|
394
|
+
expect(response).to be {}
|
395
|
+
expect(stub).to have_been_requested
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context 'bandiera is down' do
|
401
|
+
it 'returns a default response and logs a warning' do
|
402
|
+
stub_request(:get, url).to_return(status: [0, ''])
|
403
|
+
|
404
|
+
expect(logger).to receive(:warn).once
|
405
|
+
|
406
|
+
response = subject.get_all
|
407
|
+
|
408
|
+
expect(response).to be {}
|
409
|
+
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
|
+
end
|
419
|
+
|
420
|
+
context 'bandiera is having some problems' do
|
421
|
+
it 'returns a default response and logs a warning' do
|
422
|
+
stub_request(:get, url).to_return(status: 200, body: '<html></html>')
|
423
|
+
|
424
|
+
expect(logger).to receive(:warn).once
|
425
|
+
|
426
|
+
response = subject.get_all
|
427
|
+
|
428
|
+
expect(response).to be {}
|
429
|
+
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
|
+
end
|
439
|
+
|
440
|
+
context 'bandiera times out' do
|
441
|
+
it 'returns a default response and logs a warning' do
|
442
|
+
stub_request(:get, url).to_timeout
|
443
|
+
|
444
|
+
expect(logger).to receive(:warn).once
|
445
|
+
|
446
|
+
response = subject.get_all
|
447
|
+
|
448
|
+
expect(response).to be {}
|
449
|
+
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
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
private
|
462
|
+
|
463
|
+
def stub_api_request(url, response, headers = {})
|
464
|
+
headers.merge! 'User-Agent' => "Bandiera Ruby Client / #{Bandiera::Client::VERSION}"
|
465
|
+
stub_request(:get, url)
|
466
|
+
.with(headers: headers)
|
467
|
+
.to_return(
|
468
|
+
body: JSON.generate(response),
|
469
|
+
headers: { 'Content-Type' => 'application/json' }
|
470
|
+
)
|
471
|
+
end
|
472
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(__FILE__, '../../lib')
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup(:default, :test)
|
5
|
+
|
6
|
+
ENV['RACK_ENV'] = 'test'
|
7
|
+
|
8
|
+
require 'macmillan/utils/rspec/rspec_defaults'
|
9
|
+
require 'macmillan/utils/rspec/webmock_helper'
|
10
|
+
require 'macmillan/utils/test_helpers/codeclimate_helper'
|
11
|
+
require 'macmillan/utils/test_helpers/simplecov_helper'
|
12
|
+
require 'pry'
|
13
|
+
|
14
|
+
require 'bandiera/client'
|
metadata
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bandiera-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Macmillan Science and Education (New Publsihing Platforms)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: webmock
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rest-client
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: moneta
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: macmillan-utils
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Bandiera is a simple, stand-alone feature flagging service that is not
|
126
|
+
tied to any existing web framework or language. This is a client for talking to
|
127
|
+
the web service.
|
128
|
+
email:
|
129
|
+
- npp-developers@macmillan.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- ".gitignore"
|
135
|
+
- ".hound.yml"
|
136
|
+
- ".rspec"
|
137
|
+
- ".rubocop.yml"
|
138
|
+
- Gemfile
|
139
|
+
- HISTORY
|
140
|
+
- LICENSE.txt
|
141
|
+
- README.md
|
142
|
+
- Rakefile
|
143
|
+
- bandiera-client.gemspec
|
144
|
+
- lib/bandiera/client.rb
|
145
|
+
- lib/bandiera/client/version.rb
|
146
|
+
- spec/lib/bandiera/client_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
homepage: https://github.com/nature/bandiera-client-ruby
|
149
|
+
licenses:
|
150
|
+
- GPL-3
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.4.5
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: Simple feature flagging API client.
|
172
|
+
test_files: []
|
173
|
+
has_rdoc:
|