eight_ball 1.0.4 → 2.2.1

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
  SHA256:
3
- metadata.gz: eabee3041028eda40c2773fbbf543c90acf5b96a3433b061e7955bd9ef617742
4
- data.tar.gz: 044cfffe29fa8f4f6aca313dd720f2a86f9c497f6450c13c0a043715232dd0a4
3
+ metadata.gz: c05f1bec9958e4b2602bc9559ff20c54bddbfecbc791b25ade0996ca854a61f7
4
+ data.tar.gz: 7f8ee4370d9a1d1d935442623f847cad7b75b96ca8c497525ef704e715376dcf
5
5
  SHA512:
6
- metadata.gz: '02996ea7f5974cebd0611b8bf3cc204e1cbbce2576df86434e53550b732b3897dcb6da9877ef0857bd7c1a3c4a8a375333d5c371e735f07e21b27c31bc6e5e57'
7
- data.tar.gz: 0d1d97fa17de0e9fcf0e3c81d9b0d4cc2130dc5b00bd820038ec2eefc46cb262d9398ca2df3343b3becd6f4152d6163fdc37884ec5a06d8ff8ed84461a420810
6
+ metadata.gz: b507c5aec0f43afad5d2f25541ebb9939be8478e9d0e5feac685a06c19b5433dd3c62d01270aa5843fd5836054f28cb860bc4df2bec808dc083f6e82696508e6
7
+ data.tar.gz: 1205c3924565b64c8d86eb4bed1ca0b7e67e1f16bd3c0c0c61c7b48501fb419575dbff352692573c03ab42c2266ac26fce8e6e470bc67d0745eac6cb63c07a84
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -1,17 +1,42 @@
1
1
  # Changelog
2
2
 
3
- ## [1.0.4]
4
- Update .travis.yml
3
+ ## [2.2.1]
4
+
5
+ - Switch release to github actions
6
+
7
+ ## [2.2.0]
8
+
9
+ - Add `==` to `Feature` and `Conditions`
10
+
11
+ ## [2.1.0]
12
+
13
+ - Add `features` parameter to `EightBall.marshall` to allow marshalling any Features, not just the ones
14
+ from the configured Provider.
15
+
16
+ ## [2.0.0]
17
+
18
+ - [BREAKING] `Parsers` have been replaced with `Marshallers`, allowing bi-directional conversions
19
+ - Added `EightBall.marshall` as a way to output the Feature list to an external format (e.g. to create a JSON file)
20
+ - Added `EightBall.features` as a shortcut to `EightBall.provider.features`
21
+ - Testing framework has been moved from Minitest to rspec
22
+ - Updated dev dependencies
23
+
24
+ ## [1.0.5]
25
+
26
+ Security: Update rake to >= 12.3.3
5
27
 
6
28
  ## [1.0.3]
29
+
7
30
  Update .travis.yml
8
31
 
9
32
  ## [1.0.2]
33
+
10
34
  Update .travis.yml
11
35
 
12
36
  ## [1.0.1]
37
+
13
38
  Security: Update yard 0.9.16 -> 0.9.20
14
39
 
15
40
  ## [1.0.0]
16
- Initial release!
17
41
 
42
+ Initial release!
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in eight_ball.gemspec
6
6
  gemspec
@@ -1,57 +1,64 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- eight_ball (1.0.4)
4
+ eight_ball (2.2.0)
5
+ awrence (~> 1.1)
5
6
  plissken (~> 1.2)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  ansi (1.5.0)
11
- builder (3.2.3)
12
- byebug (10.0.2)
13
- coderay (1.1.2)
14
- docile (1.3.1)
15
- hirb (0.7.3)
12
+ awrence (1.1.1)
13
+ byebug (11.1.3)
14
+ coderay (1.1.3)
15
+ diff-lcs (1.3)
16
+ docile (1.3.2)
16
17
  inch (0.8.0)
17
18
  pry
18
19
  sparkr (>= 0.2.0)
19
20
  term-ansicolor
20
21
  yard (~> 0.9.12)
21
- json (2.1.0)
22
- metaclass (0.0.4)
23
- method_source (0.9.2)
24
- minitest (5.11.3)
25
- minitest-reporters (1.3.5)
26
- ansi
27
- builder
28
- minitest (>= 5.0)
29
- ruby-progressbar
30
- mocha (1.7.0)
31
- metaclass (~> 0.0.1)
32
- plissken (1.3.0)
33
- pry (0.12.2)
34
- coderay (~> 1.1.0)
35
- method_source (~> 0.9.0)
36
- pry-byebug (3.6.0)
37
- byebug (~> 10.0)
38
- pry (~> 0.10)
39
- rake (10.5.0)
40
- ruby-progressbar (1.10.0)
41
- simplecov (0.16.1)
22
+ method_source (1.0.0)
23
+ plissken (1.3.1)
24
+ pry (0.13.1)
25
+ coderay (~> 1.1)
26
+ method_source (~> 1.0)
27
+ pry-byebug (3.9.0)
28
+ byebug (~> 11.0)
29
+ pry (~> 0.13.0)
30
+ rake (13.0.1)
31
+ rspec (3.9.0)
32
+ rspec-core (~> 3.9.0)
33
+ rspec-expectations (~> 3.9.0)
34
+ rspec-mocks (~> 3.9.0)
35
+ rspec-core (3.9.2)
36
+ rspec-support (~> 3.9.3)
37
+ rspec-expectations (3.9.2)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.9.0)
40
+ rspec-mocks (3.9.1)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.9.0)
43
+ rspec-support (3.9.3)
44
+ simplecov (0.18.5)
42
45
  docile (~> 1.1)
43
- json (>= 1.8, < 3)
44
- simplecov-html (~> 0.10.0)
45
- simplecov-console (0.4.2)
46
+ simplecov-html (~> 0.11)
47
+ simplecov-console (0.7.2)
46
48
  ansi
47
- hirb
48
49
  simplecov
49
- simplecov-html (0.10.2)
50
+ terminal-table
51
+ simplecov-html (0.12.2)
50
52
  sparkr (0.4.1)
51
- term-ansicolor (1.7.0)
53
+ sync (0.5.0)
54
+ term-ansicolor (1.7.1)
52
55
  tins (~> 1.0)
53
- tins (1.20.2)
54
- yard (0.9.20)
56
+ terminal-table (1.8.0)
57
+ unicode-display_width (~> 1.1, >= 1.1.1)
58
+ tins (1.25.0)
59
+ sync
60
+ unicode-display_width (1.7.0)
61
+ yard (0.9.25)
55
62
 
56
63
  PLATFORMS
57
64
  ruby
@@ -60,11 +67,9 @@ DEPENDENCIES
60
67
  bundler (~> 1.17)
61
68
  eight_ball!
62
69
  inch (~> 0.8)
63
- minitest (~> 5.0)
64
- minitest-reporters (~> 1.3)
65
- mocha (~> 1.7)
66
70
  pry-byebug (~> 3.6)
67
- rake (~> 10.0)
71
+ rake (~> 13.0)
72
+ rspec (~> 3.9.0)
68
73
  simplecov (~> 0.16)
69
74
  simplecov-console (~> 0.4)
70
75
 
data/README.md CHANGED
@@ -42,8 +42,8 @@ json_input = %(
42
42
  )
43
43
 
44
44
  # Transform the JSON into a list of Features
45
- parser = EightBall::Parsers::Json.new
46
- features = parser.parse json_input
45
+ marshaller = EightBall::Marshallers::Json.new
46
+ features = marshaller.unmarshall json_input
47
47
 
48
48
  # Tell EightBall about these Features
49
49
  EightBall.provider = EightBall::Providers::Static.new features
@@ -76,7 +76,7 @@ A Condition must either be `true` or `false`. It describes when a Feature is ena
76
76
  A Provider is able to give EightBall the list of Features it needs to answer queries.
77
77
 
78
78
  **Supported Providers**
79
- - [HTTP](lib/eight_ball/providers/http.rb): Connect to a URL and use the given Parser to convert the response into a list of Features.
79
+ - [HTTP](lib/eight_ball/providers/http.rb): Connect to a URL and use the given Marshaller to convert the response into a list of Features.
80
80
  - [Static](lib/eight_ball/providers/static.rb): Once initialized with a list of Features, always provides that same list of Features.
81
81
 
82
82
  #### RefreshPolicies
@@ -85,15 +85,15 @@ Some Providers are able to automatically "refresh" their list of Features using
85
85
  **Supported RefreshPolicies**
86
86
  - [Interval](lib/eight_ball/providers/refresh_policies/interval.rb): The data is considered fresh for a given number of seconds, after which it is considered stale and should be refreshed.
87
87
 
88
- ### Parser
89
- A Parser converts the given input to an array of Features.
88
+ ### Marshallers
89
+ A Marshaller converts Features to and from another format.
90
90
 
91
- **Supported Parsers**
92
- - [JSON](lib/eight_ball/parsers/json.rb)
91
+ **Supported Marshaller**
92
+ - [JSON](lib/eight_ball/marshallers/json.rb)
93
93
 
94
94
  ## Development
95
95
 
96
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
96
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
97
97
 
98
98
  To install this gem onto your local machine, run `bundle exec rake install`.
99
99
 
data/Rakefile CHANGED
@@ -1,10 +1,9 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
3
2
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ rescue LoadError
8
7
  end
9
8
 
10
- task :default => :test
9
+ task default: :spec
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'eight_ball/version'
6
6
 
@@ -17,22 +17,22 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.required_ruby_version = '>= 2.5.0'
19
19
 
20
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|.github|examples)/}) }
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|.github|examples)/}) }
21
21
  spec.bindir = 'exe'
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
25
  ### DEPENDENCIES
26
+
27
+ spec.add_dependency 'awrence', '~> 1.1'
26
28
  spec.add_dependency 'plissken', '~> 1.2'
27
29
 
28
30
  # Development
29
31
  spec.add_development_dependency 'bundler', '~> 1.17'
30
32
  spec.add_development_dependency 'inch', '~> 0.8'
31
- spec.add_development_dependency 'minitest', '~> 5.0'
32
- spec.add_development_dependency 'minitest-reporters', '~> 1.3'
33
- spec.add_development_dependency 'mocha', '~> 1.7'
34
33
  spec.add_development_dependency 'pry-byebug', '~> 3.6'
35
- spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'rake', '~> 13.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.9.0'
36
36
  spec.add_development_dependency 'simplecov', '~> 0.16'
37
37
  spec.add_development_dependency 'simplecov-console', '~> 0.4'
38
38
  end
@@ -3,6 +3,8 @@
3
3
  require 'eight_ball/version'
4
4
  require 'eight_ball/feature'
5
5
 
6
+ require 'eight_ball/configuration_error'
7
+
6
8
  require 'eight_ball/conditions/conditions'
7
9
  require 'eight_ball/conditions/base'
8
10
 
@@ -11,7 +13,7 @@ require 'eight_ball/conditions/list'
11
13
  require 'eight_ball/conditions/never'
12
14
  require 'eight_ball/conditions/range'
13
15
 
14
- require 'eight_ball/parsers/json'
16
+ require 'eight_ball/marshallers/json'
15
17
 
16
18
  require 'eight_ball/providers/http'
17
19
  require 'eight_ball/providers/static'
@@ -33,6 +35,24 @@ module EightBall
33
35
  @provider = provider
34
36
  end
35
37
 
38
+ # Gets the {EightBall::Providers Provider} instance
39
+ # EightBall is configured to use
40
+ #
41
+ # @return {EightBall::Providers Provider}
42
+ def self.provider
43
+ @provider
44
+ end
45
+
46
+ # Serves as a shortcut to access the {EightBall::Feature Features} available
47
+ # on the configured {EightBall::Providers Provider}
48
+ #
49
+ # @return [Array<EightBall::Feature>]
50
+ def self.features
51
+ raise EightBall::ConfigurationError, 'No Provider has been configured; there can be no features. Please see "EightBall.provider="' unless provider
52
+
53
+ provider.features
54
+ end
55
+
36
56
  # "EightBall, is the feature named 'NewFeature' enabled?"
37
57
  #
38
58
  # @return whether or not the {EightBall::Feature} is enabled.
@@ -45,7 +65,7 @@ module EightBall
45
65
  # @example
46
66
  # EightBall.enabled? 'feature1', account_id: 1
47
67
  def self.enabled?(name, parameters = {})
48
- feature = @provider.features.find { |f| f.name == name }
68
+ feature = provider.features.find { |f| f.name == name }
49
69
  return false unless feature
50
70
 
51
71
  feature.enabled? parameters
@@ -69,7 +89,7 @@ module EightBall
69
89
  # Yields to the given block of code if the {EightBall::Feature} is enabled.
70
90
  #
71
91
  # @return [nil] if block is yielded to
72
- # @return [false] if {EightBall::Feature} is disabled
92
+ # @return [false] if no block is given
73
93
  #
74
94
  # @param name [String] The name of the {EightBall::Feature}.
75
95
  # @param parameters [Hash] The parameters the {EightBall::Conditions} of this
@@ -85,9 +105,28 @@ module EightBall
85
105
  yield if enabled? name, parameters
86
106
  end
87
107
 
108
+ # Marshalls the {EightBall::Feature Features}. This can be useful for
109
+ # converting the data to, e.g., a JSON file.
110
+ #
111
+ # If a {EightBall::Marshallers Marshaller} is provided, use it.
112
+ #
113
+ # If no {EightBall::Marshallers Marshaller} is provided, uses the same
114
+ # Marshaller that the Provider is configured with.
115
+ #
116
+ # If the {EightBall::Providers Provider} does not expose a
117
+ # {EightBall::Marshallers Marshaller}, this will default to the
118
+ # {EightBall::Marshallers::Json JSON Marshaller}.
119
+ def self.marshall(marshaller = nil, features = EightBall.features)
120
+ marshaller ||=
121
+ (provider.respond_to?(:marshaller) && provider.marshaller) ||
122
+ EightBall::Marshallers::Json.new
123
+
124
+ marshaller.marshall features
125
+ end
126
+
88
127
  def self.logger
89
128
  @logger ||= Logger.new(STDOUT).tap do |log|
90
- log.progname = self.name
129
+ log.progname = name
91
130
  end
92
131
  end
93
132
 
@@ -4,7 +4,7 @@ module EightBall::Conditions
4
4
  class Base
5
5
  attr_reader :parameter
6
6
 
7
- def initialize(options = [])
7
+ def initialize(_options = [])
8
8
  @parameter = nil
9
9
  end
10
10
 
@@ -12,11 +12,25 @@ module EightBall::Conditions
12
12
  raise 'You can never satisfy the Base condition'
13
13
  end
14
14
 
15
+ def ==(other)
16
+ other.class == self.class && other.state == state
17
+ end
18
+ alias eql? ==
19
+
20
+ def hash
21
+ state.hash
22
+ end
23
+
15
24
  protected
16
25
 
26
+ def state
27
+ [@parameter]
28
+ end
29
+
17
30
  def parameter=(parameter)
18
31
  return if parameter.nil?
19
- @parameter = parameter.gsub(/(.)([A-Z])/,'\1_\2').downcase
32
+
33
+ @parameter = parameter.gsub(/(.)([A-Z])/, '\1_\2').downcase
20
34
  end
21
35
  end
22
36
  end
@@ -31,5 +31,11 @@ module EightBall::Conditions
31
31
  def satisfied?(value)
32
32
  values.include? value
33
33
  end
34
+
35
+ protected
36
+
37
+ def state
38
+ super + [@values.sort]
39
+ end
34
40
  end
35
41
  end
@@ -44,5 +44,11 @@ module EightBall::Conditions
44
44
  def satisfied?(value)
45
45
  value >= min && value <= max
46
46
  end
47
+
48
+ protected
49
+
50
+ def state
51
+ super + [@min, @max]
52
+ end
47
53
  end
48
54
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EightBall
4
+ class ConfigurationError < StandardError
5
+ def initialize(msg = 'An invalid configuration was detected')
6
+ super
7
+ end
8
+ end
9
+ end
@@ -48,6 +48,15 @@ module EightBall
48
48
  any_satisfied?(@enabled_for, parameters) && !any_satisfied?(@disabled_for, parameters)
49
49
  end
50
50
 
51
+ def ==(other)
52
+ name == other.name &&
53
+ enabled_for.size == other.enabled_for.size &&
54
+ enabled_for.all? { |condition| other.enabled_for.any? { |other_condition| condition == other_condition } } &&
55
+ disabled_for.size == other.disabled_for.size &&
56
+ disabled_for.all? { |condition| other.disabled_for.any? { |other_condition| condition == other_condition } }
57
+ end
58
+ alias eql? ==
59
+
51
60
  private
52
61
 
53
62
  def any_satisfied?(conditions, parameters)
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'awrence'
4
+ require 'json'
5
+ require 'plissken'
6
+
7
+ # A JSON marshaller can convert back and forth between JSON and a list of {EightBall::Feature Features}
8
+ # The JSON produced will be pretty-printed, as it is assumed the output will be written to a file.
9
+ #
10
+ # When converting from JSON, the top-level JSON element must be an array and
11
+ # its keys must use camel-case; this will be converted to snake-case by EightBall
12
+ # in order to adhere to both JSON and Ruby standards.
13
+ #
14
+ # Below are some examples of valid JSON:
15
+ #
16
+ # @example A single {EightBall::Feature} is enabled for accounts 1-5 as well as region Europe
17
+ # [{
18
+ # "name": "Feature1",
19
+ # "enabledFor": [{
20
+ # "type": "range",
21
+ # "parameter": "accountId",
22
+ # "min": 1,
23
+ # "max": 5
24
+ # }, {
25
+ # "type": "list",
26
+ # "parameter": "regionName",
27
+ # "values": ["Europe"]
28
+ # }]
29
+ # }]
30
+ #
31
+ # @example A single {EightBall::Feature} is disabled completely using the {EightBall::Conditions::Always Always} condition
32
+ # [{
33
+ # "name": "Feature1",
34
+ # "disabledFor": [{
35
+ # "type": "always"
36
+ # }]
37
+ # }]
38
+ module EightBall::Marshallers
39
+ class Json
40
+ # Convert the given {EightBall::Feature Features} into a JSON array.
41
+ #
42
+ # @param [Array<EightBall::Feature>] features The {EightBall::Feature Features} to convert.
43
+ # @return [String] The resulting JSON string.
44
+ #
45
+ # @example
46
+ # json_string = <Read from somewhere>
47
+ #
48
+ # marshaller = EightBall::Marshallers::Json.new
49
+ # marshaller.marshall [Array<EightBall::Feature>] => json
50
+ def marshall(features)
51
+ JSON.generate(features.map { |feature| feature_to_hash(feature).to_camelback_keys })
52
+ end
53
+
54
+ # Convert the given JSON into a list of {EightBall::Feature Features}.
55
+ #
56
+ # @param [String] json The JSON string to convert.
57
+ # @return [Array<EightBall::Feature>] The parsed {EightBall::Feature Features}
58
+ #
59
+ # @example
60
+ # json_string = <Read from somewhere>
61
+ #
62
+ # marshaller = EightBall::Marshallers::Json.new
63
+ # marshaller.unmarshall json_string => [Features]
64
+ def unmarshall(json)
65
+ parsed = JSON.parse(json, symbolize_names: true).to_snake_keys
66
+
67
+ raise ArgumentError, 'JSON input was not an array' unless parsed.is_a? Array
68
+
69
+ parsed.map do |feature|
70
+ enabled_for = create_conditions_from_json feature[:enabled_for]
71
+ disabled_for = create_conditions_from_json feature[:disabled_for]
72
+
73
+ EightBall::Feature.new feature[:name], enabled_for, disabled_for
74
+ end
75
+ rescue JSON::ParserError => e
76
+ EightBall.logger.error { "Failed to parse JSON: #{e.message}" }
77
+ []
78
+ end
79
+
80
+ private
81
+
82
+ def feature_to_hash(feature)
83
+ hash = {
84
+ name: feature.name
85
+ }
86
+
87
+ hash[:enabled_for] = feature.enabled_for.map { |condition| condition_to_hash(condition) } unless feature.enabled_for.empty?
88
+ hash[:disabled_for] = feature.disabled_for.map { |condition| condition_to_hash(condition) } unless feature.disabled_for.empty?
89
+
90
+ hash
91
+ end
92
+
93
+ def condition_to_hash(condition)
94
+ hash = {
95
+ type: condition.class.name.split('::').last.downcase
96
+ }
97
+ condition.instance_variables.each do |var|
98
+ next unless condition.instance_variable_get(var)
99
+
100
+ hash[var.to_s.delete('@')] = condition.instance_variable_get(var)
101
+ end
102
+
103
+ hash
104
+ end
105
+
106
+ def create_conditions_from_json(json_conditions)
107
+ return [] unless json_conditions&.is_a?(Array)
108
+
109
+ json_conditions.map do |condition|
110
+ condition_class = EightBall::Conditions.by_name condition[:type]
111
+ condition_class.new condition
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,22 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'net/http'
4
+
3
5
  module EightBall::Providers
4
6
  # An HTTP Provider will make a GET request to a given URI, and convert
5
7
  # the response into an array of {EightBall::Feature Features} using the
6
- # given {EightBall::Parsers Parser}.
8
+ # given {EightBall::Marshallers Marshaller}.
7
9
  #
8
10
  # The {EightBall::Feature Features} will be automatically kept up to date
9
11
  # according to the given {EightBall::Providers::RefreshPolicies RefreshPolicy}.
10
12
  class Http
11
13
  SUPPORTED_SCHEMES = %w[http https].freeze
12
14
 
15
+ attr_reader :marshaller
16
+
13
17
  # @param uri [String] The URI to GET the {EightBall::Feature Features} from.
14
18
  # @param options [Hash] The options to create the Provider with.
15
19
  #
16
- # @option options [EightBall::Parsers] :parser
17
- # The {EightBall::Parsers Parser} used to convert the response to an array
20
+ # @option options [EightBall::Marshallers] :marshaller
21
+ # The {EightBall::Marshallers Marshaller} used to convert the response to an array
18
22
  # of {EightBall::Feature Features}. Defaults to an instance of
19
- # {EightBall::Parsers::Json}
23
+ # {EightBall::Marshallers::Json}
20
24
  #
21
25
  # @option options [EightBall::Providers::RefreshPolicies] :refresh_policy
22
26
  # The {EightBall::Providers::RefreshPolicies Policy} used to determine
@@ -34,7 +38,7 @@ module EightBall::Providers
34
38
 
35
39
  @uri = URI.parse uri
36
40
 
37
- @parser = options[:parser] || EightBall::Parsers::Json.new
41
+ @marshaller = options[:marshaller] || EightBall::Marshallers::Json.new
38
42
  @policy = options[:refresh_policy] || EightBall::Providers::RefreshPolicies::Interval.new
39
43
  end
40
44
 
@@ -48,8 +52,8 @@ module EightBall::Providers
48
52
  private
49
53
 
50
54
  def fetch
51
- @features = @parser.parse Net::HTTP.get(@uri)
52
- rescue => e
55
+ @features = @marshaller.unmarshall Net::HTTP.get(@uri)
56
+ rescue StandardError => e
53
57
  EightBall.logger.error { "Failed to fetch data from #{@uri}: #{e.message}" }
54
58
  @features = []
55
59
  end
@@ -7,7 +7,7 @@ module EightBall::Providers::RefreshPolicies
7
7
  # amount of time, after which it is considered stale and should be refreshed.
8
8
  class Interval
9
9
  SECONDS_IN_A_DAY = 86_400
10
-
10
+
11
11
  # Creates a new instance of an Interval RefreshPolicy.
12
12
  #
13
13
  # @param seconds [Integer] The number of seconds the data is considered fresh.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EightBall
4
- VERSION = '1.0.4'
4
+ VERSION = '2.2.1'
5
5
  end
metadata CHANGED
@@ -1,127 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eight_ball
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rewind.io
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-20 00:00:00.000000000 Z
11
+ date: 2020-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: plissken
14
+ name: awrence
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.2'
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.2'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.17'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.17'
26
+ version: '1.1'
41
27
  - !ruby/object:Gem::Dependency
42
- name: inch
28
+ name: plissken
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '0.8'
48
- type: :development
33
+ version: '1.2'
34
+ type: :runtime
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '0.8'
40
+ version: '1.2'
55
41
  - !ruby/object:Gem::Dependency
56
- name: minitest
42
+ name: bundler
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '5.0'
47
+ version: '1.17'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '5.0'
54
+ version: '1.17'
69
55
  - !ruby/object:Gem::Dependency
70
- name: minitest-reporters
56
+ name: inch
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: '1.3'
61
+ version: '0.8'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
66
  - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: '1.3'
68
+ version: '0.8'
83
69
  - !ruby/object:Gem::Dependency
84
- name: mocha
70
+ name: pry-byebug
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - "~>"
88
74
  - !ruby/object:Gem::Version
89
- version: '1.7'
75
+ version: '3.6'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
80
  - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: '1.7'
82
+ version: '3.6'
97
83
  - !ruby/object:Gem::Dependency
98
- name: pry-byebug
84
+ name: rake
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
87
  - - "~>"
102
88
  - !ruby/object:Gem::Version
103
- version: '3.6'
89
+ version: '13.0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
94
  - - "~>"
109
95
  - !ruby/object:Gem::Version
110
- version: '3.6'
96
+ version: '13.0'
111
97
  - !ruby/object:Gem::Dependency
112
- name: rake
98
+ name: rspec
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
101
  - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: '10.0'
103
+ version: 3.9.0
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
108
  - - "~>"
123
109
  - !ruby/object:Gem::Version
124
- version: '10.0'
110
+ version: 3.9.0
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: simplecov
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -158,7 +144,7 @@ extensions: []
158
144
  extra_rdoc_files: []
159
145
  files:
160
146
  - ".gitignore"
161
- - ".travis.yml"
147
+ - ".rspec"
162
148
  - CHANGELOG.md
163
149
  - Gemfile
164
150
  - Gemfile.lock
@@ -175,8 +161,9 @@ files:
175
161
  - lib/eight_ball/conditions/list.rb
176
162
  - lib/eight_ball/conditions/never.rb
177
163
  - lib/eight_ball/conditions/range.rb
164
+ - lib/eight_ball/configuration_error.rb
178
165
  - lib/eight_ball/feature.rb
179
- - lib/eight_ball/parsers/json.rb
166
+ - lib/eight_ball/marshallers/json.rb
180
167
  - lib/eight_ball/providers/http.rb
181
168
  - lib/eight_ball/providers/refresh_policies/interval.rb
182
169
  - lib/eight_ball/providers/static.rb
@@ -200,8 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
187
  - !ruby/object:Gem::Version
201
188
  version: '0'
202
189
  requirements: []
203
- rubyforge_project:
204
- rubygems_version: 2.7.7
190
+ rubygems_version: 3.0.3
205
191
  signing_key:
206
192
  specification_version: 4
207
193
  summary: The most cost efficient way to flag features
@@ -1,14 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.5.0
6
- before_install: gem install bundler -v 1.17.2
7
- deploy:
8
- provider: rubygems
9
- api_key:
10
- secure: DdePL5PDqSp2n3/pr45Vh75f1ImntBhR16T75Rke1O0Z2WiBMJZcHFFPQMs6cuf3H/8mdWF/lzexxM0U2U9J7eXs5WZILxougA3NAnJeC3GU6Nbpv7FvR73bJeb+W6Dcq5e6yjmQMU5cmt0LAOitvBoYjnIIRNnxnTyOaHkr2QcNNTuAOOGnmLx/SYsIrFqgjesC5Xi5QfEDFSFAPzEbb9Uq9m2vUTDx1OeXHb/6sXDZUrrTKyOdag2iqNMwrGztjZh5bSMjZmWuQjymzYU4xJRqjdUexYTfreFNde6e7Nm+5gjM0smo89rKUOUxd7FK9WLD0/uuMmkLFnyEQHa8ZMn8y17VNnaC9h3KEdJ1SVpvvzcrJ5Y7rRZxpc6Z+Q1+kwyjhz5enTZRDqoxL1j/8QO5uf3Wg6i0DNR5VqK0a+8h3CznQMdnb2e+beSZgvzj6vJLn6L7SIJ/EhU9EH4QouCDMsyrVeZWOrbzKwvO7PMd55azRlux/wYydSnxBKTZgPm3HXQ18rPoHb+OxCFDOtnK/BPJvxjosu/kbmktO/xMsAtMVzQ7qKSUSZYXc7Aw+YUALnHYO3TStfDToHH46lo0eRN/O/SzFe54WAd4oblYoglwf9htdc+59bhAfG4B4gM3J/k9TCXEoi8iKREjy4d8kVRdSImpKoGM4kx0Xyk=
11
- gem: eight_ball
12
- on:
13
- tags: true
14
- repo: rewindio/eight_ball
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plissken'
4
-
5
- module EightBall::Parsers
6
- # A JSON parser will parse JSON into a list of {EightBall::Feature Features}.
7
- # The top-level JSON element must be an array and must use camel-case;
8
- # this will be converted to snake-case by EightBall.
9
- #
10
- # Below are some examples of valid JSON:
11
- #
12
- # @example A single {EightBall::Feature} is enabled for accounts 1-5 as well as region Europe
13
- # [{
14
- # "name": "Feature1",
15
- # "enabledFor": [{
16
- # "type": "range",
17
- # "parameter": "accountId",
18
- # "min": 1,
19
- # "max": 5
20
- # }, {
21
- # "type": "list",
22
- # "parameter": "regionName",
23
- # "values": ["Europe"]
24
- # }]
25
- # }]
26
- #
27
- # @example A single {EightBall::Feature} is disabled completely using the {EightBall::Conditions::Always Always} condition
28
- # [{
29
- # "name": "Feature1",
30
- # "disabledFor": [{
31
- # "type": "always"
32
- # }]
33
- # }]
34
- class Json
35
- # Convert the JSON into a list of {EightBall::Feature Features}.
36
- #
37
- # @param [String] json The JSON string to parse.
38
- # @return [Array<EightBall::Feature>] The parsed {EightBall::Feature Features}
39
- #
40
- # @example
41
- # json_string = <Read from somewhere>
42
- #
43
- # parser = EightBall::Parsers::Json.new
44
- # parser.parse json_string => [Features]
45
- def parse(json)
46
- parsed = JSON.parse(json, :symbolize_names => true).to_snake_keys
47
-
48
- raise ArgumentError, 'JSON input was not an array' unless parsed.is_a? Array
49
-
50
- parsed.map do |feature|
51
- enabled_for = create_conditions feature[:enabled_for]
52
- disabled_for = create_conditions feature[:disabled_for]
53
-
54
- EightBall::Feature.new feature[:name], enabled_for, disabled_for
55
- end
56
- rescue JSON::ParserError => e
57
- EightBall.logger.error { "Failed to parse JSON: #{e.message}" }
58
- []
59
- end
60
-
61
- private
62
-
63
- def create_conditions(json_conditions)
64
- return [] unless json_conditions && json_conditions.is_a?(Array)
65
-
66
- json_conditions.map do |condition|
67
- condition_class = EightBall::Conditions.by_name condition[:type]
68
- condition_class.new condition
69
- end
70
- end
71
- end
72
- end