proforma-extended-evaluator 1.0.0.pre.alpha → 1.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +3 -3
- data/README.md +3 -3
- data/lib/proforma/extended_evaluator.rb +1 -0
- data/lib/proforma/extended_evaluator/formatter.rb +41 -37
- data/lib/proforma/extended_evaluator/resolver.rb +15 -3
- data/lib/proforma/extended_evaluator/version.rb +1 -1
- data/proforma-extended-evaluator.gemspec +1 -1
- data/spec/proforma/extended_evaluator/formatter_spec.rb +60 -32
- data/spec/proforma/extended_evaluator_spec.rb +12 -3
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f978fa2a4a04564213b30c11f6b1353734dd8df889c047b989a385857aade8e
|
4
|
+
data.tar.gz: 75f683c4fb9689f6b081aa2b3f1d634cf3111d3c22c23d26481d82da8b6175e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5da22925132f972ebf226007b8b0b191336db4fc6a1e91d7b1ecb26401eeb5baa9f02c97abc3c87ce4bd4622611a191f826b28295184c1e870fa2bbed2e9be03
|
7
|
+
data.tar.gz: f4e482ce9399c495a1ffd08bb5f81c000fb0ba2256c5d370de1ddcbae9fbc9c7b7feb37e056cd610187a25f2bce76db4ec1bdcadad6ff230205ffa93939b41b7
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
proforma-extended-evaluator (1.0.0
|
4
|
+
proforma-extended-evaluator (1.0.0)
|
5
5
|
stringento (~> 2)
|
6
6
|
|
7
7
|
GEM
|
@@ -46,7 +46,7 @@ GEM
|
|
46
46
|
parser (2.6.0.0)
|
47
47
|
ast (~> 2.4.0)
|
48
48
|
powerpack (0.1.2)
|
49
|
-
proforma (1.0.0
|
49
|
+
proforma (1.0.0)
|
50
50
|
acts_as_hashable (~> 1)
|
51
51
|
pry (0.12.2)
|
52
52
|
coderay (~> 1.1.0)
|
@@ -97,7 +97,7 @@ PLATFORMS
|
|
97
97
|
|
98
98
|
DEPENDENCIES
|
99
99
|
guard-rspec (~> 4.7)
|
100
|
-
proforma (
|
100
|
+
proforma (~> 1)
|
101
101
|
proforma-extended-evaluator!
|
102
102
|
pry (~> 0)
|
103
103
|
rspec (~> 3.8)
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/proforma-extended-evaluator) [](https://travis-ci.org/bluemarblepayroll/proforma-extended-evaluator) [](https://codeclimate.com/github/bluemarblepayroll/proforma-extended-evaluator/maintainability) [](https://codeclimate.com/github/bluemarblepayroll/proforma-extended-evaluator/test_coverage) [](https://opensource.org/licenses/MIT)
|
4
4
|
|
5
|
-
The core Proforma library intentionally ships with a very weak evaluator. Custom text templating and value resolution is not part of the core library's domain. This library fills that void. The goals of this library are to provide:
|
5
|
+
The core [Proforma](https://github.com/bluemarblepayroll/proforma) library intentionally ships with a very weak evaluator. Custom text templating and value resolution is not part of the core library's domain. This library fills that void. The goals of this library are to provide:
|
6
6
|
|
7
7
|
1. Nested value resolution using dot-notation: `demographics.contact.first_name`
|
8
8
|
2. Indifferent object types for value resolution: Hash, OpenStruct, any Object subclass, etc.
|
@@ -34,9 +34,9 @@ bundle add proforma-extended-evaluator
|
|
34
34
|
|
35
35
|
### Connecting to Proforma Rendering Pipeline
|
36
36
|
|
37
|
-
To use this plugin within Proforma:
|
37
|
+
To use this plugin within [Proforma](https://github.com/bluemarblepayroll/proforma):
|
38
38
|
|
39
|
-
1. Install Proforma
|
39
|
+
1. Install [Proforma](https://github.com/bluemarblepayroll/proforma)
|
40
40
|
2. Install this library
|
41
41
|
3. Require both libraries
|
42
42
|
4. Pass in an instance of Proforma::ExtendedEvaluator into the Proforma#render method
|
@@ -13,47 +13,51 @@ module Proforma
|
|
13
13
|
# plugged into Stringento to provide formatting for data types, such as: strings, dates,
|
14
14
|
# currency, numbers, etc.
|
15
15
|
class Formatter < Stringento::Formatter
|
16
|
+
extend Forwardable
|
17
|
+
|
16
18
|
DEFAULTS = {
|
17
19
|
currency_code: 'USD',
|
18
20
|
currency_round: 2,
|
19
21
|
currency_symbol: '$',
|
20
22
|
date_format: '%m/%d/%Y',
|
23
|
+
decimal_separator: '.',
|
24
|
+
iso_date_format: '%Y-%m-%d',
|
21
25
|
mask_char: 'X',
|
22
26
|
false_value: 'No',
|
23
27
|
null_value: 'Unknown',
|
24
|
-
|
28
|
+
nullish_regex: /\A(nil|null)\z/i.freeze,
|
29
|
+
thousands_regex: /[0-9](?=(?:[0-9]{3})+(?![0-9]))/.freeze,
|
30
|
+
thousands_separator: ',',
|
31
|
+
true_value: 'Yes',
|
32
|
+
truthy_regex: /\A(true|t|yes|y|1)\z/i.freeze
|
25
33
|
}.freeze
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
attr_reader :options
|
36
|
+
|
37
|
+
def_delegators :options,
|
38
|
+
:currency_code,
|
39
|
+
:currency_round,
|
40
|
+
:currency_symbol,
|
41
|
+
:date_format,
|
42
|
+
:decimal_separator,
|
43
|
+
:iso_date_format,
|
44
|
+
:false_value,
|
45
|
+
:mask_char,
|
46
|
+
:null_value,
|
47
|
+
:nullish_regex,
|
48
|
+
:thousands_regex,
|
49
|
+
:thousands_separator,
|
50
|
+
:truthy_regex,
|
51
|
+
:true_value
|
41
52
|
|
42
53
|
def initialize(opts = {})
|
43
|
-
|
44
|
-
|
45
|
-
@currency_code = opts[:currency_code]
|
46
|
-
@currency_round = opts[:currency_round]
|
47
|
-
@currency_symbol = opts[:currency_symbol]
|
48
|
-
@date_format = opts[:date_format]
|
49
|
-
@false_value = opts[:false_value]
|
50
|
-
@mask_char = opts[:mask_char]
|
51
|
-
@null_value = opts[:null_value]
|
52
|
-
@true_value = opts[:true_value]
|
54
|
+
@options = OpenStruct.new(DEFAULTS.merge(opts))
|
53
55
|
end
|
54
56
|
|
55
|
-
def left_mask_formatter(value,
|
56
|
-
keep_last =
|
57
|
+
def left_mask_formatter(value, keep_last)
|
58
|
+
keep_last = keep_last.to_s.empty? ? 4 : keep_last.to_s.to_i
|
59
|
+
|
60
|
+
raise ArgumentError, "keep_last cannot be negative (#{keep_last})" if keep_last.negative?
|
57
61
|
|
58
62
|
string_value = value.to_s
|
59
63
|
|
@@ -66,7 +70,7 @@ module Proforma
|
|
66
70
|
def date_formatter(value, _arg)
|
67
71
|
return '' if null_or_empty?(value)
|
68
72
|
|
69
|
-
date = Date.strptime(value.to_s,
|
73
|
+
date = Date.strptime(value.to_s, iso_date_format)
|
70
74
|
|
71
75
|
date.strftime(date_format)
|
72
76
|
end
|
@@ -82,16 +86,16 @@ module Proforma
|
|
82
86
|
"#{prefix}#{formatted_value}#{suffix}"
|
83
87
|
end
|
84
88
|
|
85
|
-
def number_formatter(value,
|
86
|
-
decimal_places =
|
87
|
-
|
88
|
-
regex = decimal_places.positive? ? THOUSANDS_WITH_DECIMAL : THOUSANDS_WITHOUT_DECIMAL
|
89
|
+
def number_formatter(value, decimal_places)
|
90
|
+
decimal_places = decimal_places.to_s.empty? ? 6 : decimal_places.to_s.to_i
|
89
91
|
|
90
|
-
format("%0.#{decimal_places}f", value || 0)
|
92
|
+
format("%0.#{decimal_places}f", value || 0)
|
93
|
+
.gsub(thousands_regex, "\\0#{thousands_separator}")
|
94
|
+
.gsub('.', decimal_separator)
|
91
95
|
end
|
92
96
|
|
93
|
-
def boolean_formatter(value,
|
94
|
-
nullable =
|
97
|
+
def boolean_formatter(value, nullable)
|
98
|
+
nullable = nullable.to_s == 'nullable'
|
95
99
|
|
96
100
|
if nullable && nully?(value)
|
97
101
|
null_value
|
@@ -117,11 +121,11 @@ module Proforma
|
|
117
121
|
|
118
122
|
# rubocop:disable Style/DoubleNegation
|
119
123
|
def nully?(val)
|
120
|
-
null_or_empty?(val) || !!(val.to_s =~
|
124
|
+
null_or_empty?(val) || !!(val.to_s =~ nullish_regex)
|
121
125
|
end
|
122
126
|
|
123
127
|
def truthy?(val)
|
124
|
-
!!(val.to_s =~
|
128
|
+
!!(val.to_s =~ truthy_regex)
|
125
129
|
end
|
126
130
|
# rubocop:enable Style/DoubleNegation
|
127
131
|
end
|
@@ -11,14 +11,26 @@ module Proforma
|
|
11
11
|
class ExtendedEvaluator
|
12
12
|
# This class is also meant to be plugged into Stringento to provide value resolution.
|
13
13
|
class Resolver
|
14
|
-
|
14
|
+
DEFAULT_SEPARATOR = '.'
|
15
|
+
|
16
|
+
attr_reader :separator
|
17
|
+
|
18
|
+
def initialize(separator: DEFAULT_SEPARATOR)
|
19
|
+
@separator = separator.to_s
|
20
|
+
end
|
15
21
|
|
16
22
|
def resolve(value, input)
|
17
|
-
traverse(input, value
|
23
|
+
traverse(input, key_path(value))
|
18
24
|
end
|
19
25
|
|
20
26
|
private
|
21
27
|
|
28
|
+
def key_path(value)
|
29
|
+
return Array(value.to_s) if separator.empty?
|
30
|
+
|
31
|
+
value.to_s.split(separator)
|
32
|
+
end
|
33
|
+
|
22
34
|
def traverse(object, through)
|
23
35
|
pointer = object
|
24
36
|
|
@@ -35,7 +47,7 @@ module Proforma
|
|
35
47
|
if object.is_a?(Hash)
|
36
48
|
indifferent_hash_get(object, key)
|
37
49
|
elsif object.respond_to?(key)
|
38
|
-
object.
|
50
|
+
object.public_send(key)
|
39
51
|
end
|
40
52
|
end
|
41
53
|
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_dependency('stringento', '~>2')
|
26
26
|
|
27
27
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
28
|
-
s.add_development_dependency('proforma', '
|
28
|
+
s.add_development_dependency('proforma', '~>1')
|
29
29
|
s.add_development_dependency('pry', '~>0')
|
30
30
|
s.add_development_dependency('rspec', '~> 3.8')
|
31
31
|
s.add_development_dependency('rubocop', '~>0.63.1')
|
@@ -10,6 +10,18 @@
|
|
10
10
|
require 'spec_helper'
|
11
11
|
|
12
12
|
describe Proforma::ExtendedEvaluator::Formatter do
|
13
|
+
let(:usa_formatter) { described_class.new }
|
14
|
+
|
15
|
+
let(:france_formatter) do
|
16
|
+
described_class.new(
|
17
|
+
currency_code: '€',
|
18
|
+
currency_round: 2,
|
19
|
+
currency_symbol: '',
|
20
|
+
decimal_separator: ',',
|
21
|
+
thousands_separator: ' '
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
13
25
|
describe '#left_mask_formatter' do
|
14
26
|
specify 'returns empty string if value is null' do
|
15
27
|
expect(subject.left_mask_formatter(nil, '')).to eq('')
|
@@ -43,6 +55,10 @@ describe Proforma::ExtendedEvaluator::Formatter do
|
|
43
55
|
expect(subject.left_mask_formatter('abcde', arg)).to eq('XXXde')
|
44
56
|
expect(subject.left_mask_formatter('abcdef', arg)).to eq('XXXXef')
|
45
57
|
end
|
58
|
+
|
59
|
+
specify 'raises ArgumentError for negative arg' do
|
60
|
+
expect { subject.left_mask_formatter(nil, -1) }.to raise_error(ArgumentError)
|
61
|
+
end
|
46
62
|
end
|
47
63
|
end
|
48
64
|
|
@@ -68,51 +84,63 @@ describe Proforma::ExtendedEvaluator::Formatter do
|
|
68
84
|
end
|
69
85
|
end
|
70
86
|
|
71
|
-
describe '#
|
72
|
-
|
73
|
-
|
74
|
-
currency_code: 'USD',
|
75
|
-
currency_round: 2,
|
76
|
-
currency_symbol: '$'
|
77
|
-
)
|
78
|
-
end
|
87
|
+
describe '#number_formatter' do
|
88
|
+
context 'localized for USA (default)' do
|
89
|
+
subject { usa_formatter }
|
79
90
|
|
80
|
-
|
91
|
+
let(:arg) { '3' }
|
81
92
|
|
82
|
-
|
83
|
-
|
93
|
+
specify 'returns formatted number' do
|
94
|
+
expect(subject.number_formatter('12345.67899', arg)).to eq('12,345.679')
|
95
|
+
expect(subject.number_formatter('12345', arg)).to eq('12,345.000')
|
96
|
+
end
|
84
97
|
end
|
85
98
|
|
86
|
-
|
87
|
-
|
88
|
-
end
|
99
|
+
context 'localized for France' do
|
100
|
+
subject { france_formatter }
|
89
101
|
|
90
|
-
|
91
|
-
|
102
|
+
let(:arg) { '3' }
|
103
|
+
|
104
|
+
specify 'returns formatted number' do
|
105
|
+
expect(subject.number_formatter('12345.67899', arg)).to eq('12 345,679')
|
106
|
+
expect(subject.number_formatter('12345', arg)).to eq('12 345,000')
|
107
|
+
end
|
92
108
|
end
|
93
109
|
end
|
94
110
|
|
95
|
-
describe '#
|
96
|
-
|
97
|
-
described_class.new(
|
98
|
-
currency_code: 'USD',
|
99
|
-
currency_round: 2,
|
100
|
-
currency_symbol: '$'
|
101
|
-
)
|
102
|
-
end
|
111
|
+
describe '#currency_formatter' do
|
112
|
+
let(:arg) { '' }
|
103
113
|
|
104
|
-
|
114
|
+
context 'localized for USA (default)' do
|
115
|
+
subject { usa_formatter }
|
105
116
|
|
106
|
-
|
107
|
-
|
108
|
-
|
117
|
+
specify 'returns empty string if value is null' do
|
118
|
+
expect(subject.currency_formatter(nil, arg)).to eq('')
|
119
|
+
end
|
109
120
|
|
110
|
-
|
111
|
-
|
121
|
+
specify 'returns empty string if value is empty string' do
|
122
|
+
expect(subject.currency_formatter('', arg)).to eq('')
|
123
|
+
end
|
124
|
+
|
125
|
+
specify 'returns formatted currency' do
|
126
|
+
expect(subject.currency_formatter('12345.67', arg)).to eq('$12,345.67 USD')
|
127
|
+
end
|
112
128
|
end
|
113
129
|
|
114
|
-
|
115
|
-
|
130
|
+
context 'localized for France' do
|
131
|
+
subject { france_formatter }
|
132
|
+
|
133
|
+
specify 'returns empty string if value is null' do
|
134
|
+
expect(subject.currency_formatter(nil, arg)).to eq('')
|
135
|
+
end
|
136
|
+
|
137
|
+
specify 'returns empty string if value is empty string' do
|
138
|
+
expect(subject.currency_formatter('', arg)).to eq('')
|
139
|
+
end
|
140
|
+
|
141
|
+
specify 'returns formatted currency' do
|
142
|
+
expect(subject.currency_formatter('12345.67', arg)).to eq('12 345,67 €')
|
143
|
+
end
|
116
144
|
end
|
117
145
|
end
|
118
146
|
|
@@ -115,11 +115,20 @@ describe Proforma::ExtendedEvaluator do
|
|
115
115
|
|
116
116
|
actual_documents = Proforma.render(data, template, evaluator: Proforma::ExtendedEvaluator.new)
|
117
117
|
|
118
|
+
expected_contents = <<~CONTENTS
|
119
|
+
DETAILS FOR: BOND, JAMES (1)
|
120
|
+
ID #: 1
|
121
|
+
First Name: James
|
122
|
+
Last Name: Bond
|
123
|
+
Social Security #: XXXXXXX6789
|
124
|
+
Birthdate: 05/14/1960
|
125
|
+
Smoker: No
|
126
|
+
Balance: $123.45 USD
|
127
|
+
CONTENTS
|
128
|
+
|
118
129
|
expected_documents = [
|
119
130
|
Proforma::Document.new(
|
120
|
-
contents:
|
121
|
-
" Bond\nSocial Security #: XXXXXXX6789\nBirthdate: 05/14/1960\nSmoker:"\
|
122
|
-
" No\nBalance: $123.45 USD\n",
|
131
|
+
contents: expected_contents,
|
123
132
|
extension: '.txt',
|
124
133
|
title: ''
|
125
134
|
)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: proforma-extended-evaluator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-04-
|
11
|
+
date: 2019-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: stringento
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: proforma
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1
|
47
|
+
version: '1'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1
|
54
|
+
version: '1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -168,9 +168,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
168
168
|
version: 2.3.8
|
169
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
|
-
- - "
|
171
|
+
- - ">="
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: '0'
|
174
174
|
requirements: []
|
175
175
|
rubygems_version: 3.0.1
|
176
176
|
signing_key:
|