proforma-extended-evaluator 1.0.0.pre.alpha → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/proforma-extended-evaluator.svg)](https://badge.fury.io/rb/proforma-extended-evaluator) [![Build Status](https://travis-ci.org/bluemarblepayroll/proforma-extended-evaluator.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/proforma-extended-evaluator) [![Maintainability](https://api.codeclimate.com/v1/badges/79e66b596906f633bc95/maintainability)](https://codeclimate.com/github/bluemarblepayroll/proforma-extended-evaluator/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/79e66b596906f633bc95/test_coverage)](https://codeclimate.com/github/bluemarblepayroll/proforma-extended-evaluator/test_coverage) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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:
|