hyraft 0.1.0.alpha1
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/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +231 -0
- data/exe/hyraft +5 -0
- data/lib/hyraft/boot/asset_preloader.rb +185 -0
- data/lib/hyraft/boot/preloaded_static.rb +46 -0
- data/lib/hyraft/boot/preloader.rb +206 -0
- data/lib/hyraft/cli.rb +187 -0
- data/lib/hyraft/compiler/compiler.rb +34 -0
- data/lib/hyraft/compiler/html_purifier.rb +181 -0
- data/lib/hyraft/compiler/javascript_library.rb +281 -0
- data/lib/hyraft/compiler/javascript_obfuscator.rb +141 -0
- data/lib/hyraft/compiler/parser.rb +27 -0
- data/lib/hyraft/compiler/renderer.rb +217 -0
- data/lib/hyraft/engine/circuit.rb +35 -0
- data/lib/hyraft/engine/port.rb +17 -0
- data/lib/hyraft/engine/source.rb +19 -0
- data/lib/hyraft/engine.rb +11 -0
- data/lib/hyraft/router/api_router.rb +65 -0
- data/lib/hyraft/router/web_router.rb +136 -0
- data/lib/hyraft/system_info.rb +26 -0
- data/lib/hyraft/version.rb +5 -0
- data/lib/hyraft.rb +48 -0
- data/templates/do_app/Gemfile +50 -0
- data/templates/do_app/Rakefile +88 -0
- data/templates/do_app/adapter-intake/web-app/display/pages/home/home.hyr +174 -0
- data/templates/do_app/adapter-intake/web-app/request/home_web_adapter.rb +19 -0
- data/templates/do_app/boot.rb +41 -0
- data/templates/do_app/framework/adapters/server/server_api_adapter.rb +51 -0
- data/templates/do_app/framework/adapters/server/server_web_adapter.rb +178 -0
- data/templates/do_app/framework/compiler/style_resolver.rb +33 -0
- data/templates/do_app/framework/errors/error_handler.rb +75 -0
- data/templates/do_app/framework/errors/templates/304.html +22 -0
- data/templates/do_app/framework/errors/templates/400.html +22 -0
- data/templates/do_app/framework/errors/templates/401.html +22 -0
- data/templates/do_app/framework/errors/templates/403.html +22 -0
- data/templates/do_app/framework/errors/templates/404.html +62 -0
- data/templates/do_app/framework/errors/templates/500.html +73 -0
- data/templates/do_app/framework/middleware/cors_middleware.rb +37 -0
- data/templates/do_app/infra/config/environment.rb +86 -0
- data/templates/do_app/infra/config/error_config.rb +80 -0
- data/templates/do_app/infra/config/routes/api_routes.rb +2 -0
- data/templates/do_app/infra/config/routes/web_routes.rb +10 -0
- data/templates/do_app/infra/database/sequel_connection.rb +62 -0
- data/templates/do_app/infra/gems/database.rb +7 -0
- data/templates/do_app/infra/gems/load_all.rb +4 -0
- data/templates/do_app/infra/gems/utilities.rb +1 -0
- data/templates/do_app/infra/gems/web.rb +3 -0
- data/templates/do_app/infra/server/api-server.ru +13 -0
- data/templates/do_app/infra/server/web-server.ru +32 -0
- data/templates/do_app/package.json +9 -0
- data/templates/do_app/public/favicon.ico +0 -0
- data/templates/do_app/public/icons/docs.svg +10 -0
- data/templates/do_app/public/icons/expli.svg +13 -0
- data/templates/do_app/public/icons/git-repo.svg +13 -0
- data/templates/do_app/public/icons/hexagonal-arch.svg +15 -0
- data/templates/do_app/public/icons/template-engine.svg +26 -0
- data/templates/do_app/public/images/hyr-logo.png +0 -0
- data/templates/do_app/public/images/hyr-logo.webp +0 -0
- data/templates/do_app/public/index.html +22 -0
- data/templates/do_app/public/styles/css/main.css +418 -0
- data/templates/do_app/public/styles/css/spa.css +171 -0
- data/templates/do_app/shared/helpers/pagination_helper.rb +44 -0
- data/templates/do_app/shared/helpers/response_formatter.rb +25 -0
- data/templates/do_app/test/acceptance/api/articles_api_acceptance_test.rb +43 -0
- data/templates/do_app/test/acceptance/web/articles_acceptance_test.rb +31 -0
- data/templates/do_app/test/acceptance/web/home_acceptance_test.rb +17 -0
- data/templates/do_app/test/db.rb +106 -0
- data/templates/do_app/test/integration/adapter-exhaust/data-gateway/sequel_articles_gateway_test.rb +79 -0
- data/templates/do_app/test/integration/adapter-intake/api-app/request/articles_api_adapter_test.rb +61 -0
- data/templates/do_app/test/integration/adapter-intake/web-app/request/articles_web_adapter_test.rb +20 -0
- data/templates/do_app/test/integration/adapter-intake/web-app/request/home_web_adapter_test.rb +17 -0
- data/templates/do_app/test/integration/database/migration_test.rb +35 -0
- data/templates/do_app/test/support/mock_api_adapter.rb +82 -0
- data/templates/do_app/test/support/mock_articles_gateway.rb +41 -0
- data/templates/do_app/test/support/mock_web_adapter.rb +85 -0
- data/templates/do_app/test/support/test_patches.rb +33 -0
- data/templates/do_app/test/test_helper.rb +526 -0
- data/templates/do_app/test/unit/engine/circuit/articles_circuit_test.rb +167 -0
- data/templates/do_app/test/unit/engine/port/articles_gateway_port_test.rb +12 -0
- data/templates/do_app/test/unit/engine/source/article_test.rb +37 -0
- metadata +291 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyraft
|
|
4
|
+
class Port
|
|
5
|
+
def implementor=(adapter)
|
|
6
|
+
@implementor = adapter
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def method_missing(method, *args, &block)
|
|
10
|
+
if @implementor&.respond_to?(method)
|
|
11
|
+
@implementor.send(method, *args, &block)
|
|
12
|
+
else
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyraft
|
|
4
|
+
class Source
|
|
5
|
+
attr_accessor :id
|
|
6
|
+
|
|
7
|
+
def initialize(attributes = {})
|
|
8
|
+
@id = attributes[:id]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_hash
|
|
12
|
+
{ id: @id }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def persisted?
|
|
16
|
+
!@id.nil?
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# lib/hyraft/router/api_router.rb
|
|
2
|
+
module Hyraft
|
|
3
|
+
module Router
|
|
4
|
+
class ApiRouter
|
|
5
|
+
Route = Struct.new(:http_method, :path, :handler_class, :action_name, :regex)
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@routes = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.draw(&block)
|
|
12
|
+
router = new
|
|
13
|
+
router.instance_eval(&block)
|
|
14
|
+
router
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def GET(path, to:)
|
|
18
|
+
add_route('GET', path, to)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def POST(path, to:)
|
|
22
|
+
add_route('POST', path, to)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def PUT(path, to:)
|
|
26
|
+
add_route('PUT', path, to)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def DELETE(path, to:)
|
|
30
|
+
add_route('DELETE', path, to)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def resolve(http_method, path_info)
|
|
34
|
+
path = path_info.split('?').first
|
|
35
|
+
|
|
36
|
+
# Try exact matches first
|
|
37
|
+
exact_match = @routes.find do |r|
|
|
38
|
+
r.http_method == http_method && r.path == path
|
|
39
|
+
end
|
|
40
|
+
return [exact_match, []] if exact_match
|
|
41
|
+
|
|
42
|
+
# Then try regex matches
|
|
43
|
+
@routes.each do |r|
|
|
44
|
+
if r.http_method == http_method && (match = r.regex.match(path))
|
|
45
|
+
return [r, match.captures]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def add_route(http_method, path, to)
|
|
53
|
+
handler_class, action_name = to
|
|
54
|
+
regex = compile(path)
|
|
55
|
+
@routes << Route.new(http_method, path, handler_class, action_name, regex)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def compile(path)
|
|
61
|
+
Regexp.new("^" + path.gsub(/:([a-zA-Z_]\w*)/, '([^/]+)') + "$")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# lib/hyraft/router/web_router.rb
|
|
2
|
+
module Hyraft
|
|
3
|
+
module Router
|
|
4
|
+
class WebRouter
|
|
5
|
+
Route = Struct.new(:path, :method, :handler_class, :action, :template)
|
|
6
|
+
|
|
7
|
+
def self.draw(&block)
|
|
8
|
+
router = new
|
|
9
|
+
router.instance_eval(&block)
|
|
10
|
+
router
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@routes = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def GET(path, to:, template: nil)
|
|
18
|
+
add_route('GET', path, to, template)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def POST(path, to:, template: nil)
|
|
22
|
+
add_route('POST', path, to, template)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def PUT(path, to:, template: nil)
|
|
26
|
+
add_route('PUT', path, to, template)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def DELETE(path, to:, template: nil)
|
|
30
|
+
add_route('DELETE', path, to, template)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_route(method, path, to, template = nil)
|
|
34
|
+
handler_class, action = to
|
|
35
|
+
@routes << Route.new(path, method, handler_class, action, template)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def resolve(method, path)
|
|
39
|
+
# Normalize path - remove trailing slashes except for root
|
|
40
|
+
normalized_path = path.gsub(%r{/+$}, '')
|
|
41
|
+
normalized_path = '/' if normalized_path.empty?
|
|
42
|
+
|
|
43
|
+
# puts "DEBUG: Resolving #{method} #{path} -> normalized: #{normalized_path}"
|
|
44
|
+
|
|
45
|
+
# First, try to find exact matches (non-parameter routes)
|
|
46
|
+
exact_match = @routes.find do |r|
|
|
47
|
+
r.method == method && !r.path.include?(':') && !r.path.include?('*') && r.path == normalized_path
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if exact_match
|
|
51
|
+
# puts "DEBUG: Exact match found: #{exact_match.path}"
|
|
52
|
+
return [exact_match, []]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Then try parameter routes (routes with :)
|
|
56
|
+
param_match = @routes.find do |r|
|
|
57
|
+
r.method == method && r.path.include?(':') && !r.path.include?('*') && match_path(r.path, normalized_path)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if param_match
|
|
61
|
+
# puts "DEBUG: Parameter match found: #{param_match.path}"
|
|
62
|
+
params = extract_params(param_match.path, normalized_path)
|
|
63
|
+
return [param_match, params]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Finally try wildcard routes
|
|
67
|
+
wildcard_match = @routes.find do |r|
|
|
68
|
+
r.method == method && r.path.include?('*') && match_path(r.path, normalized_path)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if wildcard_match
|
|
72
|
+
# puts "DEBUG: Wildcard match found: #{wildcard_match.path}"
|
|
73
|
+
params = extract_params(wildcard_match.path, normalized_path)
|
|
74
|
+
return [wildcard_match, params]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# puts "DEBUG: No route found for #{method} #{normalized_path}"
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def match_path(route_path, request_path)
|
|
84
|
+
route_segments = route_path.split('/')
|
|
85
|
+
request_segments = request_path.split('/')
|
|
86
|
+
|
|
87
|
+
# Handle wildcard routes
|
|
88
|
+
if route_path.include?('*')
|
|
89
|
+
# For wildcard routes, check if the beginning matches
|
|
90
|
+
wildcard_index = route_segments.index('*')
|
|
91
|
+
return false unless wildcard_index
|
|
92
|
+
|
|
93
|
+
# Check if all segments before the wildcard match
|
|
94
|
+
(0...wildcard_index).each do |i|
|
|
95
|
+
return false unless route_segments[i] == request_segments[i]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return true # Wildcard matches everything after
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# For parameter routes, check segment count
|
|
102
|
+
return false unless route_segments.length == request_segments.length
|
|
103
|
+
|
|
104
|
+
# Check each segment
|
|
105
|
+
route_segments.zip(request_segments).all? do |r, req|
|
|
106
|
+
r.start_with?(':') || r == req
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def extract_params(route_path, request_path)
|
|
111
|
+
route_segments = route_path.split('/')
|
|
112
|
+
request_segments = request_path.split('/')
|
|
113
|
+
|
|
114
|
+
params = []
|
|
115
|
+
|
|
116
|
+
# Handle wildcard routes
|
|
117
|
+
if route_path.include?('*')
|
|
118
|
+
wildcard_index = route_segments.index('*')
|
|
119
|
+
if wildcard_index
|
|
120
|
+
# Capture everything after the wildcard as a single parameter
|
|
121
|
+
captured_path = request_segments[wildcard_index..-1]&.join('/') || ''
|
|
122
|
+
params << captured_path
|
|
123
|
+
end
|
|
124
|
+
return params
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Normal parameter extraction
|
|
128
|
+
route_segments.zip(request_segments).each do |r, req|
|
|
129
|
+
params << req if r.start_with?(':')
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
params
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# lib/hyraft/system_info.rb
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
|
|
4
|
+
module Hyraft
|
|
5
|
+
module SystemInfo
|
|
6
|
+
def self.os_name
|
|
7
|
+
host = RbConfig::CONFIG['host_os']
|
|
8
|
+
if host =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
|
9
|
+
begin
|
|
10
|
+
require 'win32/registry'
|
|
11
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Microsoft\Windows NT\CurrentVersion') do |reg|
|
|
12
|
+
reg['ProductName'] # => "Ex. Windows 11 Pro?"
|
|
13
|
+
end
|
|
14
|
+
rescue LoadError
|
|
15
|
+
"Windows (version unknown)"
|
|
16
|
+
end
|
|
17
|
+
elsif host =~ /darwin|mac os/
|
|
18
|
+
"macOS"
|
|
19
|
+
elsif host =~ /linux/
|
|
20
|
+
"Linux"
|
|
21
|
+
else
|
|
22
|
+
host
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/hyraft.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# lib/hyraft.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require_relative "hyraft/system_info"
|
|
6
|
+
require_relative "hyraft/cli"
|
|
7
|
+
require_relative "hyraft/version"
|
|
8
|
+
|
|
9
|
+
require_relative "hyraft/engine/source"
|
|
10
|
+
require_relative "hyraft/engine/circuit"
|
|
11
|
+
require_relative "hyraft/engine/port"
|
|
12
|
+
require_relative "hyraft/engine"
|
|
13
|
+
|
|
14
|
+
# compiler components
|
|
15
|
+
require_relative "hyraft/compiler/compiler"
|
|
16
|
+
require_relative "hyraft/compiler/parser"
|
|
17
|
+
require_relative "hyraft/compiler/renderer"
|
|
18
|
+
require_relative "hyraft/compiler/javascript_library"
|
|
19
|
+
|
|
20
|
+
# router components
|
|
21
|
+
require_relative "hyraft/router/api_router"
|
|
22
|
+
require_relative "hyraft/router/web_router"
|
|
23
|
+
|
|
24
|
+
# PRELOADER
|
|
25
|
+
require_relative "hyraft/boot/preloader"
|
|
26
|
+
require_relative "hyraft/boot/asset_preloader"
|
|
27
|
+
require_relative "hyraft/boot/preloaded_static"
|
|
28
|
+
|
|
29
|
+
require_relative "hyraft/compiler/javascript_obfuscator"
|
|
30
|
+
|
|
31
|
+
module Hyraft
|
|
32
|
+
class Error < StandardError; end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Define the standard top-level constants
|
|
36
|
+
HyraftCompiler = Hyraft::Compiler::HyraftCompiler
|
|
37
|
+
HyraftParser = Hyraft::Compiler::HyraftParser
|
|
38
|
+
HyraftRenderer = Hyraft::Compiler::HyraftRenderer
|
|
39
|
+
|
|
40
|
+
# Top-level aliases for backward compatibility
|
|
41
|
+
ApiRouter = Hyraft::Router::ApiRouter
|
|
42
|
+
WebRouter = Hyraft::Router::WebRouter
|
|
43
|
+
|
|
44
|
+
# ADD PRELOADER ALIAS
|
|
45
|
+
HyraftPreloader = Hyraft::Preloader
|
|
46
|
+
HyraftAssetPreloader = Hyraft::AssetPreloader
|
|
47
|
+
HyraftPreloadedStatic = Hyraft::PreloadedStatic
|
|
48
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Gemfile
|
|
2
|
+
source 'https://rubygems.org'
|
|
3
|
+
|
|
4
|
+
#_________________________#
|
|
5
|
+
# #
|
|
6
|
+
# FRAMEWORK #
|
|
7
|
+
|
|
8
|
+
gem 'hyraft'
|
|
9
|
+
gem 'hyraft-rule'
|
|
10
|
+
|
|
11
|
+
#_________________________#
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
#_________________________#
|
|
17
|
+
# #
|
|
18
|
+
# SERVER #
|
|
19
|
+
gem "fiddle" #/ for windows
|
|
20
|
+
gem 'thin'
|
|
21
|
+
# gem 'puma'
|
|
22
|
+
# gem 'falcon', '~> 0.52.3'
|
|
23
|
+
# gem 'async-http' # for http 3 (via Falcon)
|
|
24
|
+
# gem 'iodine'
|
|
25
|
+
|
|
26
|
+
#_________________________#
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#_________________________#
|
|
30
|
+
# #
|
|
31
|
+
# DATABASE #
|
|
32
|
+
# gem 'pg'
|
|
33
|
+
# gem 'mysql2', '~> 0.5.7'
|
|
34
|
+
#_________________________#
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Test gems compatible with Ruby 3.4
|
|
38
|
+
|
|
39
|
+
=begin
|
|
40
|
+
|
|
41
|
+
gem 'minitest', '~> 5.26', '>= 5.26.1'
|
|
42
|
+
gem 'minitest-reporters', '~> 1.7', '>= 1.7.1'
|
|
43
|
+
gem 'minitest-focus', '~> 1.4'
|
|
44
|
+
gem 'mocha', '~> 2.8'
|
|
45
|
+
gem 'rack-test', '~> 2.2', group: :test
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
=end
|
|
50
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Rakefile
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
|
|
4
|
+
namespace :test do
|
|
5
|
+
desc "Run all tests"
|
|
6
|
+
Rake::TestTask.new(:all) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.test_files = FileList[
|
|
9
|
+
"test/unit/**/*_test.rb",
|
|
10
|
+
"test/integration/**/*_test.rb",
|
|
11
|
+
"test/acceptance/**/*_test.rb"
|
|
12
|
+
]
|
|
13
|
+
t.verbose = true
|
|
14
|
+
t.warning = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run API tests only"
|
|
18
|
+
Rake::TestTask.new(:api) do |t|
|
|
19
|
+
t.libs << "test"
|
|
20
|
+
t.test_files = FileList[
|
|
21
|
+
"test/integration/adapter-intake/api-app/**/*_test.rb",
|
|
22
|
+
"test/acceptance/api/**/*_test.rb"
|
|
23
|
+
]
|
|
24
|
+
t.verbose = true
|
|
25
|
+
t.warning = false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Run unit tests only"
|
|
29
|
+
Rake::TestTask.new(:unit) do |t|
|
|
30
|
+
t.libs << "test"
|
|
31
|
+
t.test_files = FileList["test/unit/**/*_test.rb"]
|
|
32
|
+
t.verbose = true
|
|
33
|
+
t.warning = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "Run integration tests only"
|
|
37
|
+
Rake::TestTask.new(:integration) do |t|
|
|
38
|
+
t.libs << "test"
|
|
39
|
+
t.test_files = FileList["test/integration/**/*_test.rb"]
|
|
40
|
+
t.verbose = true
|
|
41
|
+
t.warning = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "Run acceptance tests only"
|
|
45
|
+
Rake::TestTask.new(:acceptance) do |t|
|
|
46
|
+
t.libs << "test"
|
|
47
|
+
t.test_files = FileList["test/acceptance/web/**/*_test.rb"]
|
|
48
|
+
t.verbose = true
|
|
49
|
+
t.warning = false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc "Test database connection"
|
|
53
|
+
task :test_db do
|
|
54
|
+
require 'yaml'
|
|
55
|
+
require 'sequel'
|
|
56
|
+
|
|
57
|
+
config = YAML.load_file('env.yml')
|
|
58
|
+
test_config = config['test']
|
|
59
|
+
|
|
60
|
+
puts "Testing connection to: #{test_config['DB_DATABASE']}"
|
|
61
|
+
|
|
62
|
+
begin
|
|
63
|
+
# Map 'mysql' to 'mysql2' for Sequel
|
|
64
|
+
adapter = test_config['DB_CONNECTION'] == 'mysql' ? 'mysql2' : test_config['DB_CONNECTION']
|
|
65
|
+
|
|
66
|
+
db = Sequel.connect(
|
|
67
|
+
adapter: adapter,
|
|
68
|
+
host: test_config['DB_HOST'],
|
|
69
|
+
database: test_config['DB_DATABASE'],
|
|
70
|
+
username: test_config['DB_USERNAME'],
|
|
71
|
+
password: test_config['DB_PASSWORD'],
|
|
72
|
+
socket: test_config['DB_SOCKET']
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
db.test_connection
|
|
76
|
+
puts "✅ Connection successful!"
|
|
77
|
+
puts "📊 Tables: #{db.tables.join(', ')}"
|
|
78
|
+
|
|
79
|
+
rescue => e
|
|
80
|
+
puts "❌ Connection failed: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
desc "Run all tests"
|
|
86
|
+
task test: 'test:all'
|
|
87
|
+
|
|
88
|
+
task default: :test
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<metadata html>
|
|
2
|
+
<require file="lib/neonpulse" />
|
|
3
|
+
<title>[.page_title.]</title>
|
|
4
|
+
<meta name="description" content="[.page_description.]">
|
|
5
|
+
<meta name="keywords" content="[.page_keywords.]">
|
|
6
|
+
<link rel="canonical" href="[.page_url.]">
|
|
7
|
+
<meta name="robots" content="index, follow">
|
|
8
|
+
<meta property="og:title" content="[.page_title.]">
|
|
9
|
+
<meta property="og:description" content="[.page_description.]">
|
|
10
|
+
<meta property="og:type" content="website">
|
|
11
|
+
<meta property="og:url" content="[.page_url.]">
|
|
12
|
+
<meta property="og:image" content="[.page_image.]">
|
|
13
|
+
|
|
14
|
+
<script type="application/ld+json">
|
|
15
|
+
{
|
|
16
|
+
"@context": "https://schema.org",
|
|
17
|
+
"@type": "Home",
|
|
18
|
+
"headline": "[.page_title.]",
|
|
19
|
+
"description": "[.page_description.]",
|
|
20
|
+
"author": { "@type": "Person", "name": "[.author_name.]" },
|
|
21
|
+
"datePublished": "[.publish_date.]"
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
</metadata>
|
|
25
|
+
|
|
26
|
+
<displayer html>
|
|
27
|
+
<div class="main-container">
|
|
28
|
+
<!-- main-header -->
|
|
29
|
+
<div class="main-header">
|
|
30
|
+
<picture>
|
|
31
|
+
<source srcset="/images/hyr-logo.webp" type="image/webp">
|
|
32
|
+
<img src="/images/hyr-logo.png" alt="Hyr Logo" class="logo">
|
|
33
|
+
</picture>
|
|
34
|
+
<h1>Hyraft</h1> Version: [.hyraft_version.]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<welcome-message anyword="the new" framework="framework!"/>
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<hr>
|
|
43
|
+
<p class="home-subtitle">
|
|
44
|
+
<span style="font-weight:bold; color:black;">Hexagonal Ruby framework (not MVC) </span> with reactive front-end support and full-stack capabilities. Hybrid SSR/CSR with Single-File Components ensures lightning-fast performance, scalability, maintainability, and SEO friendliness.</p>
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
<div class="counter">
|
|
50
|
+
<button data-pulse="counter.decrease" class="counter-btn">-</button>
|
|
51
|
+
<span data-neon="count" class="count-display">10</span>
|
|
52
|
+
<button data-pulse="counter.increase" class="counter-btn">+</button>
|
|
53
|
+
</div>
|
|
54
|
+
<small data-neon="countStatus" class="status-text">Count: 10</small>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
<br>
|
|
59
|
+
|
|
60
|
+
<!-- home-features -->
|
|
61
|
+
<div class="main-feature">
|
|
62
|
+
<p class="home-subtitle">
|
|
63
|
+
<span style="font-weight:bold; color:red;">Find me:</span> adapter-intake/web-app/display/pages/home/home.hyr</p>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
<div class="home-feature-grid">
|
|
68
|
+
<div class="home-feature">
|
|
69
|
+
<div class="home-feature-icon"><img src="/icons/expli.svg" alt="No Magic" class="icon-home-feature"></div>
|
|
70
|
+
<h3>Explicit Code (No Magic)</h3>
|
|
71
|
+
<p>Enjoy a framework with explicit behavior—no hidden magic.</p>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="home-feature">
|
|
74
|
+
<div class="home-feature-icon"><img src="/icons/hexagonal-arch.svg" alt="Hexagonal Architecture" class="icon-home-feature"></div>
|
|
75
|
+
<h3>Hexagonal Architecture</h3>
|
|
76
|
+
<p>Clean separation with ports, adapters, and use cases</p>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="home-feature">
|
|
79
|
+
<div class="home-feature-icon"><img src="/icons/template-engine.svg" alt="Template Engine" class="icon-home-feature"></div>
|
|
80
|
+
<h3>Custom Template Engine</h3>
|
|
81
|
+
<p>.hyr files with metadata, displayer, transmuters, and manifestors</p>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<p style="text-align:center;">Ruby Version: [.ruby_version.] | Platform: [.ruby_platform.] | OS: [.os_name.]</p>
|
|
87
|
+
<!-- Footer Section -->
|
|
88
|
+
<footer class="site-footer">
|
|
89
|
+
<div class="footer-container">
|
|
90
|
+
<div class="footer-info">
|
|
91
|
+
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="footer-links">
|
|
95
|
+
|
|
96
|
+
<div class="social-buttons">
|
|
97
|
+
<a href="https://github.com/demjhonsilver/hyraft" target="_blank" class="social-btn">
|
|
98
|
+
<img src="/icons/git-repo.svg" alt="Git" class="icon-footer"> Git Repository
|
|
99
|
+
</a>
|
|
100
|
+
<a href="https://hyraft.com" target="_blank" class="social-btn">
|
|
101
|
+
<img src="/icons/docs.svg" alt="Docs" class="icon-footer"> Documentation
|
|
102
|
+
</a>
|
|
103
|
+
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="footer-meta">
|
|
108
|
+
<p>© <span id="current-year"></span> Hyraft Web Framework • Built by Demjhon Silver • MIT Licensed</p>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</footer>
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
</div>
|
|
117
|
+
</displayer>
|
|
118
|
+
|
|
119
|
+
<transmuter rb>
|
|
120
|
+
def page_title = @page_title || "Hyraft - Ruby framework"
|
|
121
|
+
def page_description = @page_description || "Hexagonal Ruby framework with reactive front-end support and full-stack capabilities.
|
|
122
|
+
Hybrid SSR/CSR with Single-File Components ensures performance, maintainability, and SEO friendliness."
|
|
123
|
+
def page_keywords = @page_keywords || "ruby, framework, SFC, components, hyraft, hyr"
|
|
124
|
+
def page_url = @page_url || "http://localhost:1091/"
|
|
125
|
+
def page_image = @page_image || "http://localhost:1091/favicon.ico"
|
|
126
|
+
def author_name = @author_name || "Hyraft Team"
|
|
127
|
+
def publish_date = @publish_date || Time.now.strftime("%Y-%m-%d")
|
|
128
|
+
|
|
129
|
+
def ruby_version = RUBY_VERSION
|
|
130
|
+
def ruby_platform = RUBY_PLATFORM
|
|
131
|
+
def os_name = Hyraft::SystemInfo.os_name
|
|
132
|
+
def hyraft_version = defined?(Hyraft::VERSION) ? Hyraft::VERSION : "Unknown"
|
|
133
|
+
|
|
134
|
+
def display_welcome_message(anyword:, framework:)
|
|
135
|
+
"<div class='home-tagline'>Welcome to #{anyword} Ruby #{framework}</div>"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
</transmuter>
|
|
139
|
+
|
|
140
|
+
<manifestor js>
|
|
141
|
+
// Your JavaScript code
|
|
142
|
+
// Set current year automatically
|
|
143
|
+
document.getElementById('current-year').textContent = new Date().getFullYear();
|
|
144
|
+
|
|
145
|
+
// Reactive state management
|
|
146
|
+
const appState = neonPulse.neonBatch({
|
|
147
|
+
count: 10,
|
|
148
|
+
countStatus: 'Count: 10',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
// Counter pulses
|
|
153
|
+
neonPulse.pulse('counter', {
|
|
154
|
+
increase: function() {
|
|
155
|
+
console.log('🔷 Pulse: Increasing count');
|
|
156
|
+
appState.count.value++;
|
|
157
|
+
appState.countStatus.value = `Count: ${appState.count.value}`;
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
decrease: function() {
|
|
161
|
+
console.log('🔶 Pulse: Decreasing count');
|
|
162
|
+
appState.count.value--;
|
|
163
|
+
appState.countStatus.value = `Count: ${appState.count.value}`;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
</manifestor>
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
<style src="/styles/css/main.css" />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# adapters-intake/web-app/request/home_web_adapter.rb
|
|
2
|
+
|
|
3
|
+
class HomeWebAdapter
|
|
4
|
+
def initialize
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def home_page(_request)
|
|
8
|
+
metadata = {
|
|
9
|
+
page_title: "Welcome to Hyraft",
|
|
10
|
+
page_description: "Hyraft is a full-stack Ruby web framework that combines high-performance hexagonal architecture with modern reactive frontend."
|
|
11
|
+
}
|
|
12
|
+
# puts "DEBUG: Metadata: #{metadata}" # Check if the metadata is correct
|
|
13
|
+
{
|
|
14
|
+
status: 200,
|
|
15
|
+
locals: metadata,
|
|
16
|
+
display: 'home/home.hyr'
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|