ruflet_rails 0.0.13 → 0.0.14
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 +165 -62
- data/lib/generators/ruflet/install/install_generator.rb +3 -3
- data/lib/ruflet/rails/configuration.rb +13 -7
- data/lib/ruflet/rails/install_support.rb +19 -637
- data/lib/ruflet/rails/native_app/actions.rb +167 -0
- data/lib/ruflet/rails/native_app/customization.rb +118 -0
- data/lib/ruflet/rails/native_app/html_adapter.rb +248 -0
- data/lib/ruflet/rails/native_app/navigation.rb +645 -0
- data/lib/ruflet/rails/native_app/overlays.rb +222 -0
- data/lib/ruflet/rails/native_app/shell.rb +390 -0
- data/lib/ruflet/rails/native_app.rb +25 -222
- data/lib/ruflet/rails/protocol/web_app.rb +1 -1
- data/lib/ruflet/rails/railtie.rb +3 -20
- data/lib/ruflet/rails/view_helpers.rb +205 -0
- data/lib/ruflet/rails.rb +22 -55
- data/lib/ruflet/version.rb +1 -1
- data/lib/ruflet_rails.rb +0 -4
- metadata +14 -15
- data/lib/generators/ruflet/form/form_generator.rb +0 -55
- data/lib/generators/ruflet/scaffold/scaffold_generator.rb +0 -54
- data/lib/ruflet/rails/form_helpers.rb +0 -161
- data/lib/ruflet/rails/generator_hooks.rb +0 -25
- data/lib/ruflet/rails/resource_component.rb +0 -370
- data/lib/ruflet/rails/route_stack.rb +0 -90
- data/lib/ruflet/rails/webview_app.rb +0 -54
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '097cfb74aac7af1e15b8bcb49135851595cbd7a1856eef1b4df39672c95ebab8'
|
|
4
|
+
data.tar.gz: 27d2e764c20535ebc41befd5672741500cb78387ee74f56ea284a60de9d8d721
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad4c0ab96a6741c9ff4339590e854abf2e72648a0b10c85bdf371ff7aaaee005e394f5063eecf9395756e1d9260d021a4d6c54737a8ad6326e811e63dca9d75a
|
|
7
|
+
data.tar.gz: c5aa7b3df32bedad4122d379739e05f770b8caa359e3ac01b114a29ee9c89bc7c5a3ecd175857dc79eaccb737b33bcd007d977086061b9ad25c9187872a2f994
|
data/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
`ruflet_rails` is the Rails-first integration package for Ruflet.
|
|
4
4
|
|
|
5
5
|
It mounts Ruby-driven Ruflet interfaces in a Rails application, makes Rails
|
|
6
|
-
|
|
7
|
-
clients to the same application entrypoint.
|
|
6
|
+
views able to opt into native WebView chrome, and connects web, mobile, and
|
|
7
|
+
desktop clients to the same application entrypoint.
|
|
8
8
|
|
|
9
9
|
## Add The Gem
|
|
10
10
|
|
|
@@ -24,27 +24,29 @@ bin/rails generate ruflet:install --web --desktop
|
|
|
24
24
|
|
|
25
25
|
This generator will:
|
|
26
26
|
- create `app/views/ruflet/main.rb`
|
|
27
|
-
- create `ruflet.
|
|
27
|
+
- create `config/initializers/ruflet.rb`
|
|
28
28
|
- add the Ruflet WebSocket route to `config/routes.rb`
|
|
29
29
|
- add a `/ruflet` web mount when `--web` is used
|
|
30
30
|
- download prebuilt clients from GitHub releases when `--web`, `--desktop`, or
|
|
31
31
|
`--client=web|desktop|all` is used
|
|
32
32
|
|
|
33
|
-
Generated `ruflet.
|
|
33
|
+
Generated `config/initializers/ruflet.rb`:
|
|
34
34
|
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
backend_url
|
|
35
|
+
```ruby
|
|
36
|
+
Ruflet::Rails.configure do |config|
|
|
37
|
+
config.app_name = "My App"
|
|
38
|
+
config.backend_url = ENV.fetch("RUFLET_BACKEND_URL", "http://localhost:3000")
|
|
39
39
|
|
|
40
|
-
services
|
|
40
|
+
config.services = []
|
|
41
41
|
|
|
42
|
-
assets
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
config.splash_screen = Rails.root.join("app/assets/images/splash.png")
|
|
43
|
+
config.icon_launcher = Rails.root.join("app/assets/images/icon.png")
|
|
44
|
+
end
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
At build time `ruflet_rails` serializes this Rails config into the Ruflet CLI
|
|
48
|
+
config shape, so the initializer remains the source of truth for app name,
|
|
49
|
+
backend URL, services, assets, and build colors.
|
|
48
50
|
|
|
49
51
|
## Web client
|
|
50
52
|
|
|
@@ -86,7 +88,7 @@ bundle exec rake ruflet:build[desktop]
|
|
|
86
88
|
```
|
|
87
89
|
|
|
88
90
|
Rails desktop builds are server-driven. The built desktop app connects back to the
|
|
89
|
-
Rails backend configured in `ruflet.
|
|
91
|
+
Rails backend configured in `config/initializers/ruflet.rb`; it does not package a self-contained
|
|
90
92
|
Ruby runtime.
|
|
91
93
|
|
|
92
94
|
Plain Rails dev server commands do not launch the desktop app. Request a desktop
|
|
@@ -118,82 +120,183 @@ bundle exec rake ruflet:install
|
|
|
118
120
|
bundle exec rake ruflet:install[DEVICE_ID]
|
|
119
121
|
```
|
|
120
122
|
|
|
121
|
-
##
|
|
123
|
+
## Native WebView shell
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
Beyond the server-driven UI, Ruflet can wrap an existing Rails HTML app in a
|
|
126
|
+
managed native WebView. This is an opt-in shell: calling
|
|
127
|
+
`Ruflet::Rails.native_app` wraps web pages in a native Ruflet shell whose body is
|
|
128
|
+
a WebView. Plain `Ruflet.run { |page| ... }` remains a normal Ruflet app with no
|
|
129
|
+
WebView wrapper or HTML adapter.
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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:
|
|
131
|
+
Native behavior is explicit from Rails views: ordinary links and Turbo visits
|
|
132
|
+
stay inside the WebView, while links annotated with `data-ruflet-*` can push
|
|
133
|
+
native screens, replace/root the stack, open a sheet, show a dialog, toast, or
|
|
134
|
+
promote page chrome.
|
|
133
135
|
|
|
134
136
|
```ruby
|
|
135
|
-
# app/views/ruflet/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
# app/views/ruflet/main.rb
|
|
138
|
+
Ruflet.run do |page|
|
|
139
|
+
Ruflet::Rails.native_app(
|
|
140
|
+
page,
|
|
141
|
+
start_url: "https://myapp.com",
|
|
142
|
+
title: "My App", # opt into a native AppBar (tracks <title>)
|
|
143
|
+
loading: :shimmer # default; can be "Loading..." or false
|
|
144
|
+
)
|
|
140
145
|
end
|
|
141
146
|
```
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
### Crossing web and native from HTML
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
A tiny HTML adapter reads `data-ruflet-*` attributes from the rendered page and
|
|
151
|
+
sends those payloads back to Ruby. Ruby then builds the native AppBar, drawer,
|
|
152
|
+
tabs, dialogs, sheets, services, and navigation with normal Ruflet controls. The
|
|
153
|
+
page stays a normal page in a plain browser; the attributes only activate inside
|
|
154
|
+
the native shell. Keep using normal Rails helpers like `link_to`; add Ruflet data
|
|
155
|
+
attributes only where the native app should augment the web behavior.
|
|
156
|
+
|
|
157
|
+
**Navigation** — `action` is `push` (default), `root` (reset to a root screen,
|
|
158
|
+
tab-style), `replace`, `sheet`, or `back`:
|
|
159
|
+
|
|
160
|
+
```erb
|
|
161
|
+
<%= link_to "Messages", messages_path,
|
|
162
|
+
data: { ruflet_screen: { action: "push", title: "Messages" }.to_json } %>
|
|
163
|
+
|
|
164
|
+
<%= link_to "Sign in", new_session_path,
|
|
165
|
+
data: { ruflet_screen: { action: "push", title: "Sign in",
|
|
166
|
+
leading: { icon: "close", action: "back" } }.to_json } %>
|
|
167
|
+
|
|
168
|
+
<%= link_to "Sign in", new_session_path,
|
|
169
|
+
data: { ruflet_screen: { action: "sheet" }.to_json } %>
|
|
170
|
+
|
|
171
|
+
<a href="/dashboard" data-ruflet-screen='{"action":"root","title":"Dashboard"}'>Dashboard</a>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Promote HTML chrome to native** — hide HTML header/nav elements and render
|
|
175
|
+
native AppBar, NavigationBar, NavigationDrawer, or desktop NavigationRail:
|
|
176
|
+
|
|
177
|
+
```erb
|
|
178
|
+
<%= tag.div hidden: true,
|
|
179
|
+
data: { ruflet_appbar: { title: "Sign in",
|
|
180
|
+
leading: { icon: "close", action: "back" } }.to_json } %>
|
|
181
|
+
|
|
182
|
+
<%= ruflet_appbar "Inbox", leading: { icon: "menu", action: "drawer" } do %>
|
|
183
|
+
<%= ruflet_appbar_action "search", search_path %>
|
|
184
|
+
<% end %>
|
|
185
|
+
|
|
186
|
+
<%= ruflet_drawer do %>
|
|
187
|
+
<%= ruflet_drawer_item "Home", root_path, icon: "home", selected: true %>
|
|
188
|
+
<%= ruflet_drawer_item "Settings", settings_path, icon: "settings", nav: :push %>
|
|
189
|
+
<% end %>
|
|
190
|
+
|
|
191
|
+
<%= ruflet_bottom_nav do %>
|
|
192
|
+
<%= ruflet_nav_item "Home", root_path, icon: "house", selected: true %>
|
|
193
|
+
<%= ruflet_nav_item "Profile", profile_path, icon: "person" %>
|
|
194
|
+
<% end %>
|
|
195
|
+
|
|
196
|
+
<%= ruflet_navigation_rail extended: true, breakpoint: 720 do %>
|
|
197
|
+
<%= ruflet_rail_item "Home", root_path, icon: "home", selected: true %>
|
|
198
|
+
<%= ruflet_rail_item "Inbox", inbox_path, icon: "mail" %>
|
|
199
|
+
<%= ruflet_rail_item "Settings", settings_path, icon: "settings", nav: :push %>
|
|
200
|
+
<% end %>
|
|
147
201
|
```
|
|
148
202
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
203
|
+
**Native dialogs and toasts** — from annotated Rails links and buttons.
|
|
204
|
+
Dialogs, bottom sheets, and snackbars are adaptive by default, so the native shell
|
|
205
|
+
can use platform-appropriate presentation instead of forcing the same Material
|
|
206
|
+
look everywhere:
|
|
153
207
|
|
|
154
|
-
|
|
155
|
-
|
|
208
|
+
```erb
|
|
209
|
+
<%= link_to "Delete", item_path(item),
|
|
210
|
+
data: { ruflet_action: { component: "dialog", title: "Delete?",
|
|
211
|
+
content: "This cannot be undone.", confirm: "Delete", action: "replace" }.to_json } %>
|
|
156
212
|
|
|
157
|
-
|
|
213
|
+
<%= button_tag "Copy link",
|
|
214
|
+
data: { ruflet_action: { component: "toast", message: "Copied to clipboard" }.to_json } %>
|
|
215
|
+
```
|
|
158
216
|
|
|
159
|
-
|
|
217
|
+
**Native menus and bottom sheets** — menus are regular Ruflet bottom sheets
|
|
218
|
+
driven by Rails payload data. Item taps close the native sheet first, wait for
|
|
219
|
+
the native dismiss event, then run the item callback or navigation. This keeps
|
|
220
|
+
the overlay lifecycle stable on mobile and desktop.
|
|
221
|
+
|
|
222
|
+
```erb
|
|
223
|
+
<%= ruflet_appbar "T4U",
|
|
224
|
+
actions: [
|
|
225
|
+
{
|
|
226
|
+
icon: "language",
|
|
227
|
+
action: "menu",
|
|
228
|
+
title: "Language",
|
|
229
|
+
items: [
|
|
230
|
+
{ label: "FR", icon: "check", url: url_for(locale: :fr), action: "root", selected: I18n.locale == :fr },
|
|
231
|
+
{ label: "EN", icon: "translate", url: url_for(locale: :en), action: "root" },
|
|
232
|
+
{ label: "AR", icon: "translate", url: url_for(locale: :ar), action: "root" }
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
] %>
|
|
236
|
+
```
|
|
160
237
|
|
|
161
|
-
|
|
162
|
-
|
|
238
|
+
Items close the sheet by default. Pass `close: false` only for an item that
|
|
239
|
+
should run without dismissing the sheet:
|
|
240
|
+
|
|
241
|
+
```erb
|
|
242
|
+
<%= ruflet_appbar "Filters",
|
|
243
|
+
actions: [
|
|
244
|
+
{
|
|
245
|
+
icon: "tune",
|
|
246
|
+
action: "menu",
|
|
247
|
+
title: "Filters",
|
|
248
|
+
items: [
|
|
249
|
+
{ label: "Toggle remote only", icon: "check", close: false }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
] %>
|
|
163
253
|
```
|
|
164
254
|
|
|
165
|
-
|
|
166
|
-
|
|
255
|
+
Use `action: "sheet"` to present a Rails URL inside a native bottom sheet whose
|
|
256
|
+
body is still a WebView:
|
|
167
257
|
|
|
168
|
-
```
|
|
169
|
-
|
|
258
|
+
```erb
|
|
259
|
+
<%= link_to "Choose language", languages_path,
|
|
260
|
+
data: { ruflet_action: { component: "sheet", url: languages_path }.to_json } %>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Inside a WebView sheet, plain Rails links are promoted to native actions so the
|
|
264
|
+
sheet closes before navigation:
|
|
265
|
+
|
|
266
|
+
```erb
|
|
267
|
+
<!-- app/views/languages/index.html.erb, rendered inside the sheet -->
|
|
268
|
+
<%= link_to "Français", url_for(locale: :fr) %>
|
|
269
|
+
<%= link_to "English", url_for(locale: :en) %>
|
|
270
|
+
<%= link_to "العربية", url_for(locale: :ar) %>
|
|
170
271
|
```
|
|
171
272
|
|
|
172
|
-
|
|
173
|
-
as Ruflet dropdowns populated from the associated Rails model.
|
|
273
|
+
Add `data-ruflet-close="false"` when a sheet link should not dismiss:
|
|
174
274
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
275
|
+
```erb
|
|
276
|
+
<%= link_to "Preview", preview_path, data: { ruflet_close: "false" } %>
|
|
277
|
+
```
|
|
178
278
|
|
|
179
|
-
|
|
279
|
+
**Native services** — ERB can trigger safe platform services through the same
|
|
280
|
+
`data-ruflet-action` channel:
|
|
180
281
|
|
|
181
|
-
|
|
182
|
-
|
|
282
|
+
```erb
|
|
283
|
+
<%= ruflet_share_link "Share", "#",
|
|
284
|
+
text: "Look at this", title: "My App" %>
|
|
183
285
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
end
|
|
190
|
-
end
|
|
286
|
+
<%= ruflet_copy_button "Copy invite", text: invite_url(@invite) %>
|
|
287
|
+
|
|
288
|
+
<%= ruflet_launch_link "Open docs", "https://flutteronrails.com" %>
|
|
289
|
+
|
|
290
|
+
<%= ruflet_haptic_button "Tap", style: "light" %>
|
|
191
291
|
```
|
|
192
292
|
|
|
293
|
+
For a Fizzy-style web app, keep `Ruflet::Rails.native_app` simple and opt in from
|
|
294
|
+
the views that need native treatment:
|
|
295
|
+
|
|
193
296
|
```ruby
|
|
194
297
|
# app/views/ruflet/main.rb
|
|
195
298
|
Ruflet.run do |page|
|
|
196
|
-
|
|
299
|
+
Ruflet::Rails.native_app(page, start_url: "#{Ruflet::Rails.backend_url}/")
|
|
197
300
|
end
|
|
198
301
|
```
|
|
199
302
|
|
|
@@ -19,11 +19,11 @@ module Ruflet
|
|
|
19
19
|
create_file target, Ruflet::Rails::InstallSupport.default_app_template(app_title: app_name)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
target = File.join(destination_root, "ruflet.
|
|
22
|
+
def create_ruflet_initializer
|
|
23
|
+
target = File.join(destination_root, "config", "initializers", "ruflet.rb")
|
|
24
24
|
return if File.exist?(target)
|
|
25
25
|
|
|
26
|
-
create_file target, Ruflet::Rails::InstallSupport.
|
|
26
|
+
create_file target, Ruflet::Rails::InstallSupport.default_initializer(app_name: app_name)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Mount the native WebSocket endpoint explicitly in config/routes.rb.
|
|
@@ -4,9 +4,9 @@ module Ruflet
|
|
|
4
4
|
module Rails
|
|
5
5
|
# Central configuration for ruflet_rails.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# Rails apps keep Ruflet build metadata in config/initializers/ruflet.rb.
|
|
8
|
+
# The build task serializes this object to the ruflet.yaml structure the
|
|
9
|
+
# Ruflet CLI consumes, so Rails remains the source of truth:
|
|
10
10
|
#
|
|
11
11
|
# Ruflet::Rails.configure do |config|
|
|
12
12
|
# # Runtime / server
|
|
@@ -15,8 +15,9 @@ module Ruflet
|
|
|
15
15
|
# # App metadata (ruflet.yaml → app:)
|
|
16
16
|
# config.app_name = "My App"
|
|
17
17
|
#
|
|
18
|
-
# # Services (ruflet.yaml → services:)
|
|
18
|
+
# # Services / extensions (ruflet.yaml → services: / extensions:)
|
|
19
19
|
# config.services = []
|
|
20
|
+
# config.extensions = %w[webview]
|
|
20
21
|
#
|
|
21
22
|
# # Assets (ruflet.yaml → assets:)
|
|
22
23
|
# config.splash_screen = Rails.root.join("app/assets/images/splash.png")
|
|
@@ -38,16 +39,17 @@ module Ruflet
|
|
|
38
39
|
# --- Runtime / server ---
|
|
39
40
|
|
|
40
41
|
# Backend base URL. Used as --dart-define=RUFLET_URL at build time
|
|
41
|
-
# and by the desktop launcher.
|
|
42
|
+
# and by the desktop launcher.
|
|
42
43
|
attr_accessor :backend_url
|
|
43
44
|
|
|
44
45
|
# --- App metadata (ruflet.yaml → app:) ---
|
|
45
46
|
|
|
46
47
|
attr_accessor :app_name
|
|
47
48
|
|
|
48
|
-
# --- Services (ruflet.yaml → services:) ---
|
|
49
|
+
# --- Services / extensions (ruflet.yaml → services: / extensions:) ---
|
|
49
50
|
|
|
50
51
|
attr_accessor :services
|
|
52
|
+
attr_accessor :extensions
|
|
51
53
|
|
|
52
54
|
# --- Assets (ruflet.yaml → assets:) ---
|
|
53
55
|
|
|
@@ -71,6 +73,7 @@ module Ruflet
|
|
|
71
73
|
@backend_url = nil
|
|
72
74
|
@app_name = nil
|
|
73
75
|
@services = []
|
|
76
|
+
@extensions = []
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
# Serialises config to the ruflet.yaml hash structure so the CLI can
|
|
@@ -84,7 +87,10 @@ module Ruflet
|
|
|
84
87
|
app["backend_url"] = @backend_url if @backend_url
|
|
85
88
|
hash["app"] = app unless app.empty?
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
services = Array(@services)
|
|
91
|
+
extensions = Array(@extensions)
|
|
92
|
+
hash["services"] = services unless services.empty?
|
|
93
|
+
hash["extensions"] = extensions unless extensions.empty?
|
|
88
94
|
|
|
89
95
|
assets = {}
|
|
90
96
|
assets["splash_screen"] = @splash_screen.to_s if @splash_screen
|