parameter_substitution 0.3.0.pre.1 → 1.1.0.pre.1
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 +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
|