dev_suite 0.2.2 → 0.2.4

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +37 -0
  3. data/Gemfile +11 -6
  4. data/Gemfile.lock +15 -3
  5. data/README.md +69 -46
  6. data/dev_suite.gemspec +7 -1
  7. data/exe/devsuite +6 -0
  8. data/lib/dev_suite/cli/commands/base.rb +16 -0
  9. data/lib/dev_suite/cli/commands/tree.rb +42 -0
  10. data/lib/dev_suite/cli/commands/version.rb +13 -0
  11. data/lib/dev_suite/cli/commands.rb +11 -0
  12. data/lib/dev_suite/cli/main.rb +32 -0
  13. data/lib/dev_suite/cli.rb +12 -0
  14. data/lib/dev_suite/directory_tree/config.rb +31 -6
  15. data/lib/dev_suite/directory_tree/directory_tree.rb +21 -0
  16. data/lib/dev_suite/directory_tree/renderer/simple.rb +3 -3
  17. data/lib/dev_suite/directory_tree/settings.rb +1 -12
  18. data/lib/dev_suite/directory_tree/visualizer/base.rb +13 -0
  19. data/lib/dev_suite/directory_tree/visualizer/tree.rb +50 -0
  20. data/lib/dev_suite/directory_tree/visualizer.rb +14 -13
  21. data/lib/dev_suite/directory_tree.rb +1 -8
  22. data/lib/dev_suite/emoji.rb +33 -0
  23. data/lib/dev_suite/error_handler.rb +22 -0
  24. data/lib/dev_suite/logger.rb +56 -0
  25. data/lib/dev_suite/performance/analyzer.rb +6 -23
  26. data/lib/dev_suite/performance/config.rb +25 -4
  27. data/lib/dev_suite/performance/profiler/execution_time.rb +29 -0
  28. data/lib/dev_suite/performance/profiler/memory.rb +20 -14
  29. data/lib/dev_suite/performance/profiler.rb +18 -1
  30. data/lib/dev_suite/performance/profiler_manager.rb +55 -0
  31. data/lib/dev_suite/performance/reportor/helpers/stat_mappings.rb +40 -0
  32. data/lib/dev_suite/performance/reportor/helpers/table_builder.rb +47 -0
  33. data/lib/dev_suite/performance/reportor/helpers.rb +12 -0
  34. data/lib/dev_suite/performance/reportor/simple.rb +7 -61
  35. data/lib/dev_suite/performance/reportor.rb +1 -0
  36. data/lib/dev_suite/performance.rb +1 -0
  37. data/lib/dev_suite/utils/config_tools/configuration.rb +103 -10
  38. data/lib/dev_suite/utils/config_tools/settings.rb +0 -8
  39. data/lib/dev_suite/version.rb +1 -1
  40. data/lib/dev_suite.rb +9 -6
  41. metadata +67 -6
  42. data/lib/dev_suite/performance/profiler/benchmark.rb +0 -15
@@ -2,13 +2,6 @@
2
2
 
3
3
  module DevSuite
4
4
  module DirectoryTree
5
- require "pathname"
6
-
7
- require_relative "directory_tree/node"
8
- require_relative "directory_tree/config"
9
- require_relative "directory_tree/settings"
10
- require_relative "directory_tree/renderer"
11
- require_relative "directory_tree/builder"
12
- require_relative "directory_tree/visualizer"
5
+ require_relative "directory_tree/directory_tree"
13
6
  end
14
7
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Emoji
5
+ # A frozen hash that contains all the emojis used in the application.
6
+ EMOJIS = {
7
+ start: "🚀",
8
+ success: "✅",
9
+ error: "🚨❌",
10
+ retry: "đŸ’Ĩ",
11
+ info: "â„šī¸",
12
+ warning: "âš ī¸",
13
+ note: "📝", # For notes or annotations
14
+ tip: "💡", # For tips or helpful hints
15
+ important: "❗", # For important information
16
+ caution: "âš ī¸", # For warnings or cautions
17
+ document: "📄", # For representing documents or files
18
+ code: "đŸ’ģ", # For code or technical content
19
+ update: "🔄", # For updates or changes
20
+ bug: "🐞", # For bugs or issues
21
+ fix: "🔧", # For fixes or repairs
22
+ }.freeze
23
+
24
+ class << self
25
+ # Returns the emoji corresponding to the given key.
26
+ # @param key [Symbol] the key to look up the emoji
27
+ # @return [String] the corresponding emoji or an empty string if not found
28
+ def get(key)
29
+ EMOJIS[key] || ""
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module ErrorHandler
5
+ extend self # This makes all instance methods behave like class methods
6
+
7
+ def handle_error(error)
8
+ Logger.log(
9
+ "Oops! An error occurred: #{error.message}",
10
+ level: :error,
11
+ emoji: :error,
12
+ )
13
+ Logger.log(
14
+ "For more information, please refer to the README: " \
15
+ "https://github.com/patrick204nqh/dev_suite",
16
+ level: :error,
17
+ emoji: :document,
18
+ )
19
+ # exit(1)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Logger
5
+ LOG_DETAILS = {
6
+ info: { prefix: "[INFO]", color: :green },
7
+ warn: { prefix: "[WARNING]", color: :yellow },
8
+ error: { prefix: "[ERROR]", color: :red },
9
+ debug: { prefix: "[DEBUG]", color: :blue },
10
+ }.freeze
11
+
12
+ class << self
13
+ # Logs a message with an optional emoji and specified log level.
14
+ #
15
+ # @param message [String] The message to log.
16
+ # @param level [Symbol] The log level (:info, :warn, :error, :debug).
17
+ # @param emoji [String, Symbol] Optional emoji to prepend to the message.
18
+ def log(message, level: :info, emoji: nil)
19
+ emoji_icon = resolve_emoji(emoji)
20
+ formatted_message = format_message("#{emoji_icon} #{message}", level)
21
+ output_log(formatted_message)
22
+ end
23
+
24
+ private
25
+
26
+ # Resolves the emoji, either from a symbol or directly as a string.
27
+ #
28
+ # @param emoji [String, Symbol, nil] The emoji or its symbol key.
29
+ # @return [String] The resolved emoji or an empty string if none is provided.
30
+ def resolve_emoji(emoji)
31
+ return "" unless emoji
32
+
33
+ emoji.is_a?(Symbol) ? Emoji.get(emoji) : emoji
34
+ end
35
+
36
+ # Formats the log message with the appropriate prefix and color.
37
+ #
38
+ # @param message [String] The message to format.
39
+ # @param level [Symbol] The log level (:info, :warn, :error, :debug).
40
+ # @return [String] The formatted log message.
41
+ def format_message(message, level)
42
+ details = LOG_DETAILS[level]
43
+ raise ArgumentError, "Invalid log level: #{level}" unless details
44
+
45
+ Utils::Color.colorize("#{details[:prefix]} #{message}", color: details[:color])
46
+ end
47
+
48
+ # Outputs the formatted log message to the console.
49
+ #
50
+ # @param formatted_message [String] The message to output.
51
+ def output_log(formatted_message)
52
+ puts formatted_message if formatted_message
53
+ end
54
+ end
55
+ end
56
+ end
@@ -5,10 +5,7 @@ module DevSuite
5
5
  class Analyzer
6
6
  def initialize(description: "Block")
7
7
  @description = description
8
-
9
- @benchmark_profiler = Profiler::Benchmark.new
10
- @memory_profiler = Profiler::Memory.new
11
- @memory_usage = Data::MemoryUsage.new
8
+ @profiler_manager = ProfilerManager.new
12
9
  end
13
10
 
14
11
  # Analyzes the performance of the given block
@@ -17,34 +14,20 @@ module DevSuite
17
14
  def analyze(&block)
18
15
  raise ArgumentError, "No block given" unless block_given?
19
16
 
20
- memory_before = @memory_usage.current
21
- benchmark_result = profile_benchmark(&block)
22
- memory_after = @memory_usage.current
23
-
24
- memory_stats = @memory_profiler.stats(memory_before, memory_after)
25
-
26
- generate_report(benchmark_result, memory_stats)
17
+ outcome = @profiler_manager.run(&block)
18
+ generate_report(@profiler_manager.results)
19
+ outcome
27
20
  end
28
21
 
29
22
  private
30
23
 
31
- # Profiles the benchmark of the given block
32
- # @param block [Proc] The block to be benchmarked
33
- # @return [Benchmark::Tms] The benchmark result
34
- def profile_benchmark(&block)
35
- @benchmark_profiler.run do
36
- @memory_profiler.profile(&block)
37
- end
38
- end
39
-
40
24
  # Generates a performance report
41
25
  # @param benchmark_result [Benchmark::Tms] The benchmark result
42
26
  # @param memory_stats [Hash] The memory statistics
43
- def generate_report(benchmark_result, memory_stats)
27
+ def generate_report(results)
44
28
  Config.configuration.reportor.generate(
45
29
  description: @description,
46
- benchmark_result: benchmark_result,
47
- memory_stats: memory_stats,
30
+ results: results,
48
31
  )
49
32
  end
50
33
  end
@@ -5,11 +5,32 @@ module DevSuite
5
5
  class Config
6
6
  include Utils::ConfigTools::Configuration
7
7
 
8
- attr_reader :reportor
8
+ # Define configuration attributes
9
+ config_attr :profilers, default_value: [:execution_time, :memory]
10
+ config_attr :reportor, default_value: :simple
9
11
 
10
- def initialize(reportor: :simple)
11
- @reportor = Reportor.create(reportor)
12
- freeze # Make the instance of this class immutable
12
+ private
13
+
14
+ def validate_attr!(attr, value)
15
+ case attr
16
+ when :profilers
17
+ validate_array!(attr, value)
18
+ when :reportor
19
+ validate_symbol!(attr, value)
20
+ else
21
+ raise ArgumentError, "Invalid attribute: #{attr}"
22
+ end
23
+ end
24
+
25
+ def resolve_attr(attr, value)
26
+ case attr
27
+ when :profilers
28
+ Profiler.create_multiple(value)
29
+ when :reportor
30
+ Reportor.create(value)
31
+ else
32
+ value
33
+ end
13
34
  end
14
35
  end
15
36
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Performance
5
+ module Profiler
6
+ class ExecutionTime < Base
7
+ attr_reader :stats
8
+
9
+ def run(&block)
10
+ result = nil
11
+ timing = ::Benchmark.measure { result = yield }
12
+ @stats = calculate_stats(timing)
13
+ result
14
+ end
15
+
16
+ private
17
+
18
+ def calculate_stats(timing)
19
+ {
20
+ real: timing.real,
21
+ utime: timing.utime,
22
+ stime: timing.stime,
23
+ total: timing.total,
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,7 +4,7 @@ module DevSuite
4
4
  module Performance
5
5
  module Profiler
6
6
  class Memory < Base
7
- attr_reader :max_memory, :min_memory, :average_memory
7
+ attr_reader :stats
8
8
 
9
9
  def initialize
10
10
  super
@@ -12,30 +12,36 @@ module DevSuite
12
12
  @memory_points = []
13
13
  end
14
14
 
15
- def profile(&block)
16
- yield
17
- record_memory
15
+ def run(&block)
16
+ before_memory = record_memory
17
+ result = yield
18
+ after_memory = record_memory
19
+
20
+ @stats = calculate_stats(before_memory, after_memory)
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def record_memory
27
+ current_mem = @memory_usage.current
28
+ @memory_points << current_mem
29
+ current_mem
18
30
  end
19
31
 
20
- def stats(before, after)
32
+ def calculate_stats(before, after)
21
33
  memory_used = after - before
22
- @average_memory = @memory_points.sum / @memory_points.size
34
+ average_memory = @memory_points.sum / @memory_points.size
35
+
23
36
  {
24
37
  before: before,
25
38
  after: after,
26
39
  used: memory_used,
27
40
  max: @memory_points.max,
28
41
  min: @memory_points.min,
29
- avg: @average_memory,
42
+ avg: average_memory,
30
43
  }
31
44
  end
32
-
33
- private
34
-
35
- def record_memory
36
- current_mem = @memory_usage.current
37
- @memory_points << current_mem
38
- end
39
45
  end
40
46
  end
41
47
  end
@@ -4,8 +4,25 @@ module DevSuite
4
4
  module Performance
5
5
  module Profiler
6
6
  require_relative "profiler/base"
7
- require_relative "profiler/benchmark"
7
+ require_relative "profiler/execution_time"
8
8
  require_relative "profiler/memory"
9
+
10
+ class << self
11
+ def create(profiler)
12
+ case profiler
13
+ when :execution_time
14
+ ExecutionTime.new
15
+ when :memory
16
+ Memory.new
17
+ else
18
+ raise ArgumentError, "Invalid profiler: #{profiler}"
19
+ end
20
+ end
21
+
22
+ def create_multiple(profilers)
23
+ profilers.map { |profiler| create(profiler) }
24
+ end
25
+ end
9
26
  end
10
27
  end
11
28
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Performance
5
+ class ProfilerManager
6
+ attr_reader :results
7
+
8
+ def initialize
9
+ @results = {}
10
+ end
11
+
12
+ # Runs the configured profilers and returns the final result
13
+ def run(&block)
14
+ outcome = block.call
15
+ run_profilers(outcome)
16
+ end
17
+
18
+ private
19
+
20
+ # Runs each profiler and updates the results
21
+ # @param result [Object] The initial result from the block
22
+ # @return [Object] The final result after running all profilers
23
+ def run_profilers(outcome)
24
+ Config.configuration.profilers.each do |profiler|
25
+ outcome = run_profiler(profiler, outcome)
26
+ end
27
+ outcome
28
+ end
29
+
30
+ # Runs a single profiler and updates the results
31
+ # @param profiler [Profiler] The profiler to run
32
+ # @param result [Object] The current result
33
+ # @return [Object] The result after running the profiler
34
+ def run_profiler(profiler, outcome)
35
+ outcome = profiler.run { outcome }
36
+ update_results(profiler)
37
+ outcome
38
+ end
39
+
40
+ # Updates the results hash with the stats from the profiler
41
+ # @param profiler [Profiler] The profiler whose stats to update
42
+ def update_results(profiler)
43
+ key = to_snake_case(profiler.class.name.split("::").last)
44
+ @results[key] = profiler.stats
45
+ end
46
+
47
+ # Convert a class name to a snake_case symbol
48
+ # @param class_name [String] The class name to convert
49
+ # @return [Symbol] The snake_case symbol
50
+ def to_snake_case(class_name)
51
+ class_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Performance
5
+ module Reportor
6
+ module Helpers
7
+ module StatMappings
8
+ PROFILER_STAT_TITLES = {
9
+ execution_time: {
10
+ real: "Total Time (s)",
11
+ utime: "User CPU Time (s)",
12
+ stime: "System CPU Time (s)",
13
+ total: "User + System CPU Time (s)",
14
+ },
15
+ memory: {
16
+ before: "Memory Before (MB)",
17
+ after: "Memory After (MB)",
18
+ used: "Memory Used (MB)",
19
+ max: "Max Memory Used (MB)",
20
+ min: "Min Memory Used (MB)",
21
+ avg: "Avg Memory Used (MB)",
22
+ },
23
+ }.freeze
24
+
25
+ class << self
26
+ def title_for(profiler_name, stat_name)
27
+ PROFILER_STAT_TITLES.dig(profiler_name, stat_name) || format_default_title(stat_name)
28
+ end
29
+
30
+ private
31
+
32
+ def format_default_title(metric)
33
+ metric.to_s.split("_").map(&:capitalize).join(" ")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Performance
5
+ module Reportor
6
+ module Helpers
7
+ module TableBuilder
8
+ class << self
9
+ def build_table(description, results)
10
+ table = initialize_table
11
+ add_description(table, description)
12
+ add_profiler_data(table, results)
13
+ table
14
+ end
15
+
16
+ private
17
+
18
+ def initialize_table
19
+ Utils::Table::Table.new.tap do |table|
20
+ table.title = "Performance Analysis"
21
+ table.add_columns("Metric", "Value")
22
+ end
23
+ end
24
+
25
+ def add_description(table, description)
26
+ table.add_row(["Description", description])
27
+ end
28
+
29
+ def add_profiler_data(table, results)
30
+ results.each do |profiler_name, stats|
31
+ # table.add_row(["#{profiler_name.to_s.capitalize} Profiler", ""])
32
+ stats.each do |metric, value|
33
+ title = StatMappings.title_for(profiler_name, metric)
34
+ table.add_row([title, format_value(value)])
35
+ end
36
+ end
37
+ end
38
+
39
+ def format_value(value)
40
+ format("%.6f", value)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DevSuite
4
+ module Performance
5
+ module Reportor
6
+ module Helpers
7
+ require_relative "helpers/stat_mappings"
8
+ require_relative "helpers/table_builder"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -7,79 +7,25 @@ module DevSuite
7
7
  #
8
8
  # Generates the performance report
9
9
  #
10
- def generate(description:, benchmark_result:, memory_stats:)
11
- table = create_table
12
- populate_table(
13
- table,
14
- description: description,
15
- benchmark_result: benchmark_result,
16
- memory_stats: memory_stats,
17
- )
10
+ # @param description [String] The description of the analysis
11
+ # @param results [Hash] The profiling results from the profilers
12
+ #
13
+ def generate(description:, results:)
14
+ table = Helpers::TableBuilder.build_table(description, results)
18
15
  render_table(table)
19
16
  end
20
17
 
21
18
  private
22
19
 
23
20
  #
24
- # Creates a new table with the specified configuration
25
- #
26
- def create_table
27
- table = Utils::Table::Table.new
28
- table.tap do |t|
29
- t.title = "Performance Analysis"
30
- t.add_columns("Metric", "Value")
31
- end
32
- end
33
-
34
- #
35
- # Populates the table with benchmark and memory statistics
36
- #
37
- def populate_table(table, description: "", benchmark_result: nil, memory_stats: {})
38
- validate_benchmark_result(benchmark_result)
39
- validate_memory_stats(memory_stats)
40
-
41
- table.add_row(["Description", description])
42
- table.add_row(["Total Time (s)", format("%.6f", benchmark_result.real)])
43
- table.add_row(["User CPU Time (s)", format("%.6f", benchmark_result.utime)])
44
- table.add_row(["System CPU Time (s)", format("%.6f", benchmark_result.stime)])
45
- table.add_row(["User + System CPU Time (s)", format("%.6f", benchmark_result.total)])
46
- table.add_row(["Memory Before (MB)", format("%.2f", memory_stats[:before])])
47
- table.add_row(["Memory After (MB)", format("%.2f", memory_stats[:after])])
48
- table.add_row(["Memory Used (MB)", format("%.2f", memory_stats[:used])])
49
- table.add_row(["Max Memory Used (MB)", format("%.2f", memory_stats[:max])])
50
- table.add_row(["Min Memory Used (MB)", format("%.2f", memory_stats[:min])])
51
- table.add_row(["Avg Memory Used (MB)", format("%.2f", memory_stats[:avg])])
52
- end
53
-
21
+ # Renders the table using the configured renderer
54
22
  #
55
- # Renders the table using the specified renderer
23
+ # @param table [Utils::Table::Table] The table to render
56
24
  #
57
25
  def render_table(table)
58
26
  renderer = Utils::Table::Config.configuration.renderer
59
27
  puts renderer.render(table)
60
28
  end
61
-
62
- #
63
- # Validates the benchmark_result object
64
- #
65
- def validate_benchmark_result(benchmark_result)
66
- required_methods = [:real, :utime, :stime, :total]
67
- missing_methods = required_methods.reject { |method| benchmark_result.respond_to?(method) }
68
- unless missing_methods.empty?
69
- raise ArgumentError, "benchmark_result is missing required methods: #{missing_methods.join(", ")}"
70
- end
71
- end
72
-
73
- #
74
- # Validates the memory_stats hash
75
- #
76
- def validate_memory_stats(memory_stats)
77
- required_keys = [:before, :after, :used, :max, :min, :avg]
78
- missing_keys = required_keys.reject { |key| memory_stats.key?(key) }
79
- unless missing_keys.empty?
80
- raise ArgumentError, "memory_stats is missing required keys: #{missing_keys.join(", ")}"
81
- end
82
- end
83
29
  end
84
30
  end
85
31
  end
@@ -5,6 +5,7 @@ module DevSuite
5
5
  module Reportor
6
6
  require_relative "reportor/base"
7
7
  require_relative "reportor/simple"
8
+ require_relative "reportor/helpers"
8
9
 
9
10
  class << self
10
11
  def create(reportor)
@@ -7,6 +7,7 @@ module DevSuite
7
7
 
8
8
  require_relative "performance/data"
9
9
  require_relative "performance/profiler"
10
+ require_relative "performance/profiler_manager"
10
11
  require_relative "performance/reportor"
11
12
  require_relative "performance/analyzer"
12
13
  require_relative "performance/config"