class Syntropy::App
def calculate_route_proc(entry)
def calculate_route_proc(entry) render_proc = route_render_proc(entry) @router.calc_route_proc_with_hooks(entry, render_proc) end
def call(req)
def call(req) entry = @router[req.path] render_entry(req, entry) rescue Syntropy::Error => e msg = e.message req.respond(msg.empty? ? nil : msg, ':status' => e.http_status) rescue StandardError => e p e p e.backtrace req.respond(e.message, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR) end
def default_app(opts)
def default_app(opts) new(opts[:machine], opts[:location], opts[:mount_path] || '/', opts) end
def initialize(machine, location, mount_path, opts = {})
def initialize(machine, location, mount_path, opts = {}) @machine = machine @location = File.expand_path(location) @mount_path = mount_path @opts = opts @module_loader = Syntropy::ModuleLoader.new(@location, @opts) @router = Syntropy::Router.new(@opts, @module_loader) @machine.spin do # we do startup stuff asynchronously, in order to first let TP2 do its # setup tasks @machine.sleep 0.15 @opts[:logger]&.call("Serving from #{File.expand_path(@location)}") @router.start_file_watcher if opts[:watch_files] end end
def load(opts)
def load(opts) site_file_app(opts) || default_app(opts) end
def load_module(entry)
def load_module(entry) ref = entry[:fn].gsub(%r{^#{@location}/}, '').gsub(/\.rb$/, '') o = @module_loader.load(ref) o.is_a?(Papercraft::Template) ? wrap_template(o) : o rescue Exception => e @opts[:logger]&.call("Error while loading module #{ref}: #{e.message}") :invalid end
def render_entry(req, entry)
def render_entry(req, entry) kind = entry[:kind] return respond_not_found(req) if kind == :not_found entry[:proc] ||= calculate_route_proc(entry) entry[:proc].(req) end
def render_markdown(fn)
def render_markdown(fn) atts, md = Syntropy.parse_markdown_file(fn, @opts) if atts[:layout] layout = @module_loader.load("_layout/#{atts[:layout]}") html = layout.apply(**atts) { emit_markdown(md) }.render else html = Papercraft.markdown(md) end html end
def respond_markdown(req, entry)
def respond_markdown(req, entry) entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])] headers = { 'Content-Type' => entry[:mime_type] } req.respond_by_http_method( 'head' => [nil, headers], 'get' => -> { [render_markdown(entry[:fn]), headers] } ) end
def respond_module(req, entry)
def respond_module(req, entry) entry[:proc] ||= load_module(entry) if entry[:proc] == :invalid req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR) return end entry[:proc].call(req) rescue Syntropy::Error => e req.respond(nil, ':status' => e.http_status) rescue StandardError => e p e p e.backtrace req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR) end
def respond_not_found(req)
def respond_not_found(req) headers = { ':status' => Qeweney::Status::NOT_FOUND } case req.method when 'head' req.respond(nil, headers) else req.respond('Not found', headers) end end
def respond_static(req, entry)
def respond_static(req, entry) entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])] headers = { 'Content-Type' => entry[:mime_type] } req.respond_by_http_method( 'head' => [nil, headers], 'get' => -> { [IO.read(entry[:fn]), headers] } ) end
def route_render_proc(entry)
def route_render_proc(entry) case entry[:kind] when :static ->(req) { respond_static(req, entry) } when :markdown ->(req) { respond_markdown(req, entry) } when :module load_module(entry) else raise 'Invalid entry kind' end end
def site_file_app(opts)
def site_file_app(opts) site_fn = File.join(opts[:location], '_site.rb') return nil if !File.file?(site_fn) loader = Syntropy::ModuleLoader.new(opts[:location], opts) loader.load('_site') end
def wrap_template(template)
def wrap_template(template) lambda { |req| headers = { 'Content-Type' => template.mime_type } req.respond_by_http_method( 'head' => [nil, headers], 'get' => -> { [template.render, headers] } ) } end