mp-utils 0.2.2 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eaca69fbff0b08c6113af4c650ce37b158779bd260457fcd6e8b125dd4b4fee5
4
- data.tar.gz: db821389f518caaaf4e4c130f92e8bf01e4ca2482f94683727f515363b332970
3
+ metadata.gz: 2f99dcd4e1535b4b1777fd10c0659634b4687ac24c47ba7a0c2c967f77beb9a3
4
+ data.tar.gz: c2af79547fa099dba14ffdadb49bcf5892a30fab76f02f98aff3aa048a256336
5
5
  SHA512:
6
- metadata.gz: ac347a457eb76b4cec8fd440e69fa37503e463f23c30570b5cb6aae8647b52f6327dd4b2c48734ce6132d0d4ebf322582100aac39f6ed79bab495f85b059fc76
7
- data.tar.gz: 5137278119e0ce6b5c6f54a838526eedee624eec8bb4b45f9e0bcfa4f74546f0a460863af489b880edee14231947e89d606426870944a696f090ec0e4062e4b8
6
+ metadata.gz: 0c35d7a60dcf86f0c908c2b9f2380ae4bb41ff5e73d1e42315f919646c77f1471b43ecf679aaece84182922cef639c21f75ce88320f60a94a70ce126b2ddf540
7
+ data.tar.gz: ff1a0de07a11334ac444eaf9328c482d45b8b7271dd13e9df622fc4528753cf5c0d1a0e556eaab50ee458e24118d3c6ae1a3bb8bf76ab97318c4effb9a4287f4
data/lib/mp_utils.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'utils/key'
4
- require 'utils/array'
5
- require 'utils/message'
6
- require 'utils/question'
7
- require 'resources/path_helper'
3
+ require_relative 'utils/key'
4
+ require_relative 'utils/ansi'
5
+ require_relative 'utils/array'
6
+ require_relative 'utils/message'
7
+ require_relative 'utils/question'
8
+ require_relative 'utils/version_manager'
9
+ require_relative 'utils/ansi_style_manager'
10
+ require_relative 'resources/path_helper'
data/lib/utils/ansi.rb ADDED
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+
5
+ # The ANSI class is responsible for generating ANSI codes for text styling in terminals.
6
+ # It allows the combination of multiple style and color codes.
7
+ #
8
+ # @example
9
+ # ansi = ANSI.new(:bold)
10
+ # puts ansi.to_s # => "\e[1m"
11
+ #
12
+ # @example
13
+ # ansi = ANSI.new([:bold, :red])
14
+ # puts ansi.to_s # => "\e[1;31m"
15
+ class ANSI
16
+ # Hash that maps style and color names to their respective ANSI codes.
17
+ CODES_HASH = {
18
+ reset_all: 0,
19
+
20
+ # Effects
21
+ bold: 1,
22
+ faint: 2,
23
+ italic: 3,
24
+ underlined: 4,
25
+ blinking: 5,
26
+ inverse: 7,
27
+ hidden: 8,
28
+ strike: 9,
29
+ plain: 21,
30
+
31
+ # Remove Effects
32
+ remove_bold: 22,
33
+ remove_faint: 22,
34
+ remove_italic: 23,
35
+ remove_underlined: 24,
36
+ remove_blinking: 25,
37
+ remove_inverse: 27,
38
+ remove_hidden: 28,
39
+ remove_strike: 29,
40
+ remove_plain: 24,
41
+
42
+ # Colors
43
+ black: 30,
44
+ red: 31,
45
+ green: 32,
46
+ yellow: 33,
47
+ blue: 34,
48
+ magenta: 35,
49
+ cyan: 36,
50
+ white: 37,
51
+ reset_color: 39,
52
+ rgb_format: [38, 2],
53
+ numeric_format: [38, 5],
54
+
55
+ # Background Colors
56
+ background_black: 40,
57
+ background_red: 41,
58
+ background_green: 42,
59
+ background_yellow: 43,
60
+ background_blue: 44,
61
+ background_magenta: 45,
62
+ background_cyan: 46,
63
+ background_white: 47,
64
+ background_rgb_format: [48, 2],
65
+ background_numeric_format: [48, 5],
66
+ reset_background: 49
67
+ }.freeze
68
+
69
+ # @return [Array<Integer>] the ANSI codes stored in the instance.
70
+ attr_reader :codes
71
+
72
+ # Initializes a new instance of the ANSI class.
73
+ #
74
+ # @param code [Array<Symbol>, Symbol, String] One or more ANSI codes represented as symbols, integers, or strings.
75
+ def initialize(code)
76
+ @codes = if code.is_a?(Array)
77
+ ANSI.normalize_array_codes(code)
78
+ elsif code.is_a?(Symbol)
79
+ [CODES_HASH[code]]
80
+ else
81
+ ANSI.normalize_array_codes(code.split(';'))
82
+ end
83
+ end
84
+
85
+ # Adds ANSI codes from another instance of the ANSI class.
86
+ #
87
+ # @param ansi [ANSI] The instance of the ANSI class whose codes will be added.
88
+ # @return [ANSI] A new instance of the ANSI class with the combined codes.
89
+ # @raise [RuntimeError] If the argument is not an instance of the ANSI class.
90
+ def append(ansi)
91
+ raise 'Needs be an instance of ANSI' unless ansi.is_a?(ANSI)
92
+
93
+ ANSI.new(@codes.union(ansi.codes))
94
+ end
95
+
96
+ # Adds ANSI codes from another instance of the ANSI class and updates the current instance.
97
+ #
98
+ # @param ansi [ANSI] The instance of the ANSI class whose codes will be added.
99
+ # @return [void]
100
+ def append!(ansi)
101
+ @codes = append(ansi).codes
102
+ end
103
+
104
+ # Converts the ANSI codes stored in the instance to a formatted string.
105
+ #
106
+ # @return [String] The formatted string with the ANSI codes.
107
+ def to_s
108
+ "\e[#{@codes.flatten.join(';')}m"
109
+ end
110
+
111
+ # Normalizes an array of codes, converting symbols and strings to their respective ANSI codes.
112
+ #
113
+ # @param array [Array<Symbol, String, Integer>] An array of codes to be normalized.
114
+ # @return [Array<Integer>] The normalized array of ANSI codes.
115
+ def self.normalize_array_codes(array)
116
+ array.map do |value|
117
+ next normalize_array_codes(value) if value.is_a?(Array)
118
+ next value if value.is_a?(Integer)
119
+ next CODES_HASH[value] if value.is_a?(Symbol)
120
+ next value.to_i if value.match?(CONSTANTS::ONLY_DIGITS_REGEX)
121
+
122
+ CODES_HASH[value.to_sym]
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'ansi'
5
+
6
+ # The ANSIStyleManager class is responsible for managing and applying ANSI styles to a given string.
7
+ # It can replace tokens in the string with corresponding ANSI codes for colors and effects.
8
+ #
9
+ # @example
10
+ # manager = ANSIStyleManager.new("Hello <color:red>World</color>")
11
+ # puts manager.to_s # => "Hello \e[31mWorld\e[39m"
12
+ class ANSIStyleManager
13
+ # @return [String] the string to which ANSI styles will be applied.
14
+ attr_reader :string
15
+
16
+ # Initializes a new instance of the ANSIStyleManager class.
17
+ #
18
+ # @param string [String] The string to which ANSI styles will be applied.
19
+ # @raise [RuntimeError] If the argument is not a string.
20
+ def initialize(string)
21
+ raise 'Need initialize with a string' unless string.is_a?(String)
22
+
23
+ @string = String.new(string)
24
+ end
25
+
26
+ # Replaces all color and effect tokens in the string with corresponding ANSI codes.
27
+ #
28
+ # @return [void]
29
+ def replace_all_tokens!
30
+ replace_color_tokens!
31
+ replace_effect_tokens!
32
+ end
33
+
34
+ # Replaces all color tokens in the string with corresponding ANSI codes.
35
+ #
36
+ # @return [void]
37
+ def replace_color_tokens!
38
+ scan_while_find(CONSTANTS::ANSI::COLORS) do |value|
39
+ colors = value.first.split(':')
40
+
41
+ replace_tokens_with!(
42
+ ansi_token: color_prefix(color: colors[0], background: colors[1]),
43
+ ansi_reset_token: ANSI.new(%i[reset_background reset_color]),
44
+ to_replace: "<color:#{value.first}>#{value.last}</color>",
45
+ preserve_reset_token: false,
46
+ text: value.last
47
+ )
48
+ end
49
+ end
50
+
51
+ # Replaces all effect tokens in the string with corresponding ANSI codes.
52
+ #
53
+ # @return [void]
54
+ def replace_effect_tokens!
55
+ scan_while_find(CONSTANTS::ANSI::EFFECTS) do |value|
56
+ replace_tokens_with!(
57
+ ansi_reset_token: ANSI.new("remove_#{value.first.downcase}"),
58
+ to_replace: "<effect:#{value.first}>#{value.last}</effect>",
59
+ ansi_token: ANSI.new(value.first.downcase),
60
+ preserve_reset_token: true,
61
+ text: value.last
62
+ )
63
+ end
64
+ end
65
+
66
+ # Converts the string with all tokens replaced by corresponding ANSI codes.
67
+ #
68
+ # @return [String] The string with ANSI codes applied.
69
+ def to_s
70
+ replace_all_tokens!
71
+ @string
72
+ end
73
+
74
+ private
75
+
76
+ # Generates the ANSI prefix for a given color and background.
77
+ #
78
+ # @param color [String] The color name or code.
79
+ # @param background [String, nil] The background color name or code.
80
+ # @return [ANSI] The ANSI instance representing the color and background.
81
+ def color_prefix(color:, background:)
82
+ color_ansi = generate_color_ansi(color, is_background: false)
83
+ return color_ansi.to_s if background.nil?
84
+
85
+ generate_color_ansi(background, is_background: true).append(color_ansi)
86
+ end
87
+
88
+ # Generates the ANSI instance for a given color.
89
+ #
90
+ # @param color [String] The color name or code.
91
+ # @param is_background [Boolean] Whether the color is for the background.
92
+ # @return [ANSI] The ANSI instance representing the color.
93
+ def generate_color_ansi(color, is_background:)
94
+ digit_values = color.scan(CONSTANTS::ANSI::COLOR_DIGITS_REGEX).flatten.compact
95
+
96
+ if [1, 3].include?(digit_values.count)
97
+ format = "#{digit_values.count == 1 ? 'numeric' : 'rgb'}_format"
98
+ format = "#{is_background ? 'background_' : ''}#{format}"
99
+
100
+ return ANSI.new([format, digit_values])
101
+ elsif color.match?(/^reset$/)
102
+ return ANSI.new(is_background ? :reset_background : :reset_color)
103
+ end
104
+
105
+ ANSI.new(is_background ? "background_#{color}" : color)
106
+ end
107
+
108
+ # Replaces tokens in the string with corresponding ANSI codes.
109
+ #
110
+ # @param text [String] The text to be styled.
111
+ # @param to_replace [String] The token to be replaced.
112
+ # @param ansi_token [ANSI] The ANSI instance representing the style.
113
+ # @param ansi_reset_token [ANSI] The ANSI instance representing the reset style.
114
+ # @param preserve_reset_token [Boolean] Whether to preserve the reset token in the text.
115
+ # @return [void]
116
+ def replace_tokens_with!(text:, to_replace:, ansi_token:, ansi_reset_token:, preserve_reset_token:)
117
+ colored_text = text.gsub(ansi_reset_token.to_s, "#{ansi_reset_token}#{ansi_token}") if preserve_reset_token
118
+ colored_text = text.gsub(ansi_reset_token.to_s, ansi_token.to_s) unless preserve_reset_token
119
+ @string.gsub!(to_replace, "#{ansi_token}#{colored_text}#{ansi_reset_token}")
120
+ end
121
+
122
+ # Scans the string for tokens matching the given regex and processes them with the provided block.
123
+ #
124
+ # @param scan_regex [Regexp] The regex to scan for tokens.
125
+ # @yieldparam value [Array<String>] The matched tokens.
126
+ # @return [void]
127
+ def scan_while_find(scan_regex, &block)
128
+ result = @string.scan(scan_regex)
129
+ while result.count.positive?
130
+ result.each do |value|
131
+ block.call(value)
132
+ end
133
+
134
+ result = @string.scan(scan_regex)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @!visibility private
4
+ module CONSTANTS
5
+ ONLY_DIGITS_REGEX = /^\d+$/.freeze
6
+
7
+ # @!visibility private
8
+ module ANSI
9
+ COLOR_DIGITS_REGEX = /^(\d+);(\d+);(\d+)|(\d+)$/.freeze
10
+ COLOR_TOKEN_VALUE = '(?:[\w|\d]+|\d+\;\d+\;\d+)(?::[\w|\d]+|:\d+;\d+;\d+)?'
11
+ COLORS = %r{<color:(#{COLOR_TOKEN_VALUE})>((?:(?!<color:#{COLOR_TOKEN_VALUE}>|</color>).)*)</color>}m.freeze
12
+ EFFECTS = %r{<effect:(\w+)>((?:(?!<effect:\w+>|</effect>).)*)</effect>}m.freeze
13
+ end
14
+ end
data/lib/utils/key.rb CHANGED
@@ -56,8 +56,8 @@ class Key
56
56
  def self.find_keys_in(value)
57
57
  ep = Regexp.escape(prefix)
58
58
  es = Regexp.escape(suffix)
59
- value.to_s.scan(/#{ep}[^#{ep}#{es}]+#{es}/).map do |key|
60
- Key.new(key.gsub(/(#{ep})|(#{es})/, ''))
59
+ value.to_s.scan(/#{ep}([^#{ep}#{es}]+)#{es}/).map do |key|
60
+ Key.new(key.first)
61
61
  end
62
62
  end
63
63
 
data/lib/utils/message.rb CHANGED
@@ -123,11 +123,18 @@ class Message
123
123
  end
124
124
 
125
125
  # Reads and returns the content of a message file located at a specific path.
126
+ # If the file name have the suffix ".aas.txt", the ANSIStyleManager will be applied to the file.
126
127
  #
127
128
  # @param path [String] The path where the message file is located.
128
129
  # @param file_name [String] The name of the file to be read.
129
130
  # @return [String, nil] The content of the message file, or nil if the file does not exist.
130
131
  def recover_content_with(path, file_name)
132
+ aas_file_path = File.join(path, "#{file_name}.aas.txt")
133
+ if File.exist?(aas_file_path)
134
+ content = File.read(aas_file_path)
135
+ return ANSIStyleManager.new(content).to_s
136
+ end
137
+
131
138
  file_path = File.join(path, "#{file_name}.txt")
132
139
  return nil unless File.exist?(file_path)
133
140
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Manages software versions with Major, Minor, and Patch components.
4
+ class VersionManager
5
+ attr_reader :major, :minor, :patch
6
+
7
+ # Initializes a new instance of VersionManager.
8
+ #
9
+ # @param major [Integer, String] The major version or a string in the format "major.minor.patch".
10
+ # @param minor [Integer] The minor version (optional if major is a string).
11
+ # @param patch [Integer] The patch version (optional if major is a string).
12
+ def initialize(major = 0, minor = 0, patch = 0)
13
+ if major.is_a?(String)
14
+ parse_version_string(major)
15
+ else
16
+ @major = major
17
+ @minor = minor
18
+ @patch = patch
19
+ end
20
+ end
21
+
22
+ # Increments the major version and resets minor and patch to 0.
23
+ def increment_major
24
+ @major += 1
25
+ @minor = 0
26
+ @patch = 0
27
+ end
28
+
29
+ # Increments the minor version and resets patch to 0.
30
+ def increment_minor
31
+ @minor += 1
32
+ @patch = 0
33
+ end
34
+
35
+ # Increments the patch version.
36
+ def increment_patch
37
+ @patch += 1
38
+ end
39
+
40
+ # Returns the version as a string in the format "major.minor.patch".
41
+ #
42
+ # @return [String] The formatted version.
43
+ def to_s
44
+ "#{@major}.#{@minor}.#{@patch}"
45
+ end
46
+
47
+ private
48
+
49
+ # Parses a version string in the format "major.minor.patch".
50
+ #
51
+ # @param version_string [String] The version string.
52
+ def parse_version_string(version_string)
53
+ parts = version_string.split('.')
54
+ @major = parts[0].to_i
55
+ @minor = parts[1].to_i
56
+ @patch = parts[2].to_i
57
+ end
58
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mp-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcio F Paludo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-16 00:00:00.000000000 Z
11
+ date: 2024-06-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Helpers to facilitate scripts Writing.
@@ -28,10 +28,14 @@ files:
28
28
  - lib/resources/messages/question/error/integer.txt
29
29
  - lib/resources/messages/question/error/regex.txt
30
30
  - lib/resources/path_helper.rb
31
+ - lib/utils/ansi.rb
32
+ - lib/utils/ansi_style_manager.rb
31
33
  - lib/utils/array.rb
34
+ - lib/utils/constants.rb
32
35
  - lib/utils/key.rb
33
36
  - lib/utils/message.rb
34
37
  - lib/utils/question.rb
38
+ - lib/utils/version_manager.rb
35
39
  homepage: https://github.com/MarcioFPaludo/ruby-mp-utils
36
40
  licenses:
37
41
  - MIT