Class: Desiru::API::SinatraIntegration
- Inherits:
-
Object
- Object
- Desiru::API::SinatraIntegration
- Includes:
- PersistenceExtension
- Defined in:
- lib/desiru/api/sinatra_integration.rb
Overview
Sinatra integration for Desiru - lightweight REST API generation
Instance Attribute Summary collapse
-
#async_enabled ⇒ Object
readonly
Returns the value of attribute async_enabled.
-
#modules ⇒ Object
readonly
Returns the value of attribute modules.
-
#stream_enabled ⇒ Object
readonly
Returns the value of attribute stream_enabled.
Instance Method Summary collapse
-
#generate_api ⇒ Object
Generate a Sinatra application with all registered modules.
-
#initialize(async_enabled: true, stream_enabled: false) ⇒ SinatraIntegration
constructor
A new instance of SinatraIntegration.
-
#register_module(path, desiru_module, description: nil) ⇒ Object
Register a Desiru module with an endpoint path.
-
#to_rack_app ⇒ Object
Mount the API in a Rack application with CORS.
Methods included from PersistenceExtension
Constructor Details
#initialize(async_enabled: true, stream_enabled: false) ⇒ SinatraIntegration
Returns a new instance of SinatraIntegration.
14 15 16 17 18 |
# File 'lib/desiru/api/sinatra_integration.rb', line 14 def initialize(async_enabled: true, stream_enabled: false) @modules = {} @async_enabled = async_enabled @stream_enabled = stream_enabled end |
Instance Attribute Details
#async_enabled ⇒ Object (readonly)
Returns the value of attribute async_enabled.
12 13 14 |
# File 'lib/desiru/api/sinatra_integration.rb', line 12 def async_enabled @async_enabled end |
#modules ⇒ Object (readonly)
Returns the value of attribute modules.
12 13 14 |
# File 'lib/desiru/api/sinatra_integration.rb', line 12 def modules @modules end |
#stream_enabled ⇒ Object (readonly)
Returns the value of attribute stream_enabled.
12 13 14 |
# File 'lib/desiru/api/sinatra_integration.rb', line 12 def stream_enabled @stream_enabled end |
Instance Method Details
#generate_api ⇒ Object
Generate a Sinatra application with all registered modules
29 30 31 32 33 34 35 36 37 38 39 40 41 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/desiru/api/sinatra_integration.rb', line 29 def generate_api modules_config = @modules async = @async_enabled stream = @stream_enabled Class.new(Sinatra::Base) do set :show_exceptions, false set :raise_errors, true # Helpers for parameter validation and response formatting helpers do def validate_type(value, type_string) case type_string.to_s.downcase when 'string', 'str' value.is_a?(String) when 'integer', 'int' value.is_a?(Integer) || (value.is_a?(String) && value.match?(/\A-?\d+\z/)) when 'float' value.is_a?(Numeric) when 'boolean', 'bool' [true, false, 'true', 'false'].include?(value) when /^list/ value.is_a?(Array) else true # Unknown types and literals pass validation end end def coerce_value(value, type_string) case type_string.to_s.downcase when 'integer', 'int' value.to_i when 'float' value.to_f when 'boolean', 'bool' ['true', true].include?(value) else value end end def format_response(result) if result.is_a?(Desiru::ModuleResult) result.data elsif result.is_a?(Hash) result else { result: result } end end def handle_async_request(desiru_module, inputs) result = desiru_module.call_async(inputs) { job_id: result.job_id, status: result.status, progress: result.progress, status_url: "/api/v1/jobs/#{result.job_id}" } end def parse_json_body request.body.rewind JSON.parse(request.body.read) rescue JSON::ParserError halt 400, json(error: 'Invalid JSON') end end # Content type handling before do content_type :json end # Health check endpoint get '/api/v1/health' do json status: 'ok', timestamp: Time.now.iso8601 end # Generate endpoints for each registered module modules_config.each do |path, config| desiru_module = config[:module] # Main module endpoint post "/api/v1#{path}" do params = parse_json_body # Convert string keys to symbols for module call symbolized_params = {} params.each { |k, v| symbolized_params[k.to_sym] = v } begin result = desiru_module.call(symbolized_params) json format_response(result) rescue Desiru::ModuleError => e halt 400, json(error: e.) rescue StandardError => e halt 500, json(error: e.) end end # Async endpoint if async && desiru_module.respond_to?(:call_async) post "/api/v1/async#{path}" do params = parse_json_body begin result = handle_async_request(desiru_module, params) status 202 json result rescue StandardError => e halt 500, json(error: e.) end end end # Streaming endpoint (Server-Sent Events) next unless stream && desiru_module.respond_to?(:call_stream) post "/api/v1/stream#{path}" do content_type 'text/event-stream' stream do |out| params = parse_json_body begin desiru_module.call_stream(params) do |chunk| out << "event: chunk\n" out << "data: #{JSON.generate(chunk)}\n\n" end # Send final result result = desiru_module.call(params) out << "event: result\n" out << "data: #{JSON.generate(format_response(result))}\n\n" out << "event: done\n" out << "data: #{JSON.generate({ status: 'complete' })}\n\n" rescue StandardError => e out << "event: error\n" out << "data: #{JSON.generate(error: e.)}\n\n" ensure out.close end end end end # Job status endpoint for async requests if async get '/api/v1/jobs/:job_id' do job_id = params[:job_id] if Desiru.respond_to?(:check_job_status) status = Desiru.check_job_status(job_id) if status json status else halt 404, json(error: 'Job not found') end else halt 501, json(error: 'Async job tracking not implemented') end end end end end |
#register_module(path, desiru_module, description: nil) ⇒ Object
Register a Desiru module with an endpoint path
21 22 23 24 25 26 |
# File 'lib/desiru/api/sinatra_integration.rb', line 21 def register_module(path, desiru_module, description: nil) @modules[path] = { module: desiru_module, description: description || "Endpoint for #{desiru_module.class.name}" } end |
#to_rack_app ⇒ Object
Mount the API in a Rack application with CORS
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/desiru/api/sinatra_integration.rb', line 198 def to_rack_app api = generate_api Rack::Builder.new do use Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: %i[get post put patch delete options head], expose: ['Access-Control-Allow-Origin'] end end run api end end |