leanweb 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/leanweb +107 -0
- data/lib/leanweb/app.rb +72 -0
- data/lib/leanweb/controller.rb +89 -0
- data/lib/leanweb/route.rb +164 -0
- data/lib/leanweb.rb +30 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f0437a01908da014f67d9ff669d421e9334233e4e30563ae4b3d77ebb8c7385c
|
4
|
+
data.tar.gz: f7e810131f740cc6357ec039b4b246c650169db78ee593a88f3377e7f594a342
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b155fa1e345bac7b955a9c026c7a4eabab5166646fa59d731224234aa3fe2f702298de18d63eb37d8d7a8939857998c9465405002868b34909d04f690bd0c099
|
7
|
+
data.tar.gz: 76784d5a2b39cd6539bdd444ff57bbe346d0eb2a36c363dbf6e6349726aee0b0662e4300d6999790a92d8742d732426dfcec6b4505fe081daa8c73a6979995e5
|
data/bin/leanweb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2022 Felix Freeman <libsys@hacktivista.org>
|
4
|
+
#
|
5
|
+
# This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
|
6
|
+
# General Public License version 0.1 or (at your option) any later version. You
|
7
|
+
# should have received a copy of this license along with the software. If not,
|
8
|
+
# see <https://hacktivista.org/licenses/>.
|
9
|
+
|
10
|
+
# frozen_string_literal: true
|
11
|
+
|
12
|
+
require 'fileutils'
|
13
|
+
require 'leanweb'
|
14
|
+
|
15
|
+
# Script for building a new project.
|
16
|
+
|
17
|
+
files = [
|
18
|
+
{
|
19
|
+
filename: 'Gemfile',
|
20
|
+
content: <<~RUBY
|
21
|
+
# frozen_string_literal: true
|
22
|
+
|
23
|
+
source 'https://rubygems.org'
|
24
|
+
|
25
|
+
gem 'leanweb', '~> #{LeanWeb::VERSION}'
|
26
|
+
RUBY
|
27
|
+
}, {
|
28
|
+
filename: 'config.ru',
|
29
|
+
content: <<~RUBY
|
30
|
+
# frozen_string_literal: true
|
31
|
+
|
32
|
+
require 'leanweb'
|
33
|
+
|
34
|
+
if ENV['RACK_ENV'] == 'development'
|
35
|
+
use(Rack::Reloader, 0)
|
36
|
+
use(Rack::Static, urls: [''], root: 'public', cascade: true)
|
37
|
+
end
|
38
|
+
|
39
|
+
app = LeanWeb::App.init
|
40
|
+
run app
|
41
|
+
RUBY
|
42
|
+
}, {
|
43
|
+
filename: 'routes.rb',
|
44
|
+
content: <<~RUBY
|
45
|
+
# frozen_string_literal: true
|
46
|
+
|
47
|
+
[{ path: '/' }]
|
48
|
+
RUBY
|
49
|
+
}, {
|
50
|
+
filename: 'Rakefile',
|
51
|
+
content: <<~RUBY
|
52
|
+
# frozen_string_literal: true
|
53
|
+
|
54
|
+
require 'leanweb'
|
55
|
+
|
56
|
+
task default: %w[build]
|
57
|
+
|
58
|
+
task :build do
|
59
|
+
LeanWeb::App.init.build_static
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
}, {
|
63
|
+
filename: 'src/controllers/main.rb',
|
64
|
+
content: <<~RUBY
|
65
|
+
# frozen_string_literal: true
|
66
|
+
|
67
|
+
require 'leanweb'
|
68
|
+
|
69
|
+
# Main controller is the default controller
|
70
|
+
class MainController < LeanWeb::Controller
|
71
|
+
def index_get
|
72
|
+
render 'index.haml'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
RUBY
|
76
|
+
}, {
|
77
|
+
filename: 'src/views/index.haml',
|
78
|
+
content: <<~HAML
|
79
|
+
!!!
|
80
|
+
%html
|
81
|
+
%head
|
82
|
+
%meta{charset: "utf-8"}
|
83
|
+
%meta{name: "viewport", content: "width=device-width, initial-scale=1"}
|
84
|
+
%title LeanWeb framework
|
85
|
+
%body
|
86
|
+
%h1 It works!
|
87
|
+
HAML
|
88
|
+
}
|
89
|
+
]
|
90
|
+
|
91
|
+
if !(1..2).include?(ARGV.size) || ARGV[0] != 'new'
|
92
|
+
puts 'Usage: leanweb new [directory]'
|
93
|
+
exit(1)
|
94
|
+
end
|
95
|
+
|
96
|
+
begin
|
97
|
+
path = ARGV[1] || '.'
|
98
|
+
FileUtils.mkdir_p(path)
|
99
|
+
FileUtils.cd(path)
|
100
|
+
FileUtils.mkdir_p(['public', 'src/controllers', 'src/views'])
|
101
|
+
files.each { |file| File.write(file[:filename], file[:content]) }
|
102
|
+
`git init`
|
103
|
+
puts("Project '#{File.basename(Dir.getwd)}' successfully created.")
|
104
|
+
rescue # rubocop:disable Style/RescueStandardError
|
105
|
+
puts('Woops! Something went wrong.')
|
106
|
+
raise
|
107
|
+
end
|
data/lib/leanweb/app.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright 2022 Felix Freeman <libsys@hacktivista.org>
|
2
|
+
#
|
3
|
+
# This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
|
4
|
+
# General Public License version 0.1 or (at your option) any later version. You
|
5
|
+
# should have received a copy of this license along with the software. If not,
|
6
|
+
# see <https://hacktivista.org/licenses/>.
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
require 'rack'
|
11
|
+
|
12
|
+
module LeanWeb
|
13
|
+
# App resolves and builds static files from routes.
|
14
|
+
class App
|
15
|
+
attr_accessor :routes
|
16
|
+
|
17
|
+
# @param routes [Hash] Hash of routes with {Route} attributes.
|
18
|
+
def initialize(routes)
|
19
|
+
@routes = routes
|
20
|
+
end
|
21
|
+
|
22
|
+
# Entry point for dynamic routes (Rack).
|
23
|
+
# @param env [Hash] `env` for Rack.
|
24
|
+
def call(env)
|
25
|
+
request = Rack::Request.new(env)
|
26
|
+
route = @routes.find do |r|
|
27
|
+
(r[:method] || 'GET') == request.request_method && begin
|
28
|
+
r[:path] =~ request.path
|
29
|
+
rescue TypeError
|
30
|
+
r[:path] == request.path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
return [404, {}, ['Not found']] unless route_exists?(route)
|
34
|
+
|
35
|
+
Route.new(**route).respond(request)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Build static routes to files.
|
39
|
+
def build_static
|
40
|
+
@routes.each do |route|
|
41
|
+
route = Route.new(**route)
|
42
|
+
next unless route.static
|
43
|
+
|
44
|
+
begin
|
45
|
+
route.static.each { |seed| route.build(route.seed_path(seed)) }
|
46
|
+
rescue NoMethodError
|
47
|
+
route.build
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Initialize by evaluating the routes file.
|
53
|
+
# Do this here so users don't freak out for using eval and rubocop is happy
|
54
|
+
# on client side.
|
55
|
+
# @param file [String] Routes file.
|
56
|
+
def self.init(file = 'routes.rb')
|
57
|
+
new(eval(File.read(file))) # rubocop:disable Security/Eval
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
# Check if route exists.
|
63
|
+
# If not on development environment, serve only dynamic routes.
|
64
|
+
# @param route [Hash]
|
65
|
+
def route_exists?(route)
|
66
|
+
return false if route.nil? \
|
67
|
+
|| (ENV['RACK_ENV'] != 'development' && route[:static] != false)
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Copyright 2022 Felix Freeman <libsys@hacktivista.org>
|
2
|
+
#
|
3
|
+
# This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
|
4
|
+
# General Public License version 0.1 or (at your option) any later version. You
|
5
|
+
# should have received a copy of this license along with the software. If not,
|
6
|
+
# see <https://hacktivista.org/licenses/>.
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
require 'rack'
|
11
|
+
|
12
|
+
module LeanWeb
|
13
|
+
# Controller is a base controller with `@route`, `@request` and `@response`
|
14
|
+
# private attributes that will be shared with your views when you {render}
|
15
|
+
# Haml documents.
|
16
|
+
#
|
17
|
+
# Even if you don't {render}, you can use the `.finish` method from
|
18
|
+
# `Rack::Response` on `@response` to return a proper `Rack` response.
|
19
|
+
class Controller
|
20
|
+
# @param route [Route]
|
21
|
+
# @param request [Rack::Request]
|
22
|
+
def initialize(route, request = nil)
|
23
|
+
@route = route
|
24
|
+
@request = request
|
25
|
+
@response = Rack::Response.new(nil, 200)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Render magic. Supports dynamic (and static) Haml documents, other
|
29
|
+
# documents don't support dynamic data.
|
30
|
+
#
|
31
|
+
# Depending on {Route#path} and {#render} `path` you will render different
|
32
|
+
# documents:
|
33
|
+
#
|
34
|
+
# - {Route#path} `/a/b/c` and `render('d')` will render `src/views/a/b/d`
|
35
|
+
# - {Route#path} `/a/b/c/` and `render('d')` will render `src/views/a/b/c/d`
|
36
|
+
# - `render('~/custom')` will render `src/views/custom`.
|
37
|
+
# - `render('/custom')` will render `/custom`.
|
38
|
+
#
|
39
|
+
# @param path [String] Might be an absolute path or a relative path to
|
40
|
+
# `src/views/` + the parent path of the last non-capture-group on
|
41
|
+
# `@route.path`. You can also make it relative to {LeanWeb::VIEW_PATH} by
|
42
|
+
# prepending `~`.
|
43
|
+
# @param content_type [String] Defaults to the proper Content-Type for file
|
44
|
+
# extension, `text/plain` on unknown files.
|
45
|
+
#
|
46
|
+
# @return [Rack::Request#finish] A valid rack response.
|
47
|
+
def render(path, content_type = nil) # rubocop:disable Metrics/MethodLength
|
48
|
+
path = absolute_view_path(path)
|
49
|
+
content = File.read(path)
|
50
|
+
case File.extname(path)
|
51
|
+
when '.haml'
|
52
|
+
require('haml')
|
53
|
+
@response.write(Haml::Engine.new(content).render(binding))
|
54
|
+
@response.set_header('Content-Type', content_type || 'text/html')
|
55
|
+
else
|
56
|
+
require('erb')
|
57
|
+
@response.write(ERB.new(content).result(binding))
|
58
|
+
@response.set_header('Content-Type', content_type || 'text/plain')
|
59
|
+
end
|
60
|
+
@response.finish
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# @param relative_path [String]
|
66
|
+
# @return [String] Full path.
|
67
|
+
def absolute_view_path(relative_path)
|
68
|
+
case relative_path[0]
|
69
|
+
when '/'
|
70
|
+
relative_path
|
71
|
+
when '~'
|
72
|
+
relative_path.sub('~', LeanWeb::VIEW_PATH)
|
73
|
+
else
|
74
|
+
"#{view_path_by_route_path}/#{relative_path}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def view_path_by_route_path
|
79
|
+
LeanWeb::VIEW_PATH +
|
80
|
+
if @route.path.instance_of?(Regexp)
|
81
|
+
File.dirname(@route.str_path.sub(%r{/?\(.*}, ''))
|
82
|
+
elsif @route.path[-1] != '/'
|
83
|
+
File.dirname(@route.path)
|
84
|
+
else
|
85
|
+
@route.path.chomp('/')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# Copyright 2022 Felix Freeman <libsys@hacktivista.org>
|
2
|
+
#
|
3
|
+
# This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
|
4
|
+
# General Public License version 0.1 or (at your option) any later version. You
|
5
|
+
# should have received a copy of this license along with the software. If not,
|
6
|
+
# see <https://hacktivista.org/licenses/>.
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
module LeanWeb
|
11
|
+
# Action for {Route#action}.
|
12
|
+
Action = Struct.new(:file, :controller, :action, keyword_init: true)
|
13
|
+
|
14
|
+
# A single route which routes with the {#respond} method. It can also {#build}
|
15
|
+
# an static file.
|
16
|
+
class Route
|
17
|
+
attr_reader :path, :method, :action, :static
|
18
|
+
|
19
|
+
# A new instance of Route.
|
20
|
+
#
|
21
|
+
# @param path [String, Regexp] Path matcher, can be an String or Regexp with
|
22
|
+
# positional or named capture groups, `@action` will receive these as
|
23
|
+
# positional or named arguments.
|
24
|
+
# @param method [String, nil] Must be an HTTP verb such as `GET` or `POST`.
|
25
|
+
# @param action [Proc, Hash, String, nil] References a Method/Proc to be
|
26
|
+
# triggered. It can be:
|
27
|
+
#
|
28
|
+
# - A full hash `{ file: 'val', controller: 'val', action: 'val' }`.
|
29
|
+
# - A hash with `{ 'file' => 'action' }` only (which has a controller
|
30
|
+
# class name `{File}Controller`).
|
31
|
+
# - A simple string (which will consider file `main.rb` and controller
|
32
|
+
# `MainController`). Defaults to "{path_basename}_{method}", (ex:
|
33
|
+
# `index_get`).
|
34
|
+
# - It can also be a `Proc`.
|
35
|
+
#
|
36
|
+
# @param static [Boolean|Array] Defines a route as static. Set to `false` to
|
37
|
+
# say it can only work dynamically. You can also supply an array of arrays
|
38
|
+
# or hashes to generate static files based on that positional or keyword
|
39
|
+
# params.
|
40
|
+
def initialize(path:, method: 'GET', action: nil, static: true)
|
41
|
+
@path = path
|
42
|
+
@method = method
|
43
|
+
self.action = action
|
44
|
+
@static = static
|
45
|
+
end
|
46
|
+
|
47
|
+
# Last identifier on a path, returns `index` for `/`.
|
48
|
+
def path_basename
|
49
|
+
str_path[-1] == '/' ? 'index' : File.basename(str_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param request [Rack::Request]
|
53
|
+
# @return a valid rack response.
|
54
|
+
def respond(request)
|
55
|
+
return respond_proc(request) if @action.instance_of?(Proc)
|
56
|
+
|
57
|
+
respond_method(request)
|
58
|
+
end
|
59
|
+
|
60
|
+
# String path, independent if {path} is Regexp or String.
|
61
|
+
def str_path
|
62
|
+
@path.source.gsub(/[\^$]/, '')
|
63
|
+
rescue NoMethodError
|
64
|
+
@path
|
65
|
+
end
|
66
|
+
|
67
|
+
# On Regexp paths, return a string valid for making a request to this route.
|
68
|
+
# @param seed [Array, Hash] Seeds to use as replacement on capture groups.
|
69
|
+
# @return [String] sown path.
|
70
|
+
def seed_path(seed)
|
71
|
+
sown_path = str_path
|
72
|
+
if seed.instance_of?(Hash)
|
73
|
+
seed.each { |key, val| sown_path.sub!(/\(\?<#{key}>[^)]+\)/, val) }
|
74
|
+
else
|
75
|
+
seed.each { |val| sown_path.sub!(/\([^)]+\)/, val) }
|
76
|
+
end
|
77
|
+
sown_path
|
78
|
+
end
|
79
|
+
|
80
|
+
# Build this route as an static file and place it relative to
|
81
|
+
# {LeanWeb::PUBLIC_PATH}.
|
82
|
+
# @param request_path [String] Request path for dynamic (regex) routes.
|
83
|
+
def build(request_path = @path)
|
84
|
+
response = respond(
|
85
|
+
Rack::Request.new(
|
86
|
+
{ 'PATH_INFO' => request_path, 'REQUEST_METHOD' => 'GET' }
|
87
|
+
)
|
88
|
+
)
|
89
|
+
out_path = output_path(request_path, response[1]['Content-Type'] || nil)
|
90
|
+
FileUtils.mkdir_p(File.dirname(out_path))
|
91
|
+
|
92
|
+
File.write(out_path, response[2][0])
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
# Assign value to `@action`.
|
98
|
+
def action=(value)
|
99
|
+
@action = if value.instance_of?(Proc)
|
100
|
+
value
|
101
|
+
else
|
102
|
+
Action.new(**prepare_action_hash(value))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @param value [Hash, String, nil] Check {#initialize} action param for
|
107
|
+
# valid input.
|
108
|
+
# @return [Hash] valid hash for {Action}.
|
109
|
+
def prepare_action_hash(value)
|
110
|
+
begin
|
111
|
+
value[:file], value[:action] = value.first \
|
112
|
+
unless %i[file controller action].include?(value.keys.first)
|
113
|
+
rescue NoMethodError
|
114
|
+
value = { action: value }
|
115
|
+
end
|
116
|
+
value[:file] ||= 'main'
|
117
|
+
value[:controller] ||= "#{value[:file].capitalize}Controller"
|
118
|
+
value[:action] ||= "#{path_basename}_#{@method.downcase}"
|
119
|
+
value
|
120
|
+
end
|
121
|
+
|
122
|
+
# @param request [Rack::Request]
|
123
|
+
# @return a valid rack response.
|
124
|
+
def respond_method(request)
|
125
|
+
params = action_params(request.path)
|
126
|
+
require_relative("#{LeanWeb::CONTROLLER_PATH}/#{@action.file}")
|
127
|
+
controller = Object.const_get(@action.controller).new(self, request)
|
128
|
+
return controller.public_send(@action.action, **params)\
|
129
|
+
if params.instance_of?(Hash)
|
130
|
+
|
131
|
+
controller.public_send(@action.action, *params)
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param request [Rack::Request]
|
135
|
+
# @return a valid rack response.
|
136
|
+
def respond_proc(request)
|
137
|
+
params = action_params(request.path)
|
138
|
+
return @action.call(**params) if params.instance_of?(Hash)
|
139
|
+
|
140
|
+
@action.call(*params)
|
141
|
+
end
|
142
|
+
|
143
|
+
# @param request_path [String]
|
144
|
+
# @return [Array, Hash]
|
145
|
+
def action_params(request_path)
|
146
|
+
return nil unless @path.instance_of?(Regexp)
|
147
|
+
|
148
|
+
matches = @path.match(request_path)
|
149
|
+
return matches.named_captures.transform_keys(&:to_sym)\
|
150
|
+
if matches.named_captures != {}
|
151
|
+
|
152
|
+
matches.captures
|
153
|
+
end
|
154
|
+
|
155
|
+
# Output path for public file.
|
156
|
+
# @param path [String]
|
157
|
+
# @param content_type [String]
|
158
|
+
# @return [String] absolute route to path + extension based on content_type.
|
159
|
+
def output_path(path, content_type)
|
160
|
+
path += 'index' if path[-1] == '/'
|
161
|
+
"#{LeanWeb::PUBLIC_PATH}#{path}#{LeanWeb::MEDIA_EXTENSIONS[content_type]}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/lib/leanweb.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright 2022 Felix Freeman <libsys@hacktivista.org>
|
2
|
+
#
|
3
|
+
# This file is part of "LeanWeb" and licensed under the terms of the Hacktivista
|
4
|
+
# General Public License version 0.1 or (at your option) any later version. You
|
5
|
+
# should have received a copy of this license along with the software. If not,
|
6
|
+
# see <https://hacktivista.org/licenses/>.
|
7
|
+
|
8
|
+
# frozen_string_literal: true
|
9
|
+
|
10
|
+
# LeanWeb is a minimal hybrid static / dynamic web framework.
|
11
|
+
module LeanWeb
|
12
|
+
VERSION = '0.1.1'
|
13
|
+
|
14
|
+
ROOT_PATH = ENV['LEANWEB_ROOT_PATH'] || Dir.pwd
|
15
|
+
CONTROLLER_PATH = "#{ROOT_PATH}/src/controllers"
|
16
|
+
VIEW_PATH = "#{ROOT_PATH}/src/views"
|
17
|
+
PUBLIC_PATH = "#{ROOT_PATH}/public"
|
18
|
+
|
19
|
+
MEDIA_EXTENSIONS = {
|
20
|
+
nil => '.html',
|
21
|
+
'application/javascript' => '.js',
|
22
|
+
'application/json' => '.json',
|
23
|
+
'text/html' => '.html',
|
24
|
+
'text/plain' => '.txt'
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
autoload :Route, 'leanweb/route.rb'
|
28
|
+
autoload :Controller, 'leanweb/controller.rb'
|
29
|
+
autoload :App, 'leanweb/app.rb'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: leanweb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Felix Freeman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: haml
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- libsys@hacktivista.org
|
114
|
+
executables:
|
115
|
+
- leanweb
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- bin/leanweb
|
120
|
+
- lib/leanweb.rb
|
121
|
+
- lib/leanweb/app.rb
|
122
|
+
- lib/leanweb/controller.rb
|
123
|
+
- lib/leanweb/route.rb
|
124
|
+
homepage: https://git.hacktivista.org/leanweb
|
125
|
+
licenses:
|
126
|
+
- LicenseRef-LICENSE
|
127
|
+
metadata:
|
128
|
+
homepage_uri: https://git.hacktivista.org/leanweb
|
129
|
+
source_code_uri: https://git.hacktivista.org/leanweb
|
130
|
+
rubygems_mfa_required: 'true'
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 2.5.0
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubygems_version: 3.2.3
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: LeanWeb is a minimal hybrid static / dynamic web framework
|
150
|
+
test_files: []
|