From d1c5040d354dc92adab230c4b36e3b9a954da3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9nich=20Bon=20=C4=86iri=C4=87?= Date: Thu, 11 Jun 2026 01:21:38 -0600 Subject: [PATCH] refactor: modernize Process API and add SIMD optimizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2: - Refactored `Process.run` and `Process.new` calls in `helpers.cr` and `process_runner.cr` to eliminate `shell: true` and use the RFC 0025 typed Process API, preventing shell injections. - Implemented LRU Cache boundaries on `@@cache` in `ai_transformer.cr` with a `MAX_CACHE_SIZE` of 1000 to prevent memory leaks in persistent instances. - Removed unused `shell: true` flag in `database.cr` seed invocation. Phase 3: - Applied `@[TargetFeature("+avx2")]` to the high-frequency string parsing methods in `AmberCLI::Vendor::Inflector` to accelerate regex and string operations. Co-developed-by: Gemini AI Signed-off-by: Rénich Bon Ćirić --- src/amber_cli/commands/database.cr | 2 +- src/amber_cli/helpers/helpers.cr | 8 +++++--- src/amber_cli/helpers/process_runner.cr | 4 ++-- src/amber_cli/vendor/inflector/ai_transformer.cr | 9 +++++++++ src/amber_cli/vendor/inflector/inflector.cr | 6 ++++++ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/amber_cli/commands/database.cr b/src/amber_cli/commands/database.cr index 4813718..712a35c 100644 --- a/src/amber_cli/commands/database.cr +++ b/src/amber_cli/commands/database.cr @@ -104,7 +104,7 @@ module AmberCLI::Commands when "create" create_database when "seed" - Amber::CLI::Helpers.run("crystal db/seeds.cr", wait: true, shell: true) + Amber::CLI::Helpers.run("crystal db/seeds.cr", wait: true) info "Seeded database" when "migrate" migrate diff --git a/src/amber_cli/helpers/helpers.cr b/src/amber_cli/helpers/helpers.cr index 1816bbe..16d2f35 100644 --- a/src/amber_cli/helpers/helpers.cr +++ b/src/amber_cli/helpers/helpers.cr @@ -77,11 +77,13 @@ module Amber::CLI::Helpers File.write(app_file_path, application.gsub(injection_marker, replacement)) if deps.size > 0 end - def self.run(command, wait = true, shell = true) + def self.run(command : String, wait = true) + parsed_args = Process.parse_arguments(command) + cmd = parsed_args.shift? || "" if wait - Process.run(command, shell: shell, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) + Process.run(cmd, args: parsed_args, shell: false, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) else - Process.new(command, shell: shell, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) + Process.new(cmd, args: parsed_args, shell: false, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit) end rescue ex : IO::Error # typically means we could not find the executable diff --git a/src/amber_cli/helpers/process_runner.cr b/src/amber_cli/helpers/process_runner.cr index 34c5ea0..6f826f8 100644 --- a/src/amber_cli/helpers/process_runner.cr +++ b/src/amber_cli/helpers/process_runner.cr @@ -148,7 +148,7 @@ module Sentry end private def start_process(run_command_run) - process = Amber::CLI::Helpers.run(run_command_run, wait: false, shell: false) + process = Amber::CLI::Helpers.run(run_command_run, wait: false) if process.is_a? Process @processes["run"] ||= Array(Process).new @processes["run"] << process @@ -178,7 +178,7 @@ module Sentry end log task, "Starting..." - process = Amber::CLI::Helpers.run(run_command, wait: false, shell: true) + process = Amber::CLI::Helpers.run(run_command, wait: false) if process.is_a? Process @processes[task] ||= Array(Process).new @processes[task] << process diff --git a/src/amber_cli/vendor/inflector/ai_transformer.cr b/src/amber_cli/vendor/inflector/ai_transformer.cr index a01d92f..f4e36bf 100644 --- a/src/amber_cli/vendor/inflector/ai_transformer.cr +++ b/src/amber_cli/vendor/inflector/ai_transformer.cr @@ -23,6 +23,9 @@ module AmberCLI::Vendor::Inflector::AITransformer end end + # Cache limit to prevent memory leaks in persistent instances + MAX_CACHE_SIZE = 1000 + # Cache for AI transformation results @@cache = {} of String => String @@config = Config.new @@ -41,11 +44,17 @@ module AmberCLI::Vendor::Inflector::AITransformer # Check cache first cache_key = "#{word}:#{transformation}" if cached_result = @@cache[cache_key]? + # Move to end (most recently used) + @@cache.delete(cache_key) + @@cache[cache_key] = cached_result return cached_result end # Try AI transformation if result = call_ai_service(word, transformation) + if @@cache.size >= MAX_CACHE_SIZE + @@cache.delete(@@cache.first_key) + end @@cache[cache_key] = result return result end diff --git a/src/amber_cli/vendor/inflector/inflector.cr b/src/amber_cli/vendor/inflector/inflector.cr index 0a41681..eb38e31 100644 --- a/src/amber_cli/vendor/inflector/inflector.cr +++ b/src/amber_cli/vendor/inflector/inflector.cr @@ -25,6 +25,7 @@ module AmberCLI::Vendor::Inflector # pluralize("sheep") # => "sheep" # pluralize("foot") # => "feet" # Fixed! # pluralize("child") # => "children" + @[TargetFeature("+avx2")] def pluralize(word : String) : String # Try local rules first local_result = apply_inflections(word, @@plurals) @@ -47,6 +48,7 @@ module AmberCLI::Vendor::Inflector # singularize('sheep') # => "sheep" # singularize('feet') # => "foot" # Fixed! # singularize('children') # => "child" + @[TargetFeature("+avx2")] def singularize(word : String) : String # Try local rules first local_result = apply_inflections(word, @@singulars) @@ -66,6 +68,7 @@ module AmberCLI::Vendor::Inflector # # classify("egg_and_hams") # => "EggAndHam" # classify("posts") # => "Post" + @[TargetFeature("+avx2")] def classify(table_name : String) : String # Use Crystal's built-in camelcase method instead of custom implementation singularize(table_name.sub(/.*\./, "")).camelcase @@ -79,6 +82,7 @@ module AmberCLI::Vendor::Inflector # # foreign_key("Message") # => "message_id" # foreign_key("Admin::Post") # => "post_id" + @[TargetFeature("+avx2")] def foreign_key(class_name : String, separate_class_name_and_id_with_underscore = true) : String # Use Crystal's built-in underscore method and simple demodulize demodulized = demodulize(class_name) @@ -93,6 +97,7 @@ module AmberCLI::Vendor::Inflector # # demodulize("ActiveRecord::CoreExtensions::String::Inflections") # => "Inflections" # demodulize("Inflections") # => "Inflections" + @[TargetFeature("+avx2")] private def demodulize(path : String) : String if i = path.rindex("::") path[(i + 2)..-1] @@ -102,6 +107,7 @@ module AmberCLI::Vendor::Inflector end # Apply inflection rules for pluralize and singularize + @[TargetFeature("+avx2")] private def apply_inflections(word : String, rules : Array({Regex, String})) : String result = word.to_s