Compare commits
2 Commits
55e87603e5
...
000e53e742
Author | SHA1 | Date |
---|---|---|
Yessiest | 000e53e742 | |
Yessiest | 447f51570a |
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
|
||||||
|
require 'landline'
|
||||||
|
|
||||||
|
app = Landline::Server.new do
|
||||||
|
path "/hello" do
|
||||||
|
bounce
|
||||||
|
get "/world" do
|
||||||
|
"Hello world!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
get "/hello/user" do
|
||||||
|
"Hello user!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run app
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
|
||||||
|
require 'landline'
|
||||||
|
|
||||||
|
app = Landline::Server.new do
|
||||||
|
get "/hello" do
|
||||||
|
header "content-type", "text/plain"
|
||||||
|
"Hello World!"
|
||||||
|
end
|
||||||
|
get "/error" do
|
||||||
|
raise StandardError, "I raised an error! (and that's very sad)"
|
||||||
|
end
|
||||||
|
handle do |status, backtrace: nil|
|
||||||
|
page = ([Landline::Util::HTTP_STATUS[status]] +
|
||||||
|
(backtrace || [""])).join("\n")
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"content-length": page.bytesize,
|
||||||
|
"content-type": "text/plain"
|
||||||
|
},
|
||||||
|
page
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run app
|
|
@ -0,0 +1,3 @@
|
||||||
|
#
|
||||||
|
# ~/.bash_logout
|
||||||
|
#
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# ~/.bash_profile
|
||||||
|
#
|
||||||
|
|
||||||
|
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
|
@ -0,0 +1,10 @@
|
||||||
|
#
|
||||||
|
# ~/.bashrc
|
||||||
|
#
|
||||||
|
|
||||||
|
# If not running interactively, don't do anything
|
||||||
|
[[ $- != *i* ]] && return
|
||||||
|
|
||||||
|
# alias ls='ls --color=auto'
|
||||||
|
# alias grep='grep --color=auto'
|
||||||
|
# PS1='[\u@\h \W]\$ '
|
Binary file not shown.
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
|
||||||
|
require 'landline'
|
||||||
|
|
||||||
|
app = Landline::Server.new do
|
||||||
|
root ENV["PWD"]
|
||||||
|
index ["index.html"]
|
||||||
|
post "/" do
|
||||||
|
formdata = form if form?
|
||||||
|
files = {}
|
||||||
|
if formdata
|
||||||
|
formdata["form_files"].each do |file|
|
||||||
|
filename = file.filename.split("/").last
|
||||||
|
`mv #{file.tempfile.path} $PWD/files/#{filename}`
|
||||||
|
files[file.filename] = "<a href=\"files/#{filename}\">#{filename}</a>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
erubi(file("index.rhtml"), { formdata: files }).run
|
||||||
|
end
|
||||||
|
serve "/files/*"
|
||||||
|
get "/" do
|
||||||
|
erubi(file("index.rhtml")).run
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run app
|
|
@ -0,0 +1,31 @@
|
||||||
|
<!DOCTYPE>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Form upload test</title>
|
||||||
|
<style>
|
||||||
|
.form { display: flex; flex-direction: column }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>File uploader</h1>
|
||||||
|
<hr/>
|
||||||
|
<p> Add files here: <p>
|
||||||
|
<form method="post"
|
||||||
|
class="form"
|
||||||
|
enctype="multipart/form-data">
|
||||||
|
<input id="form_files" type="file" name="form_files[]" multiple>
|
||||||
|
<label for="form_files">Attach file</label>
|
||||||
|
<input type="submit" value="Send form">
|
||||||
|
</form>
|
||||||
|
<% if (defined? formdata) and formdata %>
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
<% formdata.each do |key, part| %>
|
||||||
|
<li><%= key %>: <%= part %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../lib
|
|
@ -0,0 +1 @@
|
||||||
|
Example of handling forms in Landline
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.name = "landline"
|
spec.name = "landline"
|
||||||
spec.version = "0.9.3"
|
spec.version = "0.10.0"
|
||||||
spec.summary = "Elegant HTTP DSL"
|
spec.summary = "Elegant HTTP DSL"
|
||||||
spec.description = <<~DESC
|
spec.description = <<~DESC
|
||||||
Landline is a no-hard-dependencies HTTP routing DSL that was made entirely for fun.
|
Landline is a no-hard-dependencies HTTP routing DSL that was made entirely for fun.
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Landline
|
||||||
# Probe (and subclasses) DSL construct
|
# Probe (and subclasses) DSL construct
|
||||||
module ProbeConstructors
|
module ProbeConstructors
|
||||||
# Create a new erb template
|
# Create a new erb template
|
||||||
# @see {Landline::Template#new}
|
# @see Landline::Template#new
|
||||||
def erb(input, vars = {})
|
def erb(input, vars = {})
|
||||||
Landline::Templates::ERB.new(input,
|
Landline::Templates::ERB.new(input,
|
||||||
vars,
|
vars,
|
||||||
|
@ -14,7 +14,7 @@ module Landline
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create a new erb template using Erubi engine
|
# Create a new erb template using Erubi engine
|
||||||
# @see {Landline::Template#new}
|
# @see Landline::Template#new
|
||||||
# @param freeze [Boolean] whether to use frozen string literal
|
# @param freeze [Boolean] whether to use frozen string literal
|
||||||
# @param capture [Boolean] whether to enable output capturing
|
# @param capture [Boolean] whether to enable output capturing
|
||||||
def erubi(input, vars = {}, freeze: true, capture: false)
|
def erubi(input, vars = {}, freeze: true, capture: false)
|
||||||
|
|
|
@ -5,6 +5,27 @@ module Landline
|
||||||
module DSL
|
module DSL
|
||||||
# Common path methods
|
# Common path methods
|
||||||
module PathMethods
|
module PathMethods
|
||||||
|
# Bounce request if no handler found instead of issuing 404
|
||||||
|
def bounce
|
||||||
|
@origin.bounce = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a status code handler on path.
|
||||||
|
# Recursively applies to all paths unless overridden.
|
||||||
|
# @param code [Integer, nil] Specify a status code to handle
|
||||||
|
# @param block [#call] Block to run on given code
|
||||||
|
def handle(code = nil, &block)
|
||||||
|
@origin.properties["handle.#{code || 'default'}"] = block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Insert a pass-through pipeline into request processing
|
||||||
|
# (i.e. for error handling purposes).
|
||||||
|
# Passed block should yield request (and return yielded data back).
|
||||||
|
# @param block [#call] block that yields request
|
||||||
|
def pipeline(&block)
|
||||||
|
@origin.pipeline = block
|
||||||
|
end
|
||||||
|
|
||||||
# Set path index
|
# Set path index
|
||||||
# @param index [Array,String]
|
# @param index [Array,String]
|
||||||
def index(index)
|
def index(index)
|
||||||
|
|
|
@ -8,8 +8,17 @@ module Landline
|
||||||
# Common methods for template contexts
|
# Common methods for template contexts
|
||||||
module TemplateMethods
|
module TemplateMethods
|
||||||
# Import a template part
|
# Import a template part
|
||||||
|
# @param filepath [String, File] path to the file (or the file itself)
|
||||||
|
# @return [String] compiled template
|
||||||
def import(filepath)
|
def import(filepath)
|
||||||
@parent_template.import(file(filepath)).run
|
template = if filepath.is_a? File
|
||||||
|
filepath
|
||||||
|
elsif filepath.start_with? "/"
|
||||||
|
File.open(filepath)
|
||||||
|
else
|
||||||
|
file(filepath)
|
||||||
|
end
|
||||||
|
@parent_template.import(template).run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,7 @@ module Landline
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try to navigate the path. Run method callback in response.
|
# Try to navigate the path. Run method callback in response.
|
||||||
# @param [Landline::Request]
|
# @param request [Landline::Request]
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def go(request)
|
def go(request)
|
||||||
# rejected at pattern
|
# rejected at pattern
|
||||||
|
|
|
@ -5,12 +5,16 @@ require_relative 'node'
|
||||||
require_relative 'dsl/constructors_path'
|
require_relative 'dsl/constructors_path'
|
||||||
require_relative 'dsl/methods_path'
|
require_relative 'dsl/methods_path'
|
||||||
require_relative 'dsl/methods_common'
|
require_relative 'dsl/methods_common'
|
||||||
|
require_relative 'dsl/methods_probe'
|
||||||
|
require_relative 'dsl/constructors_probe'
|
||||||
require_relative 'util/lookup'
|
require_relative 'util/lookup'
|
||||||
|
|
||||||
module Landline
|
module Landline
|
||||||
# Execution context for filters and preprocessors.
|
# Execution context for filters and preprocessors.
|
||||||
class ProcessorContext
|
class ProcessorContext
|
||||||
include Landline::DSL::CommonMethods
|
include Landline::DSL::CommonMethods
|
||||||
|
include Landline::DSL::ProbeMethods
|
||||||
|
include Landline::DSL::ProbeConstructors
|
||||||
|
|
||||||
def initialize(path)
|
def initialize(path)
|
||||||
@origin = path
|
@origin = path
|
||||||
|
@ -54,21 +58,11 @@ module Landline
|
||||||
# @return [Boolean] true if further navigation will be done
|
# @return [Boolean] true if further navigation will be done
|
||||||
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
||||||
def process(request)
|
def process(request)
|
||||||
return false unless run_filters(request)
|
if @pipeline
|
||||||
|
@pipeline.call(request) { |inner_req| process_wrapped(inner_req) }
|
||||||
run_preprocessors(request)
|
else
|
||||||
enqueue_postprocessors(request)
|
process_wrapped(request)
|
||||||
@children.each do |x|
|
|
||||||
if (value = x.go(request))
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
value = index(request)
|
|
||||||
return value if value
|
|
||||||
|
|
||||||
_die(404)
|
|
||||||
rescue StandardError => e
|
|
||||||
_die(500, backtrace: [e.to_s] + e.backtrace)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a preprocessor to the path.
|
# Add a preprocessor to the path.
|
||||||
|
@ -95,7 +89,9 @@ module Landline
|
||||||
@filters.append(block)
|
@filters.append(block)
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :children, :properties
|
attr_reader :children, :properties, :request
|
||||||
|
|
||||||
|
attr_accessor :bounce, :pipeline
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
@ -123,6 +119,31 @@ module Landline
|
||||||
request.postprocessors.append(*@postprocessors)
|
request.postprocessors.append(*@postprocessors)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Method callback on successful request navigation.
|
||||||
|
# Finds the next appropriate path to go to.
|
||||||
|
# (inner pipeline-wrapped handler)
|
||||||
|
# @return [Boolean] true if further navigation will be done
|
||||||
|
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
||||||
|
def process_wrapped(request)
|
||||||
|
@request = request
|
||||||
|
return false unless run_filters(request)
|
||||||
|
|
||||||
|
run_preprocessors(request)
|
||||||
|
enqueue_postprocessors(request)
|
||||||
|
@children.each do |x|
|
||||||
|
value = x.go(request)
|
||||||
|
return value if value
|
||||||
|
end
|
||||||
|
value = index(request)
|
||||||
|
return value if value
|
||||||
|
|
||||||
|
@bounce ? false : _die(404)
|
||||||
|
rescue StandardError => e
|
||||||
|
_die(500, backtrace: [e.to_s] + e.backtrace)
|
||||||
|
ensure
|
||||||
|
@request = nil
|
||||||
|
end
|
||||||
|
|
||||||
# Try to perform indexing on the path if possible
|
# Try to perform indexing on the path if possible
|
||||||
# @param request [Landline::Request]
|
# @param request [Landline::Request]
|
||||||
# @return [Boolean] true if indexing succeeded
|
# @return [Boolean] true if indexing succeeded
|
||||||
|
@ -146,11 +167,12 @@ module Landline
|
||||||
# @raise [UncaughtThrowError] throws :finish to stop processing
|
# @raise [UncaughtThrowError] throws :finish to stop processing
|
||||||
def _die(errorcode, backtrace: nil)
|
def _die(errorcode, backtrace: nil)
|
||||||
throw :finish, [errorcode].append(
|
throw :finish, [errorcode].append(
|
||||||
*(@properties["handle.#{errorcode}"] or
|
*@proccontext.instance_exec(
|
||||||
@properties["handle.default"]).call(
|
errorcode,
|
||||||
errorcode,
|
backtrace: backtrace,
|
||||||
backtrace: backtrace
|
&(@properties["handle.#{errorcode}"] or
|
||||||
)
|
@properties["handle.default"])
|
||||||
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,7 +52,7 @@ module Landline
|
||||||
# - underscores
|
# - underscores
|
||||||
# - dashes
|
# - dashes
|
||||||
class Glob
|
class Glob
|
||||||
# @param input [String] Glob pattern
|
# @param pattern [String] Glob pattern
|
||||||
def initialize(pattern)
|
def initialize(pattern)
|
||||||
pattern = Landline::PatternMatching.canonicalize(pattern)
|
pattern = Landline::PatternMatching.canonicalize(pattern)
|
||||||
pieces = pattern.split(TOKENS)
|
pieces = pattern.split(TOKENS)
|
||||||
|
|
|
@ -39,7 +39,7 @@ module Landline
|
||||||
end
|
end
|
||||||
|
|
||||||
# Test if input is convertible and if it should be converted.
|
# Test if input is convertible and if it should be converted.
|
||||||
# @param input
|
# @param string [String] input to test conversion on
|
||||||
# @return [Boolean] Input can be safely converted to Glob
|
# @return [Boolean] Input can be safely converted to Glob
|
||||||
def self.can_convert?(string)
|
def self.can_convert?(string)
|
||||||
string.is_a? Regexp
|
string.is_a? Regexp
|
||||||
|
|
|
@ -7,7 +7,7 @@ module Landline
|
||||||
# Probe that executes a callback on request
|
# Probe that executes a callback on request
|
||||||
class Handler < Landline::Probe
|
class Handler < Landline::Probe
|
||||||
# @param path [Object]
|
# @param path [Object]
|
||||||
# @param parent [Landline::Node]
|
# @param args [Hash] hashed parameters to passthrough to Probe
|
||||||
# @param exec [#call]
|
# @param exec [#call]
|
||||||
def initialize(path, **args, &exec)
|
def initialize(path, **args, &exec)
|
||||||
super(path, **args)
|
super(path, **args)
|
||||||
|
@ -15,10 +15,10 @@ module Landline
|
||||||
@context = Landline::ProbeContext.new(self)
|
@context = Landline::ProbeContext.new(self)
|
||||||
@response = nil
|
@response = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :response
|
attr_accessor :response
|
||||||
attr_reader :request
|
attr_reader :request
|
||||||
|
|
||||||
# Method callback on successful request navigation.
|
# Method callback on successful request navigation.
|
||||||
# Runs block supplied with object initialization.
|
# Runs block supplied with object initialization.
|
||||||
# Request's #splat and #param are passed to block.
|
# Request's #splat and #param are passed to block.
|
||||||
|
|
|
@ -9,7 +9,6 @@ module Landline
|
||||||
class Serve < Landline::Probe
|
class Serve < Landline::Probe
|
||||||
# @param path [Object]
|
# @param path [Object]
|
||||||
# @param parent [Landline::Node]
|
# @param parent [Landline::Node]
|
||||||
# @param exec [#call]
|
|
||||||
def initialize(path, parent:)
|
def initialize(path, parent:)
|
||||||
super(path, parent: parent, filepath: true)
|
super(path, parent: parent, filepath: true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,9 +86,14 @@ module Landline
|
||||||
# ... (stub)
|
# ... (stub)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Import a template from within current template
|
# Import a template from within current template.
|
||||||
def import(filepath)
|
# @param file [File] template file to import
|
||||||
newtemp = self.class.new(filepath, {}, parent: @parent)
|
# @return [Landline::Template] loaded tempalte
|
||||||
|
def import(file)
|
||||||
|
newtemp = self.class.new(file,
|
||||||
|
{},
|
||||||
|
parent: @parent,
|
||||||
|
filename: file.path)
|
||||||
newtemp.binding = @binding
|
newtemp.binding = @binding
|
||||||
newtemp
|
newtemp
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ module Landline
|
||||||
module Templates
|
module Templates
|
||||||
# ERB Template language adapter
|
# ERB Template language adapter
|
||||||
class ERB < Landline::Template
|
class ERB < Landline::Template
|
||||||
# @see {Landline::Template#new}
|
# (see Landline::Template#new)
|
||||||
def initialize(input, vars = nil, parent:, filename:)
|
def initialize(input, vars = nil, parent:, filename:)
|
||||||
super
|
super
|
||||||
varname = "_part_#{SecureRandom.hex(10)}".to_sym
|
varname = "_part_#{SecureRandom.hex(10)}".to_sym
|
||||||
|
|
|
@ -7,7 +7,7 @@ module Landline
|
||||||
module Templates
|
module Templates
|
||||||
# Erubi (ERB) template language adapter
|
# Erubi (ERB) template language adapter
|
||||||
class Erubi < Landline::Template
|
class Erubi < Landline::Template
|
||||||
# @see {Landline::Template#new}
|
# (see Landline::Template#new)
|
||||||
def initialize(input,
|
def initialize(input,
|
||||||
vars = nil,
|
vars = nil,
|
||||||
**ext)
|
**ext)
|
||||||
|
|
|
@ -1267,7 +1267,7 @@ module Landline
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# Get MIME type by file extension
|
# Get MIME type by file extension
|
||||||
# @param ext [String] filename
|
# @param file [String] filename
|
||||||
# @return [String] MIME type, defaults to "application/octet-stream"
|
# @return [String] MIME type, defaults to "application/octet-stream"
|
||||||
def self.get_mime_type(file)
|
def self.get_mime_type(file)
|
||||||
MIME[file.match(/\.(\w+)$/)&.[](1)] or "application/octet-stream"
|
MIME[file.match(/\.(\w+)$/)&.[](1)] or "application/octet-stream"
|
||||||
|
|
|
@ -9,6 +9,8 @@ require_relative 'html'
|
||||||
|
|
||||||
module Landline
|
module Landline
|
||||||
module Util
|
module Util
|
||||||
|
FormPart = Struct.new(:data, :name, :filename,
|
||||||
|
:filetype, :tempfile, :headers)
|
||||||
# Valid element of form data with headers
|
# Valid element of form data with headers
|
||||||
# @!attribute headers [Hash] headers recevied from form data
|
# @!attribute headers [Hash] headers recevied from form data
|
||||||
# @!attribute name [String] name of the form part
|
# @!attribute name [String] name of the form part
|
||||||
|
@ -16,8 +18,7 @@ module Landline
|
||||||
# @!attribute filename [String,nil] Original name of the sent file
|
# @!attribute filename [String,nil] Original name of the sent file
|
||||||
# @!attribute filetype [String,nil] MIME-type of the file
|
# @!attribute filetype [String,nil] MIME-type of the file
|
||||||
# @!attribute tempfile [File,nil] Temporary file for storing sent file data.
|
# @!attribute tempfile [File,nil] Temporary file for storing sent file data.
|
||||||
FormPart = Struct.new(:data, :name, :filename,
|
class FormPart
|
||||||
:filetype, :tempfile, :headers) do
|
|
||||||
# Is this form part a file or plain data?
|
# Is this form part a file or plain data?
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def file?
|
def file?
|
||||||
|
@ -135,7 +136,8 @@ module Landline
|
||||||
end
|
end
|
||||||
|
|
||||||
# Setup file metadata
|
# Setup file metadata
|
||||||
# @part part [FormPart]
|
# @param part [Landline::Util::FormPart]
|
||||||
|
# @return [void]
|
||||||
def setup_file_meta(part)
|
def setup_file_meta(part)
|
||||||
part.name = part.headers.dig("content-disposition", 1, "name")
|
part.name = part.headers.dig("content-disposition", 1, "name")
|
||||||
part.filename = part.headers.dig("content-disposition", 1, "filename")
|
part.filename = part.headers.dig("content-disposition", 1, "filename")
|
||||||
|
@ -144,7 +146,8 @@ module Landline
|
||||||
end
|
end
|
||||||
|
|
||||||
# Setup plain metadata
|
# Setup plain metadata
|
||||||
# @part part [FormPart]
|
# @param part [Landline::Util::FormPart]
|
||||||
|
# @return [void]
|
||||||
def setup_data_meta(part)
|
def setup_data_meta(part)
|
||||||
part.name = part.headers.dig("content-disposition", 1, "name")
|
part.name = part.headers.dig("content-disposition", 1, "name")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue