dev_suite 0.2.2 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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"