curly_bracket_parser 0.0.1 → 0.9.9
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/README.md +52 -19
- data/lib/curly_bracket_parser.rb +194 -32
- data/lib/curly_bracket_parser/version.rb +1 -1
- data/lib/custom_errors/filter_already_registered_error.rb +2 -0
- data/lib/custom_errors/invalid_filter_error.rb +2 -0
- data/lib/custom_errors/invalid_variable_error.rb +2 -0
- data/lib/custom_errors/variable_already_registered_error.rb +2 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c287b3fc82c91116b7c0ca2f982b81bac5c37e4ca286cd1ea8606de75e0c214f
|
4
|
+
data.tar.gz: 04f59765b1f59ea540d1e152bbc88c0294ca5adc681f30bf1efed3a117cb3afa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685345c3a8551e0749742550f2dae6784501fefa07318b8e76b2b30fd743c96d1571d2550bee0731a1c1609d82055ec39ff943a6aebf3c39db435d4686d8e01c
|
7
|
+
data.tar.gz: a19c4ffb4fe23ce4945023604aeb34ee28a88bd6f89cf2df08f5eab63151fbb0f1ec6b7ec337f6cc8c0b8d2b57c8dacbfec19cbec7b70b22d68cc8b4caf965c6
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# curly_bracket_parser
|
2
2
|
|
3
|
-
|
3
|
+
Ruby gem providing a simple parser to replace curly brackets `{{like_this}}` inside strings like URLs, texts or even files easily.
|
4
4
|
|
5
|
-
|
5
|
+
Additional support for build-in filters and custom filters make them more powerful. `{{example|my_filter}}`
|
6
6
|
|
7
|
+
Using [LuckyCase](https://github.com/magynhard/lucky_case), all its case formats are supported as filter by default.
|
7
8
|
|
8
9
|
|
9
10
|
|
@@ -12,7 +13,7 @@ alpha version ... to be done ...
|
|
12
13
|
# Contents
|
13
14
|
|
14
15
|
* [Installation](#installation)
|
15
|
-
* [Usage](#usage)
|
16
|
+
* [Usage examples](#usage)
|
16
17
|
* [Documentation](#documentation)
|
17
18
|
* [Contributing](#contributing)
|
18
19
|
|
@@ -36,23 +37,13 @@ Or install it yourself as:
|
|
36
37
|
|
37
38
|
$ gem install curly_bracket_parser
|
38
39
|
|
39
|
-
## Common information
|
40
|
-
|
41
|
-
TODO
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
40
|
|
48
41
|
<a name="usage"></a>
|
49
|
-
## Usage
|
42
|
+
## Usage examples
|
50
43
|
|
51
|
-
|
44
|
+
You can either parse variables inside strings or even directly in files.
|
52
45
|
|
53
|
-
###
|
54
|
-
|
55
|
-
#### Basic
|
46
|
+
### Basic
|
56
47
|
|
57
48
|
```ruby
|
58
49
|
url = "https://my-domain.com/items/{{item_id}}"
|
@@ -60,7 +51,7 @@ TODO
|
|
60
51
|
# => "https://my-domain.com/items/123"
|
61
52
|
```
|
62
53
|
|
63
|
-
|
54
|
+
### Filters
|
64
55
|
|
65
56
|
```ruby
|
66
57
|
url = "https://my-domain.com/catalog/{{item_name|snake_case}}"
|
@@ -68,9 +59,51 @@ TODO
|
|
68
59
|
# => "https://my-domain.com/catalog/mega_super_item"
|
69
60
|
```
|
70
61
|
|
71
|
-
|
62
|
+
For a list of built-in filters visit [LuckyCase](https://github.com/magynhard/lucky_case).
|
63
|
+
|
64
|
+
#### Define your custom filter
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
CurlyBracketParser.register_filter('7times') do |string|
|
68
|
+
string.to_s * 7
|
69
|
+
end
|
70
|
+
|
71
|
+
text = "Paul went out and screamed: A{{scream|7times}}h"
|
72
|
+
final_text = CurlyBracketParser.parse text, scream: 'a'
|
73
|
+
# => "Paul went out and screamed: Aaaaaaaah"
|
74
|
+
```
|
75
|
+
|
76
|
+
### Files
|
77
|
+
|
78
|
+
<ins>test.html</ins>
|
79
|
+
```html
|
80
|
+
<h1>{{title|sentence_case}}</h1>
|
81
|
+
```
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
parsed_file = CurlyBracketParser.parse_file './test.html', title: 'WelcomeAtHome'
|
85
|
+
# => "<h1>Welcome at home</h1>"
|
86
|
+
```
|
87
|
+
|
88
|
+
Use `#parse_file!` instead to write the parsed string directly into the file!
|
89
|
+
|
90
|
+
### Default variables
|
91
|
+
|
92
|
+
You can define default variables, which will be replaced automatically without passing them by parameters, but can be overwritten with parameters.
|
72
93
|
|
73
|
-
|
94
|
+
Because of providing blocks, your variables can dynamically depend on other states (e.g. current date).
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
CurlyBracketParser.register_default_var('version') do
|
98
|
+
'1.0.2'
|
99
|
+
end
|
100
|
+
|
101
|
+
text = "You are running version {{version}}"
|
102
|
+
CurlyBracketParser.parse text
|
103
|
+
# => "You are running version 1.0.2"
|
104
|
+
CurlyBracketParser.parse text, version: '0.7.0'
|
105
|
+
# => "You are running version 0.7.0"
|
106
|
+
```
|
74
107
|
|
75
108
|
|
76
109
|
|
data/lib/curly_bracket_parser.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'lucky_case'
|
2
2
|
|
3
3
|
require 'curly_bracket_parser/version'
|
4
|
+
require_relative 'custom_errors/filter_already_registered_error'
|
5
|
+
require_relative 'custom_errors/invalid_filter_error'
|
6
|
+
require_relative 'custom_errors/invalid_variable_error'
|
4
7
|
require_relative 'custom_errors/unresolved_variables_error'
|
8
|
+
require_relative 'custom_errors/variable_already_registered_error'
|
5
9
|
|
6
10
|
#
|
7
11
|
# CurlyBracketParser
|
@@ -17,25 +21,37 @@ module CurlyBracketParser
|
|
17
21
|
VARIABLE_DECODER_REGEX = /{{([^{}\|]+)\|?([^{}\|]*)}}/
|
18
22
|
VARIABLE_REGEX = /{{[^{}]+}}/
|
19
23
|
|
20
|
-
|
21
|
-
|
24
|
+
VALID_DEFAULT_FILTERS = [
|
25
|
+
LuckyCase::CASES.keys.map(&:to_s)
|
22
26
|
].flatten
|
23
27
|
|
24
28
|
#----------------------------------------------------------------------------------------------------
|
25
29
|
|
30
|
+
# Parse given string and replace the included variables by the given variables
|
31
|
+
#
|
26
32
|
# @param [String] string to parse
|
27
33
|
# @param [Hash<Symbol => String>] variables <key: 'value'>
|
28
34
|
# @param [Symbol] unresolved_vars :raise, :keep, :replace => define how to act when unresolved variables within the string are found.
|
29
|
-
# @param [String] replace_pattern pattern used when param unresolved_vars is set to :replace. You can include the var name \\1 and filter \\
|
35
|
+
# @param [String] replace_pattern pattern used when param unresolved_vars is set to :replace. You can include the var name \\1 and filter \\2. Empty string to remove unresolved variables.
|
30
36
|
# @return [String, UnresolvedVariablesError] parsed string
|
31
37
|
def self.parse(string, variables, unresolved_vars: :raise, replace_pattern: "##\\1##")
|
38
|
+
variables ||= {}
|
32
39
|
result_string = string.clone
|
33
40
|
if CurlyBracketParser.any_variable_included? string
|
34
41
|
loop do
|
35
42
|
variables(string).each do |string_var|
|
36
|
-
|
43
|
+
dec = decode_variable(string_var)
|
44
|
+
name = dec[:name]
|
45
|
+
filter = dec[:filter]
|
37
46
|
if variables[name.to_sym]
|
38
|
-
value =
|
47
|
+
value = if filter
|
48
|
+
process_filter(filter, variables[name.to_sym])
|
49
|
+
else
|
50
|
+
variables[name.to_sym]
|
51
|
+
end
|
52
|
+
result_string.gsub!(string_var, value)
|
53
|
+
elsif registered_default_var?(name.to_s)
|
54
|
+
value = process_default_var(name)
|
39
55
|
result_string.gsub!(string_var, value)
|
40
56
|
end
|
41
57
|
end
|
@@ -56,45 +72,178 @@ module CurlyBracketParser
|
|
56
72
|
|
57
73
|
#----------------------------------------------------------------------------------------------------
|
58
74
|
|
59
|
-
|
60
|
-
|
75
|
+
# Parse the content of the file of the given path with #parse and return it.
|
76
|
+
# The original file keeps unmodified.
|
77
|
+
#
|
78
|
+
# @param [String] string to parse
|
79
|
+
# @param [Hash<Symbol => String>] variables <key: 'value'>
|
80
|
+
# @param [Symbol] unresolved_vars :raise, :keep, :replace => define how to act when unresolved variables within the string are found.
|
81
|
+
# @param [String] replace_pattern pattern used when param unresolved_vars is set to :replace. You can include the var name \\1 and filter \\1. Empty string to remove unresolved variables.
|
82
|
+
# @return [String, UnresolvedVariablesError] parsed string
|
83
|
+
def self.parse_file(path, variables, unresolved_vars: :raise, replace_pattern: "##\\1##")
|
84
|
+
file_content = File.read path
|
85
|
+
parse(file_content, variables, unresolved_vars: unresolved_vars, replace_pattern: replace_pattern)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Parse the content of the file of the given path with #parse and return it.
|
89
|
+
# The original file will be overwritten by the parsed content.
|
90
|
+
#
|
91
|
+
# @param [String] string to parse
|
92
|
+
# @param [Hash<Symbol => String>] variables <key: 'value'>
|
93
|
+
# @param [Symbol] unresolved_vars :raise, :keep, :replace => define how to act when unresolved variables within the string are found.
|
94
|
+
# @param [String] replace_pattern pattern used when param unresolved_vars is set to :replace. You can include the var name \\1 and filter \\1. Empty string to remove unresolved variables.
|
95
|
+
# @return [String, UnresolvedVariablesError] parsed string
|
96
|
+
def self.parse_file!(path, variables, unresolved_vars: :raise, replace_pattern: "##\\1##")
|
97
|
+
parsed_file = parse_file path, variables, unresolved_vars: unresolved_vars, replace_pattern: replace_pattern
|
98
|
+
File.write path, parsed_file
|
99
|
+
parsed_file
|
100
|
+
end
|
101
|
+
|
102
|
+
#----------------------------------------------------------------------------------------------------
|
103
|
+
|
104
|
+
# Register your custom filter to the filter list
|
105
|
+
#
|
106
|
+
# @param [String] filter name of the filter, also used then in your strings, e.g. {{var_name|my_filter_name}}
|
107
|
+
# @param [Lambda] function of the filter to run the variable against
|
108
|
+
# @raise [FilterAlreadyRegisteredError] if filter does already exist
|
109
|
+
# @return [Proc] given block
|
110
|
+
def self.register_filter(filter, &block)
|
111
|
+
@@registered_filters ||= {}
|
112
|
+
filter = filter.to_s
|
113
|
+
if valid_filter?(filter)
|
114
|
+
raise FilterAlreadyRegisteredError, "The given filter name '#{filter}' is already registered"
|
115
|
+
else
|
116
|
+
@@registered_filters[filter] = block
|
117
|
+
end
|
61
118
|
end
|
62
119
|
|
63
|
-
|
64
|
-
|
120
|
+
#----------------------------------------------------------------------------------------------------
|
121
|
+
|
122
|
+
# Process the given value with the given filter
|
123
|
+
#
|
124
|
+
# @param [String] filter name of the filter, also used then in your strings, e.g. {{var_name|my_filter_name}}
|
125
|
+
# @param [String] value string to apply the specified filter on
|
126
|
+
# @return [String] converted string with applied filter
|
127
|
+
def self.process_filter(filter, value)
|
128
|
+
@@registered_filters ||= {}
|
129
|
+
filter = filter.to_s
|
130
|
+
if @@registered_filters[filter]
|
131
|
+
@@registered_filters[filter].call(value)
|
132
|
+
elsif VALID_DEFAULT_FILTERS.include?(filter) && LuckyCase.valid_case_type?(filter)
|
133
|
+
LuckyCase.convert_case(value, filter)
|
134
|
+
else
|
135
|
+
message = "Invalid filter '#{filter}'. Valid filters are: #{self.valid_filters.join(' ')}"
|
136
|
+
raise InvalidFilterError, message
|
137
|
+
end
|
65
138
|
end
|
66
139
|
|
67
140
|
#----------------------------------------------------------------------------------------------------
|
68
141
|
|
69
|
-
|
70
|
-
|
142
|
+
# Retrieve Array with valid filters
|
143
|
+
#
|
144
|
+
# @return [Array<String>] of valid filters
|
145
|
+
def self.valid_filters
|
146
|
+
all_filters = VALID_DEFAULT_FILTERS
|
147
|
+
@@registered_filters ||= {}
|
148
|
+
all_filters + @@registered_filters.keys.map(&:to_s)
|
71
149
|
end
|
72
150
|
|
73
151
|
#----------------------------------------------------------------------------------------------------
|
74
152
|
|
75
|
-
|
76
|
-
|
153
|
+
# Check if a given filter is valid
|
154
|
+
#
|
155
|
+
# @param [String] name
|
156
|
+
# @return [Boolean] true if filter exists, otherwise false
|
157
|
+
def self.valid_filter?(name)
|
158
|
+
self.valid_filters.include? name
|
77
159
|
end
|
78
160
|
|
79
161
|
#----------------------------------------------------------------------------------------------------
|
80
162
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
163
|
+
# Register a default variable to be replaced automatically by the given block value in future
|
164
|
+
# If the variable exists already, it will raise an VariableAlreadyRegisteredError
|
165
|
+
#
|
166
|
+
# @param [String] name of the default var
|
167
|
+
# @param [Proc] block
|
168
|
+
# @raise [VariableAlreadyRegisteredError] if variable is already registered
|
169
|
+
# @return [Proc] given block
|
170
|
+
def self.register_default_var(name, &block)
|
171
|
+
@@registered_default_vars ||= {}
|
172
|
+
name = name.to_s
|
173
|
+
if registered_default_var?(name)
|
174
|
+
raise VariableAlreadyRegisteredError, "The given variable name '#{name}' is already registered. If you want to override that variable explicitly, call #register_default_var! instead!"
|
175
|
+
else
|
176
|
+
@@registered_default_vars[name] = block
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
#----------------------------------------------------------------------------------------------------
|
181
|
+
|
182
|
+
# Return the given default variable by returning the result of its block/proc
|
183
|
+
#
|
184
|
+
# @param [String] name of the variable to return
|
185
|
+
# @return [String] value of the variable
|
186
|
+
def self.process_default_var(name)
|
187
|
+
@@registered_default_vars ||= {}
|
188
|
+
name = name.to_s
|
189
|
+
if @@registered_default_vars[name]
|
190
|
+
@@registered_default_vars[name].call()
|
191
|
+
else
|
192
|
+
message = "Invalid default variable '#{name}'. Valid registered default variables are: #{self.registered_default_vars.keys.join(' ')}"
|
193
|
+
raise InvalidVariableError, message
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
#----------------------------------------------------------------------------------------------------
|
198
|
+
|
199
|
+
# Register a default variable to be replaced automatically by the given block value in future
|
200
|
+
# If the variable exists already, it will be overwritten
|
201
|
+
#
|
202
|
+
# @param [String] name of the default var
|
203
|
+
# @param [Proc] block
|
204
|
+
# @raise [VariableAlreadyRegisteredError] if variable is already registered
|
205
|
+
# @return [Proc] given block
|
206
|
+
def self.register_default_var!(name, &block)
|
207
|
+
@@registered_default_vars ||= {}
|
208
|
+
name = name.to_s
|
209
|
+
@@registered_default_vars[name] = block
|
210
|
+
end
|
211
|
+
|
212
|
+
#----------------------------------------------------------------------------------------------------
|
213
|
+
|
214
|
+
# Unregister / remove an existing default variable
|
215
|
+
#
|
216
|
+
# @param [String] name of the variable
|
217
|
+
# @return [Boolean] true if variable existed and was unregistered, false if it didn't exist
|
218
|
+
def self.unregister_default_var(name)
|
219
|
+
@@registered_default_vars ||= {}
|
220
|
+
name = name.to_s
|
221
|
+
if @@registered_default_vars[name]
|
222
|
+
@@registered_default_vars.delete(name)
|
223
|
+
true
|
89
224
|
else
|
90
|
-
|
225
|
+
false
|
91
226
|
end
|
92
227
|
end
|
93
228
|
|
94
229
|
#----------------------------------------------------------------------------------------------------
|
95
230
|
|
96
|
-
|
97
|
-
|
231
|
+
# Return an array of registered default variables
|
232
|
+
#
|
233
|
+
# @return [Array<String>]
|
234
|
+
def self.registered_default_vars
|
235
|
+
@@registered_default_vars ||= {}
|
236
|
+
@@registered_default_vars.keys.map(&:to_s)
|
237
|
+
end
|
238
|
+
|
239
|
+
#----------------------------------------------------------------------------------------------------
|
240
|
+
|
241
|
+
# Check if the given variable is a registered default variable
|
242
|
+
#
|
243
|
+
# @param [String] name of the variable
|
244
|
+
# @return [Boolean] true if variable is registered, otherwise false
|
245
|
+
def self.registered_default_var?(name)
|
246
|
+
self.registered_default_vars.include? name
|
98
247
|
end
|
99
248
|
|
100
249
|
#----------------------------------------------------------------------------------------------------
|
@@ -105,26 +254,30 @@ module CurlyBracketParser
|
|
105
254
|
# '{{var_name|filter_name}}' => { name: 'var_name', filter: 'filter_name' }
|
106
255
|
#
|
107
256
|
# @param [String] variable
|
108
|
-
# @return [
|
257
|
+
# @return [Hash<String => String>] name, filter
|
109
258
|
def self.decode_variable(variable)
|
110
|
-
|
111
|
-
[var.keys.first, var.values.first]
|
259
|
+
decoded_variables(variable).first
|
112
260
|
end
|
113
261
|
|
114
262
|
#----------------------------------------------------------------------------------------------------
|
115
263
|
|
116
|
-
#
|
264
|
+
# Scans the given url for variables with pattern '{{var|optional_filter}}'
|
265
|
+
#
|
266
|
+
# @example
|
267
|
+
# 'The variable {{my_var|my_filter}} is inside this string' => [{ name: "my_var", filter: "my_filter"}]
|
268
|
+
#
|
117
269
|
# @param [String] string to scan
|
118
270
|
# @return [Array<Hash<Symbol => String>>] array of variable names and its filters
|
119
271
|
def self.decoded_variables(string)
|
120
|
-
|
121
|
-
|
122
|
-
string.scan(VARIABLE_DECODER_REGEX).map { |e| {"#{e[
|
272
|
+
var_name_index = 0
|
273
|
+
var_filter_index = 1
|
274
|
+
string.scan(VARIABLE_DECODER_REGEX).map { |e| { name: "#{e[var_name_index].strip}", filter: e[var_filter_index].strip != '' ? e[var_filter_index].strip : nil } }.flatten
|
123
275
|
end
|
124
276
|
|
125
277
|
#----------------------------------------------------------------------------------------------------
|
126
278
|
|
127
|
-
#
|
279
|
+
# Scans the given url for variables with pattern '{{var|optional_filter}}'
|
280
|
+
#
|
128
281
|
# @param [String] string to scan
|
129
282
|
# @return [Array<String>] array of variable names and its filters
|
130
283
|
def self.variables(string)
|
@@ -133,12 +286,21 @@ module CurlyBracketParser
|
|
133
286
|
|
134
287
|
#----------------------------------------------------------------------------------------------------
|
135
288
|
|
289
|
+
# Check if any variable is included in the given string
|
290
|
+
#
|
291
|
+
# @param [String] string name of variable to check for
|
292
|
+
# @return [Boolean] true if any variable is included in the given string, otherwise false
|
136
293
|
def self.any_variable_included?(string)
|
137
294
|
string.match(VARIABLE_REGEX) != nil
|
138
295
|
end
|
139
296
|
|
140
297
|
#----------------------------------------------------------------------------------------------------
|
141
298
|
|
299
|
+
# Check if one of the given variable names is included in the given string
|
300
|
+
#
|
301
|
+
# @param [Array<String>] variable_names
|
302
|
+
# @param [String] string name of variable to check for
|
303
|
+
# @return [Boolean] true if one given variable name is included in given the string, otherwise false
|
142
304
|
def self.includes_one_variable_of(variable_names, string)
|
143
305
|
decoded_variables(string).each do |dvar|
|
144
306
|
return true if variable_names.include?(dvar[:name])
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: curly_bracket_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthäus Beyrle
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lucky_case
|
@@ -85,7 +85,11 @@ files:
|
|
85
85
|
- curly_bracket_parser.gemspec
|
86
86
|
- lib/curly_bracket_parser.rb
|
87
87
|
- lib/curly_bracket_parser/version.rb
|
88
|
+
- lib/custom_errors/filter_already_registered_error.rb
|
89
|
+
- lib/custom_errors/invalid_filter_error.rb
|
90
|
+
- lib/custom_errors/invalid_variable_error.rb
|
88
91
|
- lib/custom_errors/unresolved_variables_error.rb
|
92
|
+
- lib/custom_errors/variable_already_registered_error.rb
|
89
93
|
homepage: https://github.com/magynhard/curly_bracket_parser
|
90
94
|
licenses:
|
91
95
|
- MIT
|