stringento 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +8 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +123 -0
- data/Guardfile +16 -0
- data/LICENSE +7 -0
- data/README.md +162 -0
- data/bin/console +11 -0
- data/lib/stringento/formatter.rb +31 -0
- data/lib/stringento/placeholder.rb +37 -0
- data/lib/stringento/resolver.rb +25 -0
- data/lib/stringento/stringento.rb +46 -0
- data/lib/stringento/template.rb +44 -0
- data/lib/stringento/version.rb +12 -0
- data/lib/stringento.rb +10 -0
- data/spec/examples/custom_formatter.rb +24 -0
- data/spec/examples/indifferent_hash_resolver.rb +14 -0
- data/spec/examples/nested_hash_resolver.rb +16 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/stringento/formatter_spec.rb +62 -0
- data/spec/stringento/placeholder_spec.rb +94 -0
- data/spec/stringento/resolver_spec.rb +53 -0
- data/spec/stringento/stringento_spec.rb +161 -0
- data/spec/stringento/template_spec.rb +65 -0
- data/stringento.gemspec +32 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a9c5bc1e6be2d2b938d8558ffed9dfbf772e6455e5d685d191a573b4ed2f39d0
|
4
|
+
data.tar.gz: 35a3d3762d62d34e7910d96f3572b0fb99188212baa931d0c6fbc7b2c4546302
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2a0ccd6b31c1fab010b8204e5be9786fe8df043dc4f198bc44a9dc52f1b72d1bc7bb5cb242c122cbd33b3263f9a6542fe6dc9f9e19c12996f65a688812ce3ef
|
7
|
+
data.tar.gz: ca6ba92b4db5ad88ec139f2352684f3b7534224bedc631546ac21f053354824f75dcddb15d6e27d5d130b2da600ad363ab13a1c5b3f9afd2eaa291f039493242
|
data/.editorconfig
ADDED
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.0
|
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=5d35e3d9b28655a2040bdc46f31576dc73c61c7bee8c7dd6ea9832b9aeb61833
|
4
|
+
language: ruby
|
5
|
+
rvm:
|
6
|
+
# Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
|
7
|
+
- 2.3.8
|
8
|
+
- 2.4.5
|
9
|
+
- 2.5.3
|
10
|
+
- 2.6.0
|
11
|
+
cache: bundler
|
12
|
+
before_script:
|
13
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
14
|
+
- chmod +x ./cc-test-reporter
|
15
|
+
- ./cc-test-reporter before-build
|
16
|
+
script:
|
17
|
+
- bundle exec rubocop
|
18
|
+
- bundle exec rspec spec --format documentation
|
19
|
+
after_script:
|
20
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
stringento (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
Ascii85 (1.0.3)
|
10
|
+
afm (0.2.2)
|
11
|
+
ansi (1.5.0)
|
12
|
+
ast (2.4.0)
|
13
|
+
coderay (1.1.2)
|
14
|
+
concurrent-ruby (1.1.5)
|
15
|
+
diff-lcs (1.3)
|
16
|
+
docile (1.3.1)
|
17
|
+
faker (1.9.3)
|
18
|
+
i18n (>= 0.7)
|
19
|
+
ffi (1.9.25)
|
20
|
+
formatador (0.2.5)
|
21
|
+
guard (2.15.0)
|
22
|
+
formatador (>= 0.2.4)
|
23
|
+
listen (>= 2.7, < 4.0)
|
24
|
+
lumberjack (>= 1.0.12, < 2.0)
|
25
|
+
nenv (~> 0.1)
|
26
|
+
notiffany (~> 0.0)
|
27
|
+
pry (>= 0.9.12)
|
28
|
+
shellany (~> 0.0)
|
29
|
+
thor (>= 0.18.1)
|
30
|
+
guard-compat (1.2.1)
|
31
|
+
guard-rspec (4.7.3)
|
32
|
+
guard (~> 2.1)
|
33
|
+
guard-compat (~> 1.1)
|
34
|
+
rspec (>= 2.99.0, < 4.0)
|
35
|
+
hashery (2.1.2)
|
36
|
+
hirb (0.7.3)
|
37
|
+
i18n (1.6.0)
|
38
|
+
concurrent-ruby (~> 1.0)
|
39
|
+
jaro_winkler (1.5.2)
|
40
|
+
json (2.1.0)
|
41
|
+
listen (3.1.5)
|
42
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
43
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
44
|
+
ruby_dep (~> 1.2)
|
45
|
+
lumberjack (1.0.13)
|
46
|
+
method_source (0.9.2)
|
47
|
+
nenv (0.3.0)
|
48
|
+
notiffany (0.1.1)
|
49
|
+
nenv (~> 0.1)
|
50
|
+
shellany (~> 0.0)
|
51
|
+
parallel (1.13.0)
|
52
|
+
parser (2.6.0.0)
|
53
|
+
ast (~> 2.4.0)
|
54
|
+
pdf-inspector (1.3.0)
|
55
|
+
pdf-reader (>= 1.0, < 3.0.a)
|
56
|
+
pdf-reader (2.2.0)
|
57
|
+
Ascii85 (~> 1.0.0)
|
58
|
+
afm (~> 0.2.1)
|
59
|
+
hashery (~> 2.0)
|
60
|
+
ruby-rc4
|
61
|
+
ttfunk
|
62
|
+
powerpack (0.1.2)
|
63
|
+
pry (0.12.2)
|
64
|
+
coderay (~> 1.1.0)
|
65
|
+
method_source (~> 0.9.0)
|
66
|
+
rainbow (3.0.0)
|
67
|
+
rb-fsevent (0.10.3)
|
68
|
+
rb-inotify (0.9.10)
|
69
|
+
ffi (>= 0.5.0, < 2)
|
70
|
+
rspec (3.8.0)
|
71
|
+
rspec-core (~> 3.8.0)
|
72
|
+
rspec-expectations (~> 3.8.0)
|
73
|
+
rspec-mocks (~> 3.8.0)
|
74
|
+
rspec-core (3.8.0)
|
75
|
+
rspec-support (~> 3.8.0)
|
76
|
+
rspec-expectations (3.8.2)
|
77
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
78
|
+
rspec-support (~> 3.8.0)
|
79
|
+
rspec-mocks (3.8.0)
|
80
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
81
|
+
rspec-support (~> 3.8.0)
|
82
|
+
rspec-support (3.8.0)
|
83
|
+
rubocop (0.63.1)
|
84
|
+
jaro_winkler (~> 1.5.1)
|
85
|
+
parallel (~> 1.10)
|
86
|
+
parser (>= 2.5, != 2.5.1.1)
|
87
|
+
powerpack (~> 0.1)
|
88
|
+
rainbow (>= 2.2.2, < 4.0)
|
89
|
+
ruby-progressbar (~> 1.7)
|
90
|
+
unicode-display_width (~> 1.4.0)
|
91
|
+
ruby-progressbar (1.10.0)
|
92
|
+
ruby-rc4 (0.1.5)
|
93
|
+
ruby_dep (1.5.0)
|
94
|
+
shellany (0.0.1)
|
95
|
+
simplecov (0.16.1)
|
96
|
+
docile (~> 1.1)
|
97
|
+
json (>= 1.8, < 3)
|
98
|
+
simplecov-html (~> 0.10.0)
|
99
|
+
simplecov-console (0.4.2)
|
100
|
+
ansi
|
101
|
+
hirb
|
102
|
+
simplecov
|
103
|
+
simplecov-html (0.10.2)
|
104
|
+
thor (0.20.3)
|
105
|
+
ttfunk (1.5.1)
|
106
|
+
unicode-display_width (1.4.1)
|
107
|
+
|
108
|
+
PLATFORMS
|
109
|
+
ruby
|
110
|
+
|
111
|
+
DEPENDENCIES
|
112
|
+
faker (~> 1)
|
113
|
+
guard-rspec (~> 4.7)
|
114
|
+
pdf-inspector (~> 1)
|
115
|
+
pry (~> 0)
|
116
|
+
rspec (~> 3.8)
|
117
|
+
rubocop (~> 0.63.1)
|
118
|
+
simplecov (~> 0.16.1)
|
119
|
+
simplecov-console (~> 0.4.2)
|
120
|
+
stringento!
|
121
|
+
|
122
|
+
BUNDLED WITH
|
123
|
+
1.17.3
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
4
|
+
require 'guard/rspec/dsl'
|
5
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
ruby = dsl.ruby
|
15
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2019 Blue Marble Payroll, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
*This is a Ruby implementation of [Stringent](https://github.com/bluemarblepayroll/stringent). The name Stringent was already taken within RubyGems so this was named Stringento instead.*
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
# Stringento
|
6
|
+
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/stringento.svg)](https://badge.fury.io/rb/stringento) [![Build Status](https://travis-ci.org/bluemarblepayroll/stringento.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/stringento) [![Maintainability](https://api.codeclimate.com/v1/badges/22aabc80514fe3db20da/maintainability)](https://codeclimate.com/github/bluemarblepayroll/stringento/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/22aabc80514fe3db20da/test_coverage)](https://codeclimate.com/github/bluemarblepayroll/stringento/test_coverage) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
8
|
+
|
9
|
+
This library provides a pluggable string templating system. At its core, it can take a templated string (such as: '{last_name}, {first_name} {middle_name}') and a input object (default is a plain old Ruby object) and it will dynamically resolve the string based on the input. It also provides two optional arguments which help make this library 'pluggable':
|
10
|
+
|
11
|
+
1. Custom Resolver: Instead of passing in a PORO as your input, you can pass in other object types and use a custom resolver for value getting.
|
12
|
+
1. Custom Formatter: Customize the way each string token is rendered by extending the token syntax to: {token:formatter:argument}. Formatter is the formatting method to call and argument will be passed in as meta-data/options.
|
13
|
+
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
To install through Rubygems:
|
18
|
+
|
19
|
+
````
|
20
|
+
gem install install stringento
|
21
|
+
````
|
22
|
+
|
23
|
+
You can also add this to your Gemfile:
|
24
|
+
|
25
|
+
````
|
26
|
+
bundle add stringento
|
27
|
+
````
|
28
|
+
|
29
|
+
## Examples
|
30
|
+
|
31
|
+
### Getting Started
|
32
|
+
|
33
|
+
Consider the following example:
|
34
|
+
|
35
|
+
> 'The {fox_speed} brown fox jumps over the {dog_speed} dog'
|
36
|
+
|
37
|
+
We can evaluate this by executing:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
example = 'The {fox_speed} brown fox jumps over the {dog_speed} dog'
|
41
|
+
input = { 'fox_speed' => 'quick', 'dog_speed' => 'lazy' }
|
42
|
+
|
43
|
+
result = Stringento.evaluate(example, input)
|
44
|
+
```
|
45
|
+
|
46
|
+
Result should now be: 'The quick brown fox jumps over the lazy dog'
|
47
|
+
|
48
|
+
### Custom Resolution
|
49
|
+
|
50
|
+
The above example works just fine using the default object value resolver, but you can also pass in a custom function that will serve as resolver. That way you could use dot notation for value resolution. For example:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class NestedHashResolver
|
54
|
+
def resolve(value, input)
|
55
|
+
parts = value.to_s.split('.').map(&:to_sym)
|
56
|
+
|
57
|
+
input ? input.dig(*parts) : nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
example = 'The {fox.speed} brown fox jumps over the {dog.speed} dog'
|
62
|
+
input = { fox: { speed: 'quick' }, dog: { speed: 'lazy' } }
|
63
|
+
|
64
|
+
result = Stringento.evaluate(example, input, resolver: NestedHashResolver.new)
|
65
|
+
```
|
66
|
+
|
67
|
+
The ```result``` variable should now be: 'The quick brown fox jumps over the lazy dog'
|
68
|
+
|
69
|
+
### Custom Formatting
|
70
|
+
|
71
|
+
Another extendable aspect is formatting. Consider a basic example where we want to show 'Yes' for a true value, 'No' for a false value, and an 'Unknown' for a null value:
|
72
|
+
|
73
|
+
> 'The fox is quick: {fox.quick::yes_no_unknown}'
|
74
|
+
|
75
|
+
In this case we can use a custom formatter called yes_no_unknown that understands how to do this for us. The custom formatter is controlled externally from this library, which means you can use this for whatever purpose you see fit. Here would be an example implementation of this custom formatter:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class CustomFormatter < ::Stringento::Formatter
|
79
|
+
def yes_no_unknown_formatter(value, _arg)
|
80
|
+
if value.nil?
|
81
|
+
'Unknown'
|
82
|
+
elsif value
|
83
|
+
'Yes'
|
84
|
+
else
|
85
|
+
'No'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Now, we can pass this in and consume it as follows:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class NestedHashResolver
|
95
|
+
def resolve(value, input)
|
96
|
+
parts = value.to_s.split('.').map(&:to_sym)
|
97
|
+
|
98
|
+
input ? input.dig(*parts) : nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
example = 'The fox is quick: {fox.quick::yes_no_unknown}'
|
103
|
+
input = { fox: { quick: true } }
|
104
|
+
|
105
|
+
result = evaluate(example, input, resolver: NestedHashResolver.new, formatter: CustomFormatter.new)
|
106
|
+
```
|
107
|
+
|
108
|
+
The ```result``` variable should now be: 'The fox is quick: Yes'
|
109
|
+
|
110
|
+
*Note: If we wanted to use the default resolver we could omit resolver instead of the custom resolver.*
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
### Development Environment Configuration
|
115
|
+
|
116
|
+
Basic steps to take to get this repository compiling:
|
117
|
+
|
118
|
+
1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (check stringento.gemspec for versions supported)
|
119
|
+
2. Install bundler (gem install bundler)
|
120
|
+
3. Clone the repository (git clone git@github.com:bluemarblepayroll/stringento.git)
|
121
|
+
4. Navigate to the root folder (cd stringento)
|
122
|
+
5. Install dependencies (bundle)
|
123
|
+
|
124
|
+
### Running Tests
|
125
|
+
|
126
|
+
To execute the test suite run:
|
127
|
+
|
128
|
+
````
|
129
|
+
bundle exec rspec spec --format documentation
|
130
|
+
````
|
131
|
+
|
132
|
+
Alternatively, you can have Guard watch for changes:
|
133
|
+
|
134
|
+
````
|
135
|
+
bundle exec guard
|
136
|
+
````
|
137
|
+
|
138
|
+
Also, do not forget to run Rubocop:
|
139
|
+
|
140
|
+
````
|
141
|
+
bundle exec rubocop
|
142
|
+
````
|
143
|
+
|
144
|
+
### Publishing
|
145
|
+
|
146
|
+
Note: ensure you have proper authorization before trying to publish new versions.
|
147
|
+
|
148
|
+
After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
|
149
|
+
|
150
|
+
1. Merge Pull Request into master
|
151
|
+
2. Update ```lib/stringento/version.rb``` using [semantic versioning](https://semver.org/)
|
152
|
+
3. Install dependencies: ```bundle```
|
153
|
+
4. Update ```CHANGELOG.md``` with release notes
|
154
|
+
5. Commit & push master to remote and ensure CI builds master successfully
|
155
|
+
6. Build the project locally: `gem build stringento`
|
156
|
+
7. Publish package to RubyGems: `gem push stringento-X.gem` where X is the version to push
|
157
|
+
8. Tag master with new version: `git tag <version>`
|
158
|
+
9. Push tags remotely: `git push origin --tags`
|
159
|
+
|
160
|
+
## License
|
161
|
+
|
162
|
+
This project is MIT Licensed.
|
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'stringento'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Stringento
|
11
|
+
# Base class / base implementation of a formatter.
|
12
|
+
# In order to customize formatting:
|
13
|
+
# - Derive subclass
|
14
|
+
# - Implement formatter methods ending in: _formatter.
|
15
|
+
# - Pass instance into into Stringento#evaluate
|
16
|
+
class Formatter
|
17
|
+
METHOD_SUFFIX = '_formatter'
|
18
|
+
|
19
|
+
private_constant :METHOD_SUFFIX
|
20
|
+
|
21
|
+
def format(value, formatter = '', arg = '')
|
22
|
+
method_name = "#{formatter}#{METHOD_SUFFIX}"
|
23
|
+
|
24
|
+
if respond_to?(method_name)
|
25
|
+
send(method_name, value, arg)
|
26
|
+
else
|
27
|
+
value.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Stringento
|
11
|
+
# A placeholder is a to-be-resolved-and-formatted token within a string.
|
12
|
+
# A placeholder has a minimum one part and at maximum three parts (token::formatter:argument),
|
13
|
+
# for example:
|
14
|
+
# - first_name
|
15
|
+
# - first_name::capitalize
|
16
|
+
# - dob::date::mm-dd-yyyy
|
17
|
+
class Placeholder
|
18
|
+
SEPARATOR = '::'
|
19
|
+
|
20
|
+
attr_reader :arg, :formatter, :name, :value
|
21
|
+
|
22
|
+
def initialize(value)
|
23
|
+
@value = value.to_s
|
24
|
+
|
25
|
+
parts = @value.split(SEPARATOR)
|
26
|
+
count = parts.length
|
27
|
+
|
28
|
+
raise ArgumentError, "Cannot be split: #{value}" if count.negative? || count > 3
|
29
|
+
|
30
|
+
@name = parts[0] || ''
|
31
|
+
@formatter = parts[1] || ''
|
32
|
+
@arg = parts[2] || ''
|
33
|
+
|
34
|
+
freeze
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Stringento
|
11
|
+
# This is the base class / implementation for how values are resolved.
|
12
|
+
# In order to change this:
|
13
|
+
# - Derive a subclass
|
14
|
+
# - Override the #resolve method
|
15
|
+
# - Pass instance into into Stringento#evaluate
|
16
|
+
class Resolver
|
17
|
+
def resolve(value, input)
|
18
|
+
if input&.respond_to?(:[])
|
19
|
+
input[value]
|
20
|
+
elsif input&.respond_to?(value)
|
21
|
+
input.send(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'formatter'
|
11
|
+
require_relative 'placeholder'
|
12
|
+
require_relative 'resolver'
|
13
|
+
require_relative 'template'
|
14
|
+
|
15
|
+
# Top-level API for main external consumption. It is better to use this than instantiating
|
16
|
+
# your own Template objects because this top-level object will cache Template objects based
|
17
|
+
# on the expression. It also provides less default resolver and formatter instantiations.
|
18
|
+
module Stringento
|
19
|
+
class << self
|
20
|
+
def evaluate(expression, input, resolver: nil, formatter: nil)
|
21
|
+
template(expression).evaluate(
|
22
|
+
input,
|
23
|
+
resolver: (resolver || default_resolver),
|
24
|
+
formatter: (formatter || default_formatter)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def templates
|
31
|
+
@templates ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def template(expression)
|
35
|
+
templates[expression.to_s] ||= Template.new(expression)
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_resolver
|
39
|
+
@default_resolver ||= Resolver.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_formatter
|
43
|
+
@default_formatter ||= Formatter.new
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Stringento
|
11
|
+
# This is the main rendering engine for the library. It connects together the notions of:
|
12
|
+
# parsing, value resolution, and value formatting in a consumable way.
|
13
|
+
class Template
|
14
|
+
TOKEN_REGULAR_EXPRESSION = /{(.*?)}/.freeze
|
15
|
+
|
16
|
+
private_constant :TOKEN_REGULAR_EXPRESSION
|
17
|
+
|
18
|
+
attr_reader :value
|
19
|
+
|
20
|
+
def initialize(value)
|
21
|
+
@value = value.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def placeholders
|
25
|
+
@placeholders ||= value.scan(TOKEN_REGULAR_EXPRESSION)
|
26
|
+
.flatten
|
27
|
+
.map { |str| Placeholder.new(str) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def evaluate(input, resolver: Resolver.new, formatter: Formatter.new)
|
31
|
+
placeholders.inject(value) do |output, placeholder|
|
32
|
+
resolved_value = (resolver || default_resolver).resolve(placeholder.name, input)
|
33
|
+
|
34
|
+
formatted_value = (formatter || default_formatter).format(
|
35
|
+
resolved_value,
|
36
|
+
placeholder.formatter,
|
37
|
+
placeholder.arg
|
38
|
+
)
|
39
|
+
|
40
|
+
output.gsub("{#{placeholder.value}}", formatted_value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Stringento
|
11
|
+
VERSION = '1.0.0'
|
12
|
+
end
|
data/lib/stringento.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'stringento/stringento'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
class CustomFormatter < ::Stringento::Formatter
|
11
|
+
def yes_no_formatter(value, _arg)
|
12
|
+
value ? 'Yes' : 'No'
|
13
|
+
end
|
14
|
+
|
15
|
+
def yes_no_unknown_formatter(value, _arg)
|
16
|
+
if value.nil?
|
17
|
+
'Unknown'
|
18
|
+
elsif value
|
19
|
+
'Yes'
|
20
|
+
else
|
21
|
+
'No'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
class IndifferentHashResolver
|
11
|
+
def resolve(value = '', input = {})
|
12
|
+
input ? (input[value.to_s] || input[value.to_s.to_sym]) : nil
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
class NestedHashResolver
|
11
|
+
def resolve(value, input)
|
12
|
+
parts = value.to_s.split('.').map(&:to_sym)
|
13
|
+
|
14
|
+
input ? input.dig(*parts) : nil
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'pry'
|
11
|
+
require 'csv'
|
12
|
+
|
13
|
+
require 'simplecov'
|
14
|
+
require 'simplecov-console'
|
15
|
+
SimpleCov.formatter = SimpleCov::Formatter::Console
|
16
|
+
SimpleCov.start
|
17
|
+
|
18
|
+
require './lib/stringento'
|