leanweb 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|