Module: Clacky::ApiExtensionLoader
- Defined in:
- lib/clacky/api_extension_loader.rb
Overview
Discovers and loads user-defined HTTP API extensions from
~/.clacky/api_ext/
A broken extension (syntax error, missing base class, route conflict) is isolated: skipped with a logged warning, never aborts the load of others.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- DEFAULT_DIR =
File.("~/.clacky/api_ext")
- BUILTIN_DIR =
File.("../default_extensions", __FILE__)
- DISABLED_DIR =
"_disabled"
Class Method Summary collapse
- .last_result ⇒ Object
- .load_all(dir: DEFAULT_DIR, builtin: true) ⇒ Object
- .load_one(ext_id, ext_dir, handler_path, result) ⇒ Object
-
.scaffold(name, dir: DEFAULT_DIR) ⇒ Object
Generate a starter handler.rb at ~/.clacky/api_ext/
/handler.rb.
Class Method Details
.last_result ⇒ Object
50 51 52 |
# File 'lib/clacky/api_extension_loader.rb', line 50 def last_result @last_result || load_all end |
.load_all(dir: DEFAULT_DIR, builtin: true) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/clacky/api_extension_loader.rb', line 21 def load_all(dir: DEFAULT_DIR, builtin: true) result = Result.new(loaded: [], skipped: []) Clacky::ApiExtension.reset_registry! # Load built-in (gem-shipped) extensions first (lowest priority) if builtin && Dir.exist?(BUILTIN_DIR) Dir.glob(File.join(BUILTIN_DIR, "*", "handler.rb")).sort.each do |handler_path| ext_dir = File.dirname(handler_path) ext_id = File.basename(ext_dir) next if ext_id.start_with?("_") load_one(ext_id, ext_dir, handler_path, result) end end # Load user extensions (higher priority — same ext_id overwrites built-in) if Dir.exist?(dir) Dir.glob(File.join(dir, "*", "handler.rb")).sort.each do |handler_path| ext_dir = File.dirname(handler_path) ext_id = File.basename(ext_dir) next if ext_id == DISABLED_DIR || ext_id.start_with?("_") load_one(ext_id, ext_dir, handler_path, result) end end @last_result = result log_summary(result) result end |
.load_one(ext_id, ext_dir, handler_path, result) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/clacky/api_extension_loader.rb', line 54 def load_one(ext_id, ext_dir, handler_path, result) = (ext_dir) before = Clacky::ApiExtension.pending_subclasses.size require handler_path new_subclasses = Clacky::ApiExtension.pending_subclasses[before..] || [] klass = new_subclasses.last unless klass result.skipped << [ext_id, "no Clacky::ApiExtension subclass defined in handler.rb"] log_skip(ext_id, result.skipped.last[1]) return end klass.ext_id = ext_id klass.ext_dir = ext_dir klass. = if klass.routes.empty? result.skipped << [ext_id, "no routes declared (use get/post/... DSL)"] log_skip(ext_id, result.skipped.last[1]) Clacky::ApiExtension.registry.delete(ext_id) return end if (gap = validate_public_endpoints(klass, )) result.skipped << [ext_id, gap] log_skip(ext_id, gap) return end Clacky::ApiExtension.register(ext_id, klass) result.loaded << ext_id public_count = klass.public_paths.size suffix = public_count > 0 ? " (#{public_count} public)" : "" Clacky::Logger.info("[ApiExtensionLoader] Loaded '#{ext_id}' — #{klass.routes.size} route(s)#{suffix}") rescue StandardError, ScriptError => e result.skipped << [ext_id, e.] log_skip(ext_id, e.) end |
.scaffold(name, dir: DEFAULT_DIR) ⇒ Object
Generate a starter handler.rb at ~/.clacky/api_ext/
126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/clacky/api_extension_loader.rb', line 126 def scaffold(name, dir: DEFAULT_DIR) slug = name.to_s.strip.downcase.gsub(/[^a-z0-9_-]+/, "-").gsub(/\A-+|-+\z/, "") raise ArgumentError, "invalid api_ext name: #{name.inspect}" if slug.empty? target_dir = File.join(dir, slug) path = File.join(target_dir, "handler.rb") raise ArgumentError, "api_ext already exists: #{path}" if File.exist?(path) FileUtils.mkdir_p(target_dir) File.write(path, skeleton(slug)) path end |