ruflet_rails 0.0.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/README.md +36 -0
- data/lib/ruflet/rails/protocol/context.rb +25 -0
- data/lib/ruflet/rails/protocol/endpoint.rb +75 -0
- data/lib/ruflet/rails/protocol/middleware.rb +17 -0
- data/lib/ruflet/rails/protocol/mobile_loader.rb +32 -0
- data/lib/ruflet/rails/protocol/mount.rb +23 -0
- data/lib/ruflet/rails/protocol/runner.rb +37 -0
- data/lib/ruflet/rails/protocol.rb +8 -0
- data/lib/ruflet/rails/railtie.rb +11 -0
- data/lib/ruflet/rails.rb +17 -0
- data/lib/ruflet/version.rb +5 -0
- data/lib/ruflet_rails.rb +9 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e4e545e3193b769addf80f3ec9f7a902fefed5ecaab529bc34b718d66d683679
|
|
4
|
+
data.tar.gz: f9011791ffdff4236321fd0ea0df09acd688a999a3d8612c06aa5acdddcbf0af
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8dcaf1e929c4707fd9ca238332f558408d7016900de561fb8cbcbeaefcddae07fa0808b64861bc3ef5efdecbaed208f9153390ab59db0910788f7f231556a0ff
|
|
7
|
+
data.tar.gz: 8a2218ad72419c96bc5b03dca4e41938aac900c7dd70afb7f639159682e38ded805e81aa0f8dd3d3488d3cd8d76fafb58ebcd4ea07ff85fed2827762d1bdd89c
|
data/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Ruflet Rails
|
|
2
|
+
|
|
3
|
+
`ruflet_rails` is the Rails-first integration package for Ruflet.
|
|
4
|
+
|
|
5
|
+
Internal Rails transport/protocol code is bundled inside this gem as `Ruflet::Rails::Protocol`.
|
|
6
|
+
No separate protocol gem is required.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
# Gemfile
|
|
12
|
+
path "/path/to/ruflet/packages" do
|
|
13
|
+
gem "ruflet_rails"
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
# app/mobile/main.rb
|
|
19
|
+
require "ruflet"
|
|
20
|
+
|
|
21
|
+
class MyApp < Ruflet::App
|
|
22
|
+
def view(page)
|
|
23
|
+
page.title = "Hello"
|
|
24
|
+
page.add(page.text(value: "Hello Ruflet"))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
MyApp.new.run
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# config/routes.rb
|
|
33
|
+
mount Ruflet::Rails.mobile(Rails.root.join("app/mobile/main.rb")), at: "/ws"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Connect mobile to Rails base URL, e.g. `http://10.0.2.2:3000`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module Protocol
|
|
6
|
+
module Context
|
|
7
|
+
THREAD_KEY = :ruflet_rails_env
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def current_env
|
|
12
|
+
Thread.current[THREAD_KEY]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def with_env(env)
|
|
16
|
+
previous = Thread.current[THREAD_KEY]
|
|
17
|
+
Thread.current[THREAD_KEY] = env
|
|
18
|
+
yield
|
|
19
|
+
ensure
|
|
20
|
+
Thread.current[THREAD_KEY] = previous
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest/sha1"
|
|
4
|
+
|
|
5
|
+
module Ruflet
|
|
6
|
+
module Rails
|
|
7
|
+
module Protocol
|
|
8
|
+
class Endpoint
|
|
9
|
+
WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
10
|
+
|
|
11
|
+
def initialize(server:, path: "/ws")
|
|
12
|
+
@server = server
|
|
13
|
+
@path = path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
return not_found unless env["PATH_INFO"] == @path
|
|
18
|
+
return bad_request("Expected WebSocket upgrade") unless websocket_upgrade_request?(env)
|
|
19
|
+
|
|
20
|
+
hijack = env["rack.hijack"]
|
|
21
|
+
return bad_request("Rack hijack is required by Ruflet::Rails::Protocol::Endpoint") unless hijack.respond_to?(:call)
|
|
22
|
+
|
|
23
|
+
hijack.call
|
|
24
|
+
io = env["rack.hijack_io"]
|
|
25
|
+
return bad_request("Rack did not provide hijacked IO") unless io
|
|
26
|
+
|
|
27
|
+
io.sync = true if io.respond_to?(:sync=)
|
|
28
|
+
write_handshake(io, env["HTTP_SEC_WEBSOCKET_KEY"])
|
|
29
|
+
|
|
30
|
+
captured_env = env.dup
|
|
31
|
+
Thread.new(io, captured_env) do |socket, ws_env|
|
|
32
|
+
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception=)
|
|
33
|
+
Context.with_env(ws_env) do
|
|
34
|
+
@server.handle_upgraded_socket(socket)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
[-1, {}, []]
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
[500, { "content-type" => "text/plain" }, ["Ruflet Rails protocol error: #{e.message}"]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def websocket_upgrade_request?(env)
|
|
46
|
+
return false unless env["REQUEST_METHOD"] == "GET"
|
|
47
|
+
|
|
48
|
+
upgrade = env["HTTP_UPGRADE"].to_s.downcase
|
|
49
|
+
connection = env["HTTP_CONNECTION"].to_s.downcase
|
|
50
|
+
key = env["HTTP_SEC_WEBSOCKET_KEY"].to_s
|
|
51
|
+
|
|
52
|
+
upgrade == "websocket" && connection.include?("upgrade") && !key.empty?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def write_handshake(io, key)
|
|
56
|
+
accept = [Digest::SHA1.digest("#{key}#{WEBSOCKET_GUID}")].pack("m0")
|
|
57
|
+
|
|
58
|
+
io.write("HTTP/1.1 101 Switching Protocols\r\n")
|
|
59
|
+
io.write("Upgrade: websocket\r\n")
|
|
60
|
+
io.write("Connection: Upgrade\r\n")
|
|
61
|
+
io.write("Sec-WebSocket-Accept: #{accept}\r\n")
|
|
62
|
+
io.write("\r\n")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def not_found
|
|
66
|
+
[404, { "content-type" => "text/plain" }, ["Not Found"]]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bad_request(message)
|
|
70
|
+
[400, { "content-type" => "text/plain" }, [message]]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module Protocol
|
|
6
|
+
class MobileLoader
|
|
7
|
+
def initialize(file_path)
|
|
8
|
+
@file_path = file_path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def load!
|
|
12
|
+
absolute = File.expand_path(@file_path)
|
|
13
|
+
raise ArgumentError, "Mobile file not found: #{absolute}" unless File.file?(absolute)
|
|
14
|
+
|
|
15
|
+
captured_entrypoint = nil
|
|
16
|
+
interceptor = lambda do |entrypoint:, **_kwargs|
|
|
17
|
+
captured_entrypoint = entrypoint
|
|
18
|
+
:captured
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Ruflet.with_run_interceptor(interceptor) do
|
|
22
|
+
Kernel.load(absolute)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
raise ArgumentError, "No Ruflet app boot found in #{absolute}. Expected MyApp.new.run" unless captured_entrypoint
|
|
26
|
+
|
|
27
|
+
{ entrypoint: captured_entrypoint }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module Protocol
|
|
6
|
+
class Mount
|
|
7
|
+
def initialize(app, file_path:, path: "/ws")
|
|
8
|
+
@app = app
|
|
9
|
+
@path = path
|
|
10
|
+
@endpoint = Runner.new.build_mobile_endpoint(file_path: file_path, path: "/")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
return @app.call(env) unless env["PATH_INFO"] == @path
|
|
15
|
+
|
|
16
|
+
mounted_env = env.dup
|
|
17
|
+
mounted_env["PATH_INFO"] = "/"
|
|
18
|
+
@endpoint.call(mounted_env)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module Protocol
|
|
6
|
+
class Runner
|
|
7
|
+
def initialize(&entrypoint)
|
|
8
|
+
@entrypoint = entrypoint
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def build_endpoint(path: "/")
|
|
12
|
+
raise ArgumentError, "Ruflet::Rails::Protocol endpoint requires a block" unless @entrypoint.respond_to?(:call)
|
|
13
|
+
|
|
14
|
+
Endpoint.new(server: build_server(@entrypoint), path: path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def build_mobile_endpoint(file_path:, path: "/")
|
|
18
|
+
loaded = MobileLoader.new(file_path).load!
|
|
19
|
+
Endpoint.new(server: build_server(loaded[:entrypoint]), path: path)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def build_server(entrypoint)
|
|
25
|
+
Ruflet::Server.new do |page|
|
|
26
|
+
env = Context.current_env
|
|
27
|
+
if entrypoint.arity == 1
|
|
28
|
+
entrypoint.call(page)
|
|
29
|
+
else
|
|
30
|
+
entrypoint.call(page, env)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "protocol/context"
|
|
4
|
+
require_relative "protocol/middleware"
|
|
5
|
+
require_relative "protocol/endpoint"
|
|
6
|
+
require_relative "protocol/mobile_loader"
|
|
7
|
+
require_relative "protocol/runner"
|
|
8
|
+
require_relative "protocol/mount"
|
data/lib/ruflet/rails.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Mount inside Rails routes; route "at:" controls URL path.
|
|
8
|
+
def endpoint(&block)
|
|
9
|
+
Protocol::Runner.new(&block).build_endpoint
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Load app/mobile/main.rb (MyApp.new.run) and mount it in Rails routes.
|
|
13
|
+
def mobile(file_path)
|
|
14
|
+
Protocol::Runner.new.build_mobile_endpoint(file_path: file_path)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/ruflet_rails.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruflet_rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- AdamMusa
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ruflet
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - '='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.0.1
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - '='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.0.1
|
|
40
|
+
description: Rails-first integration package for mounting Ruflet mobile apps in Rails
|
|
41
|
+
routes.
|
|
42
|
+
email:
|
|
43
|
+
- adammusa2222@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- README.md
|
|
49
|
+
- lib/ruflet/rails.rb
|
|
50
|
+
- lib/ruflet/rails/protocol.rb
|
|
51
|
+
- lib/ruflet/rails/protocol/context.rb
|
|
52
|
+
- lib/ruflet/rails/protocol/endpoint.rb
|
|
53
|
+
- lib/ruflet/rails/protocol/middleware.rb
|
|
54
|
+
- lib/ruflet/rails/protocol/mobile_loader.rb
|
|
55
|
+
- lib/ruflet/rails/protocol/mount.rb
|
|
56
|
+
- lib/ruflet/rails/protocol/runner.rb
|
|
57
|
+
- lib/ruflet/rails/railtie.rb
|
|
58
|
+
- lib/ruflet/version.rb
|
|
59
|
+
- lib/ruflet_rails.rb
|
|
60
|
+
homepage: https://github.com/AdamMusa/Ruflet
|
|
61
|
+
licenses: []
|
|
62
|
+
metadata: {}
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '3.1'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.7.2
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Rails integration for Ruflet.
|
|
80
|
+
test_files: []
|