wasmify-rails 0.1.0 → 0.1.2
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 -0
- data/README.md +27 -3
- data/lib/{active_storage → action_mailer}/null_delivery.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3_wasm_adapter.rb +24 -0
- data/lib/generators/wasmify/install/install_generator.rb +6 -1
- data/lib/generators/wasmify/install/templates/config/environments/wasm.rb +4 -1
- data/lib/generators/wasmify/install/templates/config/wasmify.yml +1 -0
- data/lib/generators/wasmify/pwa/templates/pwa/database.js +2 -10
- data/lib/generators/wasmify/pwa/templates/pwa/rails.sw.js +6 -2
- data/lib/image_processing/null.rb +1 -0
- data/lib/rack/data_uri_uploads.rb +69 -0
- data/lib/wasmify/rails/railtie.rb +6 -0
- data/lib/wasmify/rails/version.rb +1 -1
- data/lib/wasmify-rails.rb +2 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67904120ba3d147d07ad14d5e15c6f9faf06eb43f3c93d132326407581be4ad7
|
4
|
+
data.tar.gz: c88da10a277060fcda66faa34f1334dc246870d4ab0d32655772d0b05ed534a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78a1328a1ee3339968a6715b1482012f77dff692b69a55062a7c0eb1c460a7c631c617149a863eac7771c5dcccbd3673fa4fb18ca916005e215615db50bea210
|
7
|
+
data.tar.gz: f128859e3c1f7e5ab2eee405afe71e7a5bd9fd593cfe88dae69cf6dd91f70b837c2bfe42364b95da2a1d60507776e28123dc7389a48231f720a5b9acbd599f80
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.1.2
|
6
|
+
|
7
|
+
- Add cache support to Rack handler.
|
8
|
+
|
9
|
+
Now we use `caches` for files with `Cache-Control`, so we don't perform a Wasm request.
|
10
|
+
|
11
|
+
- Minor fixes and improvements.
|
12
|
+
|
13
|
+
## 0.1.1
|
14
|
+
|
15
|
+
- Support multipart file uploads by converting files to data URIs.
|
16
|
+
|
17
|
+
At the Rack side, we use a `Rack::DataUriUploads` middleware to automatically convert
|
18
|
+
data-URI-encoded files to files uploads, so the application can handle them as usual.
|
19
|
+
|
5
20
|
## 0.1.0
|
6
21
|
|
7
22
|
- Initial release
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Adding to your Gemfile:
|
|
13
13
|
gem "wasmify-rails", group: [:development, :wasm]
|
14
14
|
```
|
15
15
|
|
16
|
-
## Usage
|
16
|
+
## Usage: generators and tasks
|
17
17
|
|
18
18
|
This gem comes with a few commands (Rake tasks) to help you get started with packing your Rails app into a Wasm module.
|
19
19
|
|
@@ -147,11 +147,35 @@ bin/rails wasmify:pack
|
|
147
147
|
|
148
148
|
And go to the `pwa` for the instructions on how to launch the application.
|
149
149
|
|
150
|
+
Here is an example app:
|
151
|
+
|
152
|
+
<video src="https://github.com/user-attachments/assets/34e54379-5f3e-42eb-a4fa-96c9aaa91869"></video>
|
153
|
+
|
154
|
+
## Rails/Ruby extensions
|
155
|
+
|
156
|
+
This gem provides a variety of _adapters_ and plugins to make your Rails application Wasm-compatible:
|
157
|
+
|
158
|
+
- Active Record
|
159
|
+
|
160
|
+
- `sqlite3_wasm` adapter: work with `sqlite3` Wasm just like with a regular SQLite database.
|
161
|
+
- `nulldb` adapter for testing purposes.
|
162
|
+
|
163
|
+
- Active Storage
|
164
|
+
|
165
|
+
- `null` variant processor (just leaves files as is)
|
166
|
+
|
167
|
+
- Action Mailer
|
168
|
+
|
169
|
+
- `null` delivery method (to disable emails in Wasm)
|
170
|
+
|
171
|
+
- Rack
|
172
|
+
|
173
|
+
- `Rack::DataUriUploads` middleware to transparently transform Data URI uploads into files.
|
174
|
+
|
150
175
|
## Roadmap
|
151
176
|
|
152
177
|
- PGLite support (see [this example](https://github.com/kateinoigakukun/mastodon/blob/fff2e4a626a20a616c546ddf4f91766abaf1133a/pwa/dist/pglite.rb#L1))
|
153
178
|
- Active Storage OPFS service
|
154
|
-
- File uploads support (multipart/form-data)
|
155
179
|
- Background jobs support
|
156
180
|
- WASI Preview 2 support (also [this](https://github.com/kateinoigakukun/mastodon/tree/katei/wasmify))
|
157
181
|
|
@@ -161,7 +185,7 @@ Bug reports and pull requests are welcome on GitHub at [https://github.com/](htt
|
|
161
185
|
|
162
186
|
## Credits
|
163
187
|
|
164
|
-
The `nulldb` adapter for Active Record (used for tests) is
|
188
|
+
The `nulldb` adapter for Active Record (used for tests) is ported from [this project](https://github.com/nulldb/nulldb).
|
165
189
|
|
166
190
|
## License
|
167
191
|
|
@@ -118,6 +118,7 @@ module ActiveRecord
|
|
118
118
|
str_val = val.to_s
|
119
119
|
next str_val if val.typeof == "string"
|
120
120
|
next str_val == "true" if val.typeof == "boolean"
|
121
|
+
next nil if str_val == "null"
|
121
122
|
|
122
123
|
# handle integers and floats
|
123
124
|
next str_val.include?(".") ? val.to_f : val.to_i if val.typeof == "number"
|
@@ -136,14 +137,37 @@ module ActiveRecord
|
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
140
|
+
# This type converts byte arrays represented as strings from JS to binaries in Ruby
|
141
|
+
class JSBinary < ActiveModel::Type::Binary
|
142
|
+
def deserialize(value)
|
143
|
+
bvalue = value
|
144
|
+
if value.is_a?(String)
|
145
|
+
bvalue = value.split(",").map(&:to_i).pack("c*")
|
146
|
+
end
|
147
|
+
|
148
|
+
super(bvalue)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
ActiveRecord::Type.register(:binary, JSBinary, adapter: :sqlite3_wasm)
|
153
|
+
|
139
154
|
class << self
|
140
155
|
def database_exists?(config)
|
141
156
|
true
|
142
157
|
end
|
143
158
|
|
144
159
|
def new_client(config) = ExternalInterface.new(config)
|
160
|
+
|
161
|
+
private
|
162
|
+
def initialize_type_map(m)
|
163
|
+
super
|
164
|
+
register_class_with_limit m, %r(binary)i, JSBinary
|
165
|
+
end
|
145
166
|
end
|
146
167
|
|
168
|
+
# Re-initialize type map to include JSBinary
|
169
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
170
|
+
|
147
171
|
attr_reader :external_interface
|
148
172
|
|
149
173
|
def initialize(...)
|
@@ -47,10 +47,15 @@ class Wasmify::InstallGenerator < Rails::Generators::Base
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
50
|
+
def configure_boot_file
|
51
51
|
inject_into_file "config/boot.rb", after: /require ['"]bundler\/setup['"]/ do
|
52
52
|
" unless RUBY_PLATFORM =~ /wasm/"
|
53
53
|
end
|
54
|
+
|
55
|
+
# Disable bootsnap if any
|
56
|
+
inject_into_file "config/boot.rb", after: /require ['"]bootsnap\/setup['"]/ do
|
57
|
+
" unless RUBY_PLATFORM =~ /wasm/"
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
def add_tzinfo_data_to_gemfile
|
@@ -26,5 +26,8 @@ Rails.application.configure do
|
|
26
26
|
config.active_storage.variant_processor = :null
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
# Do not use the same secret key base in a local app (for security reasons)
|
30
|
+
config.secret_key_base = "wasm-secret"
|
31
|
+
# Use a different session cookie name to avoid conflicts
|
32
|
+
config.session_store :cookie_store, key: "_local_session"
|
30
33
|
end
|
@@ -4,15 +4,7 @@ export const setupSQLiteDatabase = async () => {
|
|
4
4
|
const sqlite3 = await sqlite3InitModule();
|
5
5
|
|
6
6
|
console.log("Running SQLite3 version", sqlite3.version.libVersion);
|
7
|
-
|
8
|
-
|
9
|
-
? new sqlite3.oo1.OpfsDb("/railsdb.sqlite3")
|
10
|
-
: new sqlite3.oo1.DB("/railsdb.sqlite3", "ct");
|
11
|
-
console.log(
|
12
|
-
"opfs" in sqlite3
|
13
|
-
? `OPFS is available, created persisted database at ${db.filename}`
|
14
|
-
: `OPFS is not available, created transient database ${db.filename}`,
|
15
|
-
);
|
16
|
-
|
7
|
+
// NOTE: This database is transient and will be lost if you uninstall the service worker (aka hard reset)
|
8
|
+
const db = new sqlite3.oo1.DB("/railsdb.sqlite3", "ct");
|
17
9
|
return db;
|
18
10
|
};
|
@@ -12,9 +12,11 @@ let db = null;
|
|
12
12
|
const initDB = async (progress) => {
|
13
13
|
if (db) return db;
|
14
14
|
|
15
|
-
progress
|
15
|
+
progress?.updateStep("Initializing SQLite database...");
|
16
16
|
db = await setupSQLiteDatabase();
|
17
|
-
progress
|
17
|
+
progress?.updateStep("SQLite database created.");
|
18
|
+
|
19
|
+
return db;
|
18
20
|
};
|
19
21
|
|
20
22
|
let vm = null;
|
@@ -47,6 +49,8 @@ const initVM = async (progress, opts = {}) => {
|
|
47
49
|
vm.eval("ActiveRecord::Tasks::DatabaseTasks.prepare_all");
|
48
50
|
|
49
51
|
redirectConsole = false;
|
52
|
+
|
53
|
+
return vm;
|
50
54
|
};
|
51
55
|
|
52
56
|
const resetVM = () => {
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
class DataUriUploads
|
8
|
+
# A specific prefix we use to identify data URIs that must be transformed
|
9
|
+
# into file uploads.
|
10
|
+
PREFIX = "BbC14y"
|
11
|
+
DATAURI_REGEX = %r{^#{PREFIX}data:(.*?);(.*?),(.*)$}
|
12
|
+
|
13
|
+
def initialize(app)
|
14
|
+
@app = app
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
return @app.call(env) unless env[RACK_INPUT]
|
19
|
+
|
20
|
+
request = Rack::Request.new(env)
|
21
|
+
|
22
|
+
if request.post? || request.put? || request.patch?
|
23
|
+
transform_params(request.params)
|
24
|
+
end
|
25
|
+
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def transform_params(params)
|
32
|
+
return params unless params.is_a?(Hash)
|
33
|
+
params.each do |key, value|
|
34
|
+
if value.is_a?(String) && value.match?(DATAURI_REGEX)
|
35
|
+
params[key] = from_data_uri(value)
|
36
|
+
elsif value.is_a?(Hash)
|
37
|
+
transform_params(value)
|
38
|
+
elsif value.is_a?(Array)
|
39
|
+
value.each { transform_params(_1) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def from_data_uri(data_uri)
|
45
|
+
matches = data_uri.match(DATAURI_REGEX)
|
46
|
+
|
47
|
+
content_type = matches[1]
|
48
|
+
encoding = matches[2]
|
49
|
+
data = matches[3]
|
50
|
+
|
51
|
+
file_data = Base64.decode64(data)
|
52
|
+
|
53
|
+
file = Tempfile.new(["upload", mime_to_extension(content_type)])
|
54
|
+
file.binmode
|
55
|
+
file.write(file_data)
|
56
|
+
file.rewind
|
57
|
+
|
58
|
+
# Create a Rack::Test::UploadedFile, so it works with strong parameters
|
59
|
+
Rack::Test::UploadedFile.new(file.path, content_type)
|
60
|
+
end
|
61
|
+
|
62
|
+
def mime_to_extension(mime_type)
|
63
|
+
mime_type.split("/").then do |parts|
|
64
|
+
next "" unless parts.length == 2
|
65
|
+
".#{parts.last}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/wasmify-rails.rb
CHANGED
@@ -18,6 +18,8 @@ module ImageProcessing
|
|
18
18
|
autoload :Null, "image_processing/null"
|
19
19
|
end
|
20
20
|
|
21
|
+
require "action_mailer/null_delivery"
|
22
|
+
|
21
23
|
# NullDB for Active Record
|
22
24
|
ActiveRecord::ConnectionAdapters.register("nulldb", "ActiveRecord::ConnectionAdapters::NullDBAdapter", "active_record/connection_adapters/nulldb_adapter")
|
23
25
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wasmify-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- CHANGELOG.md
|
123
123
|
- LICENSE.txt
|
124
124
|
- README.md
|
125
|
+
- lib/action_mailer/null_delivery.rb
|
125
126
|
- lib/active_record/connection_adapters/nulldb_adapter.rb
|
126
127
|
- lib/active_record/connection_adapters/nulldb_adapter/checkpoint.rb
|
127
128
|
- lib/active_record/connection_adapters/nulldb_adapter/column.rb
|
@@ -134,7 +135,6 @@ files:
|
|
134
135
|
- lib/active_record/connection_adapters/nulldb_adapter/statement.rb
|
135
136
|
- lib/active_record/connection_adapters/nulldb_adapter/table_definition.rb
|
136
137
|
- lib/active_record/connection_adapters/sqlite3_wasm_adapter.rb
|
137
|
-
- lib/active_storage/null_delivery.rb
|
138
138
|
- lib/generators/wasmify/install/USAGE
|
139
139
|
- lib/generators/wasmify/install/install_generator.rb
|
140
140
|
- lib/generators/wasmify/install/templates/config/environments/wasm.rb
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- lib/generators/wasmify/pwa/templates/pwa/rails.sw.js
|
151
151
|
- lib/generators/wasmify/pwa/templates/pwa/vite.config.js
|
152
152
|
- lib/image_processing/null.rb
|
153
|
+
- lib/rack/data_uri_uploads.rb
|
153
154
|
- lib/wasmify-rails.rb
|
154
155
|
- lib/wasmify/rails/builder.rb
|
155
156
|
- lib/wasmify/rails/configuration.rb
|
@@ -178,7 +179,7 @@ metadata:
|
|
178
179
|
documentation_uri: https://github.com/palkan/wasmify-rails
|
179
180
|
homepage_uri: https://github.com/palkan/wasmify-rails
|
180
181
|
source_code_uri: https://github.com/palkan/wasmify-rails
|
181
|
-
post_install_message:
|
182
|
+
post_install_message:
|
182
183
|
rdoc_options: []
|
183
184
|
require_paths:
|
184
185
|
- lib
|
@@ -193,8 +194,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
194
|
- !ruby/object:Gem::Version
|
194
195
|
version: '0'
|
195
196
|
requirements: []
|
196
|
-
rubygems_version: 3.5.
|
197
|
-
signing_key:
|
197
|
+
rubygems_version: 3.5.16
|
198
|
+
signing_key:
|
198
199
|
specification_version: 4
|
199
200
|
summary: Tools and extensions to package Rails apps as Wasm modules
|
200
201
|
test_files: []
|