eight_ball 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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