Module: Clacky::ApiExtensionLoader
- Defined in:
- lib/clacky/api_extension_loader.rb
Overview
Discovers and loads user-defined HTTP API extensions from ~/.clacky/api_ext/<name>/handler.rb. Each handler is expected to define a subclass of Clacky::ApiExtension; the subclass is auto-registered with the framework and its routes become available under /api/ext/<name>/.
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")
- DISABLED_DIR =
"_disabled"
Class Method Summary collapse
- .last_result ⇒ Object
- .load_all(dir: DEFAULT_DIR) ⇒ 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/<name>/handler.rb.
Class Method Details
.last_result ⇒ Object
38 39 40 |
# File 'lib/clacky/api_extension_loader.rb', line 38 def last_result @last_result || load_all end |
.load_all(dir: DEFAULT_DIR) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/clacky/api_extension_loader.rb', line 20 def load_all(dir: DEFAULT_DIR) result = Result.new(loaded: [], skipped: []) Clacky::ApiExtension.reset_registry! 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
42 43 44 45 46 47 48 49 50 51 52 53 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 |
# File 'lib/clacky/api_extension_loader.rb', line 42 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/<name>/handler.rb. Returns the path to the generated file.
114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/clacky/api_extension_loader.rb', line 114 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 |