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.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/lib/parameter_substitution.rb +1 -2
  3. data/lib/parameter_substitution/configuration.rb +39 -0
  4. data/lib/parameter_substitution/context.rb +74 -0
  5. data/lib/parameter_substitution/encoder.rb +86 -0
  6. data/lib/parameter_substitution/expression.rb +146 -0
  7. data/lib/parameter_substitution/formatters/add_prefix.rb +19 -0
  8. data/lib/parameter_substitution/formatters/base.rb +46 -0
  9. data/lib/parameter_substitution/formatters/blank_if_nil.rb +11 -0
  10. data/lib/parameter_substitution/formatters/cgi_unescape.rb +11 -0
  11. data/lib/parameter_substitution/formatters/compare_string.rb +21 -0
  12. data/lib/parameter_substitution/formatters/date_time_custom.rb +22 -0
  13. data/lib/parameter_substitution/formatters/date_time_format.rb +53 -0
  14. data/lib/parameter_substitution/formatters/date_time_iso8601.rb +11 -0
  15. data/lib/parameter_substitution/formatters/date_time_iso8601_zulu.rb +11 -0
  16. data/lib/parameter_substitution/formatters/date_time_strftime.rb +19 -0
  17. data/lib/parameter_substitution/formatters/date_time_unix_timestamp.rb +11 -0
  18. data/lib/parameter_substitution/formatters/date_time_us_all_slashes.rb +11 -0
  19. data/lib/parameter_substitution/formatters/date_time_us_normal.rb +11 -0
  20. data/lib/parameter_substitution/formatters/date_time_us_seconds.rb +11 -0
  21. data/lib/parameter_substitution/formatters/date_time_us_short_am_pm.rb +11 -0
  22. data/lib/parameter_substitution/formatters/date_time_us_short_year.rb +11 -0
  23. data/lib/parameter_substitution/formatters/date_time_utc_year_first_dashes_seconds.rb +11 -0
  24. data/lib/parameter_substitution/formatters/date_us_dashes.rb +11 -0
  25. data/lib/parameter_substitution/formatters/date_us_normal.rb +11 -0
  26. data/lib/parameter_substitution/formatters/date_year_first_dashes.rb +11 -0
  27. data/lib/parameter_substitution/formatters/downcase.rb +11 -0
  28. data/lib/parameter_substitution/formatters/duration_as_seconds.rb +11 -0
  29. data/lib/parameter_substitution/formatters/duration_as_time.rb +11 -0
  30. data/lib/parameter_substitution/formatters/duration_grouped_by_description.rb +23 -0
  31. data/lib/parameter_substitution/formatters/greater_than_value.rb +21 -0
  32. data/lib/parameter_substitution/formatters/if_nil.rb +19 -0
  33. data/lib/parameter_substitution/formatters/if_truthy.rb +36 -0
  34. data/lib/parameter_substitution/formatters/in_timezone.rb +23 -0
  35. data/lib/parameter_substitution/formatters/json_parse.rb +26 -0
  36. data/lib/parameter_substitution/formatters/left.rb +19 -0
  37. data/lib/parameter_substitution/formatters/lookup.rb +19 -0
  38. data/lib/parameter_substitution/formatters/lower.rb +11 -0
  39. data/lib/parameter_substitution/formatters/manager.rb +35 -0
  40. data/lib/parameter_substitution/formatters/md5.rb +11 -0
  41. data/lib/parameter_substitution/formatters/mid.rb +20 -0
  42. data/lib/parameter_substitution/formatters/parse_time.rb +23 -0
  43. data/lib/parameter_substitution/formatters/right.rb +23 -0
  44. data/lib/parameter_substitution/formatters/sha256.rb +11 -0
  45. data/lib/parameter_substitution/formatters/split_after_colon.rb +11 -0
  46. data/lib/parameter_substitution/formatters/split_and_find.rb +20 -0
  47. data/lib/parameter_substitution/formatters/split_before_colon.rb +11 -0
  48. data/lib/parameter_substitution/formatters/time_with_seconds.rb +11 -0
  49. data/lib/parameter_substitution/formatters/trim.rb +11 -0
  50. data/lib/parameter_substitution/formatters/upper.rb +11 -0
  51. data/lib/parameter_substitution/method_call_expression.rb +58 -0
  52. data/lib/parameter_substitution/parse_error.rb +6 -0
  53. data/lib/parameter_substitution/parser.rb +109 -0
  54. data/lib/parameter_substitution/substitution_expression.rb +79 -0
  55. data/lib/parameter_substitution/text_expression.rb +52 -0
  56. data/lib/parameter_substitution/transform.rb +99 -0
  57. data/lib/parameter_substitution/version.rb +5 -0
  58. 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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ParameterSubstitution
4
+ class ParseError < StandardError
5
+ end
6
+ 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