mp-utils 0.2.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eaca69fbff0b08c6113af4c650ce37b158779bd260457fcd6e8b125dd4b4fee5
4
- data.tar.gz: db821389f518caaaf4e4c130f92e8bf01e4ca2482f94683727f515363b332970
3
+ metadata.gz: 472fb772fc669be45aee52e5f931823934e59cb5f36974536dbfea782024ac9d
4
+ data.tar.gz: 9c5079304a471c97261ec621457575b1b79c9454a8a60f7d19ee9b0a51a7c777
5
5
  SHA512:
6
- metadata.gz: ac347a457eb76b4cec8fd440e69fa37503e463f23c30570b5cb6aae8647b52f6327dd4b2c48734ce6132d0d4ebf322582100aac39f6ed79bab495f85b059fc76
7
- data.tar.gz: 5137278119e0ce6b5c6f54a838526eedee624eec8bb4b45f9e0bcfa4f74546f0a460863af489b880edee14231947e89d606426870944a696f090ec0e4062e4b8
6
+ metadata.gz: 79864c2f162bb4d136ae0762d4196c868e0ce4136a31bb873ce79289478734f442acfc18e882d942b53b15e26c570a8b4d18c144f88d1ceae4573c01121583d8
7
+ data.tar.gz: cd1ed6a8fdb3c724d1953c0df5107468936b5a4800b537097ca32d3f134123c93f39c45ee327a347037e6246fe23600030128fe12f7371034d11bca9fb347204
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'
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # The Resources module manages and provides access to file system paths used by a script or application.
4
+ #
4
5
  # It facilitates the definition and retrieval of a default library path, alongside a customizable path
5
- # that can be set at runtime either programmatically using the {define} method or through the environment
6
- # variable "SCRIPT_CUSTOM_RESOURCES". This flexibility allows applications to dynamically access resources
7
- # stored in various locations, depending on execution environment or user-defined settings.
6
+ # that can be set at runtime either programmatically using the {define} method or through the
7
+ # environment variable "SCRIPT_CUSTOM_RESOURCES".
8
+ #
9
+ # This flexibility allows applications to dynamically access resources stored in various locations,
10
+ # depending on execution environment or user-defined settings.
8
11
  module Resources
9
12
  # Retrieves the path to the directory containing this module, which serves as the default library path.
10
13
  #
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,207 @@
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
+ #
8
+ # It can replace tokens in the string with corresponding ANSI codes for colors, backgrounds and effects.
9
+ #
10
+ # ## Effects
11
+ # Effects can be used by adding `<effect:effect_name>` at the beginning and `</effect>` at the end of
12
+ # the sequence of characters to which you want the effect to be applied.
13
+ #
14
+ # Example:
15
+ # ```ruby
16
+ # manager = ANSIStyleManager.new('The text <effect:bold>Potato</effect> will be bold.')
17
+ # puts manager
18
+ # ```
19
+ # Output:
20
+ #
21
+ # ![bold output](./.resources/images/bold_effect_example.png)
22
+ #
23
+ # Below is the table with all available effects:
24
+ #
25
+ # | Effect Name | Description |
26
+ # |---------------|----------------------------|
27
+ # | bold | set bold mode. |
28
+ # | faint | set dim/faint mode. |
29
+ # | italic | set italic mode. |
30
+ # | underline | set underline mode. |
31
+ # | blinking | set blinking mode. |
32
+ # | inverse | set inverse/reverse mode. |
33
+ # | hidden | set hidden/invisible mode. |
34
+ # | strike | set strikethrough mode. |
35
+ # | plain | set double underline mode. |
36
+ #
37
+ # > **Note:** Some terminals may not support some of the effects listed above.
38
+ #
39
+ # ## Colors
40
+ # The ANSIStyleManager supports 3 types of coloring: Named Colors, RGB Colors, or 256 Colors.
41
+ #
42
+ # The foreground color can be changed by adding `<color:color_type>` at the beginning and `</color>`
43
+ # at the end of the character sequence you want to color.
44
+ #
45
+ # Example:
46
+ # ```ruby
47
+ # text = 'A <color:green>colorful <color:red>world <color:yellow>is <color:blue>much '
48
+ # text += '</color>more </color>beautiful</color>!</color> ;)'
49
+ # manager = ANSIStyleManager.new(text)
50
+ # puts manager
51
+ # ```
52
+ # Output:
53
+ #
54
+ # ![colored output](./.resources/images/foreground_colored_example.png)
55
+ #
56
+ # It is also possible to set the background color of a text.
57
+ #
58
+ # The background color can be changed by adding `<color:color_type:color_type>` at the
59
+ # beginning and `</color>` at the end of the character sequence you want to color.
60
+ #
61
+ # Example:
62
+ # ```ruby
63
+ # text = 'A <color:green:white>colorful <color:196>world <color:yellow:111>is <color:blue:255;255;255>much '
64
+ # text += '</color>more </color>beautiful</color>!</color> ;)'
65
+ # manager = ANSIStyleManager.new(text)
66
+ # puts manager
67
+ # ```
68
+ #
69
+ # Output:
70
+ #
71
+ # ![colored output](./.resources/images/background_colored_exemple.png)
72
+ #
73
+ # Below is the table with all available color type patterns:
74
+ #
75
+ # |Color Type |Pattern |Description |
76
+ # |-------------|--------|---------------------------------------------------------------------------------------------|
77
+ # |Reset colors |reset |Resets to the terminal's default color. |
78
+ # |256 Colors |number |Accepts numbers between 0 and 255. |
79
+ # |RGB Colors |R;G;B |R, G, and B accept values between 0 and 255. |
80
+ # |Named Colors |name |Accepts the following color names: black, red, green, yellow, blue, magenta, cyan and white. |
81
+ #
82
+ class ANSIStyleManager
83
+ # @return [String] the string to which ANSI styles will be applied.
84
+ attr_reader :string
85
+
86
+ # Initializes a new instance of the ANSIStyleManager class.
87
+ #
88
+ # @param string [String] The string to which ANSI styles will be applied.
89
+ # @raise [RuntimeError] If the argument is not a string.
90
+ def initialize(string)
91
+ raise 'Need initialize with a string' unless string.is_a?(String)
92
+
93
+ @string = String.new(string)
94
+ end
95
+
96
+ # Replaces all color and effect tokens in the string with corresponding ANSI codes.
97
+ #
98
+ # @return [void]
99
+ def replace_all_tokens!
100
+ replace_color_tokens!
101
+ replace_effect_tokens!
102
+ end
103
+
104
+ # Replaces all color tokens in the string with corresponding ANSI codes.
105
+ #
106
+ # @return [void]
107
+ def replace_color_tokens!
108
+ scan_while_find(CONSTANTS::ANSI::COLORS) do |value|
109
+ colors = value.first.split(':')
110
+
111
+ replace_tokens_with!(
112
+ ansi_token: color_prefix(color: colors[0], background: colors[1]),
113
+ ansi_reset_token: ANSI.new(%i[reset_background reset_color]),
114
+ to_replace: "<color:#{value.first}>#{value.last}</color>",
115
+ preserve_reset_token: false,
116
+ text: value.last
117
+ )
118
+ end
119
+ end
120
+
121
+ # Replaces all effect tokens in the string with corresponding ANSI codes.
122
+ #
123
+ # @return [void]
124
+ def replace_effect_tokens!
125
+ scan_while_find(CONSTANTS::ANSI::EFFECTS) do |value|
126
+ replace_tokens_with!(
127
+ ansi_reset_token: ANSI.new("remove_#{value.first.downcase}"),
128
+ to_replace: "<effect:#{value.first}>#{value.last}</effect>",
129
+ ansi_token: ANSI.new(value.first.downcase),
130
+ preserve_reset_token: true,
131
+ text: value.last
132
+ )
133
+ end
134
+ end
135
+
136
+ # Converts the string with all tokens replaced by corresponding ANSI codes.
137
+ #
138
+ # @return [String] The string with ANSI codes applied.
139
+ def to_s
140
+ replace_all_tokens!
141
+ @string
142
+ end
143
+
144
+ private
145
+
146
+ # Generates the ANSI prefix for a given color and background.
147
+ #
148
+ # @param color [String] The color name or code.
149
+ # @param background [String, nil] The background color name or code.
150
+ # @return [ANSI] The ANSI instance representing the color and background.
151
+ def color_prefix(color:, background:)
152
+ color_ansi = generate_color_ansi(color, is_background: false)
153
+ return color_ansi.to_s if background.nil?
154
+
155
+ generate_color_ansi(background, is_background: true).append(color_ansi)
156
+ end
157
+
158
+ # Generates the ANSI instance for a given color.
159
+ #
160
+ # @param color [String] The color name or code.
161
+ # @param is_background [Boolean] Whether the color is for the background.
162
+ # @return [ANSI] The ANSI instance representing the color.
163
+ def generate_color_ansi(color, is_background:)
164
+ digit_values = color.scan(CONSTANTS::ANSI::COLOR_DIGITS_REGEX).flatten.compact
165
+
166
+ if [1, 3].include?(digit_values.count)
167
+ format = "#{digit_values.count == 1 ? 'numeric' : 'rgb'}_format"
168
+ format = "#{is_background ? 'background_' : ''}#{format}"
169
+
170
+ return ANSI.new([format, digit_values])
171
+ elsif color.match?(/^reset$/)
172
+ return ANSI.new(is_background ? :reset_background : :reset_color)
173
+ end
174
+
175
+ ANSI.new(is_background ? "background_#{color}" : color)
176
+ end
177
+
178
+ # Replaces tokens in the string with corresponding ANSI codes.
179
+ #
180
+ # @param text [String] The text to be styled.
181
+ # @param to_replace [String] The token to be replaced.
182
+ # @param ansi_token [ANSI] The ANSI instance representing the style.
183
+ # @param ansi_reset_token [ANSI] The ANSI instance representing the reset style.
184
+ # @param preserve_reset_token [Boolean] Whether to preserve the reset token in the text.
185
+ # @return [void]
186
+ def replace_tokens_with!(text:, to_replace:, ansi_token:, ansi_reset_token:, preserve_reset_token:)
187
+ colored_text = text.gsub(ansi_reset_token.to_s, "#{ansi_reset_token}#{ansi_token}") if preserve_reset_token
188
+ colored_text = text.gsub(ansi_reset_token.to_s, ansi_token.to_s) unless preserve_reset_token
189
+ @string.gsub!(to_replace, "#{ansi_token}#{colored_text}#{ansi_reset_token}")
190
+ end
191
+
192
+ # Scans the string for tokens matching the given regex and processes them with the provided block.
193
+ #
194
+ # @param scan_regex [Regexp] The regex to scan for tokens.
195
+ # @yieldparam value [Array<String>] The matched tokens.
196
+ # @return [void]
197
+ def scan_while_find(scan_regex, &block)
198
+ result = @string.scan(scan_regex)
199
+ while result.count.positive?
200
+ result.each do |value|
201
+ block.call(value)
202
+ end
203
+
204
+ result = @string.scan(scan_regex)
205
+ end
206
+ end
207
+ 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
@@ -45,10 +45,7 @@ class Key
45
45
  def to_regexp
46
46
  /#{Regexp.escape(to_s)}/
47
47
  end
48
- end
49
48
 
50
- # Static Methods
51
- class Key
52
49
  # Finds and returns all Key instances within the given string.
53
50
  #
54
51
  # @param value [#to_s] the string to search for keys.
@@ -56,8 +53,8 @@ class Key
56
53
  def self.find_keys_in(value)
57
54
  ep = Regexp.escape(prefix)
58
55
  es = Regexp.escape(suffix)
59
- value.to_s.scan(/#{ep}[^#{ep}#{es}]+#{es}/).map do |key|
60
- Key.new(key.gsub(/(#{ep})|(#{es})/, ''))
56
+ value.to_s.scan(/#{ep}([^#{ep}#{es}]+)#{es}/).map do |key|
57
+ Key.new(key.first)
61
58
  end
62
59
  end
63
60
 
data/lib/utils/message.rb CHANGED
@@ -4,11 +4,15 @@ require_relative File.join('..', 'resources', 'path_helper')
4
4
  require_relative 'key'
5
5
 
6
6
  # The Message class represents a mechanism for dynamically handling and formatting messages.
7
+ #
7
8
  # It supports the substitution of placeholders within a message template with actual data.
8
- # The class leverages file-based message templates, allowing for easy localization or customization
9
- # of messages. It integrates seamlessly with the Resources module to access these templates from
10
- # a customizable path set via the "SCRIPT_CUSTOM_RESOURCES" environment variable or from a default
11
- # library path.
9
+ #
10
+ # The class leverages file-based message templates, allowing for easy localization or
11
+ # customization of messages.
12
+ #
13
+ # It integrates seamlessly with the Resources module to access these templates from
14
+ # a customizable path set via the "SCRIPT_CUSTOM_RESOURCES" environment variable or
15
+ # from a default library path.
12
16
  #
13
17
  # @example Creating a new Message instance and formatting it
14
18
  # # Assuming "hellow_world" is a file that says "Hello, world!"
@@ -46,10 +50,7 @@ class Message
46
50
  new_message = replace_message_keys(String.new(@message))
47
51
  replace_all_to_replace_elements(new_message)
48
52
  end
49
- end
50
53
 
51
- # Private Methods
52
- class Message
53
54
  private
54
55
 
55
56
  # Determines the custom path for message files, if set through the "SCRIPT_CUSTOM_RESOURCES" environment variable.
@@ -123,11 +124,18 @@ class Message
123
124
  end
124
125
 
125
126
  # Reads and returns the content of a message file located at a specific path.
127
+ # If the file name have the suffix ".aas.txt", the ANSIStyleManager will be applied to the file.
126
128
  #
127
129
  # @param path [String] The path where the message file is located.
128
130
  # @param file_name [String] The name of the file to be read.
129
131
  # @return [String, nil] The content of the message file, or nil if the file does not exist.
130
132
  def recover_content_with(path, file_name)
133
+ aas_file_path = File.join(path, "#{file_name}.aas.txt")
134
+ if File.exist?(aas_file_path)
135
+ content = File.read(aas_file_path)
136
+ return ANSIStyleManager.new(content).to_s
137
+ end
138
+
131
139
  file_path = File.join(path, "#{file_name}.txt")
132
140
  return nil unless File.exist?(file_path)
133
141
 
@@ -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.1
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-07-02 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