Class: RubynCode::Skills::RegistryClient
- Inherits:
-
Object
- Object
- RubynCode::Skills::RegistryClient
- Defined in:
- lib/rubyn_code/skills/registry_client.rb
Overview
HTTP client for the rubyn.ai skill packs registry API. Supports ETag-based conditional requests for efficient cache validation and offline resilience.
Constant Summary collapse
- LEADING_SLASHES_REGEX =
%r{\A/+}- DEFAULT_BASE_URL =
'https://rubyn.ai'- TIMEOUT_SECONDS =
10- USER_ACCEPT_HEADER =
'Rubyn Code'
Instance Attribute Summary collapse
-
#base_url ⇒ Object
readonly
Returns the value of attribute base_url.
Instance Method Summary collapse
-
#fetch_catalog(etag: nil) ⇒ Hash
Fetch the full catalog of available skill packs.
-
#fetch_file(pack_name, file_path, etag: nil) ⇒ Hash
Fetch a single skill file’s markdown content.
-
#fetch_pack(name, etag: nil) ⇒ Hash
Fetch a single pack’s full content for installation.
-
#fetch_suggestions(gems) ⇒ Array<Hash>
Fetch pack suggestions based on detected gems.
-
#initialize(base_url: nil) ⇒ RegistryClient
constructor
A new instance of RegistryClient.
-
#list_packs(etag: nil) ⇒ Array<Hash>
List all available packs (returns flat array for CLI commands).
-
#search_packs(query, etag: nil) ⇒ Hash
Search packs by keyword.
Constructor Details
#initialize(base_url: nil) ⇒ RegistryClient
Returns a new instance of RegistryClient.
20 21 22 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 20 def initialize(base_url: nil) @base_url = base_url || ENV.fetch('RUBYN_REGISTRY_URL', DEFAULT_BASE_URL) end |
Instance Attribute Details
#base_url ⇒ Object (readonly)
Returns the value of attribute base_url.
18 19 20 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 18 def base_url @base_url end |
Instance Method Details
#fetch_catalog(etag: nil) ⇒ Hash
Fetch the full catalog of available skill packs.
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 38 def fetch_catalog(etag: nil) response = conditional_get('/api/v1/skills/packs.json', etag: etag) return not_modified_result if response.status == 304 data = validate_and_parse(response) packs = normalize_packs(data) { data: packs, etag: response.headers['etag'], not_modified: false } rescue Faraday::Error => e raise RegistryError, "Failed to fetch skill catalog: #{e.}" end |
#fetch_file(pack_name, file_path, etag: nil) ⇒ Hash
Fetch a single skill file’s markdown content.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 118 def fetch_file(pack_name, file_path, etag: nil) validate_pack_name!(pack_name) safe_path = file_path.to_s.gsub('..', '').gsub(LEADING_SLASHES_REGEX, '') response = connection.get( "/api/v1/skills/packs/#{encode_name(pack_name)}/files/#{ERB::Util.url_encode(safe_path)}" ) do |req| req.headers['If-None-Match'] = etag if etag end return { content: nil, etag: nil, not_modified: true } if response.status == 304 return { content: response.body, etag: response.headers['etag'], not_modified: false } if response.success? raise RegistryError, "Failed to fetch file '#{file_path}' from pack '#{pack_name}'" rescue Faraday::Error => e raise RegistryError, "Failed to fetch file '#{file_path}': #{e.}" end |
#fetch_pack(name, etag: nil) ⇒ Hash
Fetch a single pack’s full content for installation. Fetches pack metadata and all skill file contents.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 92 def fetch_pack(name, etag: nil) validate_pack_name!(name) response = conditional_get("/api/v1/skills/packs/#{encode_name(name)}.json", etag: etag) return not_modified_result if response.status == 304 data = validate_and_parse(response) validate_pack_response!(data, name) # Fetch individual file contents. Manifests may list files under # :files (as { path: ... } hashes) or :skills (as filename strings). files = fetch_key(data, :files) || fetch_key(data, :skills) || [] files = files.map { |f| f.is_a?(String) ? { path: f } : f } data[:files] = fetch_files_with_content(name, files) { data: data, etag: response.headers['etag'], not_modified: false } rescue Faraday::Error => e raise RegistryError, "Failed to fetch pack '#{name}': #{e.}" end |
#fetch_suggestions(gems) ⇒ Array<Hash>
Fetch pack suggestions based on detected gems.
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 71 def fetch_suggestions(gems) return [] if gems.empty? gems_param = gems.join(',') response = connection.get('/api/v1/skills/packs/suggest', { gems: gems_param }) return [] if response.status == 404 data = validate_and_parse(response) suggestions = data[:suggestions] || data['suggestions'] || [] suggestions.is_a?(Array) ? suggestions : [] rescue Faraday::Error => e raise RegistryError, "Failed to fetch suggestions: #{e.}" end |
#list_packs(etag: nil) ⇒ Array<Hash>
List all available packs (returns flat array for CLI commands).
29 30 31 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 29 def list_packs(etag: nil) fetch_catalog(etag: etag)[:data] || [] end |
#search_packs(query, etag: nil) ⇒ Hash
Search packs by keyword. Note: The registry API does not support server-side search. This method fetches the catalog and filters locally.
57 58 59 60 61 62 63 64 |
# File 'lib/rubyn_code/skills/registry_client.rb', line 57 def search_packs(query, etag: nil) catalog = fetch_catalog(etag: etag) return catalog if catalog[:not_modified] q = query.to_s.downcase filtered = catalog[:data].select { |pack| matches_query?(pack, q) } { data: filtered, etag: catalog[:etag], not_modified: false } end |