dev_suite 0.2.7 → 0.2.10
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 +4 -4
- data/.codeclimate.yml +12 -0
- data/.github/workflows/ci.yml +1 -1
- data/.sonarcloud.properties +23 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +17 -33
- data/README.md +216 -0
- data/dev_suite.gemspec +1 -0
- data/examples/helpers/api_helper.rb +51 -0
- data/examples/helpers/data_helper.rb +72 -0
- data/examples/helpers/helpers.rb +4 -0
- data/examples/workflow/basic_workflow.rb +15 -0
- data/examples/workflow/composite_workflow.rb +21 -0
- data/examples/workflow/conditional_workflow.rb +17 -0
- data/examples/workflow/full_workflow.rb +79 -0
- data/examples/workflow/loop_workflow.rb +17 -0
- data/examples/workflow/order_processing_workflow.rb +163 -0
- data/examples/workflow/parallel_workflow.rb +17 -0
- data/lib/dev_suite/dev_suite.rb +3 -0
- data/lib/dev_suite/method_tracer/config/config.rb +11 -0
- data/lib/dev_suite/method_tracer/config/configuration.rb +16 -0
- data/lib/dev_suite/method_tracer/config.rb +9 -0
- data/lib/dev_suite/method_tracer/helpers.rb +41 -0
- data/lib/dev_suite/method_tracer/logger.rb +46 -0
- data/lib/dev_suite/method_tracer/method_tracer.rb +20 -0
- data/lib/dev_suite/method_tracer/tracer.rb +65 -0
- data/lib/dev_suite/method_tracer.rb +7 -0
- data/lib/dev_suite/request_builder/builder/base.rb +27 -0
- data/lib/dev_suite/request_builder/builder/builder.rb +10 -0
- data/lib/dev_suite/request_builder/builder/http.rb +32 -0
- data/lib/dev_suite/request_builder/builder.rb +9 -0
- data/lib/dev_suite/request_builder/config/config.rb +11 -0
- data/lib/dev_suite/request_builder/config/configuration.rb +24 -0
- data/lib/dev_suite/request_builder/config.rb +9 -0
- data/lib/dev_suite/request_builder/formatter/base.rb +13 -0
- data/lib/dev_suite/request_builder/formatter/formatter.rb +10 -0
- data/lib/dev_suite/request_builder/formatter/graphql.rb +19 -0
- data/lib/dev_suite/request_builder/formatter.rb +9 -0
- data/lib/dev_suite/request_builder/request_builder.rb +21 -0
- data/lib/dev_suite/request_builder/tool/base.rb +19 -0
- data/lib/dev_suite/request_builder/tool/curl.rb +91 -0
- data/lib/dev_suite/request_builder/tool/tool.rb +11 -0
- data/lib/dev_suite/request_builder/tool/validator/curl.rb +38 -0
- data/lib/dev_suite/request_builder/tool/validator/validator.rb +11 -0
- data/lib/dev_suite/request_builder/tool/validator.rb +11 -0
- data/lib/dev_suite/request_builder/tool.rb +9 -0
- data/lib/dev_suite/request_builder.rb +7 -0
- data/lib/dev_suite/request_logger/adapter/adapter.rb +11 -9
- data/lib/dev_suite/request_logger/adapter/faraday.rb +12 -1
- data/lib/dev_suite/request_logger/adapter/middleware/faraday.rb +3 -3
- data/lib/dev_suite/request_logger/adapter/net_http.rb +15 -6
- data/lib/dev_suite/request_logger/config/configuration.rb +1 -0
- data/lib/dev_suite/request_logger/extractor/base.rb +8 -2
- data/lib/dev_suite/request_logger/extractor/extractor.rb +5 -6
- data/lib/dev_suite/request_logger/extractor/faraday.rb +32 -14
- data/lib/dev_suite/request_logger/extractor/net_http.rb +53 -12
- data/lib/dev_suite/request_logger/logger.rb +9 -3
- data/lib/dev_suite/request_logger/request.rb +12 -0
- data/lib/dev_suite/request_logger/response.rb +34 -9
- data/lib/dev_suite/utils/construct/component/base.rb +13 -0
- data/lib/dev_suite/utils/construct/component/component.rb +1 -0
- data/lib/dev_suite/utils/construct/component/manager.rb +27 -10
- data/lib/dev_suite/utils/construct/component/validator/base.rb +25 -0
- data/lib/dev_suite/utils/construct/component/validator/validation_error.rb +21 -0
- data/lib/dev_suite/utils/construct/component/validator/validation_rule.rb +68 -0
- data/lib/dev_suite/utils/construct/component/validator/validator.rb +15 -0
- data/lib/dev_suite/utils/construct/component/validator.rb +13 -0
- data/lib/dev_suite/utils/construct/config/dependency_handler.rb +25 -34
- data/lib/dev_suite/utils/construct/config/settings/base.rb +43 -26
- data/lib/dev_suite/utils/data/base_operations.rb +61 -0
- data/lib/dev_suite/utils/data/data.rb +19 -0
- data/lib/dev_suite/utils/data/path_access.rb +172 -0
- data/lib/dev_suite/utils/data/search_filter.rb +60 -0
- data/lib/dev_suite/utils/data/serialization.rb +29 -0
- data/lib/dev_suite/utils/data/transformations.rb +45 -0
- data/lib/dev_suite/utils/data.rb +9 -0
- data/lib/dev_suite/utils/dependency_loader.rb +2 -2
- data/lib/dev_suite/utils/emoji.rb +19 -0
- data/lib/dev_suite/utils/file_loader/file_loader.rb +1 -5
- data/lib/dev_suite/utils/file_loader/loader/json.rb +4 -1
- data/lib/dev_suite/utils/file_loader/loader/loader.rb +23 -19
- data/lib/dev_suite/utils/file_loader/loader.rb +0 -2
- data/lib/dev_suite/utils/file_writer/atomic_writer.rb +53 -0
- data/lib/dev_suite/utils/file_writer/backup_manager.rb +21 -0
- data/lib/dev_suite/utils/file_writer/file_writer.rb +24 -0
- data/lib/dev_suite/utils/file_writer/writer/base.rb +43 -0
- data/lib/dev_suite/utils/file_writer/writer/json.rb +24 -0
- data/lib/dev_suite/utils/file_writer/writer/text.rb +27 -0
- data/lib/dev_suite/utils/file_writer/writer/writer.rb +14 -0
- data/lib/dev_suite/utils/file_writer/writer/yaml.rb +44 -0
- data/lib/dev_suite/utils/file_writer/writer.rb +11 -0
- data/lib/dev_suite/utils/file_writer/writer_manager.rb +32 -0
- data/lib/dev_suite/utils/file_writer.rb +9 -0
- data/lib/dev_suite/utils/logger.rb +7 -5
- data/lib/dev_suite/utils/store/config/config.rb +13 -0
- data/lib/dev_suite/utils/store/config/configuration.rb +30 -0
- data/lib/dev_suite/utils/store/config.rb +11 -0
- data/lib/dev_suite/utils/store/driver/base.rb +35 -0
- data/lib/dev_suite/utils/store/driver/driver.rb +18 -0
- data/lib/dev_suite/utils/store/driver/file.rb +61 -0
- data/lib/dev_suite/utils/store/driver/memory.rb +42 -0
- data/lib/dev_suite/utils/store/driver.rb +11 -0
- data/lib/dev_suite/utils/store/store.rb +69 -0
- data/lib/dev_suite/utils/store.rb +9 -0
- data/lib/dev_suite/utils/utils.rb +17 -5
- data/lib/dev_suite/utils/warning_handler.rb +25 -0
- data/lib/dev_suite/version.rb +1 -1
- data/lib/dev_suite/workflow/engine.rb +27 -0
- data/lib/dev_suite/workflow/step/base.rb +56 -0
- data/lib/dev_suite/workflow/step/composite.rb +27 -0
- data/lib/dev_suite/workflow/step/conditional.rb +23 -0
- data/lib/dev_suite/workflow/step/loop.rb +21 -0
- data/lib/dev_suite/workflow/step/parallel.rb +18 -0
- data/lib/dev_suite/workflow/step/step.rb +21 -0
- data/lib/dev_suite/workflow/step.rb +9 -0
- data/lib/dev_suite/workflow/step_context.rb +46 -0
- data/lib/dev_suite/workflow/workflow.rb +39 -0
- data/lib/dev_suite/workflow.rb +7 -0
- metadata +101 -3
- data/lib/dev_suite/utils/construct/component/initializer.rb +0 -28
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module Data
|
|
6
|
+
module SearchFilter
|
|
7
|
+
# Deep search for all occurrences of a key
|
|
8
|
+
def deep_find_by_key(data, search_key)
|
|
9
|
+
result = []
|
|
10
|
+
traverse_and_collect(data) do |key, value|
|
|
11
|
+
result << value if key == search_key
|
|
12
|
+
end
|
|
13
|
+
result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Recursively filter a nested hash or array based on a key-value condition
|
|
17
|
+
def deep_filter_by_key_value(data, filter_key, filter_value)
|
|
18
|
+
case data
|
|
19
|
+
when Hash
|
|
20
|
+
filter_hash_by_key_value(data, filter_key, filter_value)
|
|
21
|
+
when Array
|
|
22
|
+
filter_array_by_key_value(data, filter_key, filter_value)
|
|
23
|
+
else
|
|
24
|
+
raise ArgumentError, "Unsupported data type: #{data.class}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Helper method to filter a hash by a key-value condition
|
|
31
|
+
def filter_hash_by_key_value(hash, filter_key, filter_value)
|
|
32
|
+
return hash if hash[filter_key] == filter_value
|
|
33
|
+
|
|
34
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
35
|
+
filtered_value = deep_filter_by_key_value(value, filter_key, filter_value)
|
|
36
|
+
result[key] = filtered_value unless filtered_value.nil? || filtered_value.empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Helper method to filter an array by a key-value condition
|
|
41
|
+
def filter_array_by_key_value(array, filter_key, filter_value)
|
|
42
|
+
array.map { |item| deep_filter_by_key_value(item, filter_key, filter_value) }.compact
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Helper method to traverse and collect values
|
|
46
|
+
def traverse_and_collect(data, &block)
|
|
47
|
+
case data
|
|
48
|
+
when Hash
|
|
49
|
+
data.each do |key, value|
|
|
50
|
+
block.call(key, value)
|
|
51
|
+
traverse_and_collect(value, &block)
|
|
52
|
+
end
|
|
53
|
+
when Array
|
|
54
|
+
data.each { |item| traverse_and_collect(item, &block) }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "csv"
|
|
5
|
+
|
|
6
|
+
module DevSuite
|
|
7
|
+
module Utils
|
|
8
|
+
module Data
|
|
9
|
+
module Serialization
|
|
10
|
+
# Convert a hash or array of hashes to compact JSON
|
|
11
|
+
def to_json(data)
|
|
12
|
+
return if data.nil?
|
|
13
|
+
|
|
14
|
+
::JSON.generate(data) # Use JSON.generate for compact output
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Convert an array of hashes to CSV format
|
|
18
|
+
def to_csv(array_of_hashes)
|
|
19
|
+
return "" if array_of_hashes.empty?
|
|
20
|
+
|
|
21
|
+
::CSV.generate do |csv|
|
|
22
|
+
csv << array_of_hashes.first.keys # Header row
|
|
23
|
+
array_of_hashes.each { |hash| csv << hash.values }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module Data
|
|
6
|
+
module Transformations
|
|
7
|
+
# Recursively convert all values to strings without altering the structure
|
|
8
|
+
def deep_stringify_values(data)
|
|
9
|
+
case data
|
|
10
|
+
when Hash
|
|
11
|
+
data.each_with_object({}) do |(key, value), result|
|
|
12
|
+
result[key] = deep_stringify_values(value) # Recursively process nested values
|
|
13
|
+
end
|
|
14
|
+
when Array
|
|
15
|
+
data.map { |item| deep_stringify_values(item) }
|
|
16
|
+
else
|
|
17
|
+
data.to_s # Only convert the value itself to a string
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Recursively symbolize keys in a nested structure
|
|
22
|
+
def deep_symbolize_keys(data)
|
|
23
|
+
traverse_transform(data) { |key, value| [key.to_sym, value] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# Helper method for transforming structures
|
|
29
|
+
def traverse_transform(data, &block)
|
|
30
|
+
case data
|
|
31
|
+
when Hash
|
|
32
|
+
data.each_with_object({}) do |(key, value), result|
|
|
33
|
+
new_key, new_value = block.call(key, traverse_transform(value, &block))
|
|
34
|
+
result[new_key] = new_value
|
|
35
|
+
end
|
|
36
|
+
when Array
|
|
37
|
+
data.map { |item| traverse_transform(item, &block) }
|
|
38
|
+
else
|
|
39
|
+
data
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -11,7 +11,7 @@ module DevSuite
|
|
|
11
11
|
# @param dependencies [Array<String>] List of gem names to load.
|
|
12
12
|
# @param on_failure [Proc] Handler to call with missing dependencies if any are not found.
|
|
13
13
|
# @yield Executes the block if all dependencies are loaded.
|
|
14
|
-
def safe_load_dependencies(
|
|
14
|
+
def safe_load_dependencies(dependencies = [], on_failure: method(:default_failure_handler))
|
|
15
15
|
missing_dependencies = find_missing_dependencies(dependencies)
|
|
16
16
|
|
|
17
17
|
if missing_dependencies.empty?
|
|
@@ -39,7 +39,7 @@ module DevSuite
|
|
|
39
39
|
# @param dependencies [Array<String>] List of gem names to check.
|
|
40
40
|
# @return [Array<String>] List of missing dependencies.
|
|
41
41
|
def find_missing_dependencies(dependencies)
|
|
42
|
-
dependencies.reject { |gem_name| gem_installed?(gem_name) }
|
|
42
|
+
Array(dependencies).reject { |gem_name| gem_installed?(gem_name) }
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Default failure handler that logs missing dependencies.
|
|
@@ -11,6 +11,25 @@ module DevSuite
|
|
|
11
11
|
retry: "💥",
|
|
12
12
|
warning: "⚠️", # For warnings
|
|
13
13
|
caution: "🟡", # For caution or proceed with care
|
|
14
|
+
pending: "🕒", # For pending or waiting
|
|
15
|
+
paused: "⏸️", # For paused or halted
|
|
16
|
+
running: "🏃", # For running or in progress
|
|
17
|
+
done: "🎉", # For done or completed
|
|
18
|
+
finish: "🏁", # For finish or end
|
|
19
|
+
stop: "🛑", # For stopping or halting
|
|
20
|
+
cancel: "🚫", # For canceling or aborting
|
|
21
|
+
skip: "⏭️", # For skipping or jumping
|
|
22
|
+
next: "⏩", # For next or moving forward
|
|
23
|
+
previous: "⏪", # For previous or moving backward
|
|
24
|
+
up: "⬆️", # For up or moving up
|
|
25
|
+
down: "⬇️", # For down or moving down
|
|
26
|
+
left: "⬅️", # For left or moving left
|
|
27
|
+
right: "➡️", # For right or moving right
|
|
28
|
+
top: "🔝", # For top or highest
|
|
29
|
+
bottom: "🔚", # For bottom or lowest
|
|
30
|
+
middle: "🔽", # For middle or center
|
|
31
|
+
full: "🔴", # For full or maximum
|
|
32
|
+
empty: "⚪", # For empty or minimum
|
|
14
33
|
}.freeze
|
|
15
34
|
|
|
16
35
|
# Action-related emojis
|
|
@@ -3,14 +3,10 @@
|
|
|
3
3
|
module DevSuite
|
|
4
4
|
module Utils
|
|
5
5
|
module FileLoader
|
|
6
|
-
|
|
7
|
-
require "json"
|
|
8
|
-
require "yaml"
|
|
9
|
-
|
|
6
|
+
require_relative "config"
|
|
10
7
|
require_relative "loader"
|
|
11
8
|
require_relative "loader_registry"
|
|
12
9
|
require_relative "loader_manager"
|
|
13
|
-
require_relative "config"
|
|
14
10
|
|
|
15
11
|
class << self
|
|
16
12
|
def load(path)
|
|
@@ -4,37 +4,41 @@ module DevSuite
|
|
|
4
4
|
module Utils
|
|
5
5
|
module FileLoader
|
|
6
6
|
module Loader
|
|
7
|
+
include Construct::Component::Manager
|
|
8
|
+
|
|
7
9
|
require_relative "base"
|
|
8
|
-
require_relative "text"
|
|
9
|
-
require_relative "json"
|
|
10
|
-
require_relative "yaml"
|
|
11
10
|
|
|
12
11
|
class << self
|
|
12
|
+
# Registers the specified loaders with the given registry.
|
|
13
|
+
#
|
|
14
|
+
# @param registry [Object] the registry to register loaders with
|
|
15
|
+
# @param loader_symbols [Array<Symbol>] the symbols representing the loaders to register
|
|
16
|
+
# @return [Object] the updated registry
|
|
13
17
|
def registry_loaders(registry, loader_symbols)
|
|
14
|
-
loader_classes =
|
|
18
|
+
loader_classes = registered_components.values_at(*loader_symbols)
|
|
15
19
|
loader_classes.each do |loader|
|
|
16
20
|
registry.register(loader)
|
|
17
21
|
end
|
|
18
22
|
registry
|
|
19
23
|
end
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def map_loaders(loader_symbols)
|
|
24
|
-
loader_symbols.map do |symbol|
|
|
25
|
-
case symbol
|
|
26
|
-
when :json
|
|
27
|
-
Loader::Json
|
|
28
|
-
when :text
|
|
29
|
-
Loader::Text
|
|
30
|
-
when :yaml
|
|
31
|
-
Loader::Yaml
|
|
32
|
-
else
|
|
33
|
-
raise "Unknown loader: #{symbol}"
|
|
34
|
-
end
|
|
35
|
-
end
|
|
25
|
+
def handle_missing_dependencies(missing_dependencies)
|
|
26
|
+
Config.configuration.remove_failed_dependency(:loaders, :json, *missing_dependencies)
|
|
36
27
|
end
|
|
37
28
|
end
|
|
29
|
+
|
|
30
|
+
require_relative "text"
|
|
31
|
+
register_component(Text)
|
|
32
|
+
|
|
33
|
+
load_dependency(["json"], on_failure: method(:handle_missing_dependencies)) do
|
|
34
|
+
require_relative "json"
|
|
35
|
+
register_component(Json)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
load_dependency(["yaml"], on_failure: method(:handle_missing_dependencies)) do
|
|
39
|
+
require_relative "yaml"
|
|
40
|
+
register_component(Yaml)
|
|
41
|
+
end
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
class AtomicWriter
|
|
7
|
+
def initialize(path, content, mode: 0o644)
|
|
8
|
+
@path = path
|
|
9
|
+
@content = content
|
|
10
|
+
@mode = mode
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write
|
|
14
|
+
ensure_directory_exists
|
|
15
|
+
|
|
16
|
+
::File.open(@path, "w") do |file|
|
|
17
|
+
file.flock(::File::LOCK_EX) # Lock for exclusive write access
|
|
18
|
+
write_to_tempfile_and_replace
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def write_to_tempfile_and_replace
|
|
25
|
+
temp_path = "#{@path}.tmp"
|
|
26
|
+
begin
|
|
27
|
+
::File.open(temp_path, "w") do |tempfile|
|
|
28
|
+
tempfile.write(@content)
|
|
29
|
+
tempfile.flush
|
|
30
|
+
tempfile.fsync # Ensure data is physically written to disk
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Atomically replace the original file with the temporary file
|
|
34
|
+
::File.rename(temp_path, @path)
|
|
35
|
+
|
|
36
|
+
# Set correct file permissions
|
|
37
|
+
::File.chmod(@mode, @path)
|
|
38
|
+
rescue IOError, Errno::ENOSPC => e
|
|
39
|
+
raise "Failed to write or replace the original file: #{e.message}"
|
|
40
|
+
ensure
|
|
41
|
+
# Clean up the temporary file if it still exists
|
|
42
|
+
::File.delete(temp_path) if ::File.exist?(temp_path)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ensure_directory_exists
|
|
47
|
+
directory = ::File.dirname(@path)
|
|
48
|
+
::FileUtils.mkdir_p(directory) unless ::Dir.exist?(directory)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
class BackupManager
|
|
7
|
+
def initialize(path)
|
|
8
|
+
@path = path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create_backup
|
|
12
|
+
backup_path = "#{@path}.bak"
|
|
13
|
+
Logger.log("Creating backup of #{@path} at #{backup_path}", level: :info)
|
|
14
|
+
::FileUtils.cp(@path, backup_path)
|
|
15
|
+
rescue IOError => e
|
|
16
|
+
raise "Failed to create backup: #{e.message}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
require_relative "atomic_writer"
|
|
7
|
+
require_relative "backup_manager"
|
|
8
|
+
require_relative "writer"
|
|
9
|
+
require_relative "writer_manager"
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def write(path, content)
|
|
13
|
+
writer = WriterManager.new
|
|
14
|
+
writer.write(path, content)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update_key(path, key, value)
|
|
18
|
+
writer = WriterManager.new
|
|
19
|
+
writer.update_key(path, key, value)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
module Writer
|
|
7
|
+
class Base < Utils::Construct::Component::Base
|
|
8
|
+
# Abstract method that subclasses must implement for custom file write operations
|
|
9
|
+
def write(_path, _content)
|
|
10
|
+
raise NotImplementedError, "Subclasses must implement the `write` method"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# General method to update a key in any structured file
|
|
14
|
+
def update_key(path, key, value, **options)
|
|
15
|
+
# Step 1: Load the existing content of the file (this will be subclass-specific)
|
|
16
|
+
content = load_file_content(path)
|
|
17
|
+
|
|
18
|
+
# Step 2: Use a utility function to modify the content at the specified key path
|
|
19
|
+
Utils::Data.set_value_by_path(content, key, value)
|
|
20
|
+
|
|
21
|
+
# Step 3: Write the updated content back to the file using the subclass's write method
|
|
22
|
+
write(path, content, **options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def file_exists?(path)
|
|
28
|
+
::File.exist?(path)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Load the file content using a file loader
|
|
32
|
+
def load_file_content(file_path)
|
|
33
|
+
FileLoader.load(file_path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_backup(path)
|
|
37
|
+
BackupManager.new(path).create_backup if file_exists?(path)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
module Writer
|
|
7
|
+
class Json < Base
|
|
8
|
+
def write(path, content, pretty: false, backup: false)
|
|
9
|
+
create_backup(path) if backup
|
|
10
|
+
|
|
11
|
+
json_content = convert_to_json(content, pretty)
|
|
12
|
+
AtomicWriter.new(path, json_content).write
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def convert_to_json(content, pretty)
|
|
18
|
+
pretty ? ::JSON.pretty_generate(content) : ::JSON.dump(content)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
module Writer
|
|
7
|
+
class Text < Base
|
|
8
|
+
def write(path, content, backup: false)
|
|
9
|
+
create_backup(path) if backup
|
|
10
|
+
|
|
11
|
+
AtomicWriter.new(path, content).write
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Updates a key-like pattern in a plain text file (find and replace)
|
|
15
|
+
def update_key(path, key, value, backup: false)
|
|
16
|
+
content = load_file_content(path)
|
|
17
|
+
|
|
18
|
+
# Simple pattern matching and replacement (e.g., `key: value`)
|
|
19
|
+
updated_content = content.gsub(/^#{Regexp.escape(key)}:.*/, "#{key}: #{value}")
|
|
20
|
+
|
|
21
|
+
write(path, updated_content, backup: backup)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
module Writer
|
|
7
|
+
class Yaml < Base
|
|
8
|
+
def write(path, content, normalize: false, backup: false, yaml_options: {})
|
|
9
|
+
validate_content(content)
|
|
10
|
+
create_backup(path) if backup
|
|
11
|
+
|
|
12
|
+
yaml_content = prepare_yaml_content(content, normalize, yaml_options)
|
|
13
|
+
AtomicWriter.new(path, yaml_content).write
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# Prepare YAML content by normalizing and applying options
|
|
19
|
+
def prepare_yaml_content(content, normalize, yaml_options)
|
|
20
|
+
normalized_content = normalize ? normalize_yaml(content) : content
|
|
21
|
+
::YAML.dump(normalized_content, yaml_options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Validates that content is a Hash or an Array of Hashes
|
|
25
|
+
def validate_content(content)
|
|
26
|
+
unless valid_content?(content)
|
|
27
|
+
raise ArgumentError, "Content must be a Hash or an Array of Hashes"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Check if content is a valid Hash or an Array of Hashes
|
|
32
|
+
def valid_content?(content)
|
|
33
|
+
content.is_a?(Hash) || (content.is_a?(Array) && content.all? { |item| item.is_a?(Hash) })
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Normalize the YAML content (e.g., sort keys)
|
|
37
|
+
def normalize_yaml(content)
|
|
38
|
+
content.is_a?(Hash) ? content.sort.to_h : content
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DevSuite
|
|
4
|
+
module Utils
|
|
5
|
+
module FileWriter
|
|
6
|
+
class WriterManager
|
|
7
|
+
def write(path, content)
|
|
8
|
+
writer = writer_for(path)
|
|
9
|
+
writer.write(path, content)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def update_key(path, key, value)
|
|
13
|
+
writer = writer_for(path)
|
|
14
|
+
writer.update_key(path, key, value)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def writer_for(path)
|
|
20
|
+
case ::File.extname(path)
|
|
21
|
+
when ".json"
|
|
22
|
+
Writer::Json.new
|
|
23
|
+
when ".yml", ".yaml"
|
|
24
|
+
Writer::Yaml.new
|
|
25
|
+
else
|
|
26
|
+
Writer::Text.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -4,6 +4,7 @@ module DevSuite
|
|
|
4
4
|
module Utils
|
|
5
5
|
module Logger
|
|
6
6
|
LOG_DETAILS = {
|
|
7
|
+
none: { prefix: "", color: :white },
|
|
7
8
|
info: { prefix: "[INFO]", color: :green },
|
|
8
9
|
warn: { prefix: "[WARNING]", color: :yellow },
|
|
9
10
|
error: { prefix: "[ERROR]", color: :red },
|
|
@@ -16,9 +17,9 @@ module DevSuite
|
|
|
16
17
|
# @param message [String] The message to log.
|
|
17
18
|
# @param level [Symbol] The log level (:info, :warn, :error, :debug).
|
|
18
19
|
# @param emoji [String, Symbol] Optional emoji to prepend to the message.
|
|
19
|
-
def log(message, level: :
|
|
20
|
+
def log(message, level: :none, emoji: nil, prefix: nil, color: nil)
|
|
20
21
|
emoji_icon = resolve_emoji(emoji)
|
|
21
|
-
formatted_message = format_message("#{emoji_icon} #{message}", level)
|
|
22
|
+
formatted_message = format_message("#{emoji_icon} #{message}", level, prefix, color)
|
|
22
23
|
output_log(formatted_message)
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -39,11 +40,12 @@ module DevSuite
|
|
|
39
40
|
# @param message [String] The message to format.
|
|
40
41
|
# @param level [Symbol] The log level (:info, :warn, :error, :debug).
|
|
41
42
|
# @return [String] The formatted log message.
|
|
42
|
-
def format_message(message, level)
|
|
43
|
+
def format_message(message, level, custom_prefix, custom_color)
|
|
43
44
|
details = LOG_DETAILS[level]
|
|
44
|
-
|
|
45
|
+
prefix = custom_prefix || details[:prefix]
|
|
46
|
+
color = custom_color || details[:color]
|
|
45
47
|
|
|
46
|
-
Utils::Color.colorize("#{
|
|
48
|
+
Utils::Color.colorize("#{prefix} #{message}", color: color)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
# Outputs the formatted log message to the console.
|