diff --git a/Gemfile.lock b/Gemfile.lock index 50c9ee2..52f6a7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,9 +2,7 @@ PATH remote: . specs: ruby-skill-bench (1.2.0) - activesupport (>= 6.0) cgi (~> 0.5.1) - dotenv (~> 3.2.0) faraday (~> 2.14) json (~> 2.19) parallel (~> 1.26) @@ -12,19 +10,6 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (8.1.3) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - json - logger (>= 1.4.2) - minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - uri (>= 0.13.1) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) @@ -32,7 +17,6 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - base64 (0.3.0) bigdecimal (4.1.2) bundler-audit (0.9.3) bundler (>= 1.2.0) @@ -43,15 +27,12 @@ GEM coercible (1.0.0) descendants_tracker (~> 0.0.1) concurrent-ruby (1.3.7) - connection_pool (3.0.2) crack (1.0.1) bigdecimal rexml descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) docile (1.4.1) - dotenv (3.2.0) - drb (2.2.3) dry-configurable (1.4.0) dry-core (~> 1.0) zeitwerk (~> 2.6) @@ -98,8 +79,6 @@ GEM prism (~> 1.7) sexp_processor (~> 4.8) hashdiff (1.2.1) - i18n (1.15.2) - concurrent-ruby (~> 1.0) ice_nine (0.11.2) json (2.20.0) language_server-protocol (3.17.0.5) @@ -179,7 +158,6 @@ GEM simplecov (>= 0.22.0) tty-which (~> 0.5.0) virtus (~> 2.0) - securerandom (0.4.1) sexp_processor (4.17.5) simplecov (0.22.0) docile (~> 1.1) @@ -195,8 +173,6 @@ GEM thor (1.5.0) thread_safe (0.3.6) tty-which (0.5.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) unicode-display_width (2.6.0) uri (1.1.1) virtus (2.0.0) @@ -233,23 +209,18 @@ DEPENDENCIES yard (~> 0.9) CHECKSUMS - activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 axiom-types (0.1.1) sha256=c1ff113f3de516fa195b2db7e0a9a95fd1b08475a502ff660d04507a09980383 - base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 cgi (0.5.2) sha256=61ca30298171190fd4fa0d8018e57ada456eae9b7a2b78526debf7f0a0e6f8bb childprocess (5.1.0) sha256=9a8d484be2fd4096a0e90a0cd3e449a05bc3aa33f8ac9e4d6dcef6ac1455b6ec coercible (1.0.0) sha256=5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0 - connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e descendants_tracker (0.0.4) sha256=e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897 docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e - dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d - drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 dry-configurable (1.4.0) sha256=e35d1b5f3c081753ef361f564919db79000f32cfa6f20ee3a3ba5921b41b73ce dry-core (1.2.0) sha256=0cc5a7da88df397f153947eeeae42e876e999c1e30900f3c536fb173854e96a1 dry-inflector (1.3.1) sha256=7fb0c2bb04f67638f25c52e7ba39ab435d922a3a5c3cd196120f63accb682dcc @@ -263,7 +234,6 @@ CHECKSUMS flay (2.14.4) sha256=a62d96a51d1da185aa41ba95b696966df9f7d1d91a457709277f24515895de77 flog (4.9.4) sha256=12cc054fab7a2cbd2a906514397c4d7788954d530564782d6f14939dc2dfbcbb hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1 - i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5 ice_nine (0.11.2) sha256=5d506a7d2723d5592dc121b9928e4931742730131f22a1a37649df1c1e2e63db json (2.20.0) sha256=9362bc6e55a952b056abf9167cf053358181c904cb70cd6eee0808ea830fc32b language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc @@ -295,7 +265,6 @@ CHECKSUMS ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef ruby_parser (3.22.0) sha256=1eb4937cd9eb220aa2d194e352a24dba90aef00751e24c8dfffdb14000f15d23 rubycritic (4.12.0) sha256=024fed90fe656fa939f6ea80aab17569699ac3863d0b52fd72cb99892247abc8 - securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 sexp_processor (4.17.5) sha256=ae2b48ba98353d5d465ce8759836b7a05f2e12c5879fcd14d7815b026de32f0e simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5 simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246 @@ -305,7 +274,6 @@ CHECKSUMS thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 thread_safe (0.3.6) sha256=9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a tty-which (0.5.0) sha256=5824055f0d6744c97e7c4426544f01d519c40d1806ef2ef47d9854477993f466 - tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 virtus (2.0.0) sha256=8841dae4eb7fcc097320ba5ea516bf1839e5d056c61ee27138aa4bddd6e3d1c2 diff --git a/lib/skill_bench.rb b/lib/skill_bench.rb index 4ffeada..a93d6ab 100644 --- a/lib/skill_bench.rb +++ b/lib/skill_bench.rb @@ -89,9 +89,6 @@ require_relative 'skill_bench/trend_tracker/persistence' require_relative 'skill_bench/trend_tracker/trend_calculator' -# Rails integrations -require_relative 'skill_bench/rails/skill_templates' - # Migration utilities require_relative 'skill_bench/migration/provider_migrator' diff --git a/lib/skill_bench/commands/skill_new.rb b/lib/skill_bench/commands/skill_new.rb index 79eecad..a0ee6a5 100644 --- a/lib/skill_bench/commands/skill_new.rb +++ b/lib/skill_bench/commands/skill_new.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'fileutils' -require_relative '../rails/skill_templates' module SkillBench module Commands @@ -107,6 +106,9 @@ def self.create_rails_skill(path, name, template) file_name = RAILS_TEMPLATES[template] raise ArgumentError, "Invalid template: #{template}. Use one of: #{RAILS_TEMPLATES.keys.join(', ')}." unless file_name + # Lazily load the scaffold generator so a normal `skill-bench run` does + # not pull it (and its dependencies) in at boot. + require_relative '../rails/skill_templates' content = Rails::SkillTemplates.public_send(template.to_sym, name) File.write(File.join(path, file_name), content) end diff --git a/lib/skill_bench/rails/skill_templates.rb b/lib/skill_bench/rails/skill_templates.rb index 95687cb..a3b46f3 100644 --- a/lib/skill_bench/rails/skill_templates.rb +++ b/lib/skill_bench/rails/skill_templates.rb @@ -1,16 +1,30 @@ # frozen_string_literal: true -require 'active_support/inflector' - module SkillBench module Rails # Generates Rails-specific skill templates class SkillTemplates + # Convert a snake_case or kebab-case name to CamelCase. + # + # Replaces ActiveSupport's +String#camelize+ for the scaffold inputs used + # here: it splits on +_+ and +-+ separators, upcases the first letter of + # each segment, and preserves any segment that is already CamelCase. + # + # @example + # SkillTemplates.camelize('user_creator') # => "UserCreator" + # SkillTemplates.camelize('order-service') # => "OrderService" + # SkillTemplates.camelize('UserCreator') # => "UserCreator" + # @param name [String] snake_case, kebab-case, or already-CamelCase name + # @return [String] CamelCase name + def self.camelize(name) + name.split(/[-_]/).map { |segment| segment.empty? ? segment : segment[0].upcase + segment[1..] }.join + end + # Generate a service object template # @param name [String] Service name (e.g., 'my_service' or 'my-service') # @return [String] Service object Ruby class def self.service_object(name) - class_name = name.split(/[-_]/).map(&:capitalize).join + class_name = camelize(name) <<~RUBY # frozen_string_literal: true @@ -43,7 +57,7 @@ def call # @param name [String] Concern name (e.g., 'my_concern') # @return [String] Concern module def self.concern(name) - module_name = name.camelize + module_name = camelize(name) <<~RUBY # frozen_string_literal: true @@ -67,7 +81,7 @@ module #{module_name} # @param name [String] Model name (e.g., 'my_model') # @return [String] ActiveRecord model class def self.active_record_model(name) - class_name = name.camelize + class_name = camelize(name) <<~RUBY # frozen_string_literal: true diff --git a/ruby-skill-bench.gemspec b/ruby-skill-bench.gemspec index b39e53b..6a31d2a 100644 --- a/ruby-skill-bench.gemspec +++ b/ruby-skill-bench.gemspec @@ -23,9 +23,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] # Runtime dependencies - spec.add_dependency 'activesupport', '>= 6.0' - spec.add_dependency 'cgi', '~> 0.5.1' - spec.add_dependency 'dotenv', '~> 3.2.0' + spec.add_dependency 'cgi', '~> 0.5.1' spec.add_dependency 'faraday', '~> 2.14' spec.add_dependency 'json', '~> 2.19' spec.add_dependency 'parallel', '~> 1.26' diff --git a/test/agent_eval/rails/skill_templates_test.rb b/test/agent_eval/rails/skill_templates_test.rb index 3468273..a70b704 100644 --- a/test/agent_eval/rails/skill_templates_test.rb +++ b/test/agent_eval/rails/skill_templates_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'test_helper' +require 'skill_bench/rails/skill_templates' module SkillBench module Rails @@ -33,6 +34,22 @@ def test_active_record_model_generates_valid_ruby assert_includes code, 'class UserProfile < ApplicationRecord' assert_includes code, 'validates :name, presence: true' end + + def test_camelize_snake_case + assert_equal 'UserCreator', SkillTemplates.camelize('user_creator') + end + + def test_camelize_kebab_case + assert_equal 'OrderService', SkillTemplates.camelize('order-service') + end + + def test_camelize_already_camel_case + assert_equal 'UserCreator', SkillTemplates.camelize('UserCreator') + end + + def test_camelize_mixed_separators + assert_equal 'MyCoolSkill', SkillTemplates.camelize('my-cool_skill') + end end end end