parameter_substitution 0.3.0.pre.1 → 1.1.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/parameter_substitution.rb +1 -2
- data/lib/parameter_substitution/configuration.rb +39 -0
- data/lib/parameter_substitution/context.rb +74 -0
- data/lib/parameter_substitution/encoder.rb +86 -0
- data/lib/parameter_substitution/expression.rb +146 -0
- data/lib/parameter_substitution/formatters/add_prefix.rb +19 -0
- data/lib/parameter_substitution/formatters/base.rb +46 -0
- data/lib/parameter_substitution/formatters/blank_if_nil.rb +11 -0
- data/lib/parameter_substitution/formatters/cgi_unescape.rb +11 -0
- data/lib/parameter_substitution/formatters/compare_string.rb +21 -0
- data/lib/parameter_substitution/formatters/date_time_custom.rb +22 -0
- data/lib/parameter_substitution/formatters/date_time_format.rb +53 -0
- data/lib/parameter_substitution/formatters/date_time_iso8601.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_iso8601_zulu.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_strftime.rb +19 -0
- data/lib/parameter_substitution/formatters/date_time_unix_timestamp.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_us_all_slashes.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_us_normal.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_us_seconds.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_us_short_am_pm.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_us_short_year.rb +11 -0
- data/lib/parameter_substitution/formatters/date_time_utc_year_first_dashes_seconds.rb +11 -0
- data/lib/parameter_substitution/formatters/date_us_dashes.rb +11 -0
- data/lib/parameter_substitution/formatters/date_us_normal.rb +11 -0
- data/lib/parameter_substitution/formatters/date_year_first_dashes.rb +11 -0
- data/lib/parameter_substitution/formatters/downcase.rb +11 -0
- data/lib/parameter_substitution/formatters/duration_as_seconds.rb +11 -0
- data/lib/parameter_substitution/formatters/duration_as_time.rb +11 -0
- data/lib/parameter_substitution/formatters/duration_grouped_by_description.rb +23 -0
- data/lib/parameter_substitution/formatters/greater_than_value.rb +21 -0
- data/lib/parameter_substitution/formatters/if_nil.rb +19 -0
- data/lib/parameter_substitution/formatters/if_truthy.rb +36 -0
- data/lib/parameter_substitution/formatters/in_timezone.rb +23 -0
- data/lib/parameter_substitution/formatters/json_parse.rb +26 -0
- data/lib/parameter_substitution/formatters/left.rb +19 -0
- data/lib/parameter_substitution/formatters/lookup.rb +19 -0
- data/lib/parameter_substitution/formatters/lower.rb +11 -0
- data/lib/parameter_substitution/formatters/manager.rb +35 -0
- data/lib/parameter_substitution/formatters/md5.rb +11 -0
- data/lib/parameter_substitution/formatters/mid.rb +20 -0
- data/lib/parameter_substitution/formatters/parse_time.rb +23 -0
- data/lib/parameter_substitution/formatters/right.rb +23 -0
- data/lib/parameter_substitution/formatters/sha256.rb +11 -0
- data/lib/parameter_substitution/formatters/split_after_colon.rb +11 -0
- data/lib/parameter_substitution/formatters/split_and_find.rb +20 -0
- data/lib/parameter_substitution/formatters/split_before_colon.rb +11 -0
- data/lib/parameter_substitution/formatters/time_with_seconds.rb +11 -0
- data/lib/parameter_substitution/formatters/trim.rb +11 -0
- data/lib/parameter_substitution/formatters/upper.rb +11 -0
- data/lib/parameter_substitution/method_call_expression.rb +58 -0
- data/lib/parameter_substitution/parse_error.rb +6 -0
- data/lib/parameter_substitution/parser.rb +109 -0
- data/lib/parameter_substitution/substitution_expression.rb +79 -0
- data/lib/parameter_substitution/text_expression.rb +52 -0
- data/lib/parameter_substitution/transform.rb +99 -0
- data/lib/parameter_substitution/version.rb +5 -0
- metadata +117 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 01e0aa08d4f8130fd493c54e5958aed36159746476fa168180407401584aaaf6
|
4
|
+
data.tar.gz: 4bc6bd8c7b94edc3690c474f8b7ece56d6e9aadf1d825e14bcc7413a2b1fe7a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8c2436cdce9ccaf16e02fe368aaa22337d43af01cdd1c35677a7fccf8af19db089c7ebfffbf859b52b3188b7323b884d8ed7cc57f63935afc7fb2d3f1075829
|
7
|
+
data.tar.gz: 1b1b2548aaa1a4e6ed4940c4589e65f3247d76ca9aa07089d0c7cde701acecb1704b12018b57b1661ce89d4b47416c337c9ee3aaab3a7b7c208b8f88263349ae
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# See lib/parameter_substitution/readme.md
|
4
4
|
|
5
|
+
require 'active_support/all'
|
5
6
|
require "parameter_substitution/context"
|
6
7
|
require "parameter_substitution/parse_error"
|
7
8
|
require "parameter_substitution/parser"
|
@@ -18,8 +19,6 @@ Dir[File.dirname(__FILE__) + '/parameter_substitution/formatters/*.rb'].each do
|
|
18
19
|
require file
|
19
20
|
end
|
20
21
|
|
21
|
-
require 'active_support/all'
|
22
|
-
|
23
22
|
class ParameterSubstitution
|
24
23
|
class SubstitutionError < StandardError; end
|
25
24
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution
|
4
|
+
class Configuration
|
5
|
+
attr_reader :custom_formatters
|
6
|
+
|
7
|
+
def custom_formatters=(custom_formatters)
|
8
|
+
check_that_classes_are_classes(custom_formatters)
|
9
|
+
check_for_correct_base_class(custom_formatters)
|
10
|
+
|
11
|
+
@custom_formatters = custom_formatters
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def check_that_classes_are_classes(custom_formatters)
|
17
|
+
bad_formatters = custom_formatters.reject do |_formatter, klass|
|
18
|
+
klass.is_a?(Class)
|
19
|
+
end
|
20
|
+
|
21
|
+
raise_if_any(bad_formatters, "must be of type Class")
|
22
|
+
end
|
23
|
+
|
24
|
+
def check_for_correct_base_class(custom_formatters)
|
25
|
+
bad_formatters = custom_formatters.reject do |_formatter, klass|
|
26
|
+
klass.ancestors.include?(ParameterSubstitution::Formatters::Base)
|
27
|
+
end
|
28
|
+
|
29
|
+
raise_if_any(bad_formatters, "must inherit from ParameterSubstitution::Formatters::Base and did not")
|
30
|
+
end
|
31
|
+
|
32
|
+
def raise_if_any(formatters, failure_context)
|
33
|
+
if formatters.any?
|
34
|
+
log_context = formatters.map { |formatter, klass| [formatter, klass].join(": ") }.join(", ")
|
35
|
+
raise StandardError, "CONFIGURATION ERROR: custom_formatters (#{log_context}) #{failure_context}."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'invoca/utils/enumerable'
|
4
|
+
|
5
|
+
class ParameterSubstitution
|
6
|
+
class Context
|
7
|
+
attr_reader :input, :mapping, :required_parameters, :parameter_start, :parameter_end,
|
8
|
+
:destination_encoding, :allow_unknown_replacement_parameters, :allow_nil,
|
9
|
+
:allow_unmatched_parameter_end
|
10
|
+
|
11
|
+
def initialize(
|
12
|
+
input:,
|
13
|
+
mapping:,
|
14
|
+
required_parameters: [],
|
15
|
+
parameter_start: "<",
|
16
|
+
parameter_end: ">",
|
17
|
+
destination_encoding: :text,
|
18
|
+
allow_unknown_replacement_parameters: false,
|
19
|
+
allow_nil: false,
|
20
|
+
allow_unmatched_parameter_end: false
|
21
|
+
)
|
22
|
+
|
23
|
+
@input = input
|
24
|
+
@mapping = mapping
|
25
|
+
@required_parameters = required_parameters
|
26
|
+
@parameter_start = parameter_start
|
27
|
+
@parameter_end = parameter_end
|
28
|
+
@destination_encoding = destination_encoding
|
29
|
+
@allow_unknown_replacement_parameters = allow_unknown_replacement_parameters
|
30
|
+
@allow_nil = allow_nil
|
31
|
+
@allow_unmatched_parameter_end = allow_unmatched_parameter_end
|
32
|
+
end
|
33
|
+
|
34
|
+
def allow_unknown_params?(inside_quotes)
|
35
|
+
allow_unknown_replacement_parameters || (destination_encoding == :json && inside_quotes)
|
36
|
+
end
|
37
|
+
|
38
|
+
def mapping_has_key?(key)
|
39
|
+
downcased_mapping.key?(key.downcase)
|
40
|
+
end
|
41
|
+
|
42
|
+
def mapped_value(key)
|
43
|
+
downcased_mapping[key.downcase]
|
44
|
+
end
|
45
|
+
|
46
|
+
def mapping_keys
|
47
|
+
mapping.keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def formatted_arg_list(arg_list)
|
51
|
+
arg_list.sort.map { |arg| "'#{arg}'" }.join(", ")
|
52
|
+
end
|
53
|
+
|
54
|
+
def duplicate_raw
|
55
|
+
ParameterSubstitution::Context.new(
|
56
|
+
input: @input,
|
57
|
+
mapping: @mapping,
|
58
|
+
required_parameters: @required_parameters,
|
59
|
+
parameter_start: @parameter_start,
|
60
|
+
parameter_end: @parameter_end,
|
61
|
+
destination_encoding: :raw,
|
62
|
+
allow_unknown_replacement_parameters: @allow_unknown_replacement_parameters,
|
63
|
+
allow_nil: @allow_nil,
|
64
|
+
allow_unmatched_parameter_end: @allow_unmatched_parameter_end
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def downcased_mapping
|
71
|
+
@downcase_mapping ||= mapping.build_hash { |k, v| [k.downcase, v] }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'builder'
|
6
|
+
|
7
|
+
class ParameterSubstitution
|
8
|
+
class Encoder
|
9
|
+
ENCODINGS = {
|
10
|
+
cgi: "CGI encode - for html parameters",
|
11
|
+
html: "HTML encode - for html content",
|
12
|
+
xml: "XML encode",
|
13
|
+
angle_brackets: "Content converted to a string and placed in angle brackets",
|
14
|
+
json: "JSON encode",
|
15
|
+
raw: "content is not changed",
|
16
|
+
text: "content is converted to a string"
|
17
|
+
}.keys
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def encode(value, destination_encoding, source_encoding, parameter_name, inside_quotes)
|
21
|
+
destination_encoding.in?(ENCODINGS) or raise "unknown encoding #{destination_encoding}"
|
22
|
+
source_encoding.in?(ENCODINGS) or raise "unknown encoding #{source_encoding}"
|
23
|
+
|
24
|
+
result =
|
25
|
+
if source_encoding == destination_encoding
|
26
|
+
if destination_encoding == :json && inside_quotes
|
27
|
+
value.to_json
|
28
|
+
else
|
29
|
+
value
|
30
|
+
end
|
31
|
+
else
|
32
|
+
case destination_encoding
|
33
|
+
when :cgi
|
34
|
+
CGI.escape(value.to_s)
|
35
|
+
when :html
|
36
|
+
CGI.escapeHTML(value.to_s)
|
37
|
+
when :xml
|
38
|
+
value_for_xml(parameter_name, value)
|
39
|
+
when :angle_brackets
|
40
|
+
"<#{value}>"
|
41
|
+
when :json
|
42
|
+
value.to_json
|
43
|
+
when :raw
|
44
|
+
value
|
45
|
+
when :text
|
46
|
+
value.to_s
|
47
|
+
else
|
48
|
+
raise "unexepected escaping #{destination_encoding}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if destination_encoding == :json && inside_quotes
|
53
|
+
if value.is_a?(String)
|
54
|
+
result[1...-1]
|
55
|
+
else
|
56
|
+
result.to_json[1...-1]
|
57
|
+
end
|
58
|
+
else
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def value_for_xml(key, value)
|
66
|
+
if value.is_a?(Array)
|
67
|
+
array_to_xml(key, value)
|
68
|
+
else
|
69
|
+
CGI.escapeHTML(value.to_s)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def array_to_xml(key, value)
|
74
|
+
tag_name = key_to_tag_name(key)
|
75
|
+
buffer = +""
|
76
|
+
builder = Builder::XmlMarkup.new(target: buffer)
|
77
|
+
value.each { |tag_value| builder.tag!(tag_name, tag_value) }
|
78
|
+
buffer
|
79
|
+
end
|
80
|
+
|
81
|
+
def key_to_tag_name(key)
|
82
|
+
key.gsub('ids_', 'id_').singularize
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'invoca/utils'
|
4
|
+
|
5
|
+
class ParameterSubstitution
|
6
|
+
class Expression
|
7
|
+
attr_reader :expression_list, :context
|
8
|
+
|
9
|
+
def initialize(expression_list, context)
|
10
|
+
@expression_list = expression_list
|
11
|
+
@context = context
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate
|
15
|
+
validate_required_parameters
|
16
|
+
validate_unknown_parameters
|
17
|
+
validate_expressions
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate
|
21
|
+
if context.destination_encoding == :raw && @expression_list.size == 1
|
22
|
+
# When using the destination encoding of raw, the output should preserve the type from the mapping if the substitution
|
23
|
+
# is the full expression. So the input of '<id>' with the mapping { 'id' => 1 } should return 1 and not "1".
|
24
|
+
@expression_list.first.evaluate(:OutsideString, only_expression: true)
|
25
|
+
else
|
26
|
+
map_expressions_with_quote_tracking do |expression, inside_quotes|
|
27
|
+
expression.evaluate(inside_quotes).to_s
|
28
|
+
end.join("")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def warnings
|
33
|
+
unknown_parameters_message
|
34
|
+
end
|
35
|
+
|
36
|
+
def parameter_and_method_warnings
|
37
|
+
unknown_messages = unknown_parameter_messages + unknown_method_messages
|
38
|
+
unknown_messages.empty? ? nil : unknown_messages.uniq
|
39
|
+
end
|
40
|
+
|
41
|
+
def substitution_parameter_names
|
42
|
+
@expression_list.map_compact(&:parameter_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_names
|
46
|
+
@expression_list.reduce([]) do |all_method_names, expression|
|
47
|
+
all_method_names + methods_used_by_expression(expression)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def methods_used_by_expression(expression)
|
52
|
+
if (method_calls = expression.try(:method_calls))
|
53
|
+
method_calls.reduce([]) do |all_method_call_names, method_call|
|
54
|
+
all_method_call_names + [method_call.name.to_s] + method_call.arguments&.flat_map { |arg| arg.try(:method_names) }.compact # arg.try returns 'nil' when no methods are called; only method names needed
|
55
|
+
end
|
56
|
+
else
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def validate_required_parameters
|
64
|
+
matched_parameters = substitution_parameter_names.uniq
|
65
|
+
required_parameters = @context.required_parameters
|
66
|
+
if !required_parameters.empty? && (missing_fields = required_parameters.reject { |rt| rt.in?(matched_parameters) }).any?
|
67
|
+
raise ParameterSubstitution::ParseError,
|
68
|
+
"The following #{pluralize_text("field", missing_fields.size)} must be included: #{@context.formatted_arg_list(missing_fields)}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_unknown_parameters
|
73
|
+
unless @context.allow_unknown_replacement_parameters
|
74
|
+
if (message = unknown_parameters_message)
|
75
|
+
raise ParameterSubstitution::ParseError, message
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_expressions
|
81
|
+
map_expressions_with_quote_tracking do |expression, inside_quotes|
|
82
|
+
expression.validate(inside_quotes)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def unknown_method_messages
|
87
|
+
unknown_parameter_methods.map_compact do |param, methods|
|
88
|
+
method_string = pluralize_text("method", methods.size)
|
89
|
+
unless methods.empty? || unknown_parameters.include?(param)
|
90
|
+
"Unknown #{method_string} #{@context.formatted_arg_list(methods)} used on parameter '#{param}'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def unknown_parameter_messages
|
96
|
+
unknown_parameters.map do |param|
|
97
|
+
unknown_methods_for_param = unknown_parameter_methods[param]
|
98
|
+
method_string = pluralize_text("method", unknown_methods_for_param.size)
|
99
|
+
unknown_method_message = if unknown_methods_for_param.empty?
|
100
|
+
''
|
101
|
+
else
|
102
|
+
" and #{method_string} #{@context.formatted_arg_list(unknown_methods_for_param)}"
|
103
|
+
end
|
104
|
+
"Unknown param '#{param}'#{unknown_method_message}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def unknown_parameters_message
|
109
|
+
unless unknown_parameters.empty?
|
110
|
+
"Unknown replacement #{pluralize_text("parameter", unknown_parameters.size)} #{@context.formatted_arg_list(unknown_parameters)}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def map_expressions_with_quote_tracking
|
115
|
+
inside_quotes = false
|
116
|
+
@expression_list.map do |expression|
|
117
|
+
result = yield expression, inside_quotes
|
118
|
+
inside_quotes = expression.ends_inside_quotes(started_inside_quotes: inside_quotes)
|
119
|
+
result
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def unknown_parameters
|
124
|
+
@unknown_parameters ||=
|
125
|
+
map_expressions_with_quote_tracking do |expression, inside_quotes|
|
126
|
+
expression.unknown_parameters(inside_quotes)
|
127
|
+
end.compact
|
128
|
+
end
|
129
|
+
|
130
|
+
def unknown_parameter_methods
|
131
|
+
@expression_list.select(&:parameter_name).reduce({}) do |hash, expression|
|
132
|
+
hash[expression.parameter_name] ||= []
|
133
|
+
hash[expression.parameter_name].push(*missing_methods(expression))
|
134
|
+
hash
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def missing_methods(expression)
|
139
|
+
(methods_used_by_expression(expression) - ParameterSubstitution::Formatters::Manager.all_formats.map { |k, _v| k.to_s })
|
140
|
+
end
|
141
|
+
|
142
|
+
def pluralize_text(text, amount)
|
143
|
+
text + (amount > 1 ? 's' : '')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::AddPrefix < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"This takes a prefix as a constructor parameter and prepends it to the value. If the value is blank, nothing is shown."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(prefix)
|
13
|
+
@prefix = prefix
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(value)
|
17
|
+
value.presence && (@prefix + value.to_s)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution
|
4
|
+
module Formatters
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def description
|
8
|
+
raise NotImplementedError, "Derived classes must implement"
|
9
|
+
end
|
10
|
+
|
11
|
+
def format(_value)
|
12
|
+
raise NotImplementedError, "Derived classes must implement"
|
13
|
+
end
|
14
|
+
|
15
|
+
def key
|
16
|
+
name.split('::').last.gsub(/Format/, '').underscore
|
17
|
+
end
|
18
|
+
|
19
|
+
# Formats that have parameters are constructed and format is called on the instance.
|
20
|
+
# Formats without parameters have format called on the class.
|
21
|
+
def has_parameters?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def encoding
|
26
|
+
:raw
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO: Move out of the base class.
|
30
|
+
def parse_duration(duration)
|
31
|
+
if duration.is_a?(String)
|
32
|
+
if duration =~ /([\d]{1,2})\:([\d]{1,2})\:([\d]{1,2})/
|
33
|
+
(Regexp.last_match(1).to_i.hours + Regexp.last_match(2).to_i.minutes + Regexp.last_match(3).to_i.seconds).to_i
|
34
|
+
elsif duration =~ /([\d]{1,2})\:([\d]{1,2})/
|
35
|
+
(Regexp.last_match(1).to_i.minutes + Regexp.last_match(2).to_i.seconds).to_i
|
36
|
+
else
|
37
|
+
duration.to_i
|
38
|
+
end
|
39
|
+
else
|
40
|
+
duration.to_i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::BlankIfNil < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Converts nil values to empty strings: ''"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value.nil? ? "" : value
|
10
|
+
end
|
11
|
+
end
|