ruflet_rails 0.0.11 → 0.0.13
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/README.md +56 -55
- data/lib/generators/ruflet/install/install_generator.rb +46 -15
- data/lib/ruflet/rails/configuration.rb +6 -24
- data/lib/ruflet/rails/generator_hooks.rb +25 -0
- data/lib/ruflet/rails/install_support.rb +40 -67
- data/lib/ruflet/rails/native_app.rb +4 -3
- data/lib/ruflet/rails/protocol/runner.rb +0 -2
- data/lib/ruflet/rails/protocol/web_app.rb +83 -34
- data/lib/ruflet/rails/protocol.rb +0 -4
- data/lib/ruflet/rails/railtie.rb +28 -12
- data/lib/ruflet/rails/resource_component.rb +4 -5
- data/lib/ruflet/rails/web_installer.rb +137 -0
- data/lib/ruflet/rails/webview_app.rb +1 -1
- data/lib/ruflet/rails.rb +16 -201
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_rails.rb +1 -1
- metadata +11 -14
- data/lib/ruflet/rails/protocol/middleware.rb +0 -90
- data/lib/ruflet/rails/protocol/static_index_guard.rb +0 -6
- data/lib/ruflet/rails/protocol/web_app_endpoint.rb +0 -44
- data/lib/ruflet/rails/protocol/web_socket_connection.rb +0 -11
- data/lib/ruflet/rails/protocol/wire_codec.rb +0 -11
- data/lib/ruflet/rails/view.rb +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc30304299f6e6c539c8fcc838c7f843b54b2d106e6219c657ac8c856c69f9cf
|
|
4
|
+
data.tar.gz: 52ec66b70428d3d1d4ff2d3d7ac4cf6bae34b497addd74e6ba39f8f9c3cc5d3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 976c713a63c7a6f3663fbd2541474008e010947a81c6d205cb43f318d6e6fbdaae59c71564f145f34311570518c1910ead0f0278173e270f9ce6bbda3bd4d659
|
|
7
|
+
data.tar.gz: 88d2668bd895b3e817bae881f65ab8fa395ec54377b850a9f7aef163bcf1caa96982656103fdeb8b6034a40271ae1c4dfe80ae43a7f987fd0cf816e56f28cb2d
|
data/README.md
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
`ruflet_rails` is the Rails-first integration package for Ruflet.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
It mounts Ruby-driven Ruflet interfaces in a Rails application, makes Rails
|
|
6
|
+
models available to Ruflet components, and connects web, mobile, and desktop
|
|
7
|
+
clients to the same application entrypoint.
|
|
7
8
|
|
|
8
|
-
##
|
|
9
|
+
## Add The Gem
|
|
9
10
|
|
|
10
11
|
```ruby
|
|
11
12
|
# Gemfile
|
|
12
|
-
gem "ruflet_rails"
|
|
13
|
+
gem "ruflet_rails"
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
## Install
|
|
16
|
+
## Install Into Rails
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
bin/rails generate ruflet:install
|
|
@@ -24,7 +25,8 @@ bin/rails generate ruflet:install --web --desktop
|
|
|
24
25
|
This generator will:
|
|
25
26
|
- create `app/views/ruflet/main.rb`
|
|
26
27
|
- create `ruflet.yaml`
|
|
27
|
-
- add the Ruflet
|
|
28
|
+
- add the Ruflet WebSocket route to `config/routes.rb`
|
|
29
|
+
- add a `/ruflet` web mount when `--web` is used
|
|
28
30
|
- download prebuilt clients from GitHub releases when `--web`, `--desktop`, or
|
|
29
31
|
`--client=web|desktop|all` is used
|
|
30
32
|
|
|
@@ -44,12 +46,30 @@ assets:
|
|
|
44
46
|
|
|
45
47
|
For Rails apps, those asset paths are resolved from `app/assets/` during build.
|
|
46
48
|
|
|
47
|
-
##
|
|
49
|
+
## Web client
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
Rails installs the prebuilt web client into `frontend/`; it does not need
|
|
52
|
+
Flutter source or a Flutter web build:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bundle exec rake ruflet:web
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Mount the installed client and a developer-owned Ruflet entrypoint:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
mount Ruflet::Rails.web_app(app_file: Rails.root.join("app/views/ruflet/main.rb")), at: "/app"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The install generator adds the same mount at `/ruflet` when `--web` is used.
|
|
65
|
+
The mount serves the static client and its WebSocket endpoint together. The
|
|
66
|
+
same `main.rb` also drives native clients through the generated `/ws` route.
|
|
67
|
+
|
|
68
|
+
## Build native clients from Rails
|
|
69
|
+
|
|
70
|
+
Uses the same native build pipeline as `ruflet build`:
|
|
50
71
|
|
|
51
72
|
```bash
|
|
52
|
-
bundle exec rake ruflet:build[web]
|
|
53
73
|
bundle exec rake ruflet:build[macos]
|
|
54
74
|
bundle exec rake ruflet:build[windows]
|
|
55
75
|
bundle exec rake ruflet:build[linux]
|
|
@@ -59,8 +79,6 @@ bundle exec rake ruflet:build[ios]
|
|
|
59
79
|
bundle exec rake ruflet:build[aab]
|
|
60
80
|
```
|
|
61
81
|
|
|
62
|
-
Rails web builds are published to `public/ruflet` and served by Rails at `/ruflet/`.
|
|
63
|
-
|
|
64
82
|
`desktop` is also accepted as a host-platform alias:
|
|
65
83
|
|
|
66
84
|
```bash
|
|
@@ -82,16 +100,14 @@ bin/rails s --desktop
|
|
|
82
100
|
|
|
83
101
|
## Update prebuilt clients
|
|
84
102
|
|
|
85
|
-
|
|
103
|
+
Reinstall web or update native desktop clients:
|
|
86
104
|
|
|
87
105
|
```bash
|
|
88
|
-
bundle exec rake ruflet:
|
|
106
|
+
bundle exec rake ruflet:web
|
|
89
107
|
bundle exec rake ruflet:update[desktop]
|
|
90
|
-
bundle exec rake ruflet:update[all]
|
|
91
108
|
```
|
|
92
109
|
|
|
93
|
-
|
|
94
|
-
by Rails at `/ruflet/`. The Rails app does not vendor Flutter source code.
|
|
110
|
+
The Rails app does not vendor Flutter source code.
|
|
95
111
|
|
|
96
112
|
## Install mobile build
|
|
97
113
|
|
|
@@ -104,39 +120,18 @@ bundle exec rake ruflet:install[DEVICE_ID]
|
|
|
104
120
|
|
|
105
121
|
## Ruflet resource scaffolds
|
|
106
122
|
|
|
107
|
-
|
|
123
|
+
The standard Rails scaffold command also generates a mountable Ruflet CRUD
|
|
124
|
+
component:
|
|
108
125
|
|
|
109
126
|
```bash
|
|
110
|
-
bin/rails generate
|
|
127
|
+
bin/rails generate scaffold Post title:string body:text published:boolean
|
|
111
128
|
```
|
|
112
129
|
|
|
113
|
-
The
|
|
130
|
+
The Rails model attributes are passed to the Ruflet component generator. The
|
|
131
|
+
scaffold creates generated application code the Rails developer can own and
|
|
132
|
+
edit:
|
|
114
133
|
|
|
115
134
|
```ruby
|
|
116
|
-
# app/views/ruflet/posts_view.rb
|
|
117
|
-
require_relative "components/posts/post_component"
|
|
118
|
-
|
|
119
|
-
class PostView < RufletView
|
|
120
|
-
include Ruflet::Rails::FormHelpers
|
|
121
|
-
|
|
122
|
-
route "/posts"
|
|
123
|
-
|
|
124
|
-
def render
|
|
125
|
-
page.title = resource_title
|
|
126
|
-
render_index
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
private
|
|
130
|
-
|
|
131
|
-
def records
|
|
132
|
-
# edit resource query logic here
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def component
|
|
136
|
-
@component ||= PostComponent.new(page, controller: self)
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
135
|
# app/views/ruflet/components/posts/post_component.rb
|
|
141
136
|
class PostComponent < Ruflet::Rails::ResourceComponent
|
|
142
137
|
def render
|
|
@@ -145,11 +140,19 @@ class PostComponent < Ruflet::Rails::ResourceComponent
|
|
|
145
140
|
end
|
|
146
141
|
```
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
Mount it explicitly in `config/routes.rb`:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
mount Ruflet::Rails.web_app(view: "PostComponent"), at: "/posts"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The generated component contains the developer-owned UI and persistence calls.
|
|
150
|
+
`ruflet_rails` provides the reusable model, navigation, dialog, and formatting
|
|
151
|
+
helpers. Component files under `app/views/ruflet/components` are loaded by the
|
|
152
|
+
Railtie so both web mounts and `main.rb` can reference them.
|
|
153
|
+
|
|
154
|
+
Use `--skip-ruflet` when a Rails scaffold should not generate a Ruflet
|
|
155
|
+
component.
|
|
153
156
|
|
|
154
157
|
## Ruflet model forms
|
|
155
158
|
|
|
@@ -175,8 +178,8 @@ when that base component does not already exist.
|
|
|
175
178
|
|
|
176
179
|
## Shared Ruflet components
|
|
177
180
|
|
|
178
|
-
Put shared Ruflet UI components under `app/views/ruflet/components`.
|
|
179
|
-
|
|
181
|
+
Put shared Ruflet UI components under `app/views/ruflet/components`. Those files
|
|
182
|
+
are reloaded with Rails application code:
|
|
180
183
|
|
|
181
184
|
```ruby
|
|
182
185
|
# app/views/ruflet/components/page_title_component.rb
|
|
@@ -188,11 +191,9 @@ end
|
|
|
188
191
|
```
|
|
189
192
|
|
|
190
193
|
```ruby
|
|
191
|
-
# app/views/ruflet/
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
page.add(PageTitleComponent.render(page, "Posts"))
|
|
195
|
-
end
|
|
194
|
+
# app/views/ruflet/main.rb
|
|
195
|
+
Ruflet.run do |page|
|
|
196
|
+
page.add(PageTitleComponent.render(page, "Posts"))
|
|
196
197
|
end
|
|
197
198
|
```
|
|
198
199
|
|
|
@@ -6,8 +6,9 @@ require "ruflet/rails/install_support"
|
|
|
6
6
|
module Ruflet
|
|
7
7
|
module Generators
|
|
8
8
|
class InstallGenerator < ::Rails::Generators::Base
|
|
9
|
+
class_option :web, type: :boolean, default: false, desc: "Install the prebuilt Ruflet web client"
|
|
9
10
|
class_option :desktop, type: :boolean, default: false, desc: "Download the server-driven desktop Ruflet client"
|
|
10
|
-
class_option :client, type: :string, default: nil, desc: "
|
|
11
|
+
class_option :client, type: :string, default: nil, desc: "Install prebuilt clients: web, desktop, all, or none"
|
|
11
12
|
|
|
12
13
|
desc "Install Ruflet into a Rails app."
|
|
13
14
|
|
|
@@ -25,11 +26,24 @@ module Ruflet
|
|
|
25
26
|
create_file target, Ruflet::Rails::InstallSupport.default_ruflet_yaml(app_name: app_name)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# Mount the native WebSocket endpoint explicitly in config/routes.rb.
|
|
30
|
+
# Nothing is auto-mounted — the dev owns the route, like any other.
|
|
31
|
+
def mount_websocket
|
|
32
|
+
routes = File.join(destination_root, "config", "routes.rb")
|
|
33
|
+
return unless File.file?(routes)
|
|
34
|
+
return if File.read(routes).include?("Ruflet::Rails.app(")
|
|
35
|
+
|
|
36
|
+
route Ruflet::Rails::InstallSupport.route_snippet(entrypoint: entrypoint_path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def mount_web_app
|
|
40
|
+
return unless web_requested?
|
|
41
|
+
|
|
42
|
+
routes = File.join(destination_root, "config", "routes.rb")
|
|
43
|
+
return unless File.file?(routes)
|
|
44
|
+
return if File.read(routes).include?("Ruflet::Rails.web_app(")
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
route Ruflet::Rails::InstallSupport.web_route_snippet(entrypoint: entrypoint_path)
|
|
33
47
|
end
|
|
34
48
|
|
|
35
49
|
def add_desktop_flag_to_binstubs
|
|
@@ -43,14 +57,8 @@ module Ruflet
|
|
|
43
57
|
client = requested_client
|
|
44
58
|
return if client == "none"
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Ruflet::CLI.command_update([client])
|
|
49
|
-
end
|
|
50
|
-
unless exit_code.to_i.zero?
|
|
51
|
-
@client_download_failed = true
|
|
52
|
-
say_status(:warn, "Ruflet client download failed; install files were generated and build/update steps are printed below", :yellow)
|
|
53
|
-
end
|
|
60
|
+
install_web_client if %w[web all].include?(client)
|
|
61
|
+
install_desktop_client if %w[desktop all].include?(client)
|
|
54
62
|
rescue StandardError => e
|
|
55
63
|
@client_download_failed = true
|
|
56
64
|
say_status(:warn, "Ruflet client download failed: #{e.class}: #{e.message}", :yellow)
|
|
@@ -77,24 +85,47 @@ module Ruflet
|
|
|
77
85
|
def requested_client
|
|
78
86
|
explicit = options[:client].to_s.strip.downcase
|
|
79
87
|
unless explicit.empty?
|
|
80
|
-
raise Thor::Error, "--client must be desktop or none" unless %w[desktop none].include?(explicit)
|
|
88
|
+
raise Thor::Error, "--client must be web, desktop, all, or none" unless %w[web desktop all none].include?(explicit)
|
|
81
89
|
|
|
82
90
|
return explicit
|
|
83
91
|
end
|
|
84
92
|
|
|
93
|
+
return "all" if options[:web] && options[:desktop]
|
|
94
|
+
return "web" if options[:web]
|
|
85
95
|
return "desktop" if options[:desktop]
|
|
86
96
|
|
|
87
97
|
"none"
|
|
88
98
|
end
|
|
89
99
|
|
|
90
100
|
def desktop_requested?
|
|
91
|
-
requested_client
|
|
101
|
+
%w[desktop all].include?(requested_client)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def web_requested?
|
|
105
|
+
%w[web all].include?(requested_client)
|
|
92
106
|
end
|
|
93
107
|
|
|
94
108
|
def install_target
|
|
95
109
|
"ruflet"
|
|
96
110
|
end
|
|
97
111
|
|
|
112
|
+
def install_web_client
|
|
113
|
+
return if Ruflet::Rails::WebInstaller.install!(root: destination_root)
|
|
114
|
+
|
|
115
|
+
client_download_failed("web")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def install_desktop_client
|
|
119
|
+
require "ruflet/cli"
|
|
120
|
+
exit_code = Dir.chdir(destination_root) { Ruflet::CLI.command_update(["desktop"]) }
|
|
121
|
+
client_download_failed("desktop") unless exit_code.to_i.zero?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def client_download_failed(client)
|
|
125
|
+
@client_download_failed = true
|
|
126
|
+
say_status(:warn, "Ruflet #{client} client download failed; install files were still generated", :yellow)
|
|
127
|
+
end
|
|
128
|
+
|
|
98
129
|
def install_desktop_flag_bootstrap(relative_path)
|
|
99
130
|
target = File.join(destination_root, relative_path)
|
|
100
131
|
return unless File.file?(target)
|
|
@@ -4,20 +4,14 @@ module Ruflet
|
|
|
4
4
|
module Rails
|
|
5
5
|
# Central configuration for ruflet_rails.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# Set in config/initializers/ruflet.rb:
|
|
7
|
+
# An install needs no config/initializers/ruflet.rb: routes mount
|
|
8
|
+
# app/views/ruflet/main.rb explicitly and build metadata is read from
|
|
9
|
+
# ruflet.yaml. Add an initializer only to override these settings:
|
|
11
10
|
#
|
|
12
11
|
# Ruflet::Rails.configure do |config|
|
|
13
12
|
# # Runtime / server
|
|
14
|
-
# config.app_file = Rails.root.join("app/views/ruflet/main.rb")
|
|
15
|
-
# config.ws_path = "/ws"
|
|
16
13
|
# config.backend_url = Rails.env.production? ? "https://example.com" : "http://localhost:3000"
|
|
17
14
|
#
|
|
18
|
-
# # Flutter web build output directory
|
|
19
|
-
# config.web_build_dir = Rails.root.join("public/app")
|
|
20
|
-
#
|
|
21
15
|
# # App metadata (ruflet.yaml → app:)
|
|
22
16
|
# config.app_name = "My App"
|
|
23
17
|
#
|
|
@@ -43,19 +37,10 @@ module Ruflet
|
|
|
43
37
|
class Configuration
|
|
44
38
|
# --- Runtime / server ---
|
|
45
39
|
|
|
46
|
-
# Absolute path to the Ruflet app entry-point.
|
|
47
|
-
attr_accessor :app_file
|
|
48
|
-
|
|
49
|
-
# URL path the WebSocket endpoint listens on. Defaults to "/ws".
|
|
50
|
-
attr_accessor :ws_path
|
|
51
|
-
|
|
52
40
|
# Backend base URL. Used as --dart-define=RUFLET_URL at build time
|
|
53
41
|
# and by the desktop launcher. Replaces ruflet.yaml → app.backend_url.
|
|
54
42
|
attr_accessor :backend_url
|
|
55
43
|
|
|
56
|
-
# Absolute directory containing the Flutter web build (index.html + assets).
|
|
57
|
-
attr_accessor :web_build_dir
|
|
58
|
-
|
|
59
44
|
# --- App metadata (ruflet.yaml → app:) ---
|
|
60
45
|
|
|
61
46
|
attr_accessor :app_name
|
|
@@ -83,12 +68,9 @@ module Ruflet
|
|
|
83
68
|
attr_accessor :theme_color
|
|
84
69
|
|
|
85
70
|
def initialize
|
|
86
|
-
@
|
|
87
|
-
@
|
|
88
|
-
@
|
|
89
|
-
@web_build_dir = nil
|
|
90
|
-
@app_name = nil
|
|
91
|
-
@services = []
|
|
71
|
+
@backend_url = nil
|
|
72
|
+
@app_name = nil
|
|
73
|
+
@services = []
|
|
92
74
|
end
|
|
93
75
|
|
|
94
76
|
# Serialises config to the ruflet.yaml hash structure so the CLI can
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruflet
|
|
4
|
+
module Rails
|
|
5
|
+
module GeneratorHooks
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def install!
|
|
9
|
+
require "active_support/core_ext/string/filters"
|
|
10
|
+
require "rails/generators/rails/scaffold/scaffold_generator"
|
|
11
|
+
require "generators/ruflet/scaffold/scaffold_generator"
|
|
12
|
+
|
|
13
|
+
generator = ::Rails::Generators::ScaffoldGenerator
|
|
14
|
+
return if generator.class_options.key?(:ruflet)
|
|
15
|
+
|
|
16
|
+
generator.hook_for(
|
|
17
|
+
:ruflet,
|
|
18
|
+
type: :boolean,
|
|
19
|
+
default: true,
|
|
20
|
+
desc: "Generate a Ruflet resource component"
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -10,18 +10,33 @@ module Ruflet
|
|
|
10
10
|
module_function
|
|
11
11
|
|
|
12
12
|
def default_app_template(app_title:)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
require "ruflet_rails"
|
|
16
|
-
|
|
17
|
-
Ruflet::Rails.load_views(__dir__)
|
|
13
|
+
<<~RUBY
|
|
14
|
+
# frozen_string_literal: true
|
|
18
15
|
|
|
16
|
+
# The home screen for this Ruflet app. You own this file — declare your
|
|
17
|
+
# screens here; nothing is auto-discovered. It is mounted explicitly in
|
|
18
|
+
# config/routes.rb (the install generator adds this line):
|
|
19
|
+
# match "/ws", to: Ruflet::Rails.app(Rails.root.join("app/views/ruflet/main.rb")), via: :all
|
|
19
20
|
Ruflet.run do |page|
|
|
20
21
|
page.title = #{app_title.inspect}
|
|
21
|
-
|
|
22
|
+
page.add(
|
|
23
|
+
safe_area(
|
|
24
|
+
container(
|
|
25
|
+
expand: true,
|
|
26
|
+
padding: 24,
|
|
27
|
+
content: column(
|
|
28
|
+
spacing: 12,
|
|
29
|
+
controls: [
|
|
30
|
+
text(#{app_title.inspect}, size: 24, weight: "bold"),
|
|
31
|
+
text("Edit app/views/ruflet/main.rb to build your app.")
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
),
|
|
35
|
+
expand: true
|
|
36
|
+
)
|
|
37
|
+
)
|
|
22
38
|
end
|
|
23
39
|
RUBY
|
|
24
|
-
template.gsub(/^ /, " ")
|
|
25
40
|
end
|
|
26
41
|
|
|
27
42
|
def application_component_template
|
|
@@ -90,10 +105,6 @@ module Ruflet
|
|
|
90
105
|
File.join("app", "views", "ruflet", "components", "application_component.rb")
|
|
91
106
|
end
|
|
92
107
|
|
|
93
|
-
def default_mobile_app_template(app_title:)
|
|
94
|
-
default_app_template(app_title: app_title)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
108
|
def model_names(model_name)
|
|
98
109
|
raw = model_name.to_s.strip
|
|
99
110
|
class_name = raw.camelize
|
|
@@ -673,22 +684,6 @@ module Ruflet
|
|
|
673
684
|
YAML
|
|
674
685
|
end
|
|
675
686
|
|
|
676
|
-
def desktop_initializer_path
|
|
677
|
-
File.join("config", "initializers", "ruflet_desktop.rb")
|
|
678
|
-
end
|
|
679
|
-
|
|
680
|
-
def desktop_initializer_template
|
|
681
|
-
<<~RUBY
|
|
682
|
-
# frozen_string_literal: true
|
|
683
|
-
|
|
684
|
-
# Set this to true when you intentionally want the Rails server process to
|
|
685
|
-
# launch the server-driven Ruflet desktop client.
|
|
686
|
-
Rails.application.configure do
|
|
687
|
-
config.x.ruflet_rails.desktop = false
|
|
688
|
-
end
|
|
689
|
-
RUBY
|
|
690
|
-
end
|
|
691
|
-
|
|
692
687
|
def ruby_desktop_flag_bootstrap
|
|
693
688
|
<<~RUBY
|
|
694
689
|
# ruflet_rails desktop flag
|
|
@@ -746,70 +741,41 @@ module Ruflet
|
|
|
746
741
|
value
|
|
747
742
|
end
|
|
748
743
|
|
|
744
|
+
# ruflet_rails builds only native clients. The web client is installed
|
|
745
|
+
# prebuilt (rake ruflet:web), never compiled, so "web" is not a build
|
|
746
|
+
# target here.
|
|
749
747
|
def build_args_for_platform(platform, ruflet_url: nil)
|
|
750
748
|
normalized = normalize_build_platform(platform)
|
|
751
749
|
return [] if normalized.to_s.empty?
|
|
750
|
+
return [] if normalized == "web"
|
|
752
751
|
|
|
753
|
-
|
|
754
|
-
args += ["--dart-define", "RUFLET_URL=#{ruflet_url}"] if normalized == "web" && ruflet_url.to_s.strip != ""
|
|
755
|
-
args
|
|
752
|
+
[normalized]
|
|
756
753
|
end
|
|
757
754
|
|
|
758
755
|
def default_entrypoint_path
|
|
759
756
|
File.join("app", "views", "ruflet", "main.rb")
|
|
760
757
|
end
|
|
761
758
|
|
|
762
|
-
def initializer_template(entrypoint: default_entrypoint_path, ws_path: "/ws")
|
|
763
|
-
<<~RUBY
|
|
764
|
-
# frozen_string_literal: true
|
|
765
|
-
|
|
766
|
-
Ruflet::Rails.configure do |config|
|
|
767
|
-
# Ruflet app entry-point. Auto-mounts a WebSocket endpoint at ws_path —
|
|
768
|
-
# no explicit route needed in config/routes.rb.
|
|
769
|
-
config.app_file = Rails.root.join(#{entrypoint.inspect})
|
|
770
|
-
|
|
771
|
-
# URL path the WebSocket endpoint listens on (default: "/ws").
|
|
772
|
-
config.ws_path = #{ws_path.inspect}
|
|
773
|
-
|
|
774
|
-
# Base URL the Flutter client uses to reach this Rails app. Always
|
|
775
|
-
# required: it backs asset URLs (Ruflet::Rails.asset_url), the
|
|
776
|
-
# build-time RUFLET_URL define, and the desktop launcher. At runtime
|
|
777
|
-
# it can fall back to the connecting host, but a build has no request,
|
|
778
|
-
# so set it here. Point it at a LAN IP (not localhost) to test on a
|
|
779
|
-
# real device.
|
|
780
|
-
config.backend_url = ENV.fetch("RUFLET_BACKEND_URL") do
|
|
781
|
-
Rails.env.production? ? "https://example.com" : "http://localhost:3000"
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
# Directory the Flutter web build is served from. Defaults to
|
|
785
|
-
# Rails.root/build/web (where `rake ruflet:build[web]` outputs).
|
|
786
|
-
# Must stay OUTSIDE public/, or Rails would serve it statically and
|
|
787
|
-
# expose the app at a path no route declares.
|
|
788
|
-
# config.web_build_dir = Rails.root.join("build", "web")
|
|
789
|
-
end
|
|
790
|
-
RUBY
|
|
791
|
-
end
|
|
792
|
-
|
|
793
|
-
def initializer_path
|
|
794
|
-
File.join("config", "initializers", "ruflet.rb")
|
|
795
|
-
end
|
|
796
|
-
|
|
797
759
|
# Kept for backward compatibility with apps that use manual mount.
|
|
798
760
|
def route_snippet(entrypoint: default_entrypoint_path, mount_path: "/ws", helper: "app")
|
|
799
761
|
%(match "#{mount_path}", to: Ruflet::Rails.#{helper}(Rails.root.join("#{entrypoint}")), via: :all)
|
|
800
762
|
end
|
|
801
763
|
|
|
764
|
+
def web_route_snippet(entrypoint: default_entrypoint_path, mount_path: "/ruflet")
|
|
765
|
+
%(mount Ruflet::Rails.web_app(app_file: Rails.root.join("#{entrypoint}")), at: "#{mount_path}")
|
|
766
|
+
end
|
|
767
|
+
|
|
802
768
|
def install_next_steps(target:, entrypoint:, client:, mount_path: "/ws")
|
|
803
769
|
lines = [
|
|
804
770
|
"Ruflet Rails installed.",
|
|
805
771
|
"Generated entrypoint: #{entrypoint}",
|
|
806
|
-
"
|
|
772
|
+
"Added WebSocket route #{mount_path} to config/routes.rb",
|
|
807
773
|
"Next steps:",
|
|
808
774
|
" 1. Start Rails: bin/rails server",
|
|
809
775
|
" 2. Connect your Ruflet app to ws://localhost:3000#{mount_path}"
|
|
810
776
|
]
|
|
811
777
|
|
|
812
|
-
if client.to_s
|
|
778
|
+
if %w[desktop all].include?(client.to_s)
|
|
813
779
|
lines += [
|
|
814
780
|
"Desktop clients are server-driven and connect to this Rails app.",
|
|
815
781
|
"Plain bin/dev, bin/rails server, and bin/rails s do not launch desktop.",
|
|
@@ -819,6 +785,13 @@ module Ruflet
|
|
|
819
785
|
]
|
|
820
786
|
end
|
|
821
787
|
|
|
788
|
+
if %w[web all].include?(client.to_s)
|
|
789
|
+
lines += [
|
|
790
|
+
"Web client installed into frontend/.",
|
|
791
|
+
"Open the mounted Ruflet web app at http://localhost:3000/ruflet"
|
|
792
|
+
]
|
|
793
|
+
end
|
|
794
|
+
|
|
822
795
|
lines
|
|
823
796
|
end
|
|
824
797
|
end
|
|
@@ -4,7 +4,7 @@ require "uri"
|
|
|
4
4
|
|
|
5
5
|
module Ruflet
|
|
6
6
|
module Rails
|
|
7
|
-
#
|
|
7
|
+
# Managed webview navigation driver for ruflet_rails.
|
|
8
8
|
#
|
|
9
9
|
# Your existing web app is the body, rendered in a WebView. Navigation works
|
|
10
10
|
# out of the box: a tiny JS bridge injected into each page intercepts link
|
|
@@ -122,6 +122,7 @@ module Ruflet
|
|
|
122
122
|
Ruflet::UI::ControlFactory.build(
|
|
123
123
|
:webview,
|
|
124
124
|
url: screen.url,
|
|
125
|
+
method: "get",
|
|
125
126
|
expand: true,
|
|
126
127
|
on_page_ended: ->(_event) { inject_bridge(screen) },
|
|
127
128
|
on_console_message: ->(event) { handle_message(screen, message_of(event)) }
|
|
@@ -190,7 +191,7 @@ module Ruflet
|
|
|
190
191
|
# --- Modal (bottom sheet of web content) -------------------------------
|
|
191
192
|
|
|
192
193
|
def present_modal(url)
|
|
193
|
-
sheet_webview = Ruflet::UI::ControlFactory.build(:webview, url: url.to_s, expand: true)
|
|
194
|
+
sheet_webview = Ruflet::UI::ControlFactory.build(:webview, url: url.to_s, method: "get", expand: true)
|
|
194
195
|
@modal_sheet = Ruflet::UI::ControlFactory.build(
|
|
195
196
|
:bottomsheet,
|
|
196
197
|
open: true,
|
|
@@ -245,7 +246,7 @@ module Ruflet
|
|
|
245
246
|
|
|
246
247
|
module_function
|
|
247
248
|
|
|
248
|
-
# Start a
|
|
249
|
+
# Start a managed webview app. See NativeApp.
|
|
249
250
|
def native_app(page, **opts)
|
|
250
251
|
NativeApp.new(page, **opts).start
|
|
251
252
|
end
|