porous 0.2.0 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +3 -3
- data/lib/porous/application.rb +1 -2
- data/lib/porous/cli/build.rb +1 -38
- data/lib/porous/cli/server.rb +26 -21
- data/lib/porous/cli.rb +4 -0
- data/lib/porous/server/application.rb +28 -0
- data/lib/porous/server/builder.rb +52 -0
- data/lib/porous/server/connect.rb +18 -0
- data/lib/porous/server/socket.rb +39 -0
- data/lib/porous/version.rb +1 -1
- data/lib/porous.rb +1 -2
- data/lib/virtual_dom/dom.rb +3 -2
- data/opal/porous/injection.rb +1 -1
- data/opal/porous.rb +20 -9
- metadata +9 -6
- data/lib/porous/server.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87cf02a737a26d181f89e3ee42f73574a30473e490b4ce045d76960e3dbc6fa5
|
4
|
+
data.tar.gz: 679a28921b7fc66630d2ec21b75543cd7dc4906d3f77230a052ff9fb08ab1281
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e25b7156a1e9d10c8d66ea198be3ddb3da0a87aa904519b6fbe0c71006e13d33f5840c051114c7a8e8506ce73f033492fb1bc4089506534759b8bc1a86d18e6
|
7
|
+
data.tar.gz: 8da9145c152a0d93d0a2d182f3ade0da334dc9bb898a75483675173f341a3ea42e5fcd1bc8e16b5e949e92b283fc1f845f7772c3fd19e193283d5338a8acd557
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
## [Planned]
|
2
2
|
|
3
|
-
- WebSockets support
|
4
3
|
- Production mode
|
4
|
+
- Data Abstraction Layer / Object Relational Model
|
5
|
+
- Event Model
|
6
|
+
- Plugin / Extension system
|
7
|
+
|
8
|
+
- Frontend Extensions
|
9
|
+
- Tailwind CSS (tailwind-cli)
|
10
|
+
|
11
|
+
- Persistence Extensions
|
12
|
+
- Memory (default)
|
13
|
+
- Disk (file)
|
14
|
+
- Databases (SQLite, PostgreSQL)
|
15
|
+
|
16
|
+
## [0.3.0] - 22 February 2024
|
17
|
+
|
18
|
+
- WebSockets support
|
5
19
|
|
6
20
|
## [0.2.0] - 18 February 2024
|
7
21
|
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# 🧽 Porous
|
2
2
|
|
3
|
-
Porous is a web engine that uses isomorphic Ruby components to build a Progressive Web App. Its use is analogous to a web framework, but the approach is entirely different. You write only
|
3
|
+
Porous is a web engine that uses isomorphic Ruby components to build a Progressive Web App. Its use is analogous to a web framework, but the approach is entirely different. You write *only* the code that is *unique to your application* and the engine takes care of the rest!
|
4
4
|
|
5
|
-
This project is a work-in-progress and is not yet even in the Proof of Concept phase. However, if you are interested in a full-stack, everything included solution, that only requires you to use one language (that is arguably easy and enjoyable to write) then feel free to follow this project.
|
5
|
+
This project is a **work-in-progress** and is not yet even in the Proof of Concept phase. However, if you are interested in a full-stack, everything included solution, that only requires you to use one language (that is arguably easy and enjoyable to write) then feel free to follow this project.
|
6
6
|
|
7
7
|
The closest thing to this I could find was [Volt](https://github.com/voltrb/volt) or [Silica](https://github.com/youchan/silica), neither of which are active or match the overall development flow I'm looking for.
|
8
8
|
|
@@ -12,7 +12,7 @@ The closest thing to this I could find was [Volt](https://github.com/voltrb/volt
|
|
12
12
|
- 🖥️ Server-side rendering (server responds with the entire initial page populated for SEO)
|
13
13
|
- 💻 Client-side rendering (application bundle is served and interactions and subsequent pages are rendered client-side)
|
14
14
|
- 🌄 Serves static files (from `static` folder)
|
15
|
-
- 🔥 Hot reloading (via
|
15
|
+
- 🔥 Hot reloading (via WebSocket push and browser refresh)
|
16
16
|
|
17
17
|
## Design
|
18
18
|
|
data/lib/porous/application.rb
CHANGED
@@ -18,8 +18,7 @@ module Porous
|
|
18
18
|
end
|
19
19
|
meta name: 'description', content: props[:description] if props[:description]
|
20
20
|
|
21
|
-
script src: '/static/dist/application.js'
|
22
|
-
script src: '/static/dist/reload.js'
|
21
|
+
script src: '/static/dist/application.js', id: 'application'
|
23
22
|
script src: 'https://cdn.tailwindcss.com'
|
24
23
|
end
|
25
24
|
|
data/lib/porous/cli/build.rb
CHANGED
@@ -15,44 +15,7 @@ module Porous
|
|
15
15
|
desc 'build', 'Build static assets'
|
16
16
|
def build
|
17
17
|
empty_directory 'static/dist', verbose: false, force: options[:force]
|
18
|
-
|
19
|
-
live_reload
|
18
|
+
Porous::Server::Builder.new.build
|
20
19
|
end
|
21
|
-
|
22
|
-
# rubocop:disable Metrics/BlockLength
|
23
|
-
no_commands do
|
24
|
-
def transpile
|
25
|
-
components = Dir.glob(File.join('{components,pages}', '**', '*.rb')).map do |relative_path|
|
26
|
-
"require '#{relative_path}'"
|
27
|
-
end
|
28
|
-
build_string = "require 'porous'; #{components.join ";"}".gsub '.rb', ''
|
29
|
-
builder = Opal::Builder.new
|
30
|
-
builder.build_str build_string, '(inline)'
|
31
|
-
File.binwrite "#{Dir.pwd}/static/dist/application.js", builder.to_s
|
32
|
-
end
|
33
|
-
|
34
|
-
# rubocop:disable Metrics/MethodLength
|
35
|
-
def live_reload
|
36
|
-
timestamp = Time.now.to_i.to_s
|
37
|
-
File.write "#{Dir.pwd}/static/dist/timestamp", timestamp
|
38
|
-
builder = Opal::Builder.new
|
39
|
-
script = <<-BROWSER
|
40
|
-
$document.ready do
|
41
|
-
every 0.1 do
|
42
|
-
Browser::HTTP.get('/static/dist/timestamp').then do |response|
|
43
|
-
return unless response.success?
|
44
|
-
timestamp = response.text.to_i
|
45
|
-
TIMESTAMP ||= timestamp
|
46
|
-
$document.location.reload if TIMESTAMP < timestamp
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
BROWSER
|
51
|
-
builder.build_str script, '(inline)'
|
52
|
-
File.binwrite "#{Dir.pwd}/static/dist/reload.js", builder.to_s
|
53
|
-
end
|
54
|
-
# rubocop:enable Metrics/MethodLength
|
55
|
-
end
|
56
|
-
# rubocop:enable Metrics/BlockLength
|
57
20
|
end
|
58
21
|
end
|
data/lib/porous/cli/server.rb
CHANGED
@@ -19,29 +19,34 @@ module Porous
|
|
19
19
|
default: 'localhost',
|
20
20
|
desc: 'The host address Porous will bind to'
|
21
21
|
|
22
|
-
|
22
|
+
def server # rubocop:todo Metrics/MethodLength
|
23
|
+
Agoo::Log.configure(dir: '',
|
24
|
+
console: true,
|
25
|
+
classic: true,
|
26
|
+
colorize: true,
|
27
|
+
states: {
|
28
|
+
INFO: true,
|
29
|
+
DEBUG: false,
|
30
|
+
connect: false,
|
31
|
+
request: false,
|
32
|
+
response: false,
|
33
|
+
eval: true,
|
34
|
+
push: true
|
35
|
+
})
|
23
36
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Rackup::Server.start environment: 'none', app: Porous::Server.new
|
29
|
-
end
|
37
|
+
Agoo::Server.init 9292, Dir.pwd, thread_count: 1
|
38
|
+
Agoo::Server.use Rack::ContentLength
|
39
|
+
Agoo::Server.use Rack::Static, urls: ['/static']
|
40
|
+
Agoo::Server.use Rack::ShowExceptions
|
30
41
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# Rebuild for browser
|
40
|
-
Thread.new { build }
|
41
|
-
end
|
42
|
-
@listener.start
|
43
|
-
at_exit { @listener.stop }
|
44
|
-
end
|
42
|
+
# Socket Communication
|
43
|
+
$socket ||= Porous::Server::Socket.new
|
44
|
+
Agoo::Server.handle nil, '/connect', Porous::Server::Connect.new
|
45
|
+
# Server-Side Rendering
|
46
|
+
Agoo::Server.handle nil, '**', Porous::Server::Application.new
|
47
|
+
Agoo::Server.start
|
48
|
+
# Live Reload Builder
|
49
|
+
Server::Builder.new.build.start
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
data/lib/porous/cli.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Porous
|
4
|
+
module Server
|
5
|
+
class Application
|
6
|
+
MONITORING = %w[components pages].freeze
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
router = Porous::Router.new path: env['PATH_INFO'], query: env['QUERY_STRING']
|
10
|
+
route = router.find_route
|
11
|
+
page = route[:component].new(route[:params])
|
12
|
+
|
13
|
+
[200, { 'content-type' => 'text/html' }, [
|
14
|
+
Porous::Application.new(
|
15
|
+
title: page.page_title,
|
16
|
+
description: page.page_description,
|
17
|
+
path: env['PATH_INFO'],
|
18
|
+
query: env['QUERY_STRING']
|
19
|
+
).to_s
|
20
|
+
]]
|
21
|
+
rescue Porous::InvalidRouteError => e
|
22
|
+
[404, { 'content-type' => 'text/plain' }, ["404 Page not found\n", e.message]]
|
23
|
+
rescue Porous::Error => e
|
24
|
+
[500, { 'content-type' => 'text/plain' }, ["500 Internal Server Error\n", e.message]]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opal/builder_scheduler/sequential'
|
4
|
+
|
5
|
+
module Porous
|
6
|
+
module Server
|
7
|
+
class Builder
|
8
|
+
def initialize
|
9
|
+
@build_queue = Queue.new
|
10
|
+
@last_build = nil
|
11
|
+
@latest_change = Dir.glob(File.join('**', '*.rb')).map { |f| File.mtime f }.max
|
12
|
+
end
|
13
|
+
|
14
|
+
def build
|
15
|
+
components = Dir.glob(File.join('**', '*.rb')).map do |relative_path|
|
16
|
+
modified = File.mtime relative_path
|
17
|
+
@latest_change = modified if modified > @latest_change
|
18
|
+
"require '#{relative_path}'"
|
19
|
+
end
|
20
|
+
build_string = "require 'porous'; #{components.join ";"}".gsub '.rb', ''
|
21
|
+
builder = Opal::Builder.new scheduler: Opal::BuilderScheduler::Sequential, cache: false
|
22
|
+
builder.build_str build_string, '(inline)'
|
23
|
+
File.binwrite "#{Dir.pwd}/static/dist/application.js", builder.to_s
|
24
|
+
@last_build = Time.now
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:disable Metrics/AbcSize
|
29
|
+
def start
|
30
|
+
loop do
|
31
|
+
sleep 0.25
|
32
|
+
next unless @build_queue.empty?
|
33
|
+
|
34
|
+
modified = Dir.glob(File.join('**', '*.rb')).map { |f| File.mtime f }.max
|
35
|
+
next unless modified > @last_build
|
36
|
+
|
37
|
+
@build_queue.push modified
|
38
|
+
# Load for server
|
39
|
+
Dir.glob(File.join('**', '*.rb')).map { |f| load File.expand_path("#{Dir.pwd}/#{f}") }
|
40
|
+
|
41
|
+
# Rebuild for browser
|
42
|
+
Thread.new { build }.join
|
43
|
+
|
44
|
+
# Notify clients
|
45
|
+
$socket.public 'build', @last_build.inspect
|
46
|
+
@build_queue.clear
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/AbcSize
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Porous
|
4
|
+
module Server
|
5
|
+
class Connect
|
6
|
+
# Only used for WebSocket or SSE upgrades.
|
7
|
+
def call(env)
|
8
|
+
if env['rack.upgrade?'].nil?
|
9
|
+
[404, {}, []]
|
10
|
+
else
|
11
|
+
$socket ||= Socket.new
|
12
|
+
env['rack.upgrade'] = $socket
|
13
|
+
[200, {}, []]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Porous
|
4
|
+
module Server
|
5
|
+
class Socket
|
6
|
+
def initialize
|
7
|
+
@clients = []
|
8
|
+
@mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_open(client)
|
12
|
+
@mutex.synchronize do
|
13
|
+
@clients << client
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_close(client)
|
18
|
+
@mutex.synchronize do
|
19
|
+
@clients.delete(client)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_drained(_client); end
|
24
|
+
|
25
|
+
def on_message(client, data)
|
26
|
+
client.write("Handler says #{data}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def public(channel, message)
|
30
|
+
output = "#{channel}|#{message}"
|
31
|
+
@mutex.synchronize do
|
32
|
+
@clients.each do |c|
|
33
|
+
c.write output
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/porous/version.rb
CHANGED
data/lib/porous.rb
CHANGED
@@ -7,7 +7,7 @@ require 'opal-virtual-dom'
|
|
7
7
|
Opal.append_path File.expand_path('../opal', __dir__)
|
8
8
|
Opal.append_path File.expand_path(Dir.pwd)
|
9
9
|
|
10
|
-
require '
|
10
|
+
require 'agoo'
|
11
11
|
|
12
12
|
require 'porous/version'
|
13
13
|
|
@@ -27,7 +27,6 @@ Dir.glob(File.join('{components,pages}', '**', '*.rb')).each do |relative_path|
|
|
27
27
|
end
|
28
28
|
|
29
29
|
require 'porous/application'
|
30
|
-
require 'porous/server' unless RUBY_ENGINE == 'opal'
|
31
30
|
|
32
31
|
module Porous
|
33
32
|
class Error < StandardError; end
|
data/lib/virtual_dom/dom.rb
CHANGED
@@ -11,8 +11,9 @@ module VirtualDOM
|
|
11
11
|
small source span strong style sub summary sup table tbody td textarea tfoot th
|
12
12
|
thead time title tr track u ul var video wbr].freeze
|
13
13
|
|
14
|
-
SVG_TAGS = %w[
|
15
|
-
|
14
|
+
SVG_TAGS = %w[animate animateMotion animateTransform circle clipPath defs desc ellipse filter
|
15
|
+
foreignObject g image line linearGradient marker mask metadata mpath path pattern
|
16
|
+
polygon polyline radialGradient rect set stop svg switch symbol textPath tspan use view].freeze
|
16
17
|
(HTML_TAGS + SVG_TAGS).each do |tag|
|
17
18
|
define_method tag do |params = {}, &block|
|
18
19
|
if params.is_a?(String)
|
data/opal/porous/injection.rb
CHANGED
data/opal/porous.rb
CHANGED
@@ -11,15 +11,6 @@ require 'console'
|
|
11
11
|
require 'virtual_dom'
|
12
12
|
require 'virtual_dom/support/browser'
|
13
13
|
|
14
|
-
VirtualDOM::DOM::HTML_TAGS = %w[a abbr address area article aside audio b base bdi bdo big blockquote body br
|
15
|
-
button canvas caption cite code col colgroup data datalist dd del details dfn
|
16
|
-
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
17
|
-
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
18
|
-
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
19
|
-
output p param picture pre progress q rp rt ruby s samp script section select
|
20
|
-
small source span strong style sub summary sup table tbody td textarea tfoot th
|
21
|
-
thead time title tr track u ul var video wbr svg path].freeze
|
22
|
-
|
23
14
|
require 'porous/injection'
|
24
15
|
require 'porous/component/class_methods'
|
25
16
|
require 'porous/component/render'
|
@@ -38,4 +29,24 @@ end
|
|
38
29
|
|
39
30
|
$document.ready do
|
40
31
|
Porous::Application.mount_to($document.body)
|
32
|
+
Browser::Socket.new 'ws://localhost:9292/connect' do
|
33
|
+
on :open do |_e|
|
34
|
+
$console.info 'Connected to server!'
|
35
|
+
end
|
36
|
+
|
37
|
+
on :message do |e|
|
38
|
+
channel, content = e.data.split '|'
|
39
|
+
case channel
|
40
|
+
when 'build'
|
41
|
+
if content == 'started'
|
42
|
+
$console.log 'New build started…'
|
43
|
+
else
|
44
|
+
$console.log 'Reloading scripts…'
|
45
|
+
$document.location.reload
|
46
|
+
end
|
47
|
+
else
|
48
|
+
$console.log "Received #{e.data}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
41
52
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: porous
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Exa Stencil
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: agoo
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.15'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.15'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: opal-browser
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,7 +117,10 @@ files:
|
|
117
117
|
- lib/porous/page.rb
|
118
118
|
- lib/porous/router.rb
|
119
119
|
- lib/porous/routes.rb
|
120
|
-
- lib/porous/server.rb
|
120
|
+
- lib/porous/server/application.rb
|
121
|
+
- lib/porous/server/builder.rb
|
122
|
+
- lib/porous/server/connect.rb
|
123
|
+
- lib/porous/server/socket.rb
|
121
124
|
- lib/porous/version.rb
|
122
125
|
- lib/virtual_dom/dom.rb
|
123
126
|
- lib/virtual_dom/virtual_node.rb
|
data/lib/porous/server.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Porous
|
4
|
-
class Server
|
5
|
-
def initialize(*_args)
|
6
|
-
setup_rack_app
|
7
|
-
end
|
8
|
-
|
9
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
10
|
-
def setup_rack_app
|
11
|
-
@rack = Rack::Builder.new do
|
12
|
-
use Rack::ContentLength
|
13
|
-
use Rack::Static, urls: ['/static']
|
14
|
-
use Rack::CommonLogger
|
15
|
-
use Rack::ShowExceptions
|
16
|
-
use Rack::Lint
|
17
|
-
use Rack::TempfileReaper
|
18
|
-
|
19
|
-
run do |env|
|
20
|
-
router = Porous::Router.new path: env['PATH_INFO'], query: env['QUERY_STRING']
|
21
|
-
route = router.find_route
|
22
|
-
page = route[:component].new(route[:params])
|
23
|
-
|
24
|
-
[200, { 'content-type' => 'text/html' }, [
|
25
|
-
Porous::Application.new(
|
26
|
-
title: page.page_title,
|
27
|
-
description: page.page_description,
|
28
|
-
path: env['PATH_INFO'],
|
29
|
-
query: env['QUERY_STRING']
|
30
|
-
).to_s
|
31
|
-
]]
|
32
|
-
rescue Porous::InvalidRouteError => e
|
33
|
-
[404, { 'content-type' => 'text/plain' }, ["404 Page not found\n", e.message]]
|
34
|
-
rescue Porous::Error => e
|
35
|
-
[500, { 'content-type' => 'text/plain' }, ["500 Internal Server Error\n", e.message]]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
40
|
-
|
41
|
-
def call(*args)
|
42
|
-
@rack.call(*args)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|