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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +12 -0
  3. data/.github/workflows/ci.yml +1 -1
  4. data/.sonarcloud.properties +23 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +17 -33
  7. data/README.md +216 -0
  8. data/dev_suite.gemspec +1 -0
  9. data/examples/helpers/api_helper.rb +51 -0
  10. data/examples/helpers/data_helper.rb +72 -0
  11. data/examples/helpers/helpers.rb +4 -0
  12. data/examples/workflow/basic_workflow.rb +15 -0
  13. data/examples/workflow/composite_workflow.rb +21 -0
  14. data/examples/workflow/conditional_workflow.rb +17 -0
  15. data/examples/workflow/full_workflow.rb +79 -0
  16. data/examples/workflow/loop_workflow.rb +17 -0
  17. data/examples/workflow/order_processing_workflow.rb +163 -0
  18. data/examples/workflow/parallel_workflow.rb +17 -0
  19. data/lib/dev_suite/dev_suite.rb +3 -0
  20. data/lib/dev_suite/method_tracer/config/config.rb +11 -0
  21. data/lib/dev_suite/method_tracer/config/configuration.rb +16 -0
  22. data/lib/dev_suite/method_tracer/config.rb +9 -0
  23. data/lib/dev_suite/method_tracer/helpers.rb +41 -0
  24. data/lib/dev_suite/method_tracer/logger.rb +46 -0
  25. data/lib/dev_suite/method_tracer/method_tracer.rb +20 -0
  26. data/lib/dev_suite/method_tracer/tracer.rb +65 -0
  27. data/lib/dev_suite/method_tracer.rb +7 -0
  28. data/lib/dev_suite/request_builder/builder/base.rb +27 -0
  29. data/lib/dev_suite/request_builder/builder/builder.rb +10 -0
  30. data/lib/dev_suite/request_builder/builder/http.rb +32 -0
  31. data/lib/dev_suite/request_builder/builder.rb +9 -0
  32. data/lib/dev_suite/request_builder/config/config.rb +11 -0
  33. data/lib/dev_suite/request_builder/config/configuration.rb +24 -0
  34. data/lib/dev_suite/request_builder/config.rb +9 -0
  35. data/lib/dev_suite/request_builder/formatter/base.rb +13 -0
  36. data/lib/dev_suite/request_builder/formatter/formatter.rb +10 -0
  37. data/lib/dev_suite/request_builder/formatter/graphql.rb +19 -0
  38. data/lib/dev_suite/request_builder/formatter.rb +9 -0
  39. data/lib/dev_suite/request_builder/request_builder.rb +21 -0
  40. data/lib/dev_suite/request_builder/tool/base.rb +19 -0
  41. data/lib/dev_suite/request_builder/tool/curl.rb +91 -0
  42. data/lib/dev_suite/request_builder/tool/tool.rb +11 -0
  43. data/lib/dev_suite/request_builder/tool/validator/curl.rb +38 -0
  44. data/lib/dev_suite/request_builder/tool/validator/validator.rb +11 -0
  45. data/lib/dev_suite/request_builder/tool/validator.rb +11 -0
  46. data/lib/dev_suite/request_builder/tool.rb +9 -0
  47. data/lib/dev_suite/request_builder.rb +7 -0
  48. data/lib/dev_suite/request_logger/adapter/adapter.rb +11 -9
  49. data/lib/dev_suite/request_logger/adapter/faraday.rb +12 -1
  50. data/lib/dev_suite/request_logger/adapter/middleware/faraday.rb +3 -3
  51. data/lib/dev_suite/request_logger/adapter/net_http.rb +15 -6
  52. data/lib/dev_suite/request_logger/config/configuration.rb +1 -0
  53. data/lib/dev_suite/request_logger/extractor/base.rb +8 -2
  54. data/lib/dev_suite/request_logger/extractor/extractor.rb +5 -6
  55. data/lib/dev_suite/request_logger/extractor/faraday.rb +32 -14
  56. data/lib/dev_suite/request_logger/extractor/net_http.rb +53 -12
  57. data/lib/dev_suite/request_logger/logger.rb +9 -3
  58. data/lib/dev_suite/request_logger/request.rb +12 -0
  59. data/lib/dev_suite/request_logger/response.rb +34 -9
  60. data/lib/dev_suite/utils/construct/component/base.rb +13 -0
  61. data/lib/dev_suite/utils/construct/component/component.rb +1 -0
  62. data/lib/dev_suite/utils/construct/component/manager.rb +27 -10
  63. data/lib/dev_suite/utils/construct/component/validator/base.rb +25 -0
  64. data/lib/dev_suite/utils/construct/component/validator/validation_error.rb +21 -0
  65. data/lib/dev_suite/utils/construct/component/validator/validation_rule.rb +68 -0
  66. data/lib/dev_suite/utils/construct/component/validator/validator.rb +15 -0
  67. data/lib/dev_suite/utils/construct/component/validator.rb +13 -0
  68. data/lib/dev_suite/utils/construct/config/dependency_handler.rb +25 -34
  69. data/lib/dev_suite/utils/construct/config/settings/base.rb +43 -26
  70. data/lib/dev_suite/utils/data/base_operations.rb +61 -0
  71. data/lib/dev_suite/utils/data/data.rb +19 -0
  72. data/lib/dev_suite/utils/data/path_access.rb +172 -0
  73. data/lib/dev_suite/utils/data/search_filter.rb +60 -0
  74. data/lib/dev_suite/utils/data/serialization.rb +29 -0
  75. data/lib/dev_suite/utils/data/transformations.rb +45 -0
  76. data/lib/dev_suite/utils/data.rb +9 -0
  77. data/lib/dev_suite/utils/dependency_loader.rb +2 -2
  78. data/lib/dev_suite/utils/emoji.rb +19 -0
  79. data/lib/dev_suite/utils/file_loader/file_loader.rb +1 -5
  80. data/lib/dev_suite/utils/file_loader/loader/json.rb +4 -1
  81. data/lib/dev_suite/utils/file_loader/loader/loader.rb +23 -19
  82. data/lib/dev_suite/utils/file_loader/loader.rb +0 -2
  83. data/lib/dev_suite/utils/file_writer/atomic_writer.rb +53 -0
  84. data/lib/dev_suite/utils/file_writer/backup_manager.rb +21 -0
  85. data/lib/dev_suite/utils/file_writer/file_writer.rb +24 -0
  86. data/lib/dev_suite/utils/file_writer/writer/base.rb +43 -0
  87. data/lib/dev_suite/utils/file_writer/writer/json.rb +24 -0
  88. data/lib/dev_suite/utils/file_writer/writer/text.rb +27 -0
  89. data/lib/dev_suite/utils/file_writer/writer/writer.rb +14 -0
  90. data/lib/dev_suite/utils/file_writer/writer/yaml.rb +44 -0
  91. data/lib/dev_suite/utils/file_writer/writer.rb +11 -0
  92. data/lib/dev_suite/utils/file_writer/writer_manager.rb +32 -0
  93. data/lib/dev_suite/utils/file_writer.rb +9 -0
  94. data/lib/dev_suite/utils/logger.rb +7 -5
  95. data/lib/dev_suite/utils/store/config/config.rb +13 -0
  96. data/lib/dev_suite/utils/store/config/configuration.rb +30 -0
  97. data/lib/dev_suite/utils/store/config.rb +11 -0
  98. data/lib/dev_suite/utils/store/driver/base.rb +35 -0
  99. data/lib/dev_suite/utils/store/driver/driver.rb +18 -0
  100. data/lib/dev_suite/utils/store/driver/file.rb +61 -0
  101. data/lib/dev_suite/utils/store/driver/memory.rb +42 -0
  102. data/lib/dev_suite/utils/store/driver.rb +11 -0
  103. data/lib/dev_suite/utils/store/store.rb +69 -0
  104. data/lib/dev_suite/utils/store.rb +9 -0
  105. data/lib/dev_suite/utils/utils.rb +17 -5
  106. data/lib/dev_suite/utils/warning_handler.rb +25 -0
  107. data/lib/dev_suite/version.rb +1 -1
  108. data/lib/dev_suite/workflow/engine.rb +27 -0
  109. data/lib/dev_suite/workflow/step/base.rb +56 -0
  110. data/lib/dev_suite/workflow/step/composite.rb +27 -0
  111. data/lib/dev_suite/workflow/step/conditional.rb +23 -0
  112. data/lib/dev_suite/workflow/step/loop.rb +21 -0
  113. data/lib/dev_suite/workflow/step/parallel.rb +18 -0
  114. data/lib/dev_suite/workflow/step/step.rb +21 -0
  115. data/lib/dev_suite/workflow/step.rb +9 -0
  116. data/lib/dev_suite/workflow/step_context.rb +46 -0
  117. data/lib/dev_suite/workflow/workflow.rb +39 -0
  118. data/lib/dev_suite/workflow.rb +7 -0
  119. metadata +101 -3
  120. 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Utils
5
+ module Data
6
+ require_relative "data/data"
7
+ end
8
+ end
9
+ 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(*dependencies, on_failure: method(:default_failure_handler))
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
- require "pathname"
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)
@@ -12,7 +12,10 @@ module DevSuite
12
12
  end
13
13
 
14
14
  def load(path)
15
- ::JSON.parse(::File.read(path))
15
+ content = ::File.read(path)
16
+ return {} if content.strip.empty?
17
+
18
+ ::JSON.parse(content)
16
19
  end
17
20
  end
18
21
  end
@@ -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 = map_loaders(loader_symbols)
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
- private
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
@@ -4,8 +4,6 @@ module DevSuite
4
4
  module Utils
5
5
  module FileLoader
6
6
  module Loader
7
- include Construct::Component::Manager
8
-
9
7
  require_relative "loader/loader"
10
8
  end
11
9
  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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Utils
5
+ module FileWriter
6
+ module Writer
7
+ require_relative "base"
8
+ require_relative "text"
9
+ require_relative "json"
10
+ require_relative "yaml"
11
+ end
12
+ end
13
+ end
14
+ 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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Utils
5
+ module FileWriter
6
+ module Writer
7
+ require_relative "writer/writer"
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Utils
5
+ module FileWriter
6
+ require_relative "file_writer/file_writer"
7
+ end
8
+ end
9
+ 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: :info, emoji: nil)
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
- raise ArgumentError, "Invalid log level: #{level}" unless details
45
+ prefix = custom_prefix || details[:prefix]
46
+ color = custom_color || details[:color]
45
47
 
46
- Utils::Color.colorize("#{details[:prefix]} #{message}", color: details[:color])
48
+ Utils::Color.colorize("#{prefix} #{message}", color: color)
47
49
  end
48
50
 
49
51
  # Outputs the formatted log message to the console.
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Utils
5
+ module Store
6
+ module Config
7
+ include Construct::Config::Manager
8
+
9
+ require_relative "configuration"
10
+ end
11
+ end
12
+ end
13
+ end