csv_decision 0.0.1
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 +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/Gemfile +4 -0
- data/README.md +20 -0
- data/csv_decision.gemspec +34 -0
- data/lib/csv_decision.rb +19 -0
- data/lib/csv_decision/data.rb +59 -0
- data/lib/csv_decision/header.rb +23 -0
- data/lib/csv_decision/options.rb +85 -0
- data/lib/csv_decision/parse.rb +38 -0
- data/lib/csv_decision/table.rb +27 -0
- data/spec/csv_decision.rb +7 -0
- data/spec/csv_decision/data_spec.rb +42 -0
- data/spec/csv_decision/parse_spec.rb +36 -0
- data/spec/data/valid/empty.csv +0 -0
- data/spec/data/valid/valid.csv +4 -0
- data/spec/spec_helper.rb +100 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 486bc130f39ee0893ac24493bb1e4f866e23171a
|
4
|
+
data.tar.gz: 1b0b4467391b2358e0bdb6e380a9fc7cbfa815d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8dae72ca353f917f4511735e5a5d5d7f9eccc7627a41cdf53e9a92c8d63374cf69f4c84834da1e8cd9ae08178ae13aaa71443bbed5e60394507645296f5b931
|
7
|
+
data.tar.gz: b292cd9287309a084fefd21d78f6b04d90448188ff85008b17c75dc6aab66cc902e3bfddcfe66d1c266b7c924bbe5ee9d33dc73609da9bcddffccbd306217808
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# csv_decision - Work In Progress
|
2
|
+
`csv_decision` is a Ruby gem for CSV based decision tables. It accepts decision table logic encoded in
|
3
|
+
a CSV file, which can then be used to implement complex conditional logic.
|
4
|
+
|
5
|
+
`csv_decision` has many useful features:
|
6
|
+
* able to parse and load into memory many CSV files for subsequent processing
|
7
|
+
* can return the first matching row as a hash, or accumulate all matches as an array of hashes
|
8
|
+
* input columns may be indexed for fast lookup performance
|
9
|
+
* can use regular expressions, Ruby-style ranges and function calls to implement complex decision logic
|
10
|
+
* all CSV cells are parsed for correctness, and friendly error messages generated for bad input
|
11
|
+
* can be safely extended with user defined Ruby functions for tailored logic
|
12
|
+
* good decision time performance
|
13
|
+
* flexible options for tailoring behavior and defaults
|
14
|
+
|
15
|
+
### Why use CSV Decision?
|
16
|
+
|
17
|
+
Typical "business logic" is notoriously illogical -- full of corner cases and irregular exceptions.
|
18
|
+
A decision table can capture data-based decisions in a way that comes naturally to analysts and subject matter
|
19
|
+
experts, who typically use spreadsheet models. Business logic can be encapsulated, avoiding the need to write
|
20
|
+
tortuous conditional expressions in Ruby that draws the ire of `rubocop` and its ilk.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'csv_decision'
|
8
|
+
spec.version = '0.0.1'
|
9
|
+
spec.authors = ['Brett Vickers']
|
10
|
+
spec.email = ['brett@phillips-vickers.com']
|
11
|
+
spec.description = 'CSV based Ruby decision tables.'
|
12
|
+
spec.summary = spec.description
|
13
|
+
spec.homepage = 'https://github.com/bpvickers/csv_decision.git'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 2.3.1'
|
22
|
+
|
23
|
+
spec.add_dependency 'activesupport', '~> 5.0'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'benchmark-ips', '~> 2.7'
|
26
|
+
spec.add_development_dependency 'benchmark-memory', '~> 0.1'
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
28
|
+
spec.add_development_dependency 'oj', '~> 3.3'
|
29
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.51'
|
32
|
+
spec.add_development_dependency 'rufus-decision', '~> 1.3'
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.12'
|
34
|
+
end
|
data/lib/csv_decision.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true\
|
2
|
+
|
3
|
+
# CSV Decision: CSV based Ruby decision tables.
|
4
|
+
# Created December 2017 by Brett Vickers
|
5
|
+
# See LICENSE and README.md for details.
|
6
|
+
|
7
|
+
require 'active_support/core_ext/object'
|
8
|
+
require_relative '../lib/csv_decision/table'
|
9
|
+
|
10
|
+
module CSVDecision
|
11
|
+
# @return [String] gem project's root directory
|
12
|
+
def self.root
|
13
|
+
File.dirname __dir__
|
14
|
+
end
|
15
|
+
|
16
|
+
autoload :Data, 'csv_decision/data'
|
17
|
+
autoload :Options, 'csv_decision/options'
|
18
|
+
autoload :Parse, 'csv_decision/parse'
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
# CSV Decision: CSV based Ruby decision tables.
|
6
|
+
# Created December 2017 by Brett Vickers
|
7
|
+
# See LICENSE and README.md for details.
|
8
|
+
module CSVDecision
|
9
|
+
COMMENT_CHARACTER = '#'
|
10
|
+
|
11
|
+
# Methods to load data from a file, CSV string or array of arrays
|
12
|
+
module Data
|
13
|
+
# Parse the input data which may either be a file path name, CSV string or
|
14
|
+
# array of arrays. Strips out empty columns/rows and comment cells
|
15
|
+
def self.to_array(data:, options: { force_encoding: 'UTF-8', ascii_only?: true })
|
16
|
+
strip_rows(data: data_array(data), options: options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: strip empty columns
|
20
|
+
def self.strip_columns(_data:, _empty_cols:); end
|
21
|
+
|
22
|
+
# Parse the input data which may either be a file path name, CSV string or
|
23
|
+
# array of arrays
|
24
|
+
def self.data_array(input)
|
25
|
+
return CSV.read(input) if input.is_a?(Pathname)
|
26
|
+
return input.deep_dup if input.is_a?(Array) && input[0].is_a?(Array)
|
27
|
+
return CSV.parse(input) if input.is_a?(String)
|
28
|
+
|
29
|
+
raise ArgumentError,
|
30
|
+
"#{input.class} input invalid; " \
|
31
|
+
'input must be a file path name, CSV string or array of arrays'
|
32
|
+
end
|
33
|
+
private_class_method :data_array
|
34
|
+
|
35
|
+
def self.strip_rows(data:, options:)
|
36
|
+
rows = []
|
37
|
+
data.each do |row|
|
38
|
+
row = strip_cells(row: row, options: options)
|
39
|
+
rows << row if row.find { |cell| cell != '' }
|
40
|
+
end
|
41
|
+
rows
|
42
|
+
end
|
43
|
+
private_class_method :strip_rows
|
44
|
+
|
45
|
+
# Strip cells of leading/trailing spaces; treat comments as an empty cell.
|
46
|
+
# Non string values treated as empty cells.
|
47
|
+
# Non-ascii strings treated as empty cells by default.
|
48
|
+
def self.strip_cells(row:, options:)
|
49
|
+
row.map! do |cell|
|
50
|
+
next '' unless cell.is_a?(String)
|
51
|
+
cell = options[:force_encoding] ? cell.force_encoding(options[:force_encoding]) : cell
|
52
|
+
next '' if options[:ascii_only?] && !cell.ascii_only?
|
53
|
+
next '' if cell.lstrip[0] == COMMENT_CHARACTER
|
54
|
+
cell.strip
|
55
|
+
end
|
56
|
+
end
|
57
|
+
private_class_method :strip_cells
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# CSV Decision: CSV based Ruby decision tables.
|
4
|
+
# Created December 2017 by Brett Vickers
|
5
|
+
# See LICENSE and README.md for details.
|
6
|
+
module CSVDecision
|
7
|
+
# Parse the CSV file's header row
|
8
|
+
class Header
|
9
|
+
# Column header looks like IN :col_name or if:
|
10
|
+
COLUMN_TYPE = %r{\A(in|out|in/text|out/text)|set\s*:\s*(\S?.*)\z}i
|
11
|
+
|
12
|
+
def self.row?(row)
|
13
|
+
row.find { |cell| cell.match(COLUMN_TYPE) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Parse the input data which may either be a path name, CSV string or array of arrays
|
17
|
+
def self.parse(table:, options: {})
|
18
|
+
header = Header.new
|
19
|
+
|
20
|
+
header.freeze
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# CSV Decision: CSV based Ruby decision tables.
|
4
|
+
# Created December 2017 by Brett Vickers
|
5
|
+
# See LICENSE and README.md for details.
|
6
|
+
module CSVDecision
|
7
|
+
VALID_OPTIONS = %i[
|
8
|
+
force_encoding
|
9
|
+
ascii_only?
|
10
|
+
first_match
|
11
|
+
regexp_implict
|
12
|
+
text_only
|
13
|
+
index
|
14
|
+
tables
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
OPTION_DEFAULTS = {
|
18
|
+
force_encoding: 'UTF-8',
|
19
|
+
ascii_only?: true,
|
20
|
+
first_match: true,
|
21
|
+
regexp_implict: false,
|
22
|
+
text_only: false
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
CSV_OPTION_NAMES = %i[first_match accumulate regexp_implict text_only].freeze
|
26
|
+
|
27
|
+
# Parse the CSV file and create a new decision table object
|
28
|
+
class Options
|
29
|
+
def self.default(options)
|
30
|
+
result = options.deep_dup
|
31
|
+
|
32
|
+
# Default any missing options that have defaults defined
|
33
|
+
OPTION_DEFAULTS.each_pair do |key, value|
|
34
|
+
next if result.key?(key)
|
35
|
+
result[key] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.cell?(cell)
|
42
|
+
return false if cell == ''
|
43
|
+
|
44
|
+
key = cell.downcase.to_sym
|
45
|
+
return [key, true] if CSV_OPTION_NAMES.include?(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.valid?(options)
|
49
|
+
invalid_options = options.keys - VALID_OPTIONS
|
50
|
+
|
51
|
+
return true if invalid_options.empty?
|
52
|
+
|
53
|
+
raise ArgumentError, "invalid option(s) supplied: #{invalid_options.inspect}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.from_csv(table, attributes)
|
57
|
+
row = table.rows.first
|
58
|
+
return attributes unless row
|
59
|
+
|
60
|
+
return attributes if Header.row?(row)
|
61
|
+
|
62
|
+
row.each do |cell|
|
63
|
+
key, value = Options.cell?(cell)
|
64
|
+
attributes[key] = value if key
|
65
|
+
end
|
66
|
+
|
67
|
+
table.rows.shift
|
68
|
+
from_csv(table, attributes)
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_accessor :attributes
|
72
|
+
|
73
|
+
def initialize(options)
|
74
|
+
Options.valid?(options)
|
75
|
+
@attributes = Options.default(options)
|
76
|
+
end
|
77
|
+
|
78
|
+
def from_csv(table)
|
79
|
+
# Options on the CSV file override the ones passed in to the method
|
80
|
+
@attributes = Options.from_csv(table, @attributes)
|
81
|
+
|
82
|
+
self
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'table'
|
4
|
+
require_relative 'data'
|
5
|
+
require_relative 'header'
|
6
|
+
require_relative 'options'
|
7
|
+
|
8
|
+
# CSV Decision: CSV based Ruby decision tables.
|
9
|
+
# Created December 2017 by Brett Vickers
|
10
|
+
# See LICENSE and README.md for details.
|
11
|
+
module CSVDecision
|
12
|
+
# Parse the input data which may either be a path name, CSV string or array of arrays
|
13
|
+
def self.parse(input, options: {})
|
14
|
+
# Parse and normalize user supplied options
|
15
|
+
options = Options.new(options)
|
16
|
+
|
17
|
+
# Parse input data, which may include overriding options specified in a CSV file
|
18
|
+
table = Parse.data(table: Table.new, input: input, options: options)
|
19
|
+
|
20
|
+
options = options.from_csv(table)
|
21
|
+
|
22
|
+
# Set to the options hash
|
23
|
+
table.options = options.attributes.freeze
|
24
|
+
|
25
|
+
table.freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parse the CSV file and create a new decision table object
|
29
|
+
module Parse
|
30
|
+
def self.data(table:, input:, options:)
|
31
|
+
table.file = input if input.is_a?(Pathname)
|
32
|
+
table.rows = Data.to_array(data: input, options: options.attributes)
|
33
|
+
|
34
|
+
table.header = Header.parse(table: table, options: options)
|
35
|
+
table
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true\
|
2
|
+
|
3
|
+
# CSV Decision: CSV based Ruby decision tables.
|
4
|
+
# Created December 2017 by Brett Vickers
|
5
|
+
# See LICENSE and README.md for details.
|
6
|
+
module CSVDecision
|
7
|
+
# Decision Table that accepts input hashes and makes deciosn
|
8
|
+
class Table
|
9
|
+
attr_accessor :file
|
10
|
+
attr_accessor :header
|
11
|
+
attr_accessor :options
|
12
|
+
attr_accessor :rows
|
13
|
+
attr_reader :tables
|
14
|
+
|
15
|
+
def decide(_input, _symbolize_keys: true)
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@file = nil
|
21
|
+
@header = nil
|
22
|
+
@options = nil
|
23
|
+
@rows = []
|
24
|
+
@tables = nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative '../../lib/csv_decision'
|
3
|
+
|
4
|
+
describe CSVDecision::Data do
|
5
|
+
it 'parses an CSV string' do
|
6
|
+
result = CSVDecision::Data.to_array(data: '')
|
7
|
+
expect(result).to be_a Array
|
8
|
+
expect(result.empty?).to eq true
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'parses an array' do
|
12
|
+
result = CSVDecision::Data.to_array(data: [[]])
|
13
|
+
expect(result).to eq []
|
14
|
+
|
15
|
+
data = [
|
16
|
+
['#header', "R\u00E9sum\u00E9", '# comments'],
|
17
|
+
['IN :input', ' OUT :output ', nil],
|
18
|
+
['input', '# comment', nil]
|
19
|
+
]
|
20
|
+
result = CSVDecision::Data.to_array(data: data)
|
21
|
+
expect(result).to eq [['IN :input', 'OUT :output', ''], ['input', '', '']]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'parses a CSV file' do
|
25
|
+
file = Pathname(File.join(CSVDecision.root, 'spec/data/valid', 'empty.csv'))
|
26
|
+
result = CSVDecision::Data.to_array(data: file)
|
27
|
+
expect(result).to be_a Array
|
28
|
+
expect(result.empty?).to eq true
|
29
|
+
|
30
|
+
file = Pathname(File.join(CSVDecision.root, 'spec/data/valid', 'valid.csv'))
|
31
|
+
result = CSVDecision::Data.to_array(data: file)
|
32
|
+
expect(result).to be_a Array
|
33
|
+
expect(result).to eq [['IN :input', 'OUT :output'], ['input', '']]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an error for invalid input' do
|
37
|
+
expect { CSVDecision::Data.to_array(data: {}) }
|
38
|
+
.to raise_error(ArgumentError,
|
39
|
+
'Hash input invalid; ' \
|
40
|
+
'input must be a file path name, CSV string or array of arrays')
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/csv_decision/parse'
|
4
|
+
|
5
|
+
describe CSVDecision::Parse do
|
6
|
+
it 'loads an empty decision table' do
|
7
|
+
table = CSVDecision.parse('')
|
8
|
+
expect(table).to be_a CSVDecision::Table
|
9
|
+
expect(table.frozen?).to eq true
|
10
|
+
expect(table.rows.empty?).to eq true
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'parses a decision table from a CSV file' do
|
14
|
+
file = Pathname(File.join(CSVDecision.root, 'spec/data/valid', 'valid.csv'))
|
15
|
+
result = CSVDecision.parse(file)
|
16
|
+
|
17
|
+
expected = [
|
18
|
+
['IN :input', 'OUT :output'],
|
19
|
+
['input', '']
|
20
|
+
]
|
21
|
+
|
22
|
+
expect(result).to be_a(CSVDecision::Table)
|
23
|
+
expect(result.rows).to eq expected
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'it parses valid CSV files' do
|
27
|
+
Dir[File.join(CSVDecision.root, 'spec/data/valid/*.csv')].each do |file_name|
|
28
|
+
pathname = Pathname(file_name)
|
29
|
+
|
30
|
+
it "loads CSV file #{pathname.basename}" do
|
31
|
+
expect { CSVDecision.parse(pathname) }.not_to raise_error
|
32
|
+
expect(CSVDecision.parse(pathname)).to be_a CSVDecision::Table
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
4
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
5
|
+
# files.
|
6
|
+
#
|
7
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
8
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
9
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
10
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
11
|
+
# a separate helper file that requires the additional dependencies and performs
|
12
|
+
# the additional setup, and require it from the spec files that actually need
|
13
|
+
# it.
|
14
|
+
#
|
15
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
16
|
+
RSpec.configure do |config|
|
17
|
+
# rspec-expectations config goes here. You can use an alternate
|
18
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
19
|
+
# assertions if you prefer.
|
20
|
+
config.expect_with :rspec do |expectations|
|
21
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
22
|
+
# and `failure_message` of custom matchers include text for helper methods
|
23
|
+
# defined using `chain`, e.g.:
|
24
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
25
|
+
# # => "be bigger than 2 and smaller than 4"
|
26
|
+
# ...rather than:
|
27
|
+
# # => "be bigger than 2"
|
28
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
32
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
33
|
+
config.mock_with :rspec do |mocks|
|
34
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
35
|
+
# a real object. This is generally recommended, and will default to
|
36
|
+
# `true` in RSpec 4.
|
37
|
+
mocks.verify_partial_doubles = true
|
38
|
+
end
|
39
|
+
|
40
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
41
|
+
# have no way to turn it off -- the option exists only for backwards
|
42
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
43
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
44
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
45
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
46
|
+
|
47
|
+
# The settings below are suggested to provide a good initial experience
|
48
|
+
# with RSpec, but feel free to customize to your heart's content.
|
49
|
+
=begin
|
50
|
+
# This allows you to limit a spec run to individual examples or groups
|
51
|
+
# you care about by tagging them with `:focus` metadata. When nothing
|
52
|
+
# is tagged with `:focus`, all examples get run. RSpec also provides
|
53
|
+
# aliases for `it`, `describe`, and `context` that include `:focus`
|
54
|
+
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
55
|
+
config.filter_run_when_matching :focus
|
56
|
+
|
57
|
+
# Allows RSpec to persist some state between runs in order to support
|
58
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
59
|
+
# you configure your source control system to ignore this file.
|
60
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
61
|
+
|
62
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
63
|
+
# recommended. For more details, see:
|
64
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
65
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
66
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
67
|
+
config.disable_monkey_patching!
|
68
|
+
|
69
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
70
|
+
# be too noisy due to issues in dependencies.
|
71
|
+
config.warnings = true
|
72
|
+
|
73
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
74
|
+
# file, and it's useful to allow more verbose output when running an
|
75
|
+
# individual spec file.
|
76
|
+
if config.files_to_run.one?
|
77
|
+
# Use the documentation formatter for detailed output,
|
78
|
+
# unless a formatter has already been configured
|
79
|
+
# (e.g. via a command-line flag).
|
80
|
+
config.default_formatter = "doc"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Print the 10 slowest examples and example groups at the
|
84
|
+
# end of the spec run, to help surface which specs are running
|
85
|
+
# particularly slow.
|
86
|
+
config.profile_examples = 10
|
87
|
+
|
88
|
+
# Run specs in random order to surface order dependencies. If you find an
|
89
|
+
# order dependency and want to debug it, you can fix the order by providing
|
90
|
+
# the seed, which is printed after each run.
|
91
|
+
# --seed 1234
|
92
|
+
config.order = :random
|
93
|
+
|
94
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
95
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
96
|
+
# test failures related to randomization by passing the same `--seed` value
|
97
|
+
# as the one that triggered the failure.
|
98
|
+
Kernel.srand config.seed
|
99
|
+
=end
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv_decision
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brett Vickers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: benchmark-ips
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: benchmark-memory
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: oj
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '12.3'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '12.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.5'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.5'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.51'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.51'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rufus-decision
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.3'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.3'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.12'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.12'
|
153
|
+
description: CSV based Ruby decision tables.
|
154
|
+
email:
|
155
|
+
- brett@phillips-vickers.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- ".gitignore"
|
161
|
+
- ".rspec"
|
162
|
+
- ".rubocop.yml"
|
163
|
+
- Gemfile
|
164
|
+
- README.md
|
165
|
+
- csv_decision.gemspec
|
166
|
+
- lib/csv_decision.rb
|
167
|
+
- lib/csv_decision/data.rb
|
168
|
+
- lib/csv_decision/header.rb
|
169
|
+
- lib/csv_decision/options.rb
|
170
|
+
- lib/csv_decision/parse.rb
|
171
|
+
- lib/csv_decision/table.rb
|
172
|
+
- spec/csv_decision.rb
|
173
|
+
- spec/csv_decision/data_spec.rb
|
174
|
+
- spec/csv_decision/parse_spec.rb
|
175
|
+
- spec/data/valid/empty.csv
|
176
|
+
- spec/data/valid/valid.csv
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
homepage: https://github.com/bpvickers/csv_decision.git
|
179
|
+
licenses:
|
180
|
+
- MIT
|
181
|
+
metadata: {}
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 2.3.1
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubyforge_project:
|
198
|
+
rubygems_version: 2.6.14
|
199
|
+
signing_key:
|
200
|
+
specification_version: 4
|
201
|
+
summary: CSV based Ruby decision tables.
|
202
|
+
test_files:
|
203
|
+
- spec/csv_decision.rb
|
204
|
+
- spec/csv_decision/data_spec.rb
|
205
|
+
- spec/csv_decision/parse_spec.rb
|
206
|
+
- spec/data/valid/empty.csv
|
207
|
+
- spec/data/valid/valid.csv
|
208
|
+
- spec/spec_helper.rb
|