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
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::JsonParse < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Attempts to parse strings as JSON. If valid, passes along the parsed object, if not valid json, or not a string, passes the json encoded value."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.encoding
|
13
|
+
:json
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.format(value)
|
17
|
+
if value.is_a?(String)
|
18
|
+
JSON.parse(value)
|
19
|
+
value
|
20
|
+
else
|
21
|
+
value.to_json
|
22
|
+
end
|
23
|
+
rescue JSON::ParserError
|
24
|
+
value.to_json
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Left < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Takes a single n argument. Returns the left most n characters from the input."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(character_count)
|
13
|
+
@character_count = character_count
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(value)
|
17
|
+
value.to_s[0, @character_count]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Lookup < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"This takes a table as a constructor parameter and performs a lookup from the value. If the value exists as a key in the lookup table, the key's value is returned."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(lookup_table)
|
13
|
+
@lookup_table = lookup_table
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(value)
|
17
|
+
@lookup_table[value.to_s]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Lower < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Converts to string and returns all characters lowercased, preserves nil."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value&.to_s&.downcase
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution
|
4
|
+
module Formatters
|
5
|
+
class Manager
|
6
|
+
class << self
|
7
|
+
def find(key)
|
8
|
+
all_formats[key.to_s]
|
9
|
+
end
|
10
|
+
|
11
|
+
def all_formats
|
12
|
+
default_formats.merge(custom_formatters_if_any)
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_formats
|
16
|
+
@default_formats ||= formatter_class_hash(__FILE__, ["ParameterSubstitution", "Formatters"])
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def formatter_class_hash(manager_file, module_array)
|
22
|
+
Hash[Dir[Pathname.new(manager_file).dirname + '*.rb'].map do |filename|
|
23
|
+
class_key = File.basename(filename).chomp(".rb")
|
24
|
+
class_name = (module_array + [class_key.camelize.to_s]).join('::').constantize
|
25
|
+
[class_key, class_name]
|
26
|
+
end].except("manager")
|
27
|
+
end
|
28
|
+
|
29
|
+
def custom_formatters_if_any
|
30
|
+
ParameterSubstitution.config&.custom_formatters || {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Md5 < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Generates an md5 hash of the value."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
Digest::MD5.hexdigest(value.to_s)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Mid < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Takes starting_position and character_count as arguments. Returns the character_count characters starting from starting_position from the input."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(starting_position, character_count)
|
13
|
+
@starting_position = starting_position
|
14
|
+
@character_count = character_count
|
15
|
+
end
|
16
|
+
|
17
|
+
def format(value)
|
18
|
+
value.to_s[@starting_position, @character_count]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::ParseTime < ParameterSubstitution::Formatters::DateTimeFormat
|
4
|
+
def self.description
|
5
|
+
"Takes format_string as a parameter and uses format_string to parse the input as a time. Does not change the input if the value is not a time."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(format_string)
|
13
|
+
@format_string = format_string
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(value)
|
17
|
+
value && Time.strptime(value.to_s, @format_string).strftime('%Y-%m-%d %H:%M:%S')
|
18
|
+
rescue ArgumentError => ex
|
19
|
+
# strptime raises argument error if either argument is wrong.
|
20
|
+
ex.message =~ /invalid .*strptime format/ or raise
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Right < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Takes a single n argument. Returns the right most n characters from the input."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(character_count)
|
13
|
+
@character_count = character_count
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(value)
|
17
|
+
if (as_string = value.to_s).size > @character_count
|
18
|
+
as_string[-@character_count, @character_count]
|
19
|
+
else
|
20
|
+
as_string
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Sha256 < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Generates a sha256 hash of the value."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
Digest::SHA256.hexdigest(value.to_s)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::SplitAfterColon < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Returns the portion of a string after the first colon, or nil if there is no colon."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value && value.to_s.split(':', 2)[1]&.lstrip
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::SplitAndFind < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Takes delimiter and index as arguments. Splits the input on delimiters and returns the indexed element. Preserves nil."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.has_parameters?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(delimiter, index)
|
13
|
+
@delimiter = delimiter
|
14
|
+
@index = index
|
15
|
+
end
|
16
|
+
|
17
|
+
def format(value)
|
18
|
+
value && value.to_s.split(@delimiter)[@index]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::SplitBeforeColon < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Returns the portion of a string before the first colon, or the full string if there is no colon."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value && value.to_s.split(':')[0]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::TimeWithSeconds < ParameterSubstitution::Formatters::DateTimeFormat
|
4
|
+
def self.description
|
5
|
+
"hh:mm:ss"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
parse_to_time(value)&.strftime("%H:%M:%S").to_s
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Trim < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Returns the input as a string with leading and trailing whitespace removed."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value&.to_s&.strip
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution::Formatters::Upper < ParameterSubstitution::Formatters::Base
|
4
|
+
def self.description
|
5
|
+
"Converts to string and returns all characters uppercased, preserves nil."
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.format(value)
|
9
|
+
value&.to_s&.upcase
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ParameterSubstitution
|
4
|
+
class MethodCallExpression
|
5
|
+
attr_reader :name, :arguments
|
6
|
+
|
7
|
+
def initialize(name, arguments)
|
8
|
+
@name = name
|
9
|
+
@arguments = arguments || []
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate
|
13
|
+
if format_class
|
14
|
+
expected_arguments = format_class&.has_parameters? ? format_class.instance_method(:initialize).arity : 0
|
15
|
+
if @arguments.size != expected_arguments
|
16
|
+
raise ParameterSubstitution::ParseError, "Wrong number of arguments for '#{@name}' expected #{expected_arguments}, received #{@arguments.size}"
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ParameterSubstitution::ParseError, "Unknown method '#{@name}'"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def call_method(value)
|
24
|
+
column_formatter.format(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def encoding
|
28
|
+
format_class.encoding
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def column_formatter
|
34
|
+
@column_formatter ||= begin
|
35
|
+
if format_class&.has_parameters?
|
36
|
+
format_class.new(*format_args)
|
37
|
+
else
|
38
|
+
format_class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_class
|
44
|
+
@format_class ||= ParameterSubstitution::Formatters::Manager.find(@name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def format_args
|
48
|
+
@arguments.map do |arg|
|
49
|
+
case arg
|
50
|
+
when ParameterSubstitution::Expression
|
51
|
+
arg.evaluate
|
52
|
+
else
|
53
|
+
arg
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
#
|
5
|
+
# The parser describes the rules of the grammar, and when executed on a string it will
|
6
|
+
# either fail with an exception or it will return a structure describing what it found.
|
7
|
+
#
|
8
|
+
# See the parslet documentation for more detail.
|
9
|
+
#
|
10
|
+
|
11
|
+
class ParameterSubstitution
|
12
|
+
class Parser < Parslet::Parser
|
13
|
+
def initialize(parameter_start: "<", parameter_end: ">", allow_unmatched_parameter_end: false)
|
14
|
+
@parameter_start = parameter_start
|
15
|
+
@parameter_end = parameter_end
|
16
|
+
@allow_unmatched_parameter_end = allow_unmatched_parameter_end
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
root :text_with_substitution_parameters
|
21
|
+
|
22
|
+
rule(:double_quote) { str("\"") }
|
23
|
+
rule(:single_quote) { str("\'") }
|
24
|
+
rule(:escape) { str("\\") }
|
25
|
+
rule(:dot) { str(".") }
|
26
|
+
rule(:open_param) { str(@parameter_start) }
|
27
|
+
rule(:close_param) { str(@parameter_end) }
|
28
|
+
rule(:digit) { match["0-9"] }
|
29
|
+
rule(:parameter_start) { str("(") }
|
30
|
+
rule(:parameter_end) { str(")") }
|
31
|
+
rule(:space) { str(" ") }
|
32
|
+
rule(:space?) { space.repeat }
|
33
|
+
rule(:comma) { space? >> str(",") >> space? }
|
34
|
+
|
35
|
+
rule :double_quoted_string_arg do
|
36
|
+
double_quote >> (escape >> any | double_quote.absent? >> any).repeat.as(:string_arg) >> double_quote
|
37
|
+
end
|
38
|
+
|
39
|
+
rule :single_quoted_string_arg do
|
40
|
+
single_quote >> (escape >> any | single_quote.absent? >> any).repeat.as(:string_arg) >> single_quote
|
41
|
+
end
|
42
|
+
|
43
|
+
rule :string_arg do
|
44
|
+
double_quoted_string_arg | single_quoted_string_arg
|
45
|
+
end
|
46
|
+
|
47
|
+
rule :float_arg do
|
48
|
+
(digit.repeat(1) >> dot >> digit.repeat(1)).as(:float_arg)
|
49
|
+
end
|
50
|
+
|
51
|
+
rule :int_arg do
|
52
|
+
digit.repeat(1).as(:int_arg)
|
53
|
+
end
|
54
|
+
|
55
|
+
rule :nil_arg do
|
56
|
+
str('nil').as(:nil_arg)
|
57
|
+
end
|
58
|
+
|
59
|
+
rule :substitution_parameter_arg do
|
60
|
+
open_param >> parameter.repeat.as(:raw_expression) >> close_param
|
61
|
+
end
|
62
|
+
|
63
|
+
rule :arg do
|
64
|
+
float_arg | string_arg | int_arg | nil_arg | substitution_parameter_arg
|
65
|
+
end
|
66
|
+
|
67
|
+
rule :argument_list do
|
68
|
+
parameter_start >> space? >> (arg >> (comma >> arg).repeat).repeat(0, 1).as(:arg_list) >> space? >> parameter_end
|
69
|
+
end
|
70
|
+
|
71
|
+
rule :method_name do
|
72
|
+
(parameter_start.absent? >> close_param.absent? >> dot.absent? >> any).repeat(1).as(:method_call)
|
73
|
+
end
|
74
|
+
|
75
|
+
rule :method_call do
|
76
|
+
method_name >> argument_list.maybe
|
77
|
+
end
|
78
|
+
|
79
|
+
rule :method_call_list do
|
80
|
+
(dot >> method_call).repeat(0)
|
81
|
+
end
|
82
|
+
|
83
|
+
rule :parameter_name do
|
84
|
+
((escape >> any) | (open_param.absent? >> close_param.absent? >> dot.absent? >> any)).repeat(1)
|
85
|
+
end
|
86
|
+
|
87
|
+
rule :parameter do
|
88
|
+
parameter_name.as(:parameter_name) >> method_call_list.as(:method_calls)
|
89
|
+
end
|
90
|
+
|
91
|
+
rule :substitution_parameter_with_brackets do
|
92
|
+
open_param >> parameter >> close_param
|
93
|
+
end
|
94
|
+
|
95
|
+
rule :text do
|
96
|
+
text_char =
|
97
|
+
if @allow_unmatched_parameter_end
|
98
|
+
(open_param.absent? >> any)
|
99
|
+
else
|
100
|
+
(open_param.absent? >> close_param.absent? >> any)
|
101
|
+
end
|
102
|
+
text_char.repeat(1).as(:text)
|
103
|
+
end
|
104
|
+
|
105
|
+
rule :text_with_substitution_parameters do
|
106
|
+
(text | substitution_parameter_with_brackets).repeat.as(:expression)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|