eight_ball 1.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b909046d84dce15d996867c4f73e472242ba11518f0c54e202bdc5a9333dd60
4
- data.tar.gz: 01c7bece8234dd01c11d3d9cf204aabef1fb443287d53bf31d4de9e3e396e9e6
3
+ metadata.gz: d034f89a36b50a0d0c9b8b9a4831a064e5ba94040b420f87a5afd440ff219674
4
+ data.tar.gz: 9b545a863f9eb98522ae8d039fae1c4f53d21c16d9a86427f919753327ff5d7d
5
5
  SHA512:
6
- metadata.gz: 2e3cfe13696c5d5c9e60859ed02fae02f7e056771d285a60ec7bb7d85eee917617992a0753b2d54a49470555b56fc8c8a4f2cb3e8a438d743e92a1ec5a06a1dc
7
- data.tar.gz: fc0dddd891374607287e27e6a7b50f0286043c9f288d894b50266b4ab59cd690e15c949bbfaa3aa5ff314e43ca4f1cffce2e0f71d7ccb1dabe094ebc305be8d4
6
+ metadata.gz: eb363e7384b897cf5a17f6be921881de912828a496ddf1d403be301c145bcfc85cebc76acca1b3938e7c823f1176f76ab4297ed8b45407ec83a90b18ed2adfbf
7
+ data.tar.gz: ffa4369cfba089198cac8495cc1b4ccdc2c158a82feddbf6df88e33c5da77e0810fd372eb2fcd9c5d4f5cd343d1e99647a36c0ef9aa095e2d60f37c89f29456d
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.0]
4
+ - [BREAKING] `Parsers` have been replaced with `Marshallers`, allowing bi-directional conversions
5
+ - Added `EightBall.marshall` as a way to output the Feature list to an external format (e.g. to create a JSON file)
6
+ - Added `EightBall.features` as a shortcut to `EightBall.provider.features`
7
+ - Testing framework has been moved from Minitest to rspec
8
+ - Updated dev dependencies
9
+
3
10
  ## [1.0.5]
4
11
  Security: Update rake to >= 12.3.3
5
12
 
@@ -14,4 +21,3 @@ Security: Update yard 0.9.16 -> 0.9.20
14
21
 
15
22
  ## [1.0.0]
16
23
  Initial release!
17
-
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,43 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- eight_ball (1.0.5)
4
+ eight_ball (2.0.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.4)
12
- byebug (11.1.1)
13
- coderay (1.1.2)
12
+ awrence (1.1.1)
13
+ byebug (11.1.3)
14
+ coderay (1.1.3)
15
+ diff-lcs (1.3)
14
16
  docile (1.3.2)
15
17
  inch (0.8.0)
16
18
  pry
17
19
  sparkr (>= 0.2.0)
18
20
  term-ansicolor
19
21
  yard (~> 0.9.12)
20
- method_source (0.9.2)
21
- minitest (5.14.0)
22
- minitest-reporters (1.4.2)
23
- ansi
24
- builder
25
- minitest (>= 5.0)
26
- ruby-progressbar
27
- mocha (1.11.2)
22
+ method_source (1.0.0)
28
23
  plissken (1.3.1)
29
- pry (0.12.2)
30
- coderay (~> 1.1.0)
31
- method_source (~> 0.9.0)
32
- pry-byebug (3.8.0)
24
+ pry (0.13.1)
25
+ coderay (~> 1.1)
26
+ method_source (~> 1.0)
27
+ pry-byebug (3.9.0)
33
28
  byebug (~> 11.0)
34
- pry (~> 0.10)
35
- rake (12.3.3)
36
- ruby-progressbar (1.10.1)
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)
37
44
  simplecov (0.18.5)
38
45
  docile (~> 1.1)
39
46
  simplecov-html (~> 0.11)
40
- simplecov-console (0.6.0)
47
+ simplecov-console (0.7.2)
41
48
  ansi
42
49
  simplecov
43
50
  terminal-table
@@ -48,10 +55,10 @@ GEM
48
55
  tins (~> 1.0)
49
56
  terminal-table (1.8.0)
50
57
  unicode-display_width (~> 1.1, >= 1.1.1)
51
- tins (1.24.1)
58
+ tins (1.25.0)
52
59
  sync
53
- unicode-display_width (1.6.1)
54
- yard (0.9.24)
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 (~> 12.3.3)
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', '~> 12.3.3'
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)
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
 
@@ -16,7 +16,8 @@ module EightBall::Conditions
16
16
 
17
17
  def parameter=(parameter)
18
18
  return if parameter.nil?
19
- @parameter = parameter.gsub(/(.)([A-Z])/,'\1_\2').downcase
19
+
20
+ @parameter = parameter.gsub(/(.)([A-Z])/, '\1_\2').downcase
20
21
  end
21
22
  end
22
23
  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
@@ -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.5'
4
+ VERSION = '2.0.0'
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.5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rewind.io
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-02 00:00:00.000000000 Z
11
+ date: 2020-06-17 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: 12.3.3
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: 12.3.3
110
+ version: 3.9.0
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: simplecov
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -158,6 +144,7 @@ extensions: []
158
144
  extra_rdoc_files: []
159
145
  files:
160
146
  - ".gitignore"
147
+ - ".rspec"
161
148
  - ".travis.yml"
162
149
  - CHANGELOG.md
163
150
  - Gemfile
@@ -175,8 +162,9 @@ files:
175
162
  - lib/eight_ball/conditions/list.rb
176
163
  - lib/eight_ball/conditions/never.rb
177
164
  - lib/eight_ball/conditions/range.rb
165
+ - lib/eight_ball/configuration_error.rb
178
166
  - lib/eight_ball/feature.rb
179
- - lib/eight_ball/parsers/json.rb
167
+ - lib/eight_ball/marshallers/json.rb
180
168
  - lib/eight_ball/providers/http.rb
181
169
  - lib/eight_ball/providers/refresh_policies/interval.rb
182
170
  - lib/eight_ball/providers/static.rb
@@ -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