From f0e7e6eff391057579322dade615b44683944f35 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Tue, 5 Nov 2024 08:37:15 -0800 Subject: [PATCH 01/15] Start ruby --- Cargo.lock | 5 + Cargo.toml | 2 +- examples/quick-start/src/main.rs | 2 + examples/rails/Cargo.toml | 6 + examples/rails/Gemfile | 7 + examples/rails/Gemfile.lock | 195 +++++++++++ examples/rails/src/main.rs | 3 + examples/rails/todo/.dockerignore | 48 +++ examples/rails/todo/.gitattributes | 9 + examples/rails/todo/.github/dependabot.yml | 12 + examples/rails/todo/.github/workflows/ci.yml | 90 +++++ examples/rails/todo/.gitignore | 35 ++ examples/rails/todo/.rubocop.yml | 8 + examples/rails/todo/.ruby-version | 1 + examples/rails/todo/Dockerfile | 69 ++++ examples/rails/todo/Gemfile | 57 +++ examples/rails/todo/Gemfile.lock | 326 ++++++++++++++++++ examples/rails/todo/README.md | 24 ++ examples/rails/todo/Rakefile | 6 + .../rails/todo/app/assets/config/manifest.js | 2 + examples/rails/todo/app/assets/images/.keep | 0 .../app/assets/stylesheets/application.css | 15 + .../app/channels/application_cable/channel.rb | 4 + .../channels/application_cable/connection.rb | 4 + .../app/controllers/application_controller.rb | 4 + .../rails/todo/app/controllers/concerns/.keep | 0 .../todo/app/helpers/application_helper.rb | 2 + .../rails/todo/app/jobs/application_job.rb | 7 + .../todo/app/mailers/application_mailer.rb | 4 + .../todo/app/models/application_record.rb | 3 + examples/rails/todo/app/models/concerns/.keep | 0 .../app/views/layouts/application.html.erb | 22 ++ .../todo/app/views/layouts/mailer.html.erb | 13 + .../todo/app/views/layouts/mailer.text.erb | 1 + .../todo/app/views/pwa/manifest.json.erb | 22 ++ .../todo/app/views/pwa/service-worker.js | 26 ++ examples/rails/todo/bin/brakeman | 7 + examples/rails/todo/bin/bundle | 109 ++++++ examples/rails/todo/bin/docker-entrypoint | 13 + examples/rails/todo/bin/rails | 4 + examples/rails/todo/bin/rake | 4 + examples/rails/todo/bin/rubocop | 8 + examples/rails/todo/bin/setup | 37 ++ examples/rails/todo/config.ru | 6 + examples/rails/todo/config/application.rb | 27 ++ examples/rails/todo/config/boot.rb | 4 + examples/rails/todo/config/cable.yml | 10 + .../rails/todo/config/credentials.yml.enc | 1 + examples/rails/todo/config/database.yml | 32 ++ examples/rails/todo/config/environment.rb | 5 + .../todo/config/environments/development.rb | 81 +++++ .../todo/config/environments/production.rb | 105 ++++++ .../rails/todo/config/environments/test.rb | 67 ++++ .../rails/todo/config/initializers/assets.rb | 12 + .../initializers/content_security_policy.rb | 25 ++ .../initializers/filter_parameter_logging.rb | 8 + .../todo/config/initializers/inflections.rb | 16 + .../config/initializers/permissions_policy.rb | 13 + examples/rails/todo/config/locales/en.yml | 31 ++ examples/rails/todo/config/puma.rb | 34 ++ examples/rails/todo/config/routes.rb | 14 + examples/rails/todo/config/storage.yml | 34 ++ examples/rails/todo/db/seeds.rb | 9 + examples/rails/todo/lib/assets/.keep | 0 examples/rails/todo/lib/tasks/.keep | 0 examples/rails/todo/log/.keep | 0 examples/rails/todo/public/404.html | 67 ++++ .../todo/public/406-unsupported-browser.html | 66 ++++ examples/rails/todo/public/422.html | 67 ++++ examples/rails/todo/public/500.html | 66 ++++ examples/rails/todo/public/icon.png | Bin 0 -> 5599 bytes examples/rails/todo/public/icon.svg | 3 + examples/rails/todo/public/robots.txt | 1 + examples/rails/todo/storage/.keep | 0 .../todo/test/application_system_test_case.rb | 5 + .../application_cable/connection_test.rb | 13 + examples/rails/todo/test/controllers/.keep | 0 examples/rails/todo/test/fixtures/files/.keep | 0 examples/rails/todo/test/helpers/.keep | 0 examples/rails/todo/test/integration/.keep | 0 examples/rails/todo/test/mailers/.keep | 0 examples/rails/todo/test/models/.keep | 0 examples/rails/todo/test/system/.keep | 0 examples/rails/todo/test/test_helper.rb | 15 + examples/rails/todo/tmp/.keep | 0 examples/rails/todo/tmp/pids/.keep | 0 examples/rails/todo/tmp/storage/.keep | 0 examples/rails/todo/vendor/.keep | 0 rwf/Cargo.toml | 1 + rwf/src/http/mod.rs | 1 + rwf/src/http/rack/mod.rs | 55 +++ 91 files changed, 2109 insertions(+), 1 deletion(-) create mode 100644 examples/rails/Cargo.toml create mode 100644 examples/rails/Gemfile create mode 100644 examples/rails/Gemfile.lock create mode 100644 examples/rails/src/main.rs create mode 100644 examples/rails/todo/.dockerignore create mode 100644 examples/rails/todo/.gitattributes create mode 100644 examples/rails/todo/.github/dependabot.yml create mode 100644 examples/rails/todo/.github/workflows/ci.yml create mode 100644 examples/rails/todo/.gitignore create mode 100644 examples/rails/todo/.rubocop.yml create mode 100644 examples/rails/todo/.ruby-version create mode 100644 examples/rails/todo/Dockerfile create mode 100644 examples/rails/todo/Gemfile create mode 100644 examples/rails/todo/Gemfile.lock create mode 100644 examples/rails/todo/README.md create mode 100644 examples/rails/todo/Rakefile create mode 100644 examples/rails/todo/app/assets/config/manifest.js create mode 100644 examples/rails/todo/app/assets/images/.keep create mode 100644 examples/rails/todo/app/assets/stylesheets/application.css create mode 100644 examples/rails/todo/app/channels/application_cable/channel.rb create mode 100644 examples/rails/todo/app/channels/application_cable/connection.rb create mode 100644 examples/rails/todo/app/controllers/application_controller.rb create mode 100644 examples/rails/todo/app/controllers/concerns/.keep create mode 100644 examples/rails/todo/app/helpers/application_helper.rb create mode 100644 examples/rails/todo/app/jobs/application_job.rb create mode 100644 examples/rails/todo/app/mailers/application_mailer.rb create mode 100644 examples/rails/todo/app/models/application_record.rb create mode 100644 examples/rails/todo/app/models/concerns/.keep create mode 100644 examples/rails/todo/app/views/layouts/application.html.erb create mode 100644 examples/rails/todo/app/views/layouts/mailer.html.erb create mode 100644 examples/rails/todo/app/views/layouts/mailer.text.erb create mode 100644 examples/rails/todo/app/views/pwa/manifest.json.erb create mode 100644 examples/rails/todo/app/views/pwa/service-worker.js create mode 100755 examples/rails/todo/bin/brakeman create mode 100755 examples/rails/todo/bin/bundle create mode 100755 examples/rails/todo/bin/docker-entrypoint create mode 100755 examples/rails/todo/bin/rails create mode 100755 examples/rails/todo/bin/rake create mode 100755 examples/rails/todo/bin/rubocop create mode 100755 examples/rails/todo/bin/setup create mode 100644 examples/rails/todo/config.ru create mode 100644 examples/rails/todo/config/application.rb create mode 100644 examples/rails/todo/config/boot.rb create mode 100644 examples/rails/todo/config/cable.yml create mode 100644 examples/rails/todo/config/credentials.yml.enc create mode 100644 examples/rails/todo/config/database.yml create mode 100644 examples/rails/todo/config/environment.rb create mode 100644 examples/rails/todo/config/environments/development.rb create mode 100644 examples/rails/todo/config/environments/production.rb create mode 100644 examples/rails/todo/config/environments/test.rb create mode 100644 examples/rails/todo/config/initializers/assets.rb create mode 100644 examples/rails/todo/config/initializers/content_security_policy.rb create mode 100644 examples/rails/todo/config/initializers/filter_parameter_logging.rb create mode 100644 examples/rails/todo/config/initializers/inflections.rb create mode 100644 examples/rails/todo/config/initializers/permissions_policy.rb create mode 100644 examples/rails/todo/config/locales/en.yml create mode 100644 examples/rails/todo/config/puma.rb create mode 100644 examples/rails/todo/config/routes.rb create mode 100644 examples/rails/todo/config/storage.yml create mode 100644 examples/rails/todo/db/seeds.rb create mode 100644 examples/rails/todo/lib/assets/.keep create mode 100644 examples/rails/todo/lib/tasks/.keep create mode 100644 examples/rails/todo/log/.keep create mode 100644 examples/rails/todo/public/404.html create mode 100644 examples/rails/todo/public/406-unsupported-browser.html create mode 100644 examples/rails/todo/public/422.html create mode 100644 examples/rails/todo/public/500.html create mode 100644 examples/rails/todo/public/icon.png create mode 100644 examples/rails/todo/public/icon.svg create mode 100644 examples/rails/todo/public/robots.txt create mode 100644 examples/rails/todo/storage/.keep create mode 100644 examples/rails/todo/test/application_system_test_case.rb create mode 100644 examples/rails/todo/test/channels/application_cable/connection_test.rb create mode 100644 examples/rails/todo/test/controllers/.keep create mode 100644 examples/rails/todo/test/fixtures/files/.keep create mode 100644 examples/rails/todo/test/helpers/.keep create mode 100644 examples/rails/todo/test/integration/.keep create mode 100644 examples/rails/todo/test/mailers/.keep create mode 100644 examples/rails/todo/test/models/.keep create mode 100644 examples/rails/todo/test/system/.keep create mode 100644 examples/rails/todo/test/test_helper.rb create mode 100644 examples/rails/todo/tmp/.keep create mode 100644 examples/rails/todo/tmp/pids/.keep create mode 100644 examples/rails/todo/tmp/storage/.keep create mode 100644 examples/rails/todo/vendor/.keep create mode 100644 rwf/src/http/rack/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ea58e912..113ea0cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1447,6 +1447,10 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rails" +version = "0.1.0" + [[package]] name = "rand" version = "0.4.6" @@ -1638,6 +1642,7 @@ dependencies = [ "base64 0.22.1", "bytes", "colored", + "libc", "notify", "once_cell", "parking_lot", diff --git a/Cargo.toml b/Cargo.toml index aefe086d..470ee0d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,5 @@ members = [ "examples/django", "examples/request-tracking", "examples/engine", - "rwf-admin", + "rwf-admin", "examples/rails", ] diff --git a/examples/quick-start/src/main.rs b/examples/quick-start/src/main.rs index 57c01bc0..69d2519d 100644 --- a/examples/quick-start/src/main.rs +++ b/examples/quick-start/src/main.rs @@ -32,6 +32,8 @@ impl Controller for CurrentTime { #[tokio::main] async fn main() -> Result<(), http::Error> { Logger::init(); + rwf::http::rack::Ruby::init().unwrap(); + rwf::http::rack::Ruby::eval("puts 'hello world'").unwrap(); Server::new(vec![route!("/" => Index), route!("/time" => CurrentTime)]) .launch("0.0.0.0:8001") diff --git a/examples/rails/Cargo.toml b/examples/rails/Cargo.toml new file mode 100644 index 00000000..c8193989 --- /dev/null +++ b/examples/rails/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rails" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/examples/rails/Gemfile b/examples/rails/Gemfile new file mode 100644 index 00000000..6bc4e3f2 --- /dev/null +++ b/examples/rails/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +# gem "rails" + +gem "rails", "~> 7.2" diff --git a/examples/rails/Gemfile.lock b/examples/rails/Gemfile.lock new file mode 100644 index 00000000..6793e081 --- /dev/null +++ b/examples/rails/Gemfile.lock @@ -0,0 +1,195 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + actionmailer (7.2.2) + actionpack (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2) + actionview (= 7.2.2) + activesupport (= 7.2.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2) + actionpack (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2) + activesupport (= 7.2.2) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.3.6) + activemodel (7.2.2) + activesupport (= 7.2.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) + timeout (>= 0.4.0) + activestorage (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activesupport (= 7.2.2) + marcel (~> 1.0) + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + base64 (0.2.0) + benchmark (0.3.0) + bigdecimal (3.1.8) + builder (3.3.0) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + crass (1.0.6) + date (3.4.0) + drb (2.2.1) + erubi (1.13.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + io-console (0.7.2) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + logger (1.6.1) + loofah (2.23.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + mini_mime (1.1.5) + minitest (5.25.1) + net-imap (0.5.0) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.16.7-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86-linux) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) + psych (5.1.2) + stringio + racc (1.8.1) + rack (3.1.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.2.0) + rack (>= 3) + rails (7.2.2) + actioncable (= 7.2.2) + actionmailbox (= 7.2.2) + actionmailer (= 7.2.2) + actionpack (= 7.2.2) + actiontext (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activemodel (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + bundler (>= 1.15.0) + railties (= 7.2.2) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rake (13.2.1) + rdoc (6.7.0) + psych (>= 4.0.0) + reline (0.5.10) + io-console (~> 0.5) + securerandom (0.3.1) + stringio (3.1.1) + thor (1.3.2) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + useragent (0.16.10) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.7.1) + +PLATFORMS + aarch64-linux + arm-linux + arm64-darwin + x86-linux + x86_64-darwin + x86_64-linux + +DEPENDENCIES + rails (~> 7.2) + +BUNDLED WITH + 2.5.14 diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/examples/rails/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/examples/rails/todo/.dockerignore b/examples/rails/todo/.dockerignore new file mode 100644 index 00000000..cd7190b4 --- /dev/null +++ b/examples/rails/todo/.dockerignore @@ -0,0 +1,48 @@ +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ +/.gitignore + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/.keep + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore CI service files. +/.github + +# Ignore development files +/.devcontainer + +# Ignore Docker-related files +/.dockerignore +/Dockerfile* diff --git a/examples/rails/todo/.gitattributes b/examples/rails/todo/.gitattributes new file mode 100644 index 00000000..8dc43234 --- /dev/null +++ b/examples/rails/todo/.gitattributes @@ -0,0 +1,9 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored +config/credentials/*.yml.enc diff=rails_credentials +config/credentials.yml.enc diff=rails_credentials diff --git a/examples/rails/todo/.github/dependabot.yml b/examples/rails/todo/.github/dependabot.yml new file mode 100644 index 00000000..f0527e6b --- /dev/null +++ b/examples/rails/todo/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/examples/rails/todo/.github/workflows/ci.yml b/examples/rails/todo/.github/workflows/ci.yml new file mode 100644 index 00000000..00af91f6 --- /dev/null +++ b/examples/rails/todo/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +on: + pull_request: + push: + branches: [ main ] + +jobs: + scan_ruby: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for common Rails security vulnerabilities using static analysis + run: bin/brakeman --no-pager + + scan_js: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for security vulnerabilities in JavaScript dependencies + run: bin/importmap audit + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Lint code for consistent style + run: bin/rubocop -f github + + test: + runs-on: ubuntu-latest + + # services: + # redis: + # image: redis + # ports: + # - 6379:6379 + # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Install packages + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips sqlite3 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Run tests + env: + RAILS_ENV: test + # REDIS_URL: redis://localhost:6379/0 + run: bin/rails db:test:prepare test test:system + + - name: Keep screenshots from failed system tests + uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshots + path: ${{ github.workspace }}/tmp/screenshots + if-no-files-found: ignore diff --git a/examples/rails/todo/.gitignore b/examples/rails/todo/.gitignore new file mode 100644 index 00000000..4aaf1022 --- /dev/null +++ b/examples/rails/todo/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# Temporary files generated by your text editor or operating system +# belong in git's global ignore instead: +# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore` + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key diff --git a/examples/rails/todo/.rubocop.yml b/examples/rails/todo/.rubocop.yml new file mode 100644 index 00000000..f9d86d4a --- /dev/null +++ b/examples/rails/todo/.rubocop.yml @@ -0,0 +1,8 @@ +# Omakase Ruby styling for Rails +inherit_gem: { rubocop-rails-omakase: rubocop.yml } + +# Overwrite or add rules to create your own house style +# +# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` +# Layout/SpaceInsideArrayLiteralBrackets: +# Enabled: false diff --git a/examples/rails/todo/.ruby-version b/examples/rails/todo/.ruby-version new file mode 100644 index 00000000..6d5369b9 --- /dev/null +++ b/examples/rails/todo/.ruby-version @@ -0,0 +1 @@ +ruby-3.3.4 diff --git a/examples/rails/todo/Dockerfile b/examples/rails/todo/Dockerfile new file mode 100644 index 00000000..2cb400ed --- /dev/null +++ b/examples/rails/todo/Dockerfile @@ -0,0 +1,69 @@ +# syntax = docker/dockerfile:1 + +# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: +# docker build -t my-app . +# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY= my-app + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.3.4 +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# Rails app lives here +WORKDIR /rails + +# Install base packages +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + +# Throw-away build stage to reduce size of final image +FROM base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential git pkg-config && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install application gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ + bundle exec bootsnap precompile --gemfile + +# Copy application code +COPY . . + +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + + + + +# Final stage for app image +FROM base + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/examples/rails/todo/Gemfile b/examples/rails/todo/Gemfile new file mode 100644 index 00000000..a7f333af --- /dev/null +++ b/examples/rails/todo/Gemfile @@ -0,0 +1,57 @@ +source "https://rubygems.org" + +# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" +gem "rails", "~> 7.2.2" +# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] +gem "sprockets-rails" +# Use sqlite3 as the database for Active Record +gem "sqlite3", ">= 1.4" +# Use the Puma web server [https://github.com/puma/puma] +gem "puma", ">= 5.0" +# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] +gem "importmap-rails" +# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] +gem "turbo-rails" +# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] +gem "stimulus-rails" +# Build JSON APIs with ease [https://github.com/rails/jbuilder] +gem "jbuilder" +# Use Redis adapter to run Action Cable in production +# gem "redis", ">= 4.0.1" + +# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] +# gem "kredis" + +# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] +# gem "bcrypt", "~> 3.1.7" + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: %i[ windows jruby ] + +# Reduces boot times through caching; required in config/boot.rb +gem "bootsnap", require: false + +# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] +# gem "image_processing", "~> 1.2" + +group :development, :test do + # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" + + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] + gem "brakeman", require: false + + # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] + gem "rubocop-rails-omakase", require: false +end + +group :development do + # Use console on exceptions pages [https://github.com/rails/web-console] + gem "web-console" +end + +group :test do + # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] + gem "capybara" + gem "selenium-webdriver" +end diff --git a/examples/rails/todo/Gemfile.lock b/examples/rails/todo/Gemfile.lock new file mode 100644 index 00000000..4d667a2e --- /dev/null +++ b/examples/rails/todo/Gemfile.lock @@ -0,0 +1,326 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + actionmailer (7.2.2) + actionpack (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2) + actionview (= 7.2.2) + activesupport (= 7.2.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2) + actionpack (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2) + activesupport (= 7.2.2) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.3.6) + activemodel (7.2.2) + activesupport (= 7.2.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) + timeout (>= 0.4.0) + activestorage (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activesupport (= 7.2.2) + marcel (~> 1.0) + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + ast (2.4.2) + base64 (0.2.0) + benchmark (0.3.0) + bigdecimal (3.1.8) + bindex (0.8.1) + bootsnap (1.18.4) + msgpack (~> 1.2) + brakeman (6.2.2) + racc + builder (3.3.0) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + crass (1.0.6) + date (3.4.0) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) + drb (2.2.1) + erubi (1.13.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + importmap-rails (2.0.3) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) + io-console (0.7.2) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + json (2.7.6) + language_server-protocol (3.17.0.3) + logger (1.6.1) + loofah (2.23.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + mini_mime (1.1.5) + minitest (5.25.1) + msgpack (1.7.3) + net-imap (0.5.0) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.16.7-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86-linux) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) + parallel (1.26.3) + parser (3.3.6.0) + ast (~> 2.4.1) + racc + psych (5.1.2) + stringio + public_suffix (6.0.1) + puma (6.4.3) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.1.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.2.0) + rack (>= 3) + rails (7.2.2) + actioncable (= 7.2.2) + actionmailbox (= 7.2.2) + actionmailer (= 7.2.2) + actionpack (= 7.2.2) + actiontext (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activemodel (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + bundler (>= 1.15.0) + railties (= 7.2.2) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.2.1) + rdoc (6.7.0) + psych (>= 4.0.0) + regexp_parser (2.9.2) + reline (0.5.10) + io-console (~> 0.5) + rexml (3.3.9) + rubocop (1.68.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.34.0) + parser (>= 3.3.1.0) + rubocop-minitest (0.36.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.22.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.27.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails-omakase (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + ruby-progressbar (1.13.0) + rubyzip (2.3.2) + securerandom (0.3.1) + selenium-webdriver (4.26.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) + sqlite3 (2.2.0-aarch64-linux-gnu) + sqlite3 (2.2.0-aarch64-linux-musl) + sqlite3 (2.2.0-arm-linux-gnu) + sqlite3 (2.2.0-arm-linux-musl) + sqlite3 (2.2.0-arm64-darwin) + sqlite3 (2.2.0-x86-linux-gnu) + sqlite3 (2.2.0-x86-linux-musl) + sqlite3 (2.2.0-x86_64-darwin) + sqlite3 (2.2.0-x86_64-linux-gnu) + sqlite3 (2.2.0-x86_64-linux-musl) + stimulus-rails (1.3.4) + railties (>= 6.0.0) + stringio (3.1.1) + thor (1.3.2) + timeout (0.4.1) + turbo-rails (2.0.11) + actionpack (>= 6.0.0) + railties (>= 6.0.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.6.0) + useragent (0.16.10) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + websocket (1.2.11) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.7.1) + +PLATFORMS + aarch64-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86-linux + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + bootsnap + brakeman + capybara + debug + importmap-rails + jbuilder + puma (>= 5.0) + rails (~> 7.2.2) + rubocop-rails-omakase + selenium-webdriver + sprockets-rails + sqlite3 (>= 1.4) + stimulus-rails + turbo-rails + tzinfo-data + web-console + +BUNDLED WITH + 2.5.14 diff --git a/examples/rails/todo/README.md b/examples/rails/todo/README.md new file mode 100644 index 00000000..7db80e4c --- /dev/null +++ b/examples/rails/todo/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/examples/rails/todo/Rakefile b/examples/rails/todo/Rakefile new file mode 100644 index 00000000..9a5ea738 --- /dev/null +++ b/examples/rails/todo/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative "config/application" + +Rails.application.load_tasks diff --git a/examples/rails/todo/app/assets/config/manifest.js b/examples/rails/todo/app/assets/config/manifest.js new file mode 100644 index 00000000..59181933 --- /dev/null +++ b/examples/rails/todo/app/assets/config/manifest.js @@ -0,0 +1,2 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css diff --git a/examples/rails/todo/app/assets/images/.keep b/examples/rails/todo/app/assets/images/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/app/assets/stylesheets/application.css b/examples/rails/todo/app/assets/stylesheets/application.css new file mode 100644 index 00000000..288b9ab7 --- /dev/null +++ b/examples/rails/todo/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/examples/rails/todo/app/channels/application_cable/channel.rb b/examples/rails/todo/app/channels/application_cable/channel.rb new file mode 100644 index 00000000..d6726972 --- /dev/null +++ b/examples/rails/todo/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/examples/rails/todo/app/channels/application_cable/connection.rb b/examples/rails/todo/app/channels/application_cable/connection.rb new file mode 100644 index 00000000..0ff5442f --- /dev/null +++ b/examples/rails/todo/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/examples/rails/todo/app/controllers/application_controller.rb b/examples/rails/todo/app/controllers/application_controller.rb new file mode 100644 index 00000000..0d95db22 --- /dev/null +++ b/examples/rails/todo/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern +end diff --git a/examples/rails/todo/app/controllers/concerns/.keep b/examples/rails/todo/app/controllers/concerns/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/app/helpers/application_helper.rb b/examples/rails/todo/app/helpers/application_helper.rb new file mode 100644 index 00000000..de6be794 --- /dev/null +++ b/examples/rails/todo/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/examples/rails/todo/app/jobs/application_job.rb b/examples/rails/todo/app/jobs/application_job.rb new file mode 100644 index 00000000..d394c3d1 --- /dev/null +++ b/examples/rails/todo/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/examples/rails/todo/app/mailers/application_mailer.rb b/examples/rails/todo/app/mailers/application_mailer.rb new file mode 100644 index 00000000..3c34c814 --- /dev/null +++ b/examples/rails/todo/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout "mailer" +end diff --git a/examples/rails/todo/app/models/application_record.rb b/examples/rails/todo/app/models/application_record.rb new file mode 100644 index 00000000..b63caeb8 --- /dev/null +++ b/examples/rails/todo/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class +end diff --git a/examples/rails/todo/app/models/concerns/.keep b/examples/rails/todo/app/models/concerns/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/app/views/layouts/application.html.erb b/examples/rails/todo/app/views/layouts/application.html.erb new file mode 100644 index 00000000..c0122d47 --- /dev/null +++ b/examples/rails/todo/app/views/layouts/application.html.erb @@ -0,0 +1,22 @@ + + + + <%= content_for(:title) || "Todo" %> + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= yield :head %> + + + + + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + + + + <%= yield %> + + diff --git a/examples/rails/todo/app/views/layouts/mailer.html.erb b/examples/rails/todo/app/views/layouts/mailer.html.erb new file mode 100644 index 00000000..3aac9002 --- /dev/null +++ b/examples/rails/todo/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/examples/rails/todo/app/views/layouts/mailer.text.erb b/examples/rails/todo/app/views/layouts/mailer.text.erb new file mode 100644 index 00000000..37f0bddb --- /dev/null +++ b/examples/rails/todo/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/examples/rails/todo/app/views/pwa/manifest.json.erb b/examples/rails/todo/app/views/pwa/manifest.json.erb new file mode 100644 index 00000000..e1dd43cf --- /dev/null +++ b/examples/rails/todo/app/views/pwa/manifest.json.erb @@ -0,0 +1,22 @@ +{ + "name": "Todo", + "icons": [ + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ], + "start_url": "/", + "display": "standalone", + "scope": "/", + "description": "Todo.", + "theme_color": "red", + "background_color": "red" +} diff --git a/examples/rails/todo/app/views/pwa/service-worker.js b/examples/rails/todo/app/views/pwa/service-worker.js new file mode 100644 index 00000000..b3a13fb7 --- /dev/null +++ b/examples/rails/todo/app/views/pwa/service-worker.js @@ -0,0 +1,26 @@ +// Add a service worker for processing Web Push notifications: +// +// self.addEventListener("push", async (event) => { +// const { title, options } = await event.data.json() +// event.waitUntil(self.registration.showNotification(title, options)) +// }) +// +// self.addEventListener("notificationclick", function(event) { +// event.notification.close() +// event.waitUntil( +// clients.matchAll({ type: "window" }).then((clientList) => { +// for (let i = 0; i < clientList.length; i++) { +// let client = clientList[i] +// let clientPath = (new URL(client.url)).pathname +// +// if (clientPath == event.notification.data.path && "focus" in client) { +// return client.focus() +// } +// } +// +// if (clients.openWindow) { +// return clients.openWindow(event.notification.data.path) +// } +// }) +// ) +// }) diff --git a/examples/rails/todo/bin/brakeman b/examples/rails/todo/bin/brakeman new file mode 100755 index 00000000..ace1c9ba --- /dev/null +++ b/examples/rails/todo/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/examples/rails/todo/bin/bundle b/examples/rails/todo/bin/bundle new file mode 100755 index 00000000..50da5fdf --- /dev/null +++ b/examples/rails/todo/bin/bundle @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/examples/rails/todo/bin/docker-entrypoint b/examples/rails/todo/bin/docker-entrypoint new file mode 100755 index 00000000..840d093a --- /dev/null +++ b/examples/rails/todo/bin/docker-entrypoint @@ -0,0 +1,13 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then + export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)" +fi + +# If running the rails server then create or migrate existing database +if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" diff --git a/examples/rails/todo/bin/rails b/examples/rails/todo/bin/rails new file mode 100755 index 00000000..efc03774 --- /dev/null +++ b/examples/rails/todo/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/examples/rails/todo/bin/rake b/examples/rails/todo/bin/rake new file mode 100755 index 00000000..4fbf10b9 --- /dev/null +++ b/examples/rails/todo/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "rake" +Rake.application.run diff --git a/examples/rails/todo/bin/rubocop b/examples/rails/todo/bin/rubocop new file mode 100755 index 00000000..40330c0f --- /dev/null +++ b/examples/rails/todo/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/examples/rails/todo/bin/setup b/examples/rails/todo/bin/setup new file mode 100755 index 00000000..91e9a3be --- /dev/null +++ b/examples/rails/todo/bin/setup @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +require "fileutils" + +APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "todo" + +def system!(*args) + system(*args, exception: true) +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" +end diff --git a/examples/rails/todo/config.ru b/examples/rails/todo/config.ru new file mode 100644 index 00000000..4a3c09a6 --- /dev/null +++ b/examples/rails/todo/config.ru @@ -0,0 +1,6 @@ +# This file is used by Rack-based servers to start the application. + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/examples/rails/todo/config/application.rb b/examples/rails/todo/config/application.rb new file mode 100644 index 00000000..cfb46ea7 --- /dev/null +++ b/examples/rails/todo/config/application.rb @@ -0,0 +1,27 @@ +require_relative "boot" + +require "rails/all" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module Todo + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.2 + + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + end +end diff --git a/examples/rails/todo/config/boot.rb b/examples/rails/todo/config/boot.rb new file mode 100644 index 00000000..988a5ddc --- /dev/null +++ b/examples/rails/todo/config/boot.rb @@ -0,0 +1,4 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/examples/rails/todo/config/cable.yml b/examples/rails/todo/config/cable.yml new file mode 100644 index 00000000..017dcbb2 --- /dev/null +++ b/examples/rails/todo/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: todo_production diff --git a/examples/rails/todo/config/credentials.yml.enc b/examples/rails/todo/config/credentials.yml.enc new file mode 100644 index 00000000..4c61012c --- /dev/null +++ b/examples/rails/todo/config/credentials.yml.enc @@ -0,0 +1 @@ +VG/UrWpQbn13O80QailJkk5aWtnjoQFUN7OInjT7mnsGiTQVbxsnZ4i9HzRw4lR7HT3cjdItfiFRszqxQC1andUAHvY19f+5TRHVgCBTQDlGGs2EKotlRo0urxQrP7ZvooYKUcvXoci6tqLjNxuieooJ6WWNI2ZOqZ14cLO3qgKuRjiF521r0H1cl30KHgs4BofSTsJha9NmalMZYBH+hEvpyV7VHCUJm7quk/FoxI7PuEEG3Iors6qGhJXVizxtQacNzRTka/Pgj1yZXJqM38bgylPx5yDhMEWv5ocRnKWE64EHx2qUHlsxg064/qmmyYe/dtttL/s/hXi31pCJzgFlcH5BFhS34zr30ZbXc51AJkHjAKX/uQdpQHQkrO4caUNNJo5MmJaD1htSbYQ0M1PSbd5P--/3cXHqbQ6q0lXPvV--mKAOr64zucTnW41PfWHWQQ== \ No newline at end of file diff --git a/examples/rails/todo/config/database.yml b/examples/rails/todo/config/database.yml new file mode 100644 index 00000000..01bebb50 --- /dev/null +++ b/examples/rails/todo/config/database.yml @@ -0,0 +1,32 @@ +# SQLite. Versions 3.8.0 and up are supported. +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem "sqlite3" +# +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: storage/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: storage/test.sqlite3 + + +# SQLite3 write its data on the local filesystem, as such it requires +# persistent disks. If you are deploying to a managed service, you should +# make sure it provides disk persistence, as many don't. +# +# Similarly, if you deploy your application as a Docker container, you must +# ensure the database is located in a persisted volume. +production: + <<: *default + # database: path/to/persistent/storage/production.sqlite3 diff --git a/examples/rails/todo/config/environment.rb b/examples/rails/todo/config/environment.rb new file mode 100644 index 00000000..cac53157 --- /dev/null +++ b/examples/rails/todo/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/examples/rails/todo/config/environments/development.rb b/examples/rails/todo/config/environments/development.rb new file mode 100644 index 00000000..9b673600 --- /dev/null +++ b/examples/rails/todo/config/environments/development.rb @@ -0,0 +1,81 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing. + config.server_timing = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Highlight code that enqueued background job in logs. + config.active_job.verbose_enqueue_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true + + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + # config.generators.apply_rubocop_autocorrect_after_generate! +end diff --git a/examples/rails/todo/config/environments/production.rb b/examples/rails/todo/config/environments/production.rb new file mode 100644 index 00000000..9b6a5326 --- /dev/null +++ b/examples/rails/todo/config/environments/production.rb @@ -0,0 +1,105 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment + # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # config.public_file_server.enabled = false + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fall back to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Mount Action Cable outside main process or domain. + # config.action_cable.mount_path = nil + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. + # config.assume_ssl = true + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT by default + config.logger = ActiveSupport::Logger.new(STDOUT) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # "info" includes generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, set the level to "debug". + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "todo_production" + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + # Only use :id for inspections in production. + config.active_record.attributes_for_inspect = [ :id ] + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/examples/rails/todo/config/environments/test.rb b/examples/rails/todo/config/environments/test.rb new file mode 100644 index 00000000..0c616a1b --- /dev/null +++ b/examples/rails/todo/config/environments/test.rb @@ -0,0 +1,67 @@ +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false + + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "www.example.com" } + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/examples/rails/todo/config/initializers/assets.rb b/examples/rails/todo/config/initializers/assets.rb new file mode 100644 index 00000000..bd5bcd2b --- /dev/null +++ b/examples/rails/todo/config/initializers/assets.rb @@ -0,0 +1,12 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = "1.0" + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w[ admin.js admin.css ] diff --git a/examples/rails/todo/config/initializers/content_security_policy.rb b/examples/rails/todo/config/initializers/content_security_policy.rb new file mode 100644 index 00000000..b3076b38 --- /dev/null +++ b/examples/rails/todo/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/examples/rails/todo/config/initializers/filter_parameter_logging.rb b/examples/rails/todo/config/initializers/filter_parameter_logging.rb new file mode 100644 index 00000000..c010b83d --- /dev/null +++ b/examples/rails/todo/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/examples/rails/todo/config/initializers/inflections.rb b/examples/rails/todo/config/initializers/inflections.rb new file mode 100644 index 00000000..3860f659 --- /dev/null +++ b/examples/rails/todo/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym "RESTful" +# end diff --git a/examples/rails/todo/config/initializers/permissions_policy.rb b/examples/rails/todo/config/initializers/permissions_policy.rb new file mode 100644 index 00000000..7db3b957 --- /dev/null +++ b/examples/rails/todo/config/initializers/permissions_policy.rb @@ -0,0 +1,13 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide HTTP permissions policy. For further +# information see: https://developers.google.com/web/updates/2018/06/feature-policy + +# Rails.application.config.permissions_policy do |policy| +# policy.camera :none +# policy.gyroscope :none +# policy.microphone :none +# policy.usb :none +# policy.fullscreen :self +# policy.payment :self, "https://secure.example.com" +# end diff --git a/examples/rails/todo/config/locales/en.yml b/examples/rails/todo/config/locales/en.yml new file mode 100644 index 00000000..6c349ae5 --- /dev/null +++ b/examples/rails/todo/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/examples/rails/todo/config/puma.rb b/examples/rails/todo/config/puma.rb new file mode 100644 index 00000000..03c166f4 --- /dev/null +++ b/examples/rails/todo/config/puma.rb @@ -0,0 +1,34 @@ +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. + +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3000) + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart + +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/examples/rails/todo/config/routes.rb b/examples/rails/todo/config/routes.rb new file mode 100644 index 00000000..33c96390 --- /dev/null +++ b/examples/rails/todo/config/routes.rb @@ -0,0 +1,14 @@ +Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* + get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + + # Defines the root path route ("/") + # root "posts#index" +end diff --git a/examples/rails/todo/config/storage.yml b/examples/rails/todo/config/storage.yml new file mode 100644 index 00000000..4942ab66 --- /dev/null +++ b/examples/rails/todo/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/examples/rails/todo/db/seeds.rb b/examples/rails/todo/db/seeds.rb new file mode 100644 index 00000000..4fbd6ed9 --- /dev/null +++ b/examples/rails/todo/db/seeds.rb @@ -0,0 +1,9 @@ +# This file should ensure the existence of records required to run the application in every environment (production, +# development, test). The code here should be idempotent so that it can be executed at any point in every environment. +# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). +# +# Example: +# +# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| +# MovieGenre.find_or_create_by!(name: genre_name) +# end diff --git a/examples/rails/todo/lib/assets/.keep b/examples/rails/todo/lib/assets/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/lib/tasks/.keep b/examples/rails/todo/lib/tasks/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/log/.keep b/examples/rails/todo/log/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/public/404.html b/examples/rails/todo/public/404.html new file mode 100644 index 00000000..2be3af26 --- /dev/null +++ b/examples/rails/todo/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/examples/rails/todo/public/406-unsupported-browser.html b/examples/rails/todo/public/406-unsupported-browser.html new file mode 100644 index 00000000..7cf1e168 --- /dev/null +++ b/examples/rails/todo/public/406-unsupported-browser.html @@ -0,0 +1,66 @@ + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ + diff --git a/examples/rails/todo/public/422.html b/examples/rails/todo/public/422.html new file mode 100644 index 00000000..c08eac0d --- /dev/null +++ b/examples/rails/todo/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/examples/rails/todo/public/500.html b/examples/rails/todo/public/500.html new file mode 100644 index 00000000..78a030af --- /dev/null +++ b/examples/rails/todo/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/examples/rails/todo/public/icon.png b/examples/rails/todo/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b5abcbde91cf6d7a6a26e514eb7e30f476f950 GIT binary patch literal 5599 zcmeHL-D}fO6hCR_taXJlzs3}~RuB=Iujyo=i*=1|1FN%E=zNfMTjru|Q<6v{J{U!C zBEE}?j6I3sz>fzN!6}L_BKjcuASk~1;Dg|U_@d{g?V8mM`~#9U+>>*Ezw>c(PjYWA z4(;!cgge6k5E&d$G5`S-0}!Ik>CV(0Y#1}s-v_gAHhja2=W1?nBAte9D2HG<(+)uj z!5=W4u*{VKMw#{V@^NNs4TClr!FAA%ID-*gc{R%CFKEzG<6gm*9s_uy)oMGW*=nJf zw{(Mau|2FHfXIv6C0@Wk5k)F=3jo1srV-C{pl&k&)4_&JjYrnbJiul}d0^NCSh(#7h=F;3{|>EU>h z6U8_p;^wK6mAB(1b92>5-HxJ~V}@3?G`&Qq-TbJ2(&~-HsH6F#8mFaAG(45eT3VPO zM|(Jd<+;UZs;w>0Qw}0>D%{~r{uo_Fl5_Bo3ABWi zWo^j^_T3dxG6J6fH8X)$a^%TJ#PU!=LxF=#Fd9EvKx_x>q<(KY%+y-08?kN9dXjXK z**Q=yt-FTU*13ouhCdqq-0&;Ke{T3sQU9IdzhV9LhQIpq*P{N)+}|Mh+a-VV=x?R} c>%+pvTcMWshj-umO}|qP?%A)*_KlqT3uEqhU;qFB literal 0 HcmV?d00001 diff --git a/examples/rails/todo/public/icon.svg b/examples/rails/todo/public/icon.svg new file mode 100644 index 00000000..78307ccd --- /dev/null +++ b/examples/rails/todo/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/rails/todo/public/robots.txt b/examples/rails/todo/public/robots.txt new file mode 100644 index 00000000..c19f78ab --- /dev/null +++ b/examples/rails/todo/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/examples/rails/todo/storage/.keep b/examples/rails/todo/storage/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/application_system_test_case.rb b/examples/rails/todo/test/application_system_test_case.rb new file mode 100644 index 00000000..cee29fd2 --- /dev/null +++ b/examples/rails/todo/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] +end diff --git a/examples/rails/todo/test/channels/application_cable/connection_test.rb b/examples/rails/todo/test/channels/application_cable/connection_test.rb new file mode 100644 index 00000000..6340bf9c --- /dev/null +++ b/examples/rails/todo/test/channels/application_cable/connection_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +module ApplicationCable + class ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end + end +end diff --git a/examples/rails/todo/test/controllers/.keep b/examples/rails/todo/test/controllers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/fixtures/files/.keep b/examples/rails/todo/test/fixtures/files/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/helpers/.keep b/examples/rails/todo/test/helpers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/integration/.keep b/examples/rails/todo/test/integration/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/mailers/.keep b/examples/rails/todo/test/mailers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/models/.keep b/examples/rails/todo/test/models/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/system/.keep b/examples/rails/todo/test/system/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/test/test_helper.rb b/examples/rails/todo/test/test_helper.rb new file mode 100644 index 00000000..0c22470e --- /dev/null +++ b/examples/rails/todo/test/test_helper.rb @@ -0,0 +1,15 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... + end +end diff --git a/examples/rails/todo/tmp/.keep b/examples/rails/todo/tmp/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/tmp/pids/.keep b/examples/rails/todo/tmp/pids/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/tmp/storage/.keep b/examples/rails/todo/tmp/storage/.keep new file mode 100644 index 00000000..e69de29b diff --git a/examples/rails/todo/vendor/.keep b/examples/rails/todo/vendor/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf/Cargo.toml b/rwf/Cargo.toml index 0d64f9b5..e6cc40a8 100644 --- a/rwf/Cargo.toml +++ b/rwf/Cargo.toml @@ -41,6 +41,7 @@ pyo3 = { version = "0.22", features = ["auto-initialize"], optional = true } rayon = { version = "1", optional = true } uuid = { version = "1", features = ["v4"] } notify = "7" +libc = "*" [dev-dependencies] tempdir = "0.3" diff --git a/rwf/src/http/mod.rs b/rwf/src/http/mod.rs index 8d3b4bb9..ab8fffed 100644 --- a/rwf/src/http/mod.rs +++ b/rwf/src/http/mod.rs @@ -9,6 +9,7 @@ pub mod handler; pub mod head; pub mod headers; pub mod path; +pub mod rack; pub mod request; pub mod response; pub mod router; diff --git a/rwf/src/http/rack/mod.rs b/rwf/src/http/rack/mod.rs new file mode 100644 index 00000000..43e98e92 --- /dev/null +++ b/rwf/src/http/rack/mod.rs @@ -0,0 +1,55 @@ +use libc::uintptr_t; +use std::ffi::{c_int, c_void, CString}; +use std::os::raw::c_char; + +use crate::error; + +#[link(name = "ruby")] +extern "C" { + fn ruby_setup() -> c_int; + fn ruby_cleanup(code: c_int) -> c_int; + fn rb_eval_string_protect(code: *const c_char, state: *mut c_int) -> uintptr_t; +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Ruby VM did not start")] + VmInit, + + #[error("Ruby VM state: {state}")] + Eval { state: i64 }, +} + +pub struct Value { + ptr: uintptr_t, +} + +pub struct Ruby {} + +impl Ruby { + pub fn init() -> Result<(), Error> { + unsafe { + if ruby_setup() != 0 { + Err(Error::VmInit) + } else { + Ok(()) + } + } + } + + pub fn eval(code: &str) -> Result { + unsafe { + let mut state: c_int = 0; + let c_string = CString::new(code).unwrap(); + let value = rb_eval_string_protect(c_string.as_ptr(), &mut state); + + if state != 0 { + Err(Error::Eval { + state: state as i64, + }) + } else { + Ok(Value { ptr: value }) + } + } + } +} From 46ee08243b5801c14e03c35e5f17511855b4a580 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Tue, 5 Nov 2024 14:34:43 -0800 Subject: [PATCH 02/15] Save --- Cargo.lock | 167 +++++++++++++++++++++++++++++++ Cargo.toml | 2 +- examples/quick-start/src/main.rs | 5 +- examples/rails/.bundle/config | 2 + examples/rails/.gitignore | 1 + examples/rails/Cargo.toml | 1 + examples/rails/Gemfile | 2 + examples/rails/Gemfile.lock | 6 +- examples/rails/src/main.rs | 4 +- rwf-ruby/Cargo.toml | 12 +++ rwf-ruby/build.rs | 9 ++ rwf-ruby/src/bindings.c | 25 +++++ rwf-ruby/src/lib.rs | 152 ++++++++++++++++++++++++++++ rwf/Cargo.toml | 4 + rwf/src/http/rack/mod.rs | 54 ---------- 15 files changed, 383 insertions(+), 63 deletions(-) create mode 100644 examples/rails/.bundle/config create mode 100644 examples/rails/.gitignore create mode 100644 rwf-ruby/Cargo.toml create mode 100644 rwf-ruby/build.rs create mode 100644 rwf-ruby/src/bindings.c create mode 100644 rwf-ruby/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 113ea0cb..5eababea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,6 +255,29 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -294,6 +317,24 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +[[package]] +name = "cc" +version = "1.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57c4b4da2a9d619dd035f27316d7a426305b75be93d09e92f2b9229c34feaf" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -310,6 +351,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.20" @@ -537,6 +589,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -667,6 +729,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "h2" version = "0.4.6" @@ -732,6 +800,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "1.1.0" @@ -957,12 +1034,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libredox" version = "0.1.3" @@ -974,6 +1067,12 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1194,6 +1293,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1326,6 +1431,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.89" @@ -1450,6 +1565,9 @@ dependencies = [ [[package]] name = "rails" version = "0.1.0" +dependencies = [ + "rwf", +] [[package]] name = "rand" @@ -1626,6 +1744,25 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.18" @@ -1640,7 +1777,9 @@ dependencies = [ "aes-gcm-siv", "async-trait", "base64 0.22.1", + "bindgen", "bytes", + "cc", "colored", "libc", "notify", @@ -1712,6 +1851,16 @@ dependencies = [ "syn", ] +[[package]] +name = "rwf-ruby" +version = "0.1.0" +dependencies = [ + "cc", + "libc", + "once_cell", + "thiserror", +] + [[package]] name = "rwf-tests" version = "0.1.0" @@ -1829,6 +1978,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -2457,6 +2612,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "whoami" version = "1.5.2" diff --git a/Cargo.toml b/Cargo.toml index 470ee0d6..152e53e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,5 @@ members = [ "examples/django", "examples/request-tracking", "examples/engine", - "rwf-admin", "examples/rails", + "rwf-admin", "examples/rails", "rwf-ruby", ] diff --git a/examples/quick-start/src/main.rs b/examples/quick-start/src/main.rs index 69d2519d..69503985 100644 --- a/examples/quick-start/src/main.rs +++ b/examples/quick-start/src/main.rs @@ -17,9 +17,8 @@ struct CurrentTime; #[async_trait] impl Controller for CurrentTime { /// This function responds to all incoming HTTP requests. - async fn handle(&self, request: &Request) -> Result { + async fn handle(&self, _req: &Request) -> Result { let time = OffsetDateTime::now_utc(); - println!("{:?}", request.headers().get("accept")); // This creates an HTTP "200 OK" response, // with "Content-Type: text/plain" header. @@ -32,8 +31,6 @@ impl Controller for CurrentTime { #[tokio::main] async fn main() -> Result<(), http::Error> { Logger::init(); - rwf::http::rack::Ruby::init().unwrap(); - rwf::http::rack::Ruby::eval("puts 'hello world'").unwrap(); Server::new(vec![route!("/" => Index), route!("/time" => CurrentTime)]) .launch("0.0.0.0:8001") diff --git a/examples/rails/.bundle/config b/examples/rails/.bundle/config new file mode 100644 index 00000000..d5bd3974 --- /dev/null +++ b/examples/rails/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_PATH: "/home/lev/.gem" diff --git a/examples/rails/.gitignore b/examples/rails/.gitignore new file mode 100644 index 00000000..48b8bf90 --- /dev/null +++ b/examples/rails/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/examples/rails/Cargo.toml b/examples/rails/Cargo.toml index c8193989..5a1a2f2b 100644 --- a/examples/rails/Cargo.toml +++ b/examples/rails/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +rwf = { path = "../../rwf" } diff --git a/examples/rails/Gemfile b/examples/rails/Gemfile index 6bc4e3f2..fea710be 100644 --- a/examples/rails/Gemfile +++ b/examples/rails/Gemfile @@ -5,3 +5,5 @@ source "https://rubygems.org" # gem "rails" gem "rails", "~> 7.2" + +gem "bootsnap", "~> 1.18" diff --git a/examples/rails/Gemfile.lock b/examples/rails/Gemfile.lock index 6793e081..dcdba6b9 100644 --- a/examples/rails/Gemfile.lock +++ b/examples/rails/Gemfile.lock @@ -75,6 +75,8 @@ GEM base64 (0.2.0) benchmark (0.3.0) bigdecimal (3.1.8) + bootsnap (1.18.4) + msgpack (~> 1.2) builder (3.3.0) concurrent-ruby (1.3.4) connection_pool (2.4.1) @@ -102,6 +104,7 @@ GEM marcel (1.0.4) mini_mime (1.1.5) minitest (5.25.1) + msgpack (1.7.3) net-imap (0.5.0) date net-protocol @@ -189,7 +192,8 @@ PLATFORMS x86_64-linux DEPENDENCIES + bootsnap (~> 1.18) rails (~> 7.2) BUNDLED WITH - 2.5.14 + 2.5.16 diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index e7a11a96..f328e4d9 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -1,3 +1 @@ -fn main() { - println!("Hello, world!"); -} +fn main() {} diff --git a/rwf-ruby/Cargo.toml b/rwf-ruby/Cargo.toml new file mode 100644 index 00000000..7adfc7fc --- /dev/null +++ b/rwf-ruby/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rwf-ruby" +version = "0.1.0" +edition = "2021" + +[dependencies] +libc = "0.2" +once_cell = "1" +thiserror = "1" + +[build-dependencies] +cc = "1" diff --git a/rwf-ruby/build.rs b/rwf-ruby/build.rs new file mode 100644 index 00000000..32642817 --- /dev/null +++ b/rwf-ruby/build.rs @@ -0,0 +1,9 @@ +fn main() { + println!("cargo:rerun-if-changed=src/bindings.c"); + + cc::Build::new() + .file("src/bindings.c") + .flag("-I/usr/include/ruby-3.3.0") + .flag("-I/usr/include/ruby-3.3.0/x86_64-linux") + .compile("rwf_ruby"); +} diff --git a/rwf-ruby/src/bindings.c b/rwf-ruby/src/bindings.c new file mode 100644 index 00000000..9de4516c --- /dev/null +++ b/rwf-ruby/src/bindings.c @@ -0,0 +1,25 @@ +/* + * Wrap various Ruby C macros into actual functions. +*/ +#include + +/* + * Get the Ruby value data type. +*/ +int rwf_rb_type(VALUE value) { + return (int)(rb_type(value)); +} + +/* + * Convert the value into a C-string. +*/ +char* rwf_value_cstr(VALUE value) { + return StringValueCStr(value); +} + +/* + * Clear error state when an exception is thrown. +*/ +void rwf_clear_error_state() { + rb_set_errinfo(Qnil); +} diff --git a/rwf-ruby/src/lib.rs b/rwf-ruby/src/lib.rs new file mode 100644 index 00000000..3eb143d5 --- /dev/null +++ b/rwf-ruby/src/lib.rs @@ -0,0 +1,152 @@ +use libc::uintptr_t; +use once_cell::sync::OnceCell; +use std::ffi::{c_int, CStr, CString}; +use std::os::raw::c_char; + +// Make sure the Ruby VM is initialized only once. +static RUBY_INIT: OnceCell = OnceCell::new(); + +#[link(name = "ruby")] +extern "C" { + fn ruby_setup() -> c_int; + fn ruby_cleanup(code: c_int) -> c_int; + fn rb_errinfo() -> uintptr_t; + + // Execute some Ruby code. + fn rb_eval_string_protect(code: *const c_char, state: *mut c_int) -> uintptr_t; + fn rb_obj_as_string(value: uintptr_t) -> uintptr_t; +} + +#[link(name = "rwf_ruby")] +extern "C" { + fn rwf_rb_type(value: uintptr_t) -> c_int; + + fn rwf_value_cstr(value: uintptr_t) -> *mut c_char; + + fn rwf_clear_error_state(); +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Ruby VM did not start")] + VmInit, + + #[error("{err}")] + Eval { err: String }, +} + +#[derive(Debug)] +pub struct Value { + ptr: uintptr_t, +} + +#[derive(Debug, PartialEq)] +#[repr(C)] +pub enum Type { + None = 0x00, + + Object = 0x01, + Class = 0x02, + Module = 0x03, + Float = 0x04, + RString = 0x05, + Regexp = 0x06, + Array = 0x07, + Hash = 0x08, + Struct = 0x09, + Bignum = 0x0a, + File = 0x0b, + Data = 0x0c, + Match = 0x0d, + Complex = 0x0e, + Rational = 0x0f, + + Nil = 0x11, + True = 0x12, + False = 0x13, + Symbol = 0x14, + Fixnum = 0x15, + Undef = 0x16, + + IMemo = 0x1a, + Node = 0x1b, + IClass = 0x1c, + Zombie = 0x1d, + + Mask = 0x1f, +} + +impl Value { + pub fn to_string(&self) -> String { + if self.ty() == Type::RString { + unsafe { + let cstr = rwf_value_cstr(self.ptr); + CStr::from_ptr(cstr).to_string_lossy().to_string() + } + } else { + String::new() + } + } + + pub fn ty(&self) -> Type { + let ty = unsafe { rwf_rb_type(self.ptr) }; + match ty { + 0x05 => Type::RString, + _ => Type::Nil, + } + } +} + +impl From for Value { + fn from(ptr: uintptr_t) -> Value { + Value { ptr } + } +} + +pub struct Ruby; + +impl Ruby { + pub fn init() -> Result<(), Error> { + RUBY_INIT.get_or_try_init(move || Ruby::new())?; + + Ok(()) + } + + fn new() -> Result { + unsafe { + if ruby_setup() != 0 { + Err(Error::VmInit) + } else { + Ok(Ruby {}) + } + } + } + + pub fn eval(code: &str) -> Result { + Self::init()?; + + unsafe { + let mut state: c_int = 0; + let c_string = CString::new(code).unwrap(); + let value = rb_eval_string_protect(c_string.as_ptr(), &mut state); + + if state != 0 { + let err = rb_errinfo(); + let err = Value::from(rb_obj_as_string(err)).to_string(); + rwf_clear_error_state(); + + Err(Error::Eval { err }) + } else { + Ok(Value { ptr: value }) + } + } + } +} + +impl Drop for Ruby { + fn drop(&mut self) { + unsafe { + ruby_cleanup(0); + } + } +} diff --git a/rwf/Cargo.toml b/rwf/Cargo.toml index e6cc40a8..7b04f5f7 100644 --- a/rwf/Cargo.toml +++ b/rwf/Cargo.toml @@ -45,3 +45,7 @@ libc = "*" [dev-dependencies] tempdir = "0.3" + +[build-dependencies] +bindgen = "0.65.1" +cc = "*" diff --git a/rwf/src/http/rack/mod.rs b/rwf/src/http/rack/mod.rs index 43e98e92..8b137891 100644 --- a/rwf/src/http/rack/mod.rs +++ b/rwf/src/http/rack/mod.rs @@ -1,55 +1 @@ -use libc::uintptr_t; -use std::ffi::{c_int, c_void, CString}; -use std::os::raw::c_char; -use crate::error; - -#[link(name = "ruby")] -extern "C" { - fn ruby_setup() -> c_int; - fn ruby_cleanup(code: c_int) -> c_int; - fn rb_eval_string_protect(code: *const c_char, state: *mut c_int) -> uintptr_t; -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Ruby VM did not start")] - VmInit, - - #[error("Ruby VM state: {state}")] - Eval { state: i64 }, -} - -pub struct Value { - ptr: uintptr_t, -} - -pub struct Ruby {} - -impl Ruby { - pub fn init() -> Result<(), Error> { - unsafe { - if ruby_setup() != 0 { - Err(Error::VmInit) - } else { - Ok(()) - } - } - } - - pub fn eval(code: &str) -> Result { - unsafe { - let mut state: c_int = 0; - let c_string = CString::new(code).unwrap(); - let value = rb_eval_string_protect(c_string.as_ptr(), &mut state); - - if state != 0 { - Err(Error::Eval { - state: state as i64, - }) - } else { - Ok(Value { ptr: value }) - } - } - } -} From ecd2c27a3579be1c5e7619da2e914c2d309859c7 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Wed, 6 Nov 2024 16:13:22 -0800 Subject: [PATCH 03/15] wow --- Cargo.lock | 29 +- examples/rails/src/config.ru | 4 + examples/rails/src/main.rs | 20 +- rwf-ruby/Cargo.toml | 2 + rwf-ruby/build.rs | 12 + rwf-ruby/src/.clangd | 4 + rwf-ruby/src/Makefile | 2 + rwf-ruby/src/bindings.c | 197 +++++++++++ rwf-ruby/src/bindings.h | 33 ++ rwf-ruby/src/lib.rs | 242 ++++++++++++- rwf-ruby/src/main.c | 12 + rwf-ruby/src/test_bindings | Bin 0 -> 24856 bytes rwf-ruby/tests/todo/.dockerignore | 48 +++ rwf-ruby/tests/todo/.gitattributes | 9 + rwf-ruby/tests/todo/.github/dependabot.yml | 12 + rwf-ruby/tests/todo/.github/workflows/ci.yml | 90 +++++ rwf-ruby/tests/todo/.gitignore | 35 ++ rwf-ruby/tests/todo/.rubocop.yml | 8 + rwf-ruby/tests/todo/.ruby-version | 1 + rwf-ruby/tests/todo/Dockerfile | 69 ++++ rwf-ruby/tests/todo/Gemfile | 57 +++ rwf-ruby/tests/todo/Gemfile.lock | 326 ++++++++++++++++++ rwf-ruby/tests/todo/README.md | 24 ++ rwf-ruby/tests/todo/Rakefile | 6 + .../tests/todo/app/assets/config/manifest.js | 4 + rwf-ruby/tests/todo/app/assets/images/.keep | 0 .../app/assets/stylesheets/application.css | 15 + .../app/channels/application_cable/channel.rb | 4 + .../channels/application_cable/connection.rb | 4 + .../app/controllers/application_controller.rb | 4 + .../tests/todo/app/controllers/concerns/.keep | 0 .../todo/app/helpers/application_helper.rb | 2 + .../tests/todo/app/javascript/application.js | 3 + .../app/javascript/controllers/application.js | 9 + .../controllers/hello_controller.js | 7 + .../todo/app/javascript/controllers/index.js | 4 + .../tests/todo/app/jobs/application_job.rb | 7 + .../todo/app/mailers/application_mailer.rb | 4 + .../todo/app/models/application_record.rb | 3 + rwf-ruby/tests/todo/app/models/concerns/.keep | 0 .../app/views/layouts/application.html.erb | 23 ++ .../todo/app/views/layouts/mailer.html.erb | 13 + .../todo/app/views/layouts/mailer.text.erb | 1 + .../todo/app/views/pwa/manifest.json.erb | 22 ++ .../todo/app/views/pwa/service-worker.js | 26 ++ rwf-ruby/tests/todo/bin/brakeman | 7 + rwf-ruby/tests/todo/bin/bundle | 109 ++++++ rwf-ruby/tests/todo/bin/docker-entrypoint | 13 + rwf-ruby/tests/todo/bin/importmap | 4 + rwf-ruby/tests/todo/bin/rails | 4 + rwf-ruby/tests/todo/bin/rake | 4 + rwf-ruby/tests/todo/bin/rubocop | 8 + rwf-ruby/tests/todo/bin/setup | 37 ++ rwf-ruby/tests/todo/config.ru | 6 + rwf-ruby/tests/todo/config/application.rb | 27 ++ rwf-ruby/tests/todo/config/boot.rb | 4 + rwf-ruby/tests/todo/config/cable.yml | 10 + .../tests/todo/config/credentials.yml.enc | 1 + rwf-ruby/tests/todo/config/database.yml | 32 ++ rwf-ruby/tests/todo/config/environment.rb | 5 + .../todo/config/environments/development.rb | 81 +++++ .../todo/config/environments/production.rb | 105 ++++++ .../tests/todo/config/environments/test.rb | 67 ++++ rwf-ruby/tests/todo/config/importmap.rb | 7 + .../tests/todo/config/initializers/assets.rb | 12 + .../initializers/content_security_policy.rb | 25 ++ .../initializers/filter_parameter_logging.rb | 8 + .../todo/config/initializers/inflections.rb | 16 + .../config/initializers/permissions_policy.rb | 13 + rwf-ruby/tests/todo/config/locales/en.yml | 31 ++ rwf-ruby/tests/todo/config/puma.rb | 34 ++ rwf-ruby/tests/todo/config/routes.rb | 14 + rwf-ruby/tests/todo/config/storage.yml | 34 ++ rwf-ruby/tests/todo/db/seeds.rb | 9 + rwf-ruby/tests/todo/lib/assets/.keep | 0 rwf-ruby/tests/todo/lib/tasks/.keep | 0 rwf-ruby/tests/todo/log/.keep | 0 rwf-ruby/tests/todo/public/404.html | 67 ++++ .../todo/public/406-unsupported-browser.html | 66 ++++ rwf-ruby/tests/todo/public/422.html | 67 ++++ rwf-ruby/tests/todo/public/500.html | 66 ++++ rwf-ruby/tests/todo/public/icon.png | Bin 0 -> 5599 bytes rwf-ruby/tests/todo/public/icon.svg | 3 + rwf-ruby/tests/todo/public/robots.txt | 1 + rwf-ruby/tests/todo/storage/.keep | 0 .../todo/test/application_system_test_case.rb | 5 + .../application_cable/connection_test.rb | 13 + rwf-ruby/tests/todo/test/controllers/.keep | 0 rwf-ruby/tests/todo/test/fixtures/files/.keep | 0 rwf-ruby/tests/todo/test/helpers/.keep | 0 rwf-ruby/tests/todo/test/integration/.keep | 0 rwf-ruby/tests/todo/test/mailers/.keep | 0 rwf-ruby/tests/todo/test/models/.keep | 0 rwf-ruby/tests/todo/test/system/.keep | 0 rwf-ruby/tests/todo/test/test_helper.rb | 15 + rwf-ruby/tests/todo/tmp/.keep | 0 rwf-ruby/tests/todo/tmp/pids/.keep | 0 rwf-ruby/tests/todo/tmp/storage/.keep | 0 rwf-ruby/tests/todo/vendor/.keep | 0 rwf-ruby/tests/todo/vendor/javascript/.keep | 0 rwf/Cargo.toml | 6 +- rwf/src/controller/mod.rs | 1 + rwf/src/controller/rack.rs | 100 ++++++ rwf/src/http/body.rs | 6 + 104 files changed, 2526 insertions(+), 14 deletions(-) create mode 100644 examples/rails/src/config.ru create mode 100644 rwf-ruby/src/.clangd create mode 100644 rwf-ruby/src/Makefile create mode 100644 rwf-ruby/src/bindings.h create mode 100644 rwf-ruby/src/main.c create mode 100755 rwf-ruby/src/test_bindings create mode 100644 rwf-ruby/tests/todo/.dockerignore create mode 100644 rwf-ruby/tests/todo/.gitattributes create mode 100644 rwf-ruby/tests/todo/.github/dependabot.yml create mode 100644 rwf-ruby/tests/todo/.github/workflows/ci.yml create mode 100644 rwf-ruby/tests/todo/.gitignore create mode 100644 rwf-ruby/tests/todo/.rubocop.yml create mode 100644 rwf-ruby/tests/todo/.ruby-version create mode 100644 rwf-ruby/tests/todo/Dockerfile create mode 100644 rwf-ruby/tests/todo/Gemfile create mode 100644 rwf-ruby/tests/todo/Gemfile.lock create mode 100644 rwf-ruby/tests/todo/README.md create mode 100644 rwf-ruby/tests/todo/Rakefile create mode 100644 rwf-ruby/tests/todo/app/assets/config/manifest.js create mode 100644 rwf-ruby/tests/todo/app/assets/images/.keep create mode 100644 rwf-ruby/tests/todo/app/assets/stylesheets/application.css create mode 100644 rwf-ruby/tests/todo/app/channels/application_cable/channel.rb create mode 100644 rwf-ruby/tests/todo/app/channels/application_cable/connection.rb create mode 100644 rwf-ruby/tests/todo/app/controllers/application_controller.rb create mode 100644 rwf-ruby/tests/todo/app/controllers/concerns/.keep create mode 100644 rwf-ruby/tests/todo/app/helpers/application_helper.rb create mode 100644 rwf-ruby/tests/todo/app/javascript/application.js create mode 100644 rwf-ruby/tests/todo/app/javascript/controllers/application.js create mode 100644 rwf-ruby/tests/todo/app/javascript/controllers/hello_controller.js create mode 100644 rwf-ruby/tests/todo/app/javascript/controllers/index.js create mode 100644 rwf-ruby/tests/todo/app/jobs/application_job.rb create mode 100644 rwf-ruby/tests/todo/app/mailers/application_mailer.rb create mode 100644 rwf-ruby/tests/todo/app/models/application_record.rb create mode 100644 rwf-ruby/tests/todo/app/models/concerns/.keep create mode 100644 rwf-ruby/tests/todo/app/views/layouts/application.html.erb create mode 100644 rwf-ruby/tests/todo/app/views/layouts/mailer.html.erb create mode 100644 rwf-ruby/tests/todo/app/views/layouts/mailer.text.erb create mode 100644 rwf-ruby/tests/todo/app/views/pwa/manifest.json.erb create mode 100644 rwf-ruby/tests/todo/app/views/pwa/service-worker.js create mode 100755 rwf-ruby/tests/todo/bin/brakeman create mode 100755 rwf-ruby/tests/todo/bin/bundle create mode 100755 rwf-ruby/tests/todo/bin/docker-entrypoint create mode 100755 rwf-ruby/tests/todo/bin/importmap create mode 100755 rwf-ruby/tests/todo/bin/rails create mode 100755 rwf-ruby/tests/todo/bin/rake create mode 100755 rwf-ruby/tests/todo/bin/rubocop create mode 100755 rwf-ruby/tests/todo/bin/setup create mode 100644 rwf-ruby/tests/todo/config.ru create mode 100644 rwf-ruby/tests/todo/config/application.rb create mode 100644 rwf-ruby/tests/todo/config/boot.rb create mode 100644 rwf-ruby/tests/todo/config/cable.yml create mode 100644 rwf-ruby/tests/todo/config/credentials.yml.enc create mode 100644 rwf-ruby/tests/todo/config/database.yml create mode 100644 rwf-ruby/tests/todo/config/environment.rb create mode 100644 rwf-ruby/tests/todo/config/environments/development.rb create mode 100644 rwf-ruby/tests/todo/config/environments/production.rb create mode 100644 rwf-ruby/tests/todo/config/environments/test.rb create mode 100644 rwf-ruby/tests/todo/config/importmap.rb create mode 100644 rwf-ruby/tests/todo/config/initializers/assets.rb create mode 100644 rwf-ruby/tests/todo/config/initializers/content_security_policy.rb create mode 100644 rwf-ruby/tests/todo/config/initializers/filter_parameter_logging.rb create mode 100644 rwf-ruby/tests/todo/config/initializers/inflections.rb create mode 100644 rwf-ruby/tests/todo/config/initializers/permissions_policy.rb create mode 100644 rwf-ruby/tests/todo/config/locales/en.yml create mode 100644 rwf-ruby/tests/todo/config/puma.rb create mode 100644 rwf-ruby/tests/todo/config/routes.rb create mode 100644 rwf-ruby/tests/todo/config/storage.yml create mode 100644 rwf-ruby/tests/todo/db/seeds.rb create mode 100644 rwf-ruby/tests/todo/lib/assets/.keep create mode 100644 rwf-ruby/tests/todo/lib/tasks/.keep create mode 100644 rwf-ruby/tests/todo/log/.keep create mode 100644 rwf-ruby/tests/todo/public/404.html create mode 100644 rwf-ruby/tests/todo/public/406-unsupported-browser.html create mode 100644 rwf-ruby/tests/todo/public/422.html create mode 100644 rwf-ruby/tests/todo/public/500.html create mode 100644 rwf-ruby/tests/todo/public/icon.png create mode 100644 rwf-ruby/tests/todo/public/icon.svg create mode 100644 rwf-ruby/tests/todo/public/robots.txt create mode 100644 rwf-ruby/tests/todo/storage/.keep create mode 100644 rwf-ruby/tests/todo/test/application_system_test_case.rb create mode 100644 rwf-ruby/tests/todo/test/channels/application_cable/connection_test.rb create mode 100644 rwf-ruby/tests/todo/test/controllers/.keep create mode 100644 rwf-ruby/tests/todo/test/fixtures/files/.keep create mode 100644 rwf-ruby/tests/todo/test/helpers/.keep create mode 100644 rwf-ruby/tests/todo/test/integration/.keep create mode 100644 rwf-ruby/tests/todo/test/mailers/.keep create mode 100644 rwf-ruby/tests/todo/test/models/.keep create mode 100644 rwf-ruby/tests/todo/test/system/.keep create mode 100644 rwf-ruby/tests/todo/test/test_helper.rb create mode 100644 rwf-ruby/tests/todo/tmp/.keep create mode 100644 rwf-ruby/tests/todo/tmp/pids/.keep create mode 100644 rwf-ruby/tests/todo/tmp/storage/.keep create mode 100644 rwf-ruby/tests/todo/vendor/.keep create mode 100644 rwf-ruby/tests/todo/vendor/javascript/.keep create mode 100644 rwf/src/controller/rack.rs diff --git a/Cargo.lock b/Cargo.lock index 5eababea..7a53c85c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,6 +278,29 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -1777,11 +1800,10 @@ dependencies = [ "aes-gcm-siv", "async-trait", "base64 0.22.1", - "bindgen", + "bindgen 0.65.1", "bytes", "cc", "colored", - "libc", "notify", "once_cell", "parking_lot", @@ -1790,6 +1812,7 @@ dependencies = [ "rayon", "regex", "rwf-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rwf-ruby", "serde", "serde_json", "sha1", @@ -1855,9 +1878,11 @@ dependencies = [ name = "rwf-ruby" version = "0.1.0" dependencies = [ + "bindgen 0.68.1", "cc", "libc", "once_cell", + "parking_lot", "thiserror", ] diff --git a/examples/rails/src/config.ru b/examples/rails/src/config.ru new file mode 100644 index 00000000..d7479c33 --- /dev/null +++ b/examples/rails/src/config.ru @@ -0,0 +1,4 @@ +run do |env| + puts env + [200, {}, ["Hello World"]] +end diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index f328e4d9..28561370 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -1 +1,19 @@ -fn main() {} +use rwf::controller::rack::RackController; +use rwf::http::{self, Server}; +use rwf::prelude::*; + +// fn main() { + +// } + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), http::Error> { + Logger::init(); + + let controller = RackController::new("todo"); + // controller.load(); + + Server::new(vec![controller.wildcard("/")]) + .launch("0.0.0.0:8000") + .await +} diff --git a/rwf-ruby/Cargo.toml b/rwf-ruby/Cargo.toml index 7adfc7fc..2fd8b88d 100644 --- a/rwf-ruby/Cargo.toml +++ b/rwf-ruby/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" libc = "0.2" once_cell = "1" thiserror = "1" +parking_lot = "*" [build-dependencies] cc = "1" +bindgen = "0.68" diff --git a/rwf-ruby/build.rs b/rwf-ruby/build.rs index 32642817..1f246dce 100644 --- a/rwf-ruby/build.rs +++ b/rwf-ruby/build.rs @@ -1,5 +1,17 @@ +use std::{env, path::PathBuf}; + fn main() { println!("cargo:rerun-if-changed=src/bindings.c"); + println!("cargo:rerun-if-changed=src/bindings.h"); + + // let bindings = bindgen::Builder::default() + // .header("src/bindings.h") + // .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // .generate() + // .expect("bindgen"); + + // let out_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/bindings.rs"); + // bindings.write_to_file(out_path).expect("write bindings"); cc::Build::new() .file("src/bindings.c") diff --git a/rwf-ruby/src/.clangd b/rwf-ruby/src/.clangd new file mode 100644 index 00000000..a5e4a1a2 --- /dev/null +++ b/rwf-ruby/src/.clangd @@ -0,0 +1,4 @@ +CompileFlags: + Add: + - "-I/usr/include/ruby-3.3.0" + - "-I/usr/include/ruby-3.3.0/x86_64-linux" diff --git a/rwf-ruby/src/Makefile b/rwf-ruby/src/Makefile new file mode 100644 index 00000000..802ca45b --- /dev/null +++ b/rwf-ruby/src/Makefile @@ -0,0 +1,2 @@ +all: + gcc bindings.c main.c -o test_bindings -lruby -I/usr/include/ruby-3.3.0/ -I/usr/include/ruby-3.3.0/x86_64-linux -g diff --git a/rwf-ruby/src/bindings.c b/rwf-ruby/src/bindings.c index 9de4516c..be3776fb 100644 --- a/rwf-ruby/src/bindings.c +++ b/rwf-ruby/src/bindings.c @@ -1,7 +1,189 @@ /* * Wrap various Ruby C macros into actual functions. */ +#include +#include #include +#include +#include "bindings.h" +#include "ruby/internal/arithmetic/int.h" +#include "ruby/internal/core/rstring.h" +#include "ruby/internal/eval.h" +#include "ruby/internal/special_consts.h" +#include "ruby/internal/value_type.h" + +static void rwf_print_error(void); + +void rwf_init_ruby() { + ruby_setup(); + ruby_init_loadpath(); + ruby_script("rwf_loader"); +} + +int rwf_load_app(const char* path) { + int state; + void *node; + + /* Ruby code to load the app. */ + char *require = malloc(strlen(path) + strlen("-erequire '") + strlen("'") + 1); + sprintf(require, "-erequire '%s'", path); + + char* options[] = { + "-v", + require, + }; + + node = ruby_options(2, options); + + if (ruby_executable_node(node, &state)) { + state = ruby_exec_node(node); + + if (state) { + rwf_print_error(); + return -1; + } + } else { + rwf_print_error(); + return -1; + } + + free(require); + return 0; +} + +typedef enum RackBody { + PROXY, + FILES, +} RackBody; + + +void rwf_debug_value(VALUE v) { + int state; + VALUE kernel = rb_eval_string_protect("Kernel", &state); + VALUE str = rb_obj_as_string(v); + rb_funcall(kernel, rb_intern("puts"), 1, str); + + VALUE methods = rb_funcall(v, rb_intern("inspect"), 0); + rb_funcall(kernel, rb_intern("puts"), 1, methods); +} + +int rwf_responds_to(VALUE value, const char* name) { + if (value == Qnil) { + return 0; + } + + VALUE name_s = rb_str_new_cstr(name); + VALUE responds_to = rb_funcall(value, rb_intern("respond_to?"), 1, name_s); + + /* I feel dumb, but Qtrue doesn't work. */ + VALUE object_id = rb_funcall(responds_to, rb_intern("object_id"), 0); + int is_true = NUM2INT(object_id); + + if (is_true == 20) { + return 0; + } else { + return 1; + } +} + +VALUE rwf_get_body(VALUE value, int *is_file) { + if (rwf_responds_to(value, "to_ary") == 0) { + VALUE proxy_body_ar = rb_funcall(value, rb_intern("to_ary"), 0); + *is_file = 0; + return rb_ary_entry(proxy_body_ar, 0); + } else if (rwf_responds_to(value, "path") == 0) { + VALUE path = rb_funcall(value, rb_intern("path"), 0); + *is_file = 1; + return path; + } else { + *is_file = 0; + return Qnil; + } +} + + +RackResponse rwf_rack_response_new(VALUE value) { + assert(TYPE(value) == T_ARRAY); + assert(RARRAY_LEN(value) == 3); + + VALUE headers = rb_ary_entry(value, 1); + assert(TYPE(headers) == T_HASH); + + RackResponse response; + + response.code = NUM2INT(rb_ary_entry(value, 0)); + response.num_headers = RHASH_SIZE(headers); + + VALUE header_keys = rb_funcall(headers, rb_intern("keys"), 0); + response.headers = malloc(response.num_headers * sizeof(EnvKey)); + + for(int i = 0; i < response.num_headers; i++) { + VALUE header_key = rb_ary_entry(header_keys, i); + VALUE header_value = rb_hash_fetch(headers, header_key); + + /* There is a MRI function for this, but I can't find it anymore */ + VALUE header_key_symbol_str = rb_funcall(header_key, rb_intern("to_s"), 0); + + char *header_key_str = StringValueCStr(header_key_symbol_str); + char *header_value_str = StringValueCStr(header_value); + + EnvKey env_key; + env_key.key = header_key_str; + env_key.value = header_value_str; + + response.headers[i] = env_key; + } + + VALUE body_entry = rb_ary_entry(value, 2); + + // It can be an array or it can be a Proxy object which duck-types + // to array. + VALUE body = rwf_get_body(body_entry, &response.is_file); + + response.body = StringValueCStr(body); + response.value = value; + + return response; +} + +void rwf_debug_key(EnvKey *k) { + int state; + VALUE kernel = rb_eval_string_protect("Kernel", &state); + + VALUE key = rb_str_new_cstr(k->key); + VALUE value = rb_str_new_cstr(k->value); + + rb_funcall(kernel, rb_intern("puts"), 1, key); + rb_funcall(kernel, rb_intern("puts"), 1, value); +} + +RackResponse rwf_app_call(RackRequest request) { + int state; + + VALUE hash = rb_hash_new(); + for (int i = 0; i < request.length; i++) { + VALUE key = rb_str_new_cstr(request.env[i].key); + VALUE value = rb_str_new_cstr(request.env[i].value); + + rb_hash_aset(hash, key, value); + } + + VALUE app = rb_eval_string_protect("Rails.application", &state); + + if (state) { + rwf_print_error(); + } + + VALUE response = rb_funcall(app, rb_intern("call"), 1, hash); + + rwf_print_error(); + + return rwf_rack_response_new(response); +} + +void rwf_rack_response_drop(RackResponse *response) { + free(response->headers); +} /* * Get the Ruby value data type. @@ -23,3 +205,18 @@ char* rwf_value_cstr(VALUE value) { void rwf_clear_error_state() { rb_set_errinfo(Qnil); } + +void rwf_print_error() { + VALUE error = rb_errinfo(); + + if (error != Qnil) { + VALUE error_str = rb_obj_as_string(error); + char *error_msg = StringValueCStr(error_str); + VALUE backtrace = rb_funcall(error, rb_intern("backtrace"), 0); + VALUE backtrace_obj = rb_obj_as_string(backtrace); + char *backtrace_str = StringValueCStr(backtrace_obj); + printf("error: %s\nbacktrace: %s", error_msg, backtrace_str); + } + + rb_set_errinfo(Qnil); +} diff --git a/rwf-ruby/src/bindings.h b/rwf-ruby/src/bindings.h new file mode 100644 index 00000000..e4daef6f --- /dev/null +++ b/rwf-ruby/src/bindings.h @@ -0,0 +1,33 @@ + +#include +#include + +#ifndef BINDINGS_H +#define BINDINGS_H + +typedef struct EnvKey { + const char *key; + const char *value; +} EnvKey; + +typedef struct RackResponse { + uintptr_t value; + int code; + int num_headers; + EnvKey *headers; + char* body; + int is_file; +} RackResponse; + +typedef struct RackRequest { + const EnvKey* env; + const int length; +} RackRequest; + + +int rwf_load_app(const char* path); +void rwf_init_ruby(void); +RackResponse rwf_rack_response_new(VALUE value); +RackResponse rwf_app_call(RackRequest request); + +#endif diff --git a/rwf-ruby/src/lib.rs b/rwf-ruby/src/lib.rs index 3eb143d5..9b842da3 100644 --- a/rwf-ruby/src/lib.rs +++ b/rwf-ruby/src/lib.rs @@ -1,29 +1,186 @@ use libc::uintptr_t; -use once_cell::sync::OnceCell; -use std::ffi::{c_int, CStr, CString}; -use std::os::raw::c_char; +use once_cell::sync::{Lazy, OnceCell}; +use std::ffi::{c_char, c_int, CStr, CString}; +use std::fs::canonicalize; +use std::path::Path; + +use parking_lot::Mutex; +use std::collections::HashMap; +use std::sync::Arc; // Make sure the Ruby VM is initialized only once. static RUBY_INIT: OnceCell = OnceCell::new(); +#[repr(C)] +#[derive(Debug, Clone)] +pub struct RackResponse { + pub value: usize, + pub code: c_int, + pub num_headers: c_int, + pub headers: *mut EnvKey, + pub body: *mut c_char, + pub is_file: c_int, +} + +#[repr(C)] +#[derive(Debug)] +struct EnvKey { + key: *const c_char, + value: *const c_char, +} + +#[repr(C)] +#[derive(Debug)] +pub struct RackRequest { + env: *const EnvKey, + length: c_int, +} + +impl RackRequest { + pub fn send(env: HashMap) -> RackResponse { + // let mut c_strings = vec![]; + let mut keys = vec![]; + + let (mut k, mut v) = (vec![], vec![]); + + for (key, value) in &env { + let key = CString::new(key.as_str()).unwrap(); + let value = CString::new(value.as_str()).unwrap(); + k.push(key); + v.push(value); + + let env_key = EnvKey { + key: k.last().unwrap().as_ptr(), + value: v.last().unwrap().as_ptr(), + }; + + // unsafe { rwf_debug_key(&env_key) }; + + keys.push(env_key); + + // Keep the references or they will be dropped and + // we'll segfault. + // c_strings.push(key); + // c_strings.push(value); + } + + // unsafe { rwf_debug_key(&keys[2]) }; + + let req = RackRequest { + length: keys.len() as c_int, + env: keys.as_ptr(), + }; + + unsafe { rwf_app_call(req) } + } +} + +#[derive(Debug)] +pub struct RackResponseOwned { + code: u16, + headers: HashMap, + body: Vec, + is_file: bool, +} + +impl RackResponseOwned { + pub fn body(&self) -> &[u8] { + &self.body + } + + pub fn code(&self) -> u16 { + self.code + } + + pub fn is_file(&self) -> bool { + self.is_file + } +} + +impl From for RackResponseOwned { + fn from(response: RackResponse) -> RackResponseOwned { + let code = response.code as u16; + + let mut headers = HashMap::new(); + + for n in 0..response.num_headers { + let env_key = unsafe { response.headers.offset(n as isize) }; + let name = unsafe { CStr::from_ptr((*env_key).key) }; + let value = unsafe { CStr::from_ptr((*env_key).value) }; + + headers.insert( + name.to_string_lossy().to_string(), + value.to_string_lossy().to_string(), + ); + } + + let body = unsafe { CStr::from_ptr(response.body) }; + let body = Vec::from(body.to_bytes()); + + RackResponseOwned { + code, + headers, + body, + is_file: response.is_file == 1, + } + } +} + +impl RackResponse { + pub fn new(value: &Value) -> Self { + unsafe { rwf_rack_response_new(value.raw_ptr()) } + } +} + +impl Drop for RackResponse { + fn drop(&mut self) { + unsafe { rwf_rack_response_drop(self) } + } +} + #[link(name = "ruby")] extern "C" { - fn ruby_setup() -> c_int; fn ruby_cleanup(code: c_int) -> c_int; fn rb_errinfo() -> uintptr_t; // Execute some Ruby code. fn rb_eval_string_protect(code: *const c_char, state: *mut c_int) -> uintptr_t; fn rb_obj_as_string(value: uintptr_t) -> uintptr_t; + + fn rb_gc_disable() -> c_int; + fn rb_gc_enable() -> c_int; } #[link(name = "rwf_ruby")] extern "C" { + /// Get the type of the object. fn rwf_rb_type(value: uintptr_t) -> c_int; + /// Get the CStr value. Careful with this one, + /// if the object isn't a string, this will segfault. fn rwf_value_cstr(value: uintptr_t) -> *mut c_char; + /// Clear error state after handling an exception. fn rwf_clear_error_state(); + + /// Convert the Rack response to a struct we can work with. + /// The Rack response is an array of three elements: + /// - HTTP code (int) + /// - headers (Hash) + /// - body (String) + fn rwf_rack_response_new(value: uintptr_t) -> RackResponse; + + /// Deallocate memory allocated for converting the Rack response + /// from Ruby to Rust. + fn rwf_rack_response_drop(response: &RackResponse); + + /// Load an app into the VM. + fn rwf_load_app(path: *const c_char) -> c_int; + + /// Initialize Ruby correctly. + fn rwf_init_ruby(); + + fn rwf_app_call(request: RackRequest) -> RackResponse; } #[derive(Debug, thiserror::Error)] @@ -33,6 +190,9 @@ pub enum Error { #[error("{err}")] Eval { err: String }, + + #[error("Ruby app failed to load")] + App, } #[derive(Debug)] @@ -95,6 +255,10 @@ impl Value { _ => Type::Nil, } } + + pub fn raw_ptr(&self) -> uintptr_t { + self.ptr + } } impl From for Value { @@ -114,12 +278,29 @@ impl Ruby { fn new() -> Result { unsafe { - if ruby_setup() != 0 { - Err(Error::VmInit) - } else { - Ok(Ruby {}) + rwf_init_ruby(); + Ok(Ruby {}) + } + } + + pub fn load_app(path: impl AsRef + Copy) -> Result<(), Error> { + Self::init()?; + + let path = path.as_ref(); + + if path.exists() { + let absolute = canonicalize(path).unwrap(); + let s = absolute.display().to_string(); + let cs = CString::new(s).unwrap(); + + unsafe { + if rwf_load_app(cs.as_ptr()) != 0 { + return Err(Error::App); + } } } + + Ok(()) } pub fn eval(code: &str) -> Result { @@ -141,6 +322,18 @@ impl Ruby { } } } + + pub fn gc_disable() { + unsafe { + rb_gc_disable(); + } + } + + pub fn gc_enable() { + unsafe { + rb_gc_enable(); + } + } } impl Drop for Ruby { @@ -150,3 +343,36 @@ impl Drop for Ruby { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_rack_response() { + let response = Ruby::eval(r#"[200, {"hello": "world", "the year is 2024": "linux desktop is coming"}, ["apples and oranges"]]"#).unwrap(); + let response = RackResponse::new(&response); + + assert_eq!(response.code, 200); + assert_eq!(response.num_headers, 2); + + let owned = RackResponseOwned::from(response); + assert_eq!( + owned.headers.get("the year is 2024"), + Some(&String::from("linux desktop is coming")) + ); + assert_eq!( + String::from_utf8_lossy(&owned.body), + "apples and oranges".to_string() + ); + } + + #[test] + fn test_load_rails() { + Ruby::load_app(&Path::new("tests/todo/config/environment.rb")).unwrap(); + let response = Ruby::eval("Rails.application.call({})").unwrap(); + let response = RackResponse::new(&response); + let owned = RackResponseOwned::from(response); + assert_eq!(owned.code, 403); + } +} diff --git a/rwf-ruby/src/main.c b/rwf-ruby/src/main.c new file mode 100644 index 00000000..0e5eae1d --- /dev/null +++ b/rwf-ruby/src/main.c @@ -0,0 +1,12 @@ +#include "bindings.h" +#include + +int main() { + int state; + + rwf_init_ruby(); + rwf_load_app("/home/lev/code/rwf/rwf-ruby/tests/todo/config/environment.rb"); + + VALUE response = rb_eval_string_protect("Rails.application.call({})", &state); + RackResponse res = rwf_rack_response_new(response); +} diff --git a/rwf-ruby/src/test_bindings b/rwf-ruby/src/test_bindings new file mode 100755 index 0000000000000000000000000000000000000000..160098b316f30de22480722733933589e549c4bf GIT binary patch literal 24856 zcmeHv3v^u7b@n+kb4MeM-jZ$kJ$_*W(a29QV4Kln?2#>5vLqXul8i^vNE$4SlzAAL z0CEseE5fba65>Ua)XD!ZN(;0Oh13BGa-i5w6Y4zckgTGnkV-Xa+?pg#NKwCi9(P99 znlvk2tLtCu*PS(YpZz@h?DM$i-h1vD@Yij%anAH%XImIyr-~%Srx;~>RRX}rHnKdN zm$PNe3EUxQlJAoMTzOoyO@%H+w*!)186~EH3nZCos68Y|dYKMauHg#X12p@lxSL?Gd=~kDI6l=AlXv8q|z9)nHamY?*XCwoCYX*9D z2Ky8j5B}C83Bq*w9?n1yWw76of&LNL@!)Sg9)bK)D-rl@8SH#I1ATP{`iczn*%|0R zM&8rK;V(1L8#CDXK?eCZGtfsf(ElR?eOCs0B7;9H80_rnjRxb1P%IG)vS4jXeXu>mv(T-jgOST>ghWo>9 z$pqO5Mtj@CBxCYhgI%F`S1=yA7tCZ2@j8;xworGs)Qv)pXn z91bIcP9g~Xj&Pz)utPEA7fr+tNp<*=>{H3MSfo!l7(N*4RtAE7vED?u4P1G|VaF~J99gead zl&ZH4G8hcoNDv|K2}PoywDpG&!f2=)abWR2lr7Q0AVzM{D7ntNj?gwr-^8}n)mBvp z*Lv5jHOleb(2-ldAKaQ`cWjZ`5?3P&2Qi=M+AAyQCv?F}6#L zv2$s3HPsNT3u*LxEev=ujb4yOA4{VzOrz@ydlqP_NP_F~Ng<-EOPwGTrqR={CuM1L zF<%nWjCs=NR5pFA^2rq0r@uZ{`(%n}I(9_a=%cfg&5=oQ_W2}2@@|{q1UC*s0W#&8+ftd*W{}zEO1&e+a7<|ta7&g9C!C2to(S&XCLSXQ9*J~malN-JU z^5lxI<6N-VhZNym#G4qK#IfS5gwrKw;)1|mB-}>$sKB2ioNjX_P6_P6~V};dIHEI41CUgwv&B;;_Jr38zcML?3Xf zuQflZ9s1_S0z*Fv48C)@vBke;bj|s|(an1iq{&%sFJtd_cncOk1Qk^1(ajT}1dbX% zB5=#q#4J?xn|QiFRh>OKRE`dzdASu9Yq)qob_Lwq&$ZG7&<%tO=SGgXfi7>c>h(F%sNfY{E~4RX9ge6 zOCJW`^FU`@>J~R4L) z@}&}pTqgdPP#&Q&WE0s9i^lp-DlIj~zX&7a&wc>OqsFc1-=z34(2kj5YlP*~?SWy( zidFECqPk`jHcpe|cSvz?G>_5&N)P!jAj{L>UkDt{dmjTxVCYS7MP-lwt<2{=LH3Uc zsUei-DAGf3yh_8}c%?LRF>rME*CB9G74~btnw&&eTPjIs$NwB+L;kN1`@arq+4w8a zJ&p2CURpT*oS80?Ih-9|0BPX@#iKoNW2q=h!WjQ!FjNdb`Q9;6zQ5_GxDCBPxd(9e!9LlYfB5r+LIN&FF%VaWeOMEWNfX;EUNp147( zY(PT@44om-7p0k0*gpKFi0o6t&^-}2Gv**;-s)Q6}Ocp+NOHzfHO@~1IhdQzr5d0O-<^5pb2o+M zr(o*TD^z46Ua>gTEO87YqDEwUJSu9Gn&j|qv?m@I+J!4MwZGlyOO@z4yUKu5!;p;} z4dY%iH2g4WTKX7`7Q;R$ogM$%tLP{u+m$}%9Z5B;!S^7HA@`^pW==s#>by!_etbW2 zO7%9MrOAy~LL9}oh|%Vvqu`+vRB+KP&W^t!S?6FG0X#dtN8)3s*2i%blhs;_UQ0~T zRYe24culo_?iV5tbMWnvhR}r(GwA-O@E4sGd2AASsIGl0L}Yo6LHU?9oHv4K z2I`tJ#;Cs>HJsAm_@F@juge0IE9xrg6qS}L5+xRG*@wYu+OB;X9-YVZ%bc5;voUHs zGsjE>W+E^Xftd)*MBslO0ea#dyQf3Eu?WXlIo_mvJQ<0FJu6qlSF-Yh>_GTXoF#gL zan_3W1c_LvEzIyTq&Ie}XGJ{6q!C8mRCM+Bge$tk2P@j}=A;6q>0eH-1u7EZcp_er z=xy%>Gujd9tO!RBMq<6u9=rhY##&huUO>dXp}xNENLz^BYp8}-!`JV-(_xS_3 z-Z#<{4;=a#F~7Ho@%}lRmTj!masb z+Y8+HxDK=1=iPS0y5&nroOJKPe**gAd3I%f;Ul){JZBv;Q2hG<-vWOLAk+N2Qv4B+ ze*k`2I=?x^e;M>|gD;*>YyGAa|A*jz4Se#~wBMWJ{~F|f1RswFQv87we*xlPLme(i z=iiayZvy{A=s%OrxBaOQAf3Cw{}lAsr1QH|`uBnVS@3U7=OV&y%hgt z#sAlIzU^Hz{~v-sfHveo8!*dnpKl`p^6%HgM;lt4u779B{sQPd0RFsm{_Yfi6ZrkV zfqxhHG4M0iKlp9nzmsm?_O|K&2>2_}wnfL)^=pet2S{h;n2Eql1ZE;I6M>ls%tT-& z0y7c#e=Y)goswRcgej*uKBp2Ir?pXZ(2GxTh`SBk>nL^7|Dd-S$ zRY1KiO3dAXYyQtZoa`mJQFZ^L*ASgji-z>tp)tkB3l(wvgYr+W{So&h;H0mmAwi|a z#??9~Oku_0Qwe>)g-5UA&}(bX;cWpOn(k7#_Ip%t8B=6)?F}C2it>OhMDemi;OCSg zp0W#E#~HUP0zaboctkF6?YGM2e;w%c=Wd+|* z@OujWSizqw=oB|8SOcZtbqe03;8q3qDA=LkK?NUC@Nos7R8W`aoKL2@vL3FcKzy&t2eL=tXtNsW*>pFY(TQw{*2y};&?zeSBaWuDgPE4ia8>kL>9N7fqvb6vTM0G!{OLod}G+;s_U z$zjX78xA|T^Cto`=()r^he0b^9WKLzES!ggxbuAolu+7vm^<>&`{K9SzUe*p<_mU|ez+SMuiDIxkoIxVWLVXiJgEX{(} z29n)F>!0}$#g@N~lY=LrTgs^$F6VoY&f@bS%=s6Pw)0hxwDC8QcP^);n=ZEa3K)g1 zY#(n2p4AI1yWUM1vaGium%X>>Eu>wnl*&k%+WY9nnm>UgzRhA*~vLG%oR z@5yl$#z4)A!BkFm!Jj~mW!()cIXT%KNV~E=0{iKuUrphW~ zxnwRU-@XjiT(qd)eSqhZg~vg#J&Z&Fa}5FHuCirOG@m892( zY9q#{p`3q4xc(-c{z1AHNpT%9y+m={f}+m)3@@OfY$q~R8k@bHa=H9_ zCSmC}Ay+z=J7?XGOiDj5{99HBqS5E9R+rItNEHVkp}Mv20r%sg57~|fa-vG;gp^_S zW$->xKyAT({PckRMMx(LgvZAl?E&O`P%v%B_u9)LdXK_<_Fn?)SCad4{y^DXpzVGa zc^@n%U$X7vK1Ibi@GH=bZs%GhuSa%1l6MtCVbMm)6jRSX_(&(T+|*CP3&>U5@h+D4 zCeV+G2z;n6{3-d*X!(!L(ef*vqnz(>zO958E1&qt>m-~7Yj@m8Niy!Fj+yr;inu19 zlCP?%i3e#q_(%yxHQ~{k8)Wdt95fZsrF*^cjLPSP#PLxzr0VQ0Itv&OWu6qkLe zSIQSky}hgG^9sjoo=d%(TGyO!AsqHGy2gE;hFBcWNB~coVId#ikw8H8wJvu3M)+=j zL0lCdR2sTz@R7$%jTcmF-&+_9Cz$;e(V#?*^yDf6Sx!KgXna?&g z-bP$KLgNQYLk$B63VpvJ^YCyn3n90P?}J-zq4A>9SYmqhw5j3dIx07uhf_SCi%A4o zxbfSN=f4mp^yDlxB8ghyzcBL?-a$W~{n%8kTud`F-ayggd zaxQ1k+|AxOhZMP-dAP;Q!{I8-gQkr})a-(Lz-Bfv@yOnbq{~M04!20>+GtSA&HWtc zg*KXXq^N51_vU``q-9dq)@Xa)X`Qy?z&88eTq=+bz5MpMV7rOCa&B$O zrnwwC&I0CM;v(&=Jkl2I+LDd1>M}Kqbd79-* z)B*i1OS0Tc9JM9F@DfgunB`^G0AaFX#=w2c66eafGH7#@3!q)&LRk@CkzeArFP^`c z3Rj2$VlHBgc+OLmfq`j0=d~r2>4GKjsRGP}8-#O<5HPc}m|K^?SGW5JcgqNnibv8? zge9hrqMoFSOU=?KB4f);uTlT$fnRRQBFLJ-*b39GXfc{WK3pfOoRTZkl~Z;xCsq|_ zwv+4A^Vd}`tL6sLa?{$>jnm7bJ@8bMwuu(I#39=THCemanjNxB7*iXdb83-vC&-c= zho>5u>)Kk(PE}uyY)NiwP2^6E#iqHbCa)T>4kE5i)^^RdV7M`G8~7xZlJgJp^9azu zfpTVNt~SY)v7_C0y`hTi9bx}Q9|hkK-?cXo}?5BI}FaUZTb>p{{`np;h@2j z_F}J5^w6L&_d!C62r*V94Bk&^k~nYA_Zqn`86IYH@E38iIk)3%Pwd6n;l;_h*Wk|0 z4x9bE4|t9IQ6mpD{-RN^!?n!F{h7gcIPCUp82*&8V7uYoWaK76voG-)?w4@o7P2k^ zn~cI8M&3oEFlpo+w4U=1YP%4cf@I%A%1+@LOgJ|i{7#3%KF@0uzhvZIF}SP3?nY); zjKT^dU*s1zauau&d7Gv z8Nx{y3bMl}kOFyIjNDB|-VUR5pOJl!QMk`A?x9%mea0HsovsIsS>vul!aLU{+{#ky z9yAtgHs(q7@-4<(O1YjfR=Qp?%J!lMh;Zc98Aifbu+LcJ8kBh<+b#9RN+nI%+hE>P zXDq#96uocE-EJ&AXA~tVvDj6N`%=`9J(-ZTwq4rYWR%(Sj~NSGNy9A+6&thagj=Ys z(j7+bUl`nWU5T;5^)iLdK#LeO7FAN3zho?1Crb8`v1lE_W$-^S%CYgB ztd7Y19Y*;TW4`ipF-oOEwRne-eZ??b--o7c4!X$4(UhqNZ3UdR2id6yQLmw1BzlaK zCOb@=sAF*I7&g%-sQufFT(od~p`P`iVf@QJWA=}YGS_jmfuwCVvQeKHH|?(47u=PM zwud|DJ7xXJviQn6!IHo+5f6%wp{IU+EfiaVO_eRR4Ld9ANICUIbyqlqPs4*)!bD#_ zGbLN!u*Y8`Kbe*vLMIOOiEq58eyOdbcQ)+wvvfm;dRlwM*VqhSns<7{DH)ANI-}us z4?TZtstRtc-Me#leXvnxxhsnAs+GCwhWf@j|6Z}?D(y3DOiudnA$K?}r!@0q6$R6u ztSeK^ElssMw^4Sr_4WRm+R7F`a%}BwKZFp(ARUXt847GmaA$3uHZ5`$V(K$<`Ng?f z0~W;UEAxA9ozcGfss<5S{Z+YOG&lOIYb)!5&4JpjE%;VEen6B8j*5Wd8LX#z&COFD z!{S-1r@SYTjD|ONhNIzFqzz~&*4DLI?yun~7hlNh59&o(dM8fwRL`xyZ92FzSi%J30!=RPpi*ZysI~s@Jy{K8Jzmc<{hf0Izrv31Qa3dBYl-c zWm8k-K4pa}MC1|diFfK4@7`JC-zsA|brS$(pr*2=l1!^0qLE{3kucStj%0Pi&gK@~ zYFbn)QW;SLHdouxrUE)!G=tsMEgEa`Z}abMoKn~6ZNI7()l$zout9-nsLd_tIMu=C zebiWK9U#{C9SG{y7>tJRA&=8HNKl2jv!TAK79poy2vQpl2qui7xyV{N&>f1$S+J_N zx0{)vZ|e%BmxW3u?8N)RZ4vBrfW?7WC@4#$dXlJe1TH9hBA8Xds@=79Ewwv?E&Ce% zsnF;^)K|7t2jG09r>|R$C(^L&SM9w?+9E=gW@kfBX{ z^@J1fS(wGP78RXsZ50@@f;X)%?`u{4VP$MA{9B63a$vMh z?>62pmb$6|iHqwMB@*qpy2=DJwH#oql_~@YGr&SR(n~y92S`zk5P?QUKwM%%hlor= zEg;MzFh~fm`tYzROk}E0)K|uX<_`|`?~_)6To7z5-+Vu&AHmQ~Ezzw{cUCF7^*Iqe zlOlQR6Q^oLw?4btqv+O0VqJ=Eebly;<~gu$eQ4LO{y}SziYa< z%fj1w>w9J&p4@owXlT@DN{U3LeVrB|p2di#_4)mMu%jO6O)*p>==tV;b=N8G4Y1>x zA}Ntulzr>NV4A)b+X|Ro2ccE#mAv)kM4O;%sT4ezf&R%1^v9Ha>of2#WRRzI9Mi?& zE1-K)ZkVa>W{@8Py@+L4t=gWoj>2mK>xm==U?--=%=6; zqF-D6p%ZO&RF)_Lcgn^}QVJA4v6UE!RK> zJAaUY{&$JKM;-?XtCbo9J9 zSlfVUBBq7GWIWtX8BcvaK$Gk#u3^kVzRwoHOWj@xR88?k)m?M5 z77Vt=X|Fl*7j~vT(Xj6IW_q?>-G9j^0qIXrur*yOsOFw^(@oCFpR~dU!xTC4A^qm@ znw{iK7qBzjwIvca7IMQmYC(*7F`LD{e1M6Vgk3r%r=83j#rAk!OvSy}D6cOZOB_lg zwI(Cj&@>p9E!q?$2wp7Q9U=vl>g!H0 zuW-|w2=^l`x{Np0D+1vScLh6Qp`LKCs~x%;lN$8ACIm-GL#JUl)Pq|NZ)Y!T;p7z= zNMopE=56ckp%3TaY`8Vq3B6FX6YsbbDt0r}tk%|8_@L&9rw)pP=%tgX6;ByGe`kc& zjbbW6(*|*rVSJ`TL;b2+Ql+d;6I_NrUW%Fe8KJd~HJiLf**IM*Pl)i=@EvTSX{|?Z9j6HZ1jSVAo9lJ)4xf%uamJe!1+C@(4Ctg^rTBXNuZBKlnDp_I@tXL3 z0W^wuJ)n zNTlbl?`t&7(}D_G{r@Yauj9`=N>IV46sm!azs6reg7Vk;PkAIq!{;Sxo~`+x2Pa*B z{}L&x;mg{vvS7{s+o0pUz32lZqSv$QeW0!K8rP5(A{M0S>-FyE%E?-)y+|yDYyC@L zP}@=6*1Rv)`+8rd!r{=>sP%L`z7HOWXnnn}=qN34#IZ^O^Q`qX{3ST)`ucu-TR8Z%u$+S#^@>k-T%(Om@ z@9780(xKsOr7x+evvZA1?@y6LO6zGd?YC^D5wAn(KSqOd+F_MllR^JiYT*2P9T;W9 zs(*V1{qsJl@Yny1{tl&Yjo;xKss9mkV3WAcU&B2a^q<)-SgZB4MKP)KwF9WM+ A=>Px# literal 0 HcmV?d00001 diff --git a/rwf-ruby/tests/todo/.dockerignore b/rwf-ruby/tests/todo/.dockerignore new file mode 100644 index 00000000..cd7190b4 --- /dev/null +++ b/rwf-ruby/tests/todo/.dockerignore @@ -0,0 +1,48 @@ +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ +/.gitignore + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/.keep + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore CI service files. +/.github + +# Ignore development files +/.devcontainer + +# Ignore Docker-related files +/.dockerignore +/Dockerfile* diff --git a/rwf-ruby/tests/todo/.gitattributes b/rwf-ruby/tests/todo/.gitattributes new file mode 100644 index 00000000..8dc43234 --- /dev/null +++ b/rwf-ruby/tests/todo/.gitattributes @@ -0,0 +1,9 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored +config/credentials/*.yml.enc diff=rails_credentials +config/credentials.yml.enc diff=rails_credentials diff --git a/rwf-ruby/tests/todo/.github/dependabot.yml b/rwf-ruby/tests/todo/.github/dependabot.yml new file mode 100644 index 00000000..f0527e6b --- /dev/null +++ b/rwf-ruby/tests/todo/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/rwf-ruby/tests/todo/.github/workflows/ci.yml b/rwf-ruby/tests/todo/.github/workflows/ci.yml new file mode 100644 index 00000000..00af91f6 --- /dev/null +++ b/rwf-ruby/tests/todo/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +on: + pull_request: + push: + branches: [ main ] + +jobs: + scan_ruby: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for common Rails security vulnerabilities using static analysis + run: bin/brakeman --no-pager + + scan_js: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for security vulnerabilities in JavaScript dependencies + run: bin/importmap audit + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Lint code for consistent style + run: bin/rubocop -f github + + test: + runs-on: ubuntu-latest + + # services: + # redis: + # image: redis + # ports: + # - 6379:6379 + # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Install packages + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips sqlite3 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Run tests + env: + RAILS_ENV: test + # REDIS_URL: redis://localhost:6379/0 + run: bin/rails db:test:prepare test test:system + + - name: Keep screenshots from failed system tests + uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshots + path: ${{ github.workspace }}/tmp/screenshots + if-no-files-found: ignore diff --git a/rwf-ruby/tests/todo/.gitignore b/rwf-ruby/tests/todo/.gitignore new file mode 100644 index 00000000..4aaf1022 --- /dev/null +++ b/rwf-ruby/tests/todo/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# Temporary files generated by your text editor or operating system +# belong in git's global ignore instead: +# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore` + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key diff --git a/rwf-ruby/tests/todo/.rubocop.yml b/rwf-ruby/tests/todo/.rubocop.yml new file mode 100644 index 00000000..f9d86d4a --- /dev/null +++ b/rwf-ruby/tests/todo/.rubocop.yml @@ -0,0 +1,8 @@ +# Omakase Ruby styling for Rails +inherit_gem: { rubocop-rails-omakase: rubocop.yml } + +# Overwrite or add rules to create your own house style +# +# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` +# Layout/SpaceInsideArrayLiteralBrackets: +# Enabled: false diff --git a/rwf-ruby/tests/todo/.ruby-version b/rwf-ruby/tests/todo/.ruby-version new file mode 100644 index 00000000..f13c6f45 --- /dev/null +++ b/rwf-ruby/tests/todo/.ruby-version @@ -0,0 +1 @@ +ruby-3.3.5 diff --git a/rwf-ruby/tests/todo/Dockerfile b/rwf-ruby/tests/todo/Dockerfile new file mode 100644 index 00000000..29b442ef --- /dev/null +++ b/rwf-ruby/tests/todo/Dockerfile @@ -0,0 +1,69 @@ +# syntax = docker/dockerfile:1 + +# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: +# docker build -t my-app . +# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY= my-app + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.3.5 +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# Rails app lives here +WORKDIR /rails + +# Install base packages +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + +# Throw-away build stage to reduce size of final image +FROM base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential git pkg-config && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install application gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ + bundle exec bootsnap precompile --gemfile + +# Copy application code +COPY . . + +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + + + + +# Final stage for app image +FROM base + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/rwf-ruby/tests/todo/Gemfile b/rwf-ruby/tests/todo/Gemfile new file mode 100644 index 00000000..a7f333af --- /dev/null +++ b/rwf-ruby/tests/todo/Gemfile @@ -0,0 +1,57 @@ +source "https://rubygems.org" + +# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" +gem "rails", "~> 7.2.2" +# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] +gem "sprockets-rails" +# Use sqlite3 as the database for Active Record +gem "sqlite3", ">= 1.4" +# Use the Puma web server [https://github.com/puma/puma] +gem "puma", ">= 5.0" +# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] +gem "importmap-rails" +# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] +gem "turbo-rails" +# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] +gem "stimulus-rails" +# Build JSON APIs with ease [https://github.com/rails/jbuilder] +gem "jbuilder" +# Use Redis adapter to run Action Cable in production +# gem "redis", ">= 4.0.1" + +# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] +# gem "kredis" + +# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] +# gem "bcrypt", "~> 3.1.7" + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: %i[ windows jruby ] + +# Reduces boot times through caching; required in config/boot.rb +gem "bootsnap", require: false + +# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] +# gem "image_processing", "~> 1.2" + +group :development, :test do + # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" + + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] + gem "brakeman", require: false + + # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] + gem "rubocop-rails-omakase", require: false +end + +group :development do + # Use console on exceptions pages [https://github.com/rails/web-console] + gem "web-console" +end + +group :test do + # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] + gem "capybara" + gem "selenium-webdriver" +end diff --git a/rwf-ruby/tests/todo/Gemfile.lock b/rwf-ruby/tests/todo/Gemfile.lock new file mode 100644 index 00000000..321ee6ec --- /dev/null +++ b/rwf-ruby/tests/todo/Gemfile.lock @@ -0,0 +1,326 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + actionmailer (7.2.2) + actionpack (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2) + actionview (= 7.2.2) + activesupport (= 7.2.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2) + actionpack (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2) + activesupport (= 7.2.2) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.3.6) + activemodel (7.2.2) + activesupport (= 7.2.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) + timeout (>= 0.4.0) + activestorage (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activesupport (= 7.2.2) + marcel (~> 1.0) + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + ast (2.4.2) + base64 (0.2.0) + benchmark (0.3.0) + bigdecimal (3.1.8) + bindex (0.8.1) + bootsnap (1.18.4) + msgpack (~> 1.2) + brakeman (6.2.2) + racc + builder (3.3.0) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + crass (1.0.6) + date (3.4.0) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) + drb (2.2.1) + erubi (1.13.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + importmap-rails (2.0.3) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) + io-console (0.7.2) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + json (2.8.0) + language_server-protocol (3.17.0.3) + logger (1.6.1) + loofah (2.23.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + mini_mime (1.1.5) + minitest (5.25.1) + msgpack (1.7.3) + net-imap (0.5.0) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.16.7-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86-linux) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) + parallel (1.26.3) + parser (3.3.6.0) + ast (~> 2.4.1) + racc + psych (5.1.2) + stringio + public_suffix (6.0.1) + puma (6.4.3) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.1.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.2.0) + rack (>= 3) + rails (7.2.2) + actioncable (= 7.2.2) + actionmailbox (= 7.2.2) + actionmailer (= 7.2.2) + actionpack (= 7.2.2) + actiontext (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activemodel (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + bundler (>= 1.15.0) + railties (= 7.2.2) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.2.1) + rdoc (6.7.0) + psych (>= 4.0.0) + regexp_parser (2.9.2) + reline (0.5.10) + io-console (~> 0.5) + rexml (3.3.9) + rubocop (1.68.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.34.0) + parser (>= 3.3.1.0) + rubocop-minitest (0.36.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.22.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.27.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails-omakase (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + ruby-progressbar (1.13.0) + rubyzip (2.3.2) + securerandom (0.3.1) + selenium-webdriver (4.26.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) + sqlite3 (2.2.0-aarch64-linux-gnu) + sqlite3 (2.2.0-aarch64-linux-musl) + sqlite3 (2.2.0-arm-linux-gnu) + sqlite3 (2.2.0-arm-linux-musl) + sqlite3 (2.2.0-arm64-darwin) + sqlite3 (2.2.0-x86-linux-gnu) + sqlite3 (2.2.0-x86-linux-musl) + sqlite3 (2.2.0-x86_64-darwin) + sqlite3 (2.2.0-x86_64-linux-gnu) + sqlite3 (2.2.0-x86_64-linux-musl) + stimulus-rails (1.3.4) + railties (>= 6.0.0) + stringio (3.1.1) + thor (1.3.2) + timeout (0.4.2) + turbo-rails (2.0.11) + actionpack (>= 6.0.0) + railties (>= 6.0.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.6.0) + useragent (0.16.10) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + websocket (1.2.11) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.7.1) + +PLATFORMS + aarch64-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86-linux + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + bootsnap + brakeman + capybara + debug + importmap-rails + jbuilder + puma (>= 5.0) + rails (~> 7.2.2) + rubocop-rails-omakase + selenium-webdriver + sprockets-rails + sqlite3 (>= 1.4) + stimulus-rails + turbo-rails + tzinfo-data + web-console + +BUNDLED WITH + 2.5.16 diff --git a/rwf-ruby/tests/todo/README.md b/rwf-ruby/tests/todo/README.md new file mode 100644 index 00000000..7db80e4c --- /dev/null +++ b/rwf-ruby/tests/todo/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/rwf-ruby/tests/todo/Rakefile b/rwf-ruby/tests/todo/Rakefile new file mode 100644 index 00000000..9a5ea738 --- /dev/null +++ b/rwf-ruby/tests/todo/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative "config/application" + +Rails.application.load_tasks diff --git a/rwf-ruby/tests/todo/app/assets/config/manifest.js b/rwf-ruby/tests/todo/app/assets/config/manifest.js new file mode 100644 index 00000000..ddd546a0 --- /dev/null +++ b/rwf-ruby/tests/todo/app/assets/config/manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/rwf-ruby/tests/todo/app/assets/images/.keep b/rwf-ruby/tests/todo/app/assets/images/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/app/assets/stylesheets/application.css b/rwf-ruby/tests/todo/app/assets/stylesheets/application.css new file mode 100644 index 00000000..288b9ab7 --- /dev/null +++ b/rwf-ruby/tests/todo/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/rwf-ruby/tests/todo/app/channels/application_cable/channel.rb b/rwf-ruby/tests/todo/app/channels/application_cable/channel.rb new file mode 100644 index 00000000..d6726972 --- /dev/null +++ b/rwf-ruby/tests/todo/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/rwf-ruby/tests/todo/app/channels/application_cable/connection.rb b/rwf-ruby/tests/todo/app/channels/application_cable/connection.rb new file mode 100644 index 00000000..0ff5442f --- /dev/null +++ b/rwf-ruby/tests/todo/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/rwf-ruby/tests/todo/app/controllers/application_controller.rb b/rwf-ruby/tests/todo/app/controllers/application_controller.rb new file mode 100644 index 00000000..0d95db22 --- /dev/null +++ b/rwf-ruby/tests/todo/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern +end diff --git a/rwf-ruby/tests/todo/app/controllers/concerns/.keep b/rwf-ruby/tests/todo/app/controllers/concerns/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/app/helpers/application_helper.rb b/rwf-ruby/tests/todo/app/helpers/application_helper.rb new file mode 100644 index 00000000..de6be794 --- /dev/null +++ b/rwf-ruby/tests/todo/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/rwf-ruby/tests/todo/app/javascript/application.js b/rwf-ruby/tests/todo/app/javascript/application.js new file mode 100644 index 00000000..0d7b4940 --- /dev/null +++ b/rwf-ruby/tests/todo/app/javascript/application.js @@ -0,0 +1,3 @@ +// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails +import "@hotwired/turbo-rails" +import "controllers" diff --git a/rwf-ruby/tests/todo/app/javascript/controllers/application.js b/rwf-ruby/tests/todo/app/javascript/controllers/application.js new file mode 100644 index 00000000..1213e85c --- /dev/null +++ b/rwf-ruby/tests/todo/app/javascript/controllers/application.js @@ -0,0 +1,9 @@ +import { Application } from "@hotwired/stimulus" + +const application = Application.start() + +// Configure Stimulus development experience +application.debug = false +window.Stimulus = application + +export { application } diff --git a/rwf-ruby/tests/todo/app/javascript/controllers/hello_controller.js b/rwf-ruby/tests/todo/app/javascript/controllers/hello_controller.js new file mode 100644 index 00000000..5975c078 --- /dev/null +++ b/rwf-ruby/tests/todo/app/javascript/controllers/hello_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.textContent = "Hello World!" + } +} diff --git a/rwf-ruby/tests/todo/app/javascript/controllers/index.js b/rwf-ruby/tests/todo/app/javascript/controllers/index.js new file mode 100644 index 00000000..1156bf83 --- /dev/null +++ b/rwf-ruby/tests/todo/app/javascript/controllers/index.js @@ -0,0 +1,4 @@ +// Import and register all your controllers from the importmap via controllers/**/*_controller +import { application } from "controllers/application" +import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" +eagerLoadControllersFrom("controllers", application) diff --git a/rwf-ruby/tests/todo/app/jobs/application_job.rb b/rwf-ruby/tests/todo/app/jobs/application_job.rb new file mode 100644 index 00000000..d394c3d1 --- /dev/null +++ b/rwf-ruby/tests/todo/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/rwf-ruby/tests/todo/app/mailers/application_mailer.rb b/rwf-ruby/tests/todo/app/mailers/application_mailer.rb new file mode 100644 index 00000000..3c34c814 --- /dev/null +++ b/rwf-ruby/tests/todo/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout "mailer" +end diff --git a/rwf-ruby/tests/todo/app/models/application_record.rb b/rwf-ruby/tests/todo/app/models/application_record.rb new file mode 100644 index 00000000..b63caeb8 --- /dev/null +++ b/rwf-ruby/tests/todo/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class +end diff --git a/rwf-ruby/tests/todo/app/models/concerns/.keep b/rwf-ruby/tests/todo/app/models/concerns/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/app/views/layouts/application.html.erb b/rwf-ruby/tests/todo/app/views/layouts/application.html.erb new file mode 100644 index 00000000..cda86778 --- /dev/null +++ b/rwf-ruby/tests/todo/app/views/layouts/application.html.erb @@ -0,0 +1,23 @@ + + + + <%= content_for(:title) || "Todo" %> + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= yield :head %> + + + + + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= javascript_importmap_tags %> + + + + <%= yield %> + + diff --git a/rwf-ruby/tests/todo/app/views/layouts/mailer.html.erb b/rwf-ruby/tests/todo/app/views/layouts/mailer.html.erb new file mode 100644 index 00000000..3aac9002 --- /dev/null +++ b/rwf-ruby/tests/todo/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/rwf-ruby/tests/todo/app/views/layouts/mailer.text.erb b/rwf-ruby/tests/todo/app/views/layouts/mailer.text.erb new file mode 100644 index 00000000..37f0bddb --- /dev/null +++ b/rwf-ruby/tests/todo/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/rwf-ruby/tests/todo/app/views/pwa/manifest.json.erb b/rwf-ruby/tests/todo/app/views/pwa/manifest.json.erb new file mode 100644 index 00000000..e1dd43cf --- /dev/null +++ b/rwf-ruby/tests/todo/app/views/pwa/manifest.json.erb @@ -0,0 +1,22 @@ +{ + "name": "Todo", + "icons": [ + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ], + "start_url": "/", + "display": "standalone", + "scope": "/", + "description": "Todo.", + "theme_color": "red", + "background_color": "red" +} diff --git a/rwf-ruby/tests/todo/app/views/pwa/service-worker.js b/rwf-ruby/tests/todo/app/views/pwa/service-worker.js new file mode 100644 index 00000000..b3a13fb7 --- /dev/null +++ b/rwf-ruby/tests/todo/app/views/pwa/service-worker.js @@ -0,0 +1,26 @@ +// Add a service worker for processing Web Push notifications: +// +// self.addEventListener("push", async (event) => { +// const { title, options } = await event.data.json() +// event.waitUntil(self.registration.showNotification(title, options)) +// }) +// +// self.addEventListener("notificationclick", function(event) { +// event.notification.close() +// event.waitUntil( +// clients.matchAll({ type: "window" }).then((clientList) => { +// for (let i = 0; i < clientList.length; i++) { +// let client = clientList[i] +// let clientPath = (new URL(client.url)).pathname +// +// if (clientPath == event.notification.data.path && "focus" in client) { +// return client.focus() +// } +// } +// +// if (clients.openWindow) { +// return clients.openWindow(event.notification.data.path) +// } +// }) +// ) +// }) diff --git a/rwf-ruby/tests/todo/bin/brakeman b/rwf-ruby/tests/todo/bin/brakeman new file mode 100755 index 00000000..ace1c9ba --- /dev/null +++ b/rwf-ruby/tests/todo/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/rwf-ruby/tests/todo/bin/bundle b/rwf-ruby/tests/todo/bin/bundle new file mode 100755 index 00000000..50da5fdf --- /dev/null +++ b/rwf-ruby/tests/todo/bin/bundle @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/rwf-ruby/tests/todo/bin/docker-entrypoint b/rwf-ruby/tests/todo/bin/docker-entrypoint new file mode 100755 index 00000000..840d093a --- /dev/null +++ b/rwf-ruby/tests/todo/bin/docker-entrypoint @@ -0,0 +1,13 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then + export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)" +fi + +# If running the rails server then create or migrate existing database +if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" diff --git a/rwf-ruby/tests/todo/bin/importmap b/rwf-ruby/tests/todo/bin/importmap new file mode 100755 index 00000000..36502ab1 --- /dev/null +++ b/rwf-ruby/tests/todo/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/rwf-ruby/tests/todo/bin/rails b/rwf-ruby/tests/todo/bin/rails new file mode 100755 index 00000000..efc03774 --- /dev/null +++ b/rwf-ruby/tests/todo/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/rwf-ruby/tests/todo/bin/rake b/rwf-ruby/tests/todo/bin/rake new file mode 100755 index 00000000..4fbf10b9 --- /dev/null +++ b/rwf-ruby/tests/todo/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "rake" +Rake.application.run diff --git a/rwf-ruby/tests/todo/bin/rubocop b/rwf-ruby/tests/todo/bin/rubocop new file mode 100755 index 00000000..40330c0f --- /dev/null +++ b/rwf-ruby/tests/todo/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/rwf-ruby/tests/todo/bin/setup b/rwf-ruby/tests/todo/bin/setup new file mode 100755 index 00000000..91e9a3be --- /dev/null +++ b/rwf-ruby/tests/todo/bin/setup @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +require "fileutils" + +APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "todo" + +def system!(*args) + system(*args, exception: true) +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" +end diff --git a/rwf-ruby/tests/todo/config.ru b/rwf-ruby/tests/todo/config.ru new file mode 100644 index 00000000..4a3c09a6 --- /dev/null +++ b/rwf-ruby/tests/todo/config.ru @@ -0,0 +1,6 @@ +# This file is used by Rack-based servers to start the application. + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/rwf-ruby/tests/todo/config/application.rb b/rwf-ruby/tests/todo/config/application.rb new file mode 100644 index 00000000..cfb46ea7 --- /dev/null +++ b/rwf-ruby/tests/todo/config/application.rb @@ -0,0 +1,27 @@ +require_relative "boot" + +require "rails/all" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module Todo + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.2 + + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + end +end diff --git a/rwf-ruby/tests/todo/config/boot.rb b/rwf-ruby/tests/todo/config/boot.rb new file mode 100644 index 00000000..988a5ddc --- /dev/null +++ b/rwf-ruby/tests/todo/config/boot.rb @@ -0,0 +1,4 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/rwf-ruby/tests/todo/config/cable.yml b/rwf-ruby/tests/todo/config/cable.yml new file mode 100644 index 00000000..017dcbb2 --- /dev/null +++ b/rwf-ruby/tests/todo/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: todo_production diff --git a/rwf-ruby/tests/todo/config/credentials.yml.enc b/rwf-ruby/tests/todo/config/credentials.yml.enc new file mode 100644 index 00000000..7fa7d571 --- /dev/null +++ b/rwf-ruby/tests/todo/config/credentials.yml.enc @@ -0,0 +1 @@ +L5MABPObAssYsDkM1D6DAUFb7VuLwuaGvPBY0CSZ54DIMHt9yfR9S0bwJa8FW33FnH58/fAjswSH8PTI74CtKLjE7LU1f+ayyicMNBiQiBf8FJ+Sb91DjX8pICEGn15+RizGNGhkjXIIcNW8+00Ek9xgT2qSziKmHjGdquLZHyARYAUfxuECWQHCVrfPP4LFAJC5vESyo65kkUzbHnClYsxtIwmIITh4RdbzI3BU4DD/vTPF3WTytoYKe9ugPSUcNakKAkN010gF4Xlh4OzKOoHoNl3x1b6Y4BUuiyCpEfFGcJCLUFPiZ979ZeejKPoeBjdDrWBHZtF0FnVD6fjM/fgIKlIVct3BO4U9B8tAFsgp7zfDx5UMpgAFnyyma9sobnoUcCEczp94nm4XOTRg8JgtouSv--gOvoDY6XCCoo/yS8--0Gl2UGvbB+nFeO3v020nww== \ No newline at end of file diff --git a/rwf-ruby/tests/todo/config/database.yml b/rwf-ruby/tests/todo/config/database.yml new file mode 100644 index 00000000..01bebb50 --- /dev/null +++ b/rwf-ruby/tests/todo/config/database.yml @@ -0,0 +1,32 @@ +# SQLite. Versions 3.8.0 and up are supported. +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem "sqlite3" +# +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: storage/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: storage/test.sqlite3 + + +# SQLite3 write its data on the local filesystem, as such it requires +# persistent disks. If you are deploying to a managed service, you should +# make sure it provides disk persistence, as many don't. +# +# Similarly, if you deploy your application as a Docker container, you must +# ensure the database is located in a persisted volume. +production: + <<: *default + # database: path/to/persistent/storage/production.sqlite3 diff --git a/rwf-ruby/tests/todo/config/environment.rb b/rwf-ruby/tests/todo/config/environment.rb new file mode 100644 index 00000000..cac53157 --- /dev/null +++ b/rwf-ruby/tests/todo/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/rwf-ruby/tests/todo/config/environments/development.rb b/rwf-ruby/tests/todo/config/environments/development.rb new file mode 100644 index 00000000..9b673600 --- /dev/null +++ b/rwf-ruby/tests/todo/config/environments/development.rb @@ -0,0 +1,81 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing. + config.server_timing = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Highlight code that enqueued background job in logs. + config.active_job.verbose_enqueue_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true + + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + # config.generators.apply_rubocop_autocorrect_after_generate! +end diff --git a/rwf-ruby/tests/todo/config/environments/production.rb b/rwf-ruby/tests/todo/config/environments/production.rb new file mode 100644 index 00000000..9b6a5326 --- /dev/null +++ b/rwf-ruby/tests/todo/config/environments/production.rb @@ -0,0 +1,105 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment + # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # config.public_file_server.enabled = false + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fall back to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Mount Action Cable outside main process or domain. + # config.action_cable.mount_path = nil + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. + # config.assume_ssl = true + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT by default + config.logger = ActiveSupport::Logger.new(STDOUT) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # "info" includes generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, set the level to "debug". + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "todo_production" + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + # Only use :id for inspections in production. + config.active_record.attributes_for_inspect = [ :id ] + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/rwf-ruby/tests/todo/config/environments/test.rb b/rwf-ruby/tests/todo/config/environments/test.rb new file mode 100644 index 00000000..0c616a1b --- /dev/null +++ b/rwf-ruby/tests/todo/config/environments/test.rb @@ -0,0 +1,67 @@ +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false + + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "www.example.com" } + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/rwf-ruby/tests/todo/config/importmap.rb b/rwf-ruby/tests/todo/config/importmap.rb new file mode 100644 index 00000000..909dfc54 --- /dev/null +++ b/rwf-ruby/tests/todo/config/importmap.rb @@ -0,0 +1,7 @@ +# Pin npm packages by running ./bin/importmap + +pin "application" +pin "@hotwired/turbo-rails", to: "turbo.min.js" +pin "@hotwired/stimulus", to: "stimulus.min.js" +pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" +pin_all_from "app/javascript/controllers", under: "controllers" diff --git a/rwf-ruby/tests/todo/config/initializers/assets.rb b/rwf-ruby/tests/todo/config/initializers/assets.rb new file mode 100644 index 00000000..bd5bcd2b --- /dev/null +++ b/rwf-ruby/tests/todo/config/initializers/assets.rb @@ -0,0 +1,12 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = "1.0" + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w[ admin.js admin.css ] diff --git a/rwf-ruby/tests/todo/config/initializers/content_security_policy.rb b/rwf-ruby/tests/todo/config/initializers/content_security_policy.rb new file mode 100644 index 00000000..b3076b38 --- /dev/null +++ b/rwf-ruby/tests/todo/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/rwf-ruby/tests/todo/config/initializers/filter_parameter_logging.rb b/rwf-ruby/tests/todo/config/initializers/filter_parameter_logging.rb new file mode 100644 index 00000000..c010b83d --- /dev/null +++ b/rwf-ruby/tests/todo/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/rwf-ruby/tests/todo/config/initializers/inflections.rb b/rwf-ruby/tests/todo/config/initializers/inflections.rb new file mode 100644 index 00000000..3860f659 --- /dev/null +++ b/rwf-ruby/tests/todo/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym "RESTful" +# end diff --git a/rwf-ruby/tests/todo/config/initializers/permissions_policy.rb b/rwf-ruby/tests/todo/config/initializers/permissions_policy.rb new file mode 100644 index 00000000..7db3b957 --- /dev/null +++ b/rwf-ruby/tests/todo/config/initializers/permissions_policy.rb @@ -0,0 +1,13 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide HTTP permissions policy. For further +# information see: https://developers.google.com/web/updates/2018/06/feature-policy + +# Rails.application.config.permissions_policy do |policy| +# policy.camera :none +# policy.gyroscope :none +# policy.microphone :none +# policy.usb :none +# policy.fullscreen :self +# policy.payment :self, "https://secure.example.com" +# end diff --git a/rwf-ruby/tests/todo/config/locales/en.yml b/rwf-ruby/tests/todo/config/locales/en.yml new file mode 100644 index 00000000..6c349ae5 --- /dev/null +++ b/rwf-ruby/tests/todo/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/rwf-ruby/tests/todo/config/puma.rb b/rwf-ruby/tests/todo/config/puma.rb new file mode 100644 index 00000000..03c166f4 --- /dev/null +++ b/rwf-ruby/tests/todo/config/puma.rb @@ -0,0 +1,34 @@ +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. + +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3000) + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart + +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/rwf-ruby/tests/todo/config/routes.rb b/rwf-ruby/tests/todo/config/routes.rb new file mode 100644 index 00000000..33c96390 --- /dev/null +++ b/rwf-ruby/tests/todo/config/routes.rb @@ -0,0 +1,14 @@ +Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* + get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + + # Defines the root path route ("/") + # root "posts#index" +end diff --git a/rwf-ruby/tests/todo/config/storage.yml b/rwf-ruby/tests/todo/config/storage.yml new file mode 100644 index 00000000..4942ab66 --- /dev/null +++ b/rwf-ruby/tests/todo/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/rwf-ruby/tests/todo/db/seeds.rb b/rwf-ruby/tests/todo/db/seeds.rb new file mode 100644 index 00000000..4fbd6ed9 --- /dev/null +++ b/rwf-ruby/tests/todo/db/seeds.rb @@ -0,0 +1,9 @@ +# This file should ensure the existence of records required to run the application in every environment (production, +# development, test). The code here should be idempotent so that it can be executed at any point in every environment. +# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). +# +# Example: +# +# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| +# MovieGenre.find_or_create_by!(name: genre_name) +# end diff --git a/rwf-ruby/tests/todo/lib/assets/.keep b/rwf-ruby/tests/todo/lib/assets/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/lib/tasks/.keep b/rwf-ruby/tests/todo/lib/tasks/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/log/.keep b/rwf-ruby/tests/todo/log/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/public/404.html b/rwf-ruby/tests/todo/public/404.html new file mode 100644 index 00000000..2be3af26 --- /dev/null +++ b/rwf-ruby/tests/todo/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/rwf-ruby/tests/todo/public/406-unsupported-browser.html b/rwf-ruby/tests/todo/public/406-unsupported-browser.html new file mode 100644 index 00000000..7cf1e168 --- /dev/null +++ b/rwf-ruby/tests/todo/public/406-unsupported-browser.html @@ -0,0 +1,66 @@ + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ + diff --git a/rwf-ruby/tests/todo/public/422.html b/rwf-ruby/tests/todo/public/422.html new file mode 100644 index 00000000..c08eac0d --- /dev/null +++ b/rwf-ruby/tests/todo/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/rwf-ruby/tests/todo/public/500.html b/rwf-ruby/tests/todo/public/500.html new file mode 100644 index 00000000..78a030af --- /dev/null +++ b/rwf-ruby/tests/todo/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/rwf-ruby/tests/todo/public/icon.png b/rwf-ruby/tests/todo/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b5abcbde91cf6d7a6a26e514eb7e30f476f950 GIT binary patch literal 5599 zcmeHL-D}fO6hCR_taXJlzs3}~RuB=Iujyo=i*=1|1FN%E=zNfMTjru|Q<6v{J{U!C zBEE}?j6I3sz>fzN!6}L_BKjcuASk~1;Dg|U_@d{g?V8mM`~#9U+>>*Ezw>c(PjYWA z4(;!cgge6k5E&d$G5`S-0}!Ik>CV(0Y#1}s-v_gAHhja2=W1?nBAte9D2HG<(+)uj z!5=W4u*{VKMw#{V@^NNs4TClr!FAA%ID-*gc{R%CFKEzG<6gm*9s_uy)oMGW*=nJf zw{(Mau|2FHfXIv6C0@Wk5k)F=3jo1srV-C{pl&k&)4_&JjYrnbJiul}d0^NCSh(#7h=F;3{|>EU>h z6U8_p;^wK6mAB(1b92>5-HxJ~V}@3?G`&Qq-TbJ2(&~-HsH6F#8mFaAG(45eT3VPO zM|(Jd<+;UZs;w>0Qw}0>D%{~r{uo_Fl5_Bo3ABWi zWo^j^_T3dxG6J6fH8X)$a^%TJ#PU!=LxF=#Fd9EvKx_x>q<(KY%+y-08?kN9dXjXK z**Q=yt-FTU*13ouhCdqq-0&;Ke{T3sQU9IdzhV9LhQIpq*P{N)+}|Mh+a-VV=x?R} c>%+pvTcMWshj-umO}|qP?%A)*_KlqT3uEqhU;qFB literal 0 HcmV?d00001 diff --git a/rwf-ruby/tests/todo/public/icon.svg b/rwf-ruby/tests/todo/public/icon.svg new file mode 100644 index 00000000..78307ccd --- /dev/null +++ b/rwf-ruby/tests/todo/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/rwf-ruby/tests/todo/public/robots.txt b/rwf-ruby/tests/todo/public/robots.txt new file mode 100644 index 00000000..c19f78ab --- /dev/null +++ b/rwf-ruby/tests/todo/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/rwf-ruby/tests/todo/storage/.keep b/rwf-ruby/tests/todo/storage/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/application_system_test_case.rb b/rwf-ruby/tests/todo/test/application_system_test_case.rb new file mode 100644 index 00000000..cee29fd2 --- /dev/null +++ b/rwf-ruby/tests/todo/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] +end diff --git a/rwf-ruby/tests/todo/test/channels/application_cable/connection_test.rb b/rwf-ruby/tests/todo/test/channels/application_cable/connection_test.rb new file mode 100644 index 00000000..6340bf9c --- /dev/null +++ b/rwf-ruby/tests/todo/test/channels/application_cable/connection_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +module ApplicationCable + class ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end + end +end diff --git a/rwf-ruby/tests/todo/test/controllers/.keep b/rwf-ruby/tests/todo/test/controllers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/fixtures/files/.keep b/rwf-ruby/tests/todo/test/fixtures/files/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/helpers/.keep b/rwf-ruby/tests/todo/test/helpers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/integration/.keep b/rwf-ruby/tests/todo/test/integration/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/mailers/.keep b/rwf-ruby/tests/todo/test/mailers/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/models/.keep b/rwf-ruby/tests/todo/test/models/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/system/.keep b/rwf-ruby/tests/todo/test/system/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/test/test_helper.rb b/rwf-ruby/tests/todo/test/test_helper.rb new file mode 100644 index 00000000..0c22470e --- /dev/null +++ b/rwf-ruby/tests/todo/test/test_helper.rb @@ -0,0 +1,15 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... + end +end diff --git a/rwf-ruby/tests/todo/tmp/.keep b/rwf-ruby/tests/todo/tmp/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/tmp/pids/.keep b/rwf-ruby/tests/todo/tmp/pids/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/tmp/storage/.keep b/rwf-ruby/tests/todo/tmp/storage/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/vendor/.keep b/rwf-ruby/tests/todo/vendor/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf-ruby/tests/todo/vendor/javascript/.keep b/rwf-ruby/tests/todo/vendor/javascript/.keep new file mode 100644 index 00000000..e69de29b diff --git a/rwf/Cargo.toml b/rwf/Cargo.toml index 7b04f5f7..d25b287f 100644 --- a/rwf/Cargo.toml +++ b/rwf/Cargo.toml @@ -8,7 +8,7 @@ description = "Framework for building web applications in the Rust programming l # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -wsgi = ["pyo3", "rayon"] +wsgi = ["pyo3"] default = [] [dependencies] @@ -38,10 +38,10 @@ regex = "1" sha1 = "0.10" toml = "0.8" pyo3 = { version = "0.22", features = ["auto-initialize"], optional = true } -rayon = { version = "1", optional = true } +rayon = { version = "1" } uuid = { version = "1", features = ["v4"] } notify = "7" -libc = "*" +rwf-ruby = { path = "../rwf-ruby" } [dev-dependencies] tempdir = "0.3" diff --git a/rwf/src/controller/mod.rs b/rwf/src/controller/mod.rs index 78cba574..bc7e8dbb 100644 --- a/rwf/src/controller/mod.rs +++ b/rwf/src/controller/mod.rs @@ -4,6 +4,7 @@ pub mod auth; pub mod engine; pub mod error; pub mod middleware; +pub mod rack; pub mod ser; pub mod static_files; pub mod turbo_stream; diff --git a/rwf/src/controller/rack.rs b/rwf/src/controller/rack.rs new file mode 100644 index 00000000..2e198d44 --- /dev/null +++ b/rwf/src/controller/rack.rs @@ -0,0 +1,100 @@ +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, Ordering}; + +use super::{Controller, Error}; +use crate::http::{Request, Response}; + +use async_trait::async_trait; +use rayon::{ThreadPool, ThreadPoolBuilder}; +use tokio::sync::oneshot::channel; +use tokio::time::{timeout, Duration}; +use tracing::warn; + +use tokio::fs::{metadata, File}; + +use rwf_ruby::{RackRequest, RackResponse, RackResponseOwned, Ruby}; +use std::sync::Arc; + +pub struct RackController { + pool: ThreadPool, + path: PathBuf, + loaded: Arc, +} + +impl RackController { + pub fn new(path: &str) -> Self { + Self { + // There can only be _one_ Rust thread. + // The only way to multi-thread safely + // would be to do it in Ruby using GVL-protected threads. + // Even if we have a Mutex in Rust, loading the app in one thread and running it in + // another will segfault. + pool: Self::runtime(1), + path: PathBuf::from(path).join("config/environment.rb"), + loaded: Arc::new(AtomicBool::new(false)), + } + } + + pub fn load(&self) { + Ruby::load_app(&self.path).unwrap(); + } + + fn runtime(threads: usize) -> ThreadPool { + ThreadPoolBuilder::new() + .num_threads(threads) + .panic_handler(|_| { + warn!("WSGI thread panicked. This is a bug in the WSGI application."); + }) + .build() + .unwrap() + } +} + +#[async_trait] +impl Controller for RackController { + async fn handle(&self, request: &Request) -> Result { + let (tx, rx) = channel(); + let path = PathBuf::from(&self.path); + let loaded = self.loaded.clone(); + + let req_path = request.path().path().to_string(); + let method = request.method().to_string(); + + self.pool.spawn(move || { + if !loaded.load(Ordering::Relaxed) { + Ruby::load_app(&path).unwrap(); + loaded.store(true, Ordering::Relaxed); + } + + let env = HashMap::from([ + ("REQUEST_URI".into(), req_path.clone()), + ("PATH_INFO".into(), req_path.clone()), + ("REQUEST_PATH".into(), req_path.clone()), + ("SERVER_PROTOCOL".into(), "HTTP/1.1".into()), + ("HTTP_HOST".into(), ("127.0.0.1:8000".into())), + ("REQUEST_METHOD".into(), method), + ]); + + let response = RackRequest::send(env); + let owned = RackResponseOwned::from(response); + + let _ = tx.send(owned); + }); + + let response = rx.await.unwrap(); + + if response.is_file() { + let path = PathBuf::from(String::from_utf8_lossy(response.body()).to_string()); + let meta = metadata(&path).await.unwrap(); + let file = File::open(&path).await.unwrap(); + + Ok(Response::new().body((path, file, meta))) + } else { + Ok(Response::new() + .body(response.body()) + .header("Content-Type", "text/html") + .code(response.code())) + } + } +} diff --git a/rwf/src/http/body.rs b/rwf/src/http/body.rs index fe0c978d..c28598ff 100644 --- a/rwf/src/http/body.rs +++ b/rwf/src/http/body.rs @@ -167,6 +167,12 @@ impl From> for Body { } } +impl From<&[u8]> for Body { + fn from(body: &[u8]) -> Self { + Self::Bytes(body.to_vec()) + } +} + impl From<(PathBuf, File, Metadata)> for Body { fn from(file: (PathBuf, File, Metadata)) -> Self { Self::File { From 287d5193d5a4ffcf075227453ce5656b6d77ac4e Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 08:54:19 -0800 Subject: [PATCH 04/15] alright! --- examples/rails/src/main.rs | 1 - rwf-ruby/build.rs | 28 ++++----- rwf-ruby/headers.rb | 13 ++++ rwf-ruby/src/bindings.c | 124 +++++++++++++++++++++++-------------- rwf-ruby/src/bindings.h | 10 +-- rwf-ruby/src/lib.rs | 63 +++++++++++++------ rwf/src/controller/rack.rs | 66 +++++++++++++------- rwf/src/http/headers.rs | 6 +- rwf/src/lib.rs | 2 + 9 files changed, 202 insertions(+), 111 deletions(-) create mode 100644 rwf-ruby/headers.rb diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index 28561370..27ff607e 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -11,7 +11,6 @@ async fn main() -> Result<(), http::Error> { Logger::init(); let controller = RackController::new("todo"); - // controller.load(); Server::new(vec![controller.wildcard("/")]) .launch("0.0.0.0:8000") diff --git a/rwf-ruby/build.rs b/rwf-ruby/build.rs index 1f246dce..bf295289 100644 --- a/rwf-ruby/build.rs +++ b/rwf-ruby/build.rs @@ -1,21 +1,21 @@ -use std::{env, path::PathBuf}; +use std::process::Command; fn main() { println!("cargo:rerun-if-changed=src/bindings.c"); - println!("cargo:rerun-if-changed=src/bindings.h"); + println!("cargo:rerun-if-changed=src/bindings.h"); // Bindings are generated manually because bindgen goes overboard with ruby.h - // let bindings = bindgen::Builder::default() - // .header("src/bindings.h") - // .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - // .generate() - // .expect("bindgen"); + let output = Command::new("ruby") + .arg("headers.rb") + .output() + .expect("Is ruby installed on your system?") + .stdout; + let flags = String::from_utf8_lossy(&output).to_string(); - // let out_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/bindings.rs"); - // bindings.write_to_file(out_path).expect("write bindings"); + let mut build = cc::Build::new(); - cc::Build::new() - .file("src/bindings.c") - .flag("-I/usr/include/ruby-3.3.0") - .flag("-I/usr/include/ruby-3.3.0/x86_64-linux") - .compile("rwf_ruby"); + for flag in flags.split(" ") { + build.flag(flag); + } + + build.file("src/bindings.c").compile("rwf_ruby"); } diff --git a/rwf-ruby/headers.rb b/rwf-ruby/headers.rb new file mode 100644 index 00000000..0bcd382f --- /dev/null +++ b/rwf-ruby/headers.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby +# Source: https://silverhammermba.github.io/emberb/embed/ +require 'shellwords' + +# location of ruby.h +hdrdir = Shellwords.escape RbConfig::CONFIG["rubyhdrdir"] +# location of ruby/config.h +archhdrdir = Shellwords.escape RbConfig::CONFIG["rubyarchhdrdir"] +# location of libruby +libdir = Shellwords.escape RbConfig::CONFIG["libdir"] + +# args for GCC +puts "-I#{hdrdir} -I#{archhdrdir} -L#{libdir} -Wl,-rpath,#{libdir}" diff --git a/rwf-ruby/src/bindings.c b/rwf-ruby/src/bindings.c index be3776fb..52a11c6f 100644 --- a/rwf-ruby/src/bindings.c +++ b/rwf-ruby/src/bindings.c @@ -6,13 +6,9 @@ #include #include #include "bindings.h" -#include "ruby/internal/arithmetic/int.h" -#include "ruby/internal/core/rstring.h" -#include "ruby/internal/eval.h" -#include "ruby/internal/special_consts.h" -#include "ruby/internal/value_type.h" -static void rwf_print_error(void); + +static int rwf_print_error(void); void rwf_init_ruby() { ruby_setup(); @@ -20,6 +16,10 @@ void rwf_init_ruby() { ruby_script("rwf_loader"); } +/* + * Load the Ruby app into memory. + * This is the only known way to execute Ruby apps from C in a way that works. +*/ int rwf_load_app(const char* path) { int state; void *node; @@ -51,15 +51,12 @@ int rwf_load_app(const char* path) { return 0; } -typedef enum RackBody { - PROXY, - FILES, -} RackBody; - - +/* + * Inspect a Ruby value and print that to the standard output. +*/ void rwf_debug_value(VALUE v) { int state; - VALUE kernel = rb_eval_string_protect("Kernel", &state); + VALUE kernel = rb_eval_string_protect("Kernel", &state); /* Kernel is always available. */ VALUE str = rb_obj_as_string(v); rb_funcall(kernel, rb_intern("puts"), 1, str); @@ -67,16 +64,9 @@ void rwf_debug_value(VALUE v) { rb_funcall(kernel, rb_intern("puts"), 1, methods); } -int rwf_responds_to(VALUE value, const char* name) { - if (value == Qnil) { - return 0; - } - - VALUE name_s = rb_str_new_cstr(name); - VALUE responds_to = rb_funcall(value, rb_intern("respond_to?"), 1, name_s); - +static int rwf_is_true(VALUE v) { /* I feel dumb, but Qtrue doesn't work. */ - VALUE object_id = rb_funcall(responds_to, rb_intern("object_id"), 0); + VALUE object_id = rb_funcall(v, rb_intern("object_id"), 0); int is_true = NUM2INT(object_id); if (is_true == 20) { @@ -86,6 +76,33 @@ int rwf_responds_to(VALUE value, const char* name) { } } +static int rwf_is_nil(VALUE v) { + return rwf_is_true(rb_funcall(v, rb_intern("nil?"), 0)); +} + +/* + * Check if this value can accept a method. + * Use this unless you're sure of the data type you're dealing with. + * If you're wrong, the VM will segfault though. +*/ +int rwf_responds_to(VALUE value, const char* name) { + if (value == Qnil) { + return 0; + } + + VALUE name_s = rb_str_new_cstr(name); + VALUE responds_to = rb_funcall(value, rb_intern("respond_to?"), 1, name_s); + + return rwf_is_true(responds_to); +} + +/* + * Try to figure out what Rack returned as the body. + * + * So far I've discovered it can either be a BodyProxy which duck-types to array, + * or a File::Iterator which I'm not sure what it does, but I can get the path to the file, + * which Rust can then read. +*/ VALUE rwf_get_body(VALUE value, int *is_file) { if (rwf_responds_to(value, "to_ary") == 0) { VALUE proxy_body_ar = rb_funcall(value, rb_intern("to_ary"), 0); @@ -101,8 +118,14 @@ VALUE rwf_get_body(VALUE value, int *is_file) { } } - +/* Parse the response from Rack. */ RackResponse rwf_rack_response_new(VALUE value) { + /* + Rack returns an array of 3 elements: + - HTTP code + - headers hash + - response body, which can be a few things + */ assert(TYPE(value) == T_ARRAY); assert(RARRAY_LEN(value) == 3); @@ -115,7 +138,7 @@ RackResponse rwf_rack_response_new(VALUE value) { response.num_headers = RHASH_SIZE(headers); VALUE header_keys = rb_funcall(headers, rb_intern("keys"), 0); - response.headers = malloc(response.num_headers * sizeof(EnvKey)); + response.headers = malloc(response.num_headers * sizeof(KeyValue)); for(int i = 0; i < response.num_headers; i++) { VALUE header_key = rb_ary_entry(header_keys, i); @@ -127,7 +150,7 @@ RackResponse rwf_rack_response_new(VALUE value) { char *header_key_str = StringValueCStr(header_key_symbol_str); char *header_value_str = StringValueCStr(header_value); - EnvKey env_key; + KeyValue env_key; env_key.key = header_key_str; env_key.value = header_value_str; @@ -140,45 +163,52 @@ RackResponse rwf_rack_response_new(VALUE value) { // to array. VALUE body = rwf_get_body(body_entry, &response.is_file); - response.body = StringValueCStr(body); + if (rwf_is_nil(body) == 0) { + VALUE empty = rb_str_new_cstr(""); + response.body = StringValueCStr(empty); + } else { + response.body = StringValueCStr(body); + } + response.value = value; return response; } -void rwf_debug_key(EnvKey *k) { - int state; - VALUE kernel = rb_eval_string_protect("Kernel", &state); - - VALUE key = rb_str_new_cstr(k->key); - VALUE value = rb_str_new_cstr(k->value); - - rb_funcall(kernel, rb_intern("puts"), 1, key); - rb_funcall(kernel, rb_intern("puts"), 1, value); -} - -RackResponse rwf_app_call(RackRequest request) { +/* + * Execute a Rack app and return an HTTP response. + * + * The app_name is a Ruby string which evaluates to the Rack app, for example: `Rails.application`. + * + * This function isn't super safe yet. For example, if the app_name is not a Rack app, we'll segfault. +*/ +int rwf_app_call(RackRequest request, const char *app_name, RackResponse *res) { int state; - VALUE hash = rb_hash_new(); + VALUE env = rb_hash_new(); for (int i = 0; i < request.length; i++) { VALUE key = rb_str_new_cstr(request.env[i].key); VALUE value = rb_str_new_cstr(request.env[i].value); - rb_hash_aset(hash, key, value); + rb_hash_aset(env, key, value); } - VALUE app = rb_eval_string_protect("Rails.application", &state); + VALUE app = rb_eval_string_protect(app_name, &state); if (state) { rwf_print_error(); + return -1; } - VALUE response = rb_funcall(app, rb_intern("call"), 1, hash); + VALUE response = rb_funcall(app, rb_intern("call"), 1, env); + + if (rwf_print_error() != 0) { + return -1; + } - rwf_print_error(); + *res = rwf_rack_response_new(response); - return rwf_rack_response_new(response); + return 0; } void rwf_rack_response_drop(RackResponse *response) { @@ -206,7 +236,7 @@ void rwf_clear_error_state() { rb_set_errinfo(Qnil); } -void rwf_print_error() { +int rwf_print_error() { VALUE error = rb_errinfo(); if (error != Qnil) { @@ -216,7 +246,9 @@ void rwf_print_error() { VALUE backtrace_obj = rb_obj_as_string(backtrace); char *backtrace_str = StringValueCStr(backtrace_obj); printf("error: %s\nbacktrace: %s", error_msg, backtrace_str); + rb_set_errinfo(Qnil); + return 1; } - rb_set_errinfo(Qnil); + return 0; } diff --git a/rwf-ruby/src/bindings.h b/rwf-ruby/src/bindings.h index e4daef6f..243c792f 100644 --- a/rwf-ruby/src/bindings.h +++ b/rwf-ruby/src/bindings.h @@ -8,26 +8,26 @@ typedef struct EnvKey { const char *key; const char *value; -} EnvKey; +} KeyValue; typedef struct RackResponse { uintptr_t value; int code; int num_headers; - EnvKey *headers; + KeyValue *headers; char* body; int is_file; } RackResponse; typedef struct RackRequest { - const EnvKey* env; + const KeyValue* env; const int length; } RackRequest; -int rwf_load_app(const char* path); +int rwf_load_app(const char *path); void rwf_init_ruby(void); RackResponse rwf_rack_response_new(VALUE value); -RackResponse rwf_app_call(RackRequest request); +int rwf_app_call(RackRequest request, const char *app_name, RackResponse *res); #endif diff --git a/rwf-ruby/src/lib.rs b/rwf-ruby/src/lib.rs index 9b842da3..a4b95ceb 100644 --- a/rwf-ruby/src/lib.rs +++ b/rwf-ruby/src/lib.rs @@ -1,12 +1,11 @@ use libc::uintptr_t; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; use std::ffi::{c_char, c_int, CStr, CString}; use std::fs::canonicalize; +use std::mem::MaybeUninit; use std::path::Path; -use parking_lot::Mutex; use std::collections::HashMap; -use std::sync::Arc; // Make sure the Ruby VM is initialized only once. static RUBY_INIT: OnceCell = OnceCell::new(); @@ -14,17 +13,17 @@ static RUBY_INIT: OnceCell = OnceCell::new(); #[repr(C)] #[derive(Debug, Clone)] pub struct RackResponse { - pub value: usize, + pub value: uintptr_t, pub code: c_int, pub num_headers: c_int, - pub headers: *mut EnvKey, + pub headers: *mut KeyValue, pub body: *mut c_char, pub is_file: c_int, } #[repr(C)] #[derive(Debug)] -struct EnvKey { +pub struct KeyValue { key: *const c_char, value: *const c_char, } @@ -32,12 +31,12 @@ struct EnvKey { #[repr(C)] #[derive(Debug)] pub struct RackRequest { - env: *const EnvKey, + env: *const KeyValue, length: c_int, } impl RackRequest { - pub fn send(env: HashMap) -> RackResponse { + pub fn send(env: HashMap) -> Result { // let mut c_strings = vec![]; let mut keys = vec![]; @@ -49,32 +48,35 @@ impl RackRequest { k.push(key); v.push(value); - let env_key = EnvKey { + let env_key = KeyValue { key: k.last().unwrap().as_ptr(), value: v.last().unwrap().as_ptr(), }; - // unsafe { rwf_debug_key(&env_key) }; - keys.push(env_key); - - // Keep the references or they will be dropped and - // we'll segfault. - // c_strings.push(key); - // c_strings.push(value); } - // unsafe { rwf_debug_key(&keys[2]) }; - let req = RackRequest { length: keys.len() as c_int, env: keys.as_ptr(), }; - unsafe { rwf_app_call(req) } + // Hardcoded to Rails, but can be any other Rack app. + let app_name = CString::new("Rails.application").unwrap(); + + let mut response: RackResponse = unsafe { MaybeUninit::zeroed().assume_init() }; + + let result = unsafe { rwf_app_call(req, app_name.as_ptr(), &mut response) }; + + if result != 0 { + return Err(Error::App); + } else { + Ok(response) + } } } +/// RackResponse with values allocated in Rust memory space. #[derive(Debug)] pub struct RackResponseOwned { code: u16, @@ -84,20 +86,31 @@ pub struct RackResponseOwned { } impl RackResponseOwned { + /// Request body. pub fn body(&self) -> &[u8] { &self.body } + /// Request HTTP code. pub fn code(&self) -> u16 { self.code } + /// Is the request a file? pub fn is_file(&self) -> bool { self.is_file } + + /// Request headers. + pub fn headers(&self) -> &HashMap { + &self.headers + } } impl From for RackResponseOwned { + /// Move all data out of C into Rust-owned memory. + /// This also drops the reference to the Rack response array, + /// allowing it to be garbage collected. fn from(response: RackResponse) -> RackResponseOwned { let code = response.code as u16; @@ -108,12 +121,14 @@ impl From for RackResponseOwned { let name = unsafe { CStr::from_ptr((*env_key).key) }; let value = unsafe { CStr::from_ptr((*env_key).value) }; + // Headers should be valid UTF-8. headers.insert( name.to_string_lossy().to_string(), value.to_string_lossy().to_string(), ); } + // Body can be anything. let body = unsafe { CStr::from_ptr(response.body) }; let body = Vec::from(body.to_bytes()); @@ -127,6 +142,7 @@ impl From for RackResponseOwned { } impl RackResponse { + /// Parse the Rack response from a Ruby value. pub fn new(value: &Value) -> Self { unsafe { rwf_rack_response_new(value.raw_ptr()) } } @@ -180,7 +196,11 @@ extern "C" { /// Initialize Ruby correctly. fn rwf_init_ruby(); - fn rwf_app_call(request: RackRequest) -> RackResponse; + fn rwf_app_call( + request: RackRequest, + app_name: *const c_char, + response: *mut RackResponse, + ) -> c_int; } #[derive(Debug, thiserror::Error)] @@ -283,12 +303,14 @@ impl Ruby { } } + /// Preload the Rack app into memory. Run this before trying to run anything else. pub fn load_app(path: impl AsRef + Copy) -> Result<(), Error> { Self::init()?; let path = path.as_ref(); if path.exists() { + // We use `require`, which only works with abslute paths. let absolute = canonicalize(path).unwrap(); let s = absolute.display().to_string(); let cs = CString::new(s).unwrap(); @@ -303,6 +325,7 @@ impl Ruby { Ok(()) } + /// Run some Ruby code. If an exception is thrown, return the error. pub fn eval(code: &str) -> Result { Self::init()?; diff --git a/rwf/src/controller/rack.rs b/rwf/src/controller/rack.rs index 2e198d44..4ed69941 100644 --- a/rwf/src/controller/rack.rs +++ b/rwf/src/controller/rack.rs @@ -8,12 +8,11 @@ use crate::http::{Request, Response}; use async_trait::async_trait; use rayon::{ThreadPool, ThreadPoolBuilder}; use tokio::sync::oneshot::channel; -use tokio::time::{timeout, Duration}; -use tracing::warn; +use tracing::{info, warn}; use tokio::fs::{metadata, File}; -use rwf_ruby::{RackRequest, RackResponse, RackResponseOwned, Ruby}; +use rwf_ruby::{RackRequest, RackResponseOwned, Ruby}; use std::sync::Arc; pub struct RackController { @@ -36,15 +35,11 @@ impl RackController { } } - pub fn load(&self) { - Ruby::load_app(&self.path).unwrap(); - } - fn runtime(threads: usize) -> ThreadPool { ThreadPoolBuilder::new() .num_threads(threads) .panic_handler(|_| { - warn!("WSGI thread panicked. This is a bug in the WSGI application."); + warn!("Rack thread panicked. This is a bug in the Rack application."); }) .build() .unwrap() @@ -61,22 +56,32 @@ impl Controller for RackController { let req_path = request.path().path().to_string(); let method = request.method().to_string(); + let mut env = HashMap::from([ + ("REQUEST_URI".into(), req_path.clone()), + ("PATH_INFO".into(), req_path.clone()), + ("REQUEST_PATH".into(), req_path.clone()), + ("SERVER_PROTOCOL".into(), "HTTP/1.1".into()), + ("REQUEST_METHOD".into(), method), + ]); + + for (key, value) in request.headers().iter() { + env.insert( + format!("HTTP_{}", crate::snake_case(key).to_ascii_uppercase()), + value.to_string(), + ); + } + self.pool.spawn(move || { + // We only have one thread in Rust, so there is no race. + // Besides, if you try this from multiple threads, you'll segfault. if !loaded.load(Ordering::Relaxed) { + info!("Loading the Rack app, hold your horses..."); Ruby::load_app(&path).unwrap(); loaded.store(true, Ordering::Relaxed); + info!("Rack app loaded, let's go!"); } - let env = HashMap::from([ - ("REQUEST_URI".into(), req_path.clone()), - ("PATH_INFO".into(), req_path.clone()), - ("REQUEST_PATH".into(), req_path.clone()), - ("SERVER_PROTOCOL".into(), "HTTP/1.1".into()), - ("HTTP_HOST".into(), ("127.0.0.1:8000".into())), - ("REQUEST_METHOD".into(), method), - ]); - - let response = RackRequest::send(env); + let response = RackRequest::send(env).unwrap(); let owned = RackResponseOwned::from(response); let _ = tx.send(owned); @@ -86,15 +91,28 @@ impl Controller for RackController { if response.is_file() { let path = PathBuf::from(String::from_utf8_lossy(response.body()).to_string()); - let meta = metadata(&path).await.unwrap(); - let file = File::open(&path).await.unwrap(); + + let meta = if let Ok(meta) = metadata(&path).await { + meta + } else { + return Ok(Response::not_found()); + }; + + // Don't think the file will disappear here, but you really can't know. + let file = if let Ok(file) = File::open(&path).await { + file + } else { + return Ok(Response::not_found()); + }; Ok(Response::new().body((path, file, meta))) } else { - Ok(Response::new() - .body(response.body()) - .header("Content-Type", "text/html") - .code(response.code())) + let mut res = Response::new().body(response.body()); + for (key, value) in response.headers() { + res = res.header(key, value); + } + + Ok(res.code(response.code())) } } } diff --git a/rwf/src/http/headers.rs b/rwf/src/http/headers.rs index 7e26ffbf..8f2bcea5 100644 --- a/rwf/src/http/headers.rs +++ b/rwf/src/http/headers.rs @@ -1,5 +1,5 @@ //! HTTP request headers. -use std::collections::HashMap; +use std::collections::{hash_map::Iter, HashMap}; /// HTTP headers. #[derive(Clone, Debug, Default)] @@ -41,6 +41,10 @@ impl Headers { self.headers } + pub fn iter(&self) -> Iter { + self.headers.iter() + } + /// Convert headers to bytes (UTF-8). /// /// Used to send headers over the wire to the client as part of a response. diff --git a/rwf/src/lib.rs b/rwf/src/lib.rs index 8b223d9e..99e7a5db 100644 --- a/rwf/src/lib.rs +++ b/rwf/src/lib.rs @@ -33,6 +33,8 @@ pub fn snake_case(string: &str) -> String { if c.is_ascii_uppercase() && i != 0 { result.push('_'); result.push(c.to_ascii_lowercase()); + } else if c == '-' { + result.push('_'); } else { result.push(c.to_ascii_lowercase()); } From cf712398496ef3043c5f3425f6fc64e6449232a4 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 09:03:06 -0800 Subject: [PATCH 05/15] add query string --- rwf/src/controller/rack.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rwf/src/controller/rack.rs b/rwf/src/controller/rack.rs index 4ed69941..96babb58 100644 --- a/rwf/src/controller/rack.rs +++ b/rwf/src/controller/rack.rs @@ -55,13 +55,16 @@ impl Controller for RackController { let req_path = request.path().path().to_string(); let method = request.method().to_string(); + let query = request.query().to_string(); + let req_uri = format!("{}{}", req_path, query); let mut env = HashMap::from([ - ("REQUEST_URI".into(), req_path.clone()), + ("REQUEST_URI".into(), req_uri), ("PATH_INFO".into(), req_path.clone()), - ("REQUEST_PATH".into(), req_path.clone()), + ("REQUEST_PATH".into(), req_path), ("SERVER_PROTOCOL".into(), "HTTP/1.1".into()), ("REQUEST_METHOD".into(), method), + ("QUERY_STRING".into(), query.replace("?", "")), ]); for (key, value) in request.headers().iter() { From cc2b082432ad39127a9e365f959d7bd2bed773e9 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 09:06:45 -0800 Subject: [PATCH 06/15] optional --- .github/workflows/ci.yml | 4 ++++ examples/rails/Cargo.toml | 2 +- examples/rails/src/main.rs | 4 ---- rwf/Cargo.toml | 7 ++++--- rwf/src/controller/mod.rs | 1 + 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e000da9c..543d8b20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: with: toolchain: stable override: true + - name: Install libruby + run: sudo apt update && sudo apt install ruby-dev -y - name: Build run: cargo build tests: @@ -38,6 +40,8 @@ jobs: with: toolchain: stable override: true + - name: Install libruby + run: sudo apt update && sudo apt install ruby-dev -y - name: Install cargo-nextest run: cargo install cargo-nextest - name: Test diff --git a/examples/rails/Cargo.toml b/examples/rails/Cargo.toml index 5a1a2f2b..c9d1c6e0 100644 --- a/examples/rails/Cargo.toml +++ b/examples/rails/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -rwf = { path = "../../rwf" } +rwf = { path = "../../rwf", features = ["rack"] } diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index 27ff607e..67ff3085 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -2,10 +2,6 @@ use rwf::controller::rack::RackController; use rwf::http::{self, Server}; use rwf::prelude::*; -// fn main() { - -// } - #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<(), http::Error> { Logger::init(); diff --git a/rwf/Cargo.toml b/rwf/Cargo.toml index d25b287f..a006e743 100644 --- a/rwf/Cargo.toml +++ b/rwf/Cargo.toml @@ -8,8 +8,9 @@ description = "Framework for building web applications in the Rust programming l # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -wsgi = ["pyo3"] +wsgi = ["pyo3", "rayon"] default = [] +rack = ["rwf-ruby", "rayon"] [dependencies] time = { version = "0.3", features = ["formatting", "serde", "parsing"] } @@ -38,10 +39,10 @@ regex = "1" sha1 = "0.10" toml = "0.8" pyo3 = { version = "0.22", features = ["auto-initialize"], optional = true } -rayon = { version = "1" } +rayon = { version = "1", optional = true } uuid = { version = "1", features = ["v4"] } notify = "7" -rwf-ruby = { path = "../rwf-ruby" } +rwf-ruby = { path = "../rwf-ruby", optional = true } [dev-dependencies] tempdir = "0.3" diff --git a/rwf/src/controller/mod.rs b/rwf/src/controller/mod.rs index bc7e8dbb..07f583a1 100644 --- a/rwf/src/controller/mod.rs +++ b/rwf/src/controller/mod.rs @@ -4,6 +4,7 @@ pub mod auth; pub mod engine; pub mod error; pub mod middleware; +#[cfg(feature = "rack")] pub mod rack; pub mod ser; pub mod static_files; From c08e10cae9061be602e72daa57c5de060561c1d1 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 09:11:26 -0800 Subject: [PATCH 07/15] debug games --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 543d8b20..41f82e97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,12 @@ jobs: toolchain: stable override: true - name: Install libruby - run: sudo apt update && sudo apt install ruby-dev -y + run: | + echo "Looking for ruby" + find / -name libruby.so 2>/dev/null + echo "Ruby maybe found" + exit 1 + sudo apt update && sudo apt install ruby-dev -y - name: Build run: cargo build tests: From fef6a519c31ec1bb9f49ea2b62de4a0431201976 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 09:23:51 -0800 Subject: [PATCH 08/15] Try dynamic github fix --- .github/workflows/ci.yml | 7 ------- rwf-ruby/build.rs | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41f82e97..e167a117 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,13 +21,6 @@ jobs: with: toolchain: stable override: true - - name: Install libruby - run: | - echo "Looking for ruby" - find / -name libruby.so 2>/dev/null - echo "Ruby maybe found" - exit 1 - sudo apt update && sudo apt install ruby-dev -y - name: Build run: cargo build tests: diff --git a/rwf-ruby/build.rs b/rwf-ruby/build.rs index bf295289..d1a485fe 100644 --- a/rwf-ruby/build.rs +++ b/rwf-ruby/build.rs @@ -17,5 +17,26 @@ fn main() { build.flag(flag); } + // Github actions workaround + match Command::new("find") + .arg("/opt/hostedtoolcache/Ruby") + .arg("-name") + .arg("libruby.so") + .output() + { + Ok(output) => { + let lib = String::from_utf8_lossy(&output.stdout) + .to_string() + .trim() + .to_string(); + let lib = lib.split("\n").next().unwrap_or("").trim(); + if !lib.is_empty() { + build.flag(format!("-L{}", lib)); + } + } + + Err(_) => (), + }; + build.file("src/bindings.c").compile("rwf_ruby"); } From 26b75555c6750ab5790307f2e652776557d89431 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:00:36 -0800 Subject: [PATCH 09/15] working on mac --- examples/rails/.cargo/config | 2 ++ examples/rails/todo/Gemfile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 examples/rails/.cargo/config diff --git a/examples/rails/.cargo/config b/examples/rails/.cargo/config new file mode 100644 index 00000000..20eda45c --- /dev/null +++ b/examples/rails/.cargo/config @@ -0,0 +1,2 @@ +[build] +rustflags = ["-C", "link-args=-L/opt/homebrew/Cellar/ruby/3.3.4/lib"] diff --git a/examples/rails/todo/Gemfile b/examples/rails/todo/Gemfile index a7f333af..1e9f646a 100644 --- a/examples/rails/todo/Gemfile +++ b/examples/rails/todo/Gemfile @@ -36,7 +36,7 @@ gem "bootsnap", require: false group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" + gem "debug", platforms: %i[ mri ], require: "debug/prelude" # Static analysis for security vulnerabilities [https://brakemanscanner.org/] gem "brakeman", require: false From 4db3297fff7f20c1cd883c13bd4def5e10311695 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:02:54 -0800 Subject: [PATCH 10/15] save --- Cargo.lock | 152 +++++++++++++++++++++++++++++++++++-------- rwf-ruby/Cargo.toml | 5 +- rwf-ruby/build.rs | 1 + rwf-ruby/src/.clangd | 1 + rwf-ruby/src/lib.rs | 5 +- 5 files changed, 134 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a53c85c..399f1a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", ] @@ -235,7 +235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -358,6 +358,12 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -425,6 +431,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -495,7 +510,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -634,7 +649,7 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "libredox", "windows-sys 0.59.0", @@ -741,7 +756,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] @@ -900,7 +915,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "smallvec", + "smallvec 1.13.2", "tokio", "want", ] @@ -998,7 +1013,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1075,7 +1090,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-targets 0.52.6", ] @@ -1087,7 +1102,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall", + "redox_syscall 0.5.7", ] [[package]] @@ -1096,6 +1111,15 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -1127,13 +1151,19 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "digest", ] @@ -1293,14 +1323,40 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.6.3", + "rustc_version", +] + [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "lock_api", - "parking_lot_core", + "lock_api 0.4.12", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version", + "smallvec 0.6.14", + "winapi", ] [[package]] @@ -1309,10 +1365,10 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", - "redox_syscall", - "smallvec", + "redox_syscall 0.5.7", + "smallvec 1.13.2", "windows-targets 0.52.6", ] @@ -1394,7 +1450,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "opaque-debug", "universal-hash", @@ -1511,7 +1567,7 @@ version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "indoc", "libc", "memoffset", @@ -1679,6 +1735,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -1773,6 +1835,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.39" @@ -1806,7 +1877,7 @@ dependencies = [ "colored", "notify", "once_cell", - "parking_lot", + "parking_lot 0.12.3", "pyo3", "rand 0.8.5", "rayon", @@ -1882,8 +1953,9 @@ dependencies = [ "cc", "libc", "once_cell", - "parking_lot", + "parking_lot 0.9.0", "thiserror", + "tracing", ] [[package]] @@ -1891,7 +1963,7 @@ name = "rwf-tests" version = "0.1.0" dependencies = [ "console-subscriber", - "parking_lot", + "parking_lot 0.12.3", "rwf", "rwf-macros 0.1.5", "serde", @@ -1931,6 +2003,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.214" @@ -1978,7 +2065,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -1989,7 +2076,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -2033,6 +2120,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -2145,7 +2241,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] @@ -2205,7 +2301,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2238,7 +2334,7 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot", + "parking_lot 0.12.3", "percent-encoding", "phf", "pin-project-lite", @@ -2439,7 +2535,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", - "smallvec", + "smallvec 1.13.2", "thread_local", "tracing", "tracing-core", @@ -2578,7 +2674,7 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", "wasm-bindgen-macro", ] @@ -2655,7 +2751,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall", + "redox_syscall 0.5.7", "wasite", "web-sys", ] diff --git a/rwf-ruby/Cargo.toml b/rwf-ruby/Cargo.toml index 2fd8b88d..708dd142 100644 --- a/rwf-ruby/Cargo.toml +++ b/rwf-ruby/Cargo.toml @@ -1,5 +1,7 @@ [package] name = "rwf-ruby" +license = "MIT" +description = "Ruby (specifically Rack/Rails) bindings for Rwf" version = "0.1.0" edition = "2021" @@ -7,7 +9,8 @@ edition = "2021" libc = "0.2" once_cell = "1" thiserror = "1" -parking_lot = "*" +parking_lot = "0.9" +tracing = "0.1" [build-dependencies] cc = "1" diff --git a/rwf-ruby/build.rs b/rwf-ruby/build.rs index d1a485fe..97edd9f4 100644 --- a/rwf-ruby/build.rs +++ b/rwf-ruby/build.rs @@ -3,6 +3,7 @@ use std::process::Command; fn main() { println!("cargo:rerun-if-changed=src/bindings.c"); println!("cargo:rerun-if-changed=src/bindings.h"); // Bindings are generated manually because bindgen goes overboard with ruby.h + // println!("cargo:rustc-link-arg=-l/opt/homebrew/Cellar/ruby/3.3.4/lib/libruby.dylib"); let output = Command::new("ruby") .arg("headers.rb") diff --git a/rwf-ruby/src/.clangd b/rwf-ruby/src/.clangd index a5e4a1a2..22b97819 100644 --- a/rwf-ruby/src/.clangd +++ b/rwf-ruby/src/.clangd @@ -2,3 +2,4 @@ CompileFlags: Add: - "-I/usr/include/ruby-3.3.0" - "-I/usr/include/ruby-3.3.0/x86_64-linux" + - "-I/opt/homebrew/Cellar/ruby/3.3.4/include" diff --git a/rwf-ruby/src/lib.rs b/rwf-ruby/src/lib.rs index a4b95ceb..0edd8f69 100644 --- a/rwf-ruby/src/lib.rs +++ b/rwf-ruby/src/lib.rs @@ -6,6 +6,7 @@ use std::mem::MaybeUninit; use std::path::Path; use std::collections::HashMap; +use tracing::info; // Make sure the Ruby VM is initialized only once. static RUBY_INIT: OnceCell = OnceCell::new(); @@ -306,9 +307,11 @@ impl Ruby { /// Preload the Rack app into memory. Run this before trying to run anything else. pub fn load_app(path: impl AsRef + Copy) -> Result<(), Error> { Self::init()?; - let path = path.as_ref(); + let version = Self::eval("RUBY_VERSION").unwrap().to_string(); + info!("Using {}", version); + if path.exists() { // We use `require`, which only works with abslute paths. let absolute = canonicalize(path).unwrap(); From 9cdb5f690fb1c51834ae0d83e4fc3a2539ad3820 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:07:08 -0800 Subject: [PATCH 11/15] rust example --- examples/rails/.bundle/config | 2 -- examples/rails/src/config.ru | 4 ---- examples/rails/src/main.rs | 12 +++++++++++- 3 files changed, 11 insertions(+), 7 deletions(-) delete mode 100644 examples/rails/.bundle/config delete mode 100644 examples/rails/src/config.ru diff --git a/examples/rails/.bundle/config b/examples/rails/.bundle/config deleted file mode 100644 index d5bd3974..00000000 --- a/examples/rails/.bundle/config +++ /dev/null @@ -1,2 +0,0 @@ ---- -BUNDLE_PATH: "/home/lev/.gem" diff --git a/examples/rails/src/config.ru b/examples/rails/src/config.ru deleted file mode 100644 index d7479c33..00000000 --- a/examples/rails/src/config.ru +++ /dev/null @@ -1,4 +0,0 @@ -run do |env| - puts env - [200, {}, ["Hello World"]] -end diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index 67ff3085..6653d003 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -8,7 +8,17 @@ async fn main() -> Result<(), http::Error> { let controller = RackController::new("todo"); - Server::new(vec![controller.wildcard("/")]) + Server::new(vec![route!("/rust" => Index), controller.wildcard("/")]) .launch("0.0.0.0:8000") .await } + +#[derive(Default)] +struct Index; + +#[async_trait] +impl Controller for Index { + async fn handle(&self, request: &Request) -> Result { + Ok(Response::new().html("

This is served by Rust!

")) + } +} From 684b6193b4a3560fe1a0cd57319c7dda1adc6dbd Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:07:30 -0800 Subject: [PATCH 12/15] warning --- examples/rails/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index 6653d003..d9b4aea5 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -18,7 +18,7 @@ struct Index; #[async_trait] impl Controller for Index { - async fn handle(&self, request: &Request) -> Result { + async fn handle(&self, _request: &Request) -> Result { Ok(Response::new().html("

This is served by Rust!

")) } } From e231b4f1df6d8c6c8bf33e36927c425c9b514031 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:14:21 -0800 Subject: [PATCH 13/15] Make python/ruby optional --- Cargo.lock | 17 - Cargo.toml | 4 +- examples/django/Cargo.lock | 1980 +++++++++++++++++++++++++++++++++++ examples/rails/Cargo.lock | 2006 ++++++++++++++++++++++++++++++++++++ 4 files changed, 3988 insertions(+), 19 deletions(-) create mode 100644 examples/django/Cargo.lock create mode 100644 examples/rails/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index 399f1a70..dcc29cbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,16 +588,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "django" -version = "0.1.0" -dependencies = [ - "pyo3", - "rwf", - "tokio", - "tracing", -] - [[package]] name = "dynamic-templates" version = "0.1.0" @@ -1641,13 +1631,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rails" -version = "0.1.0" -dependencies = [ - "rwf", -] - [[package]] name = "rand" version = "0.4.6" diff --git a/Cargo.toml b/Cargo.toml index 152e53e8..93c89662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ members = [ "rwf-cli", "rwf-macros", "rwf-tests", - "examples/django", "examples/request-tracking", "examples/engine", - "rwf-admin", "examples/rails", "rwf-ruby", + "rwf-admin", ] +exclude = ["examples/rails", "rwf-ruby", "examples/django"] diff --git a/examples/django/Cargo.lock b/examples/django/Cargo.lock new file mode 100644 index 00000000..325f5d71 --- /dev/null +++ b/examples/django/Cargo.lock @@ -0,0 +1,1980 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cc" +version = "1.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "django" +version = "0.1.0" +dependencies = [ + "pyo3", + "rwf", + "tokio", + "tracing", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pluralizer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4616e94b67b8b61846ea69d4bf041a62147d569d16f437689229e2677d38c" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "postgres-protocol" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", + "serde", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rwf" +version = "0.1.5" +dependencies = [ + "aes", + "aes-gcm-siv", + "async-trait", + "base64", + "bindgen", + "bytes", + "cc", + "colored", + "notify", + "once_cell", + "parking_lot", + "pyo3", + "rand", + "rayon", + "regex", + "rwf-macros", + "serde", + "serde_json", + "sha1", + "thiserror", + "time", + "tokio", + "tokio-postgres", + "toml", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "rwf-macros" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250b073095aab2f99aff0cc793b85db8ddb5fcd9581a694a9c75dea28deaad9c" +dependencies = [ + "pluralizer", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/examples/rails/Cargo.lock b/examples/rails/Cargo.lock new file mode 100644 index 00000000..1c52f977 --- /dev/null +++ b/examples/rails/Cargo.lock @@ -0,0 +1,2006 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cc" +version = "1.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.7", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if 1.0.0", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.6.3", + "rustc_version", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api 0.4.12", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version", + "smallvec 0.6.14", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.5.7", + "smallvec 1.13.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pluralizer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4616e94b67b8b61846ea69d4bf041a62147d569d16f437689229e2677d38c" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", + "serde", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rails" +version = "0.1.0" +dependencies = [ + "rwf", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rwf" +version = "0.1.5" +dependencies = [ + "aes", + "aes-gcm-siv", + "async-trait", + "base64", + "bindgen 0.65.1", + "bytes", + "cc", + "colored", + "notify", + "once_cell", + "parking_lot 0.12.3", + "rand", + "rayon", + "regex", + "rwf-macros", + "rwf-ruby", + "serde", + "serde_json", + "sha1", + "thiserror", + "time", + "tokio", + "tokio-postgres", + "toml", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "rwf-macros" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250b073095aab2f99aff0cc793b85db8ddb5fcd9581a694a9c75dea28deaad9c" +dependencies = [ + "pluralizer", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rwf-ruby" +version = "0.1.0" +dependencies = [ + "bindgen 0.68.1", + "cc", + "libc", + "once_cell", + "parking_lot 0.9.0", + "thiserror", + "tracing", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot 0.12.3", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot 0.12.3", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec 1.13.2", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall 0.5.7", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" From fbdf224609d09d11c15d41f87941b36dc4f592e5 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:14:48 -0800 Subject: [PATCH 14/15] remove ruby from ci --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e167a117..e000da9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,6 @@ jobs: with: toolchain: stable override: true - - name: Install libruby - run: sudo apt update && sudo apt install ruby-dev -y - name: Install cargo-nextest run: cargo install cargo-nextest - name: Test From e3bb6b61d363f383f977f99172139b3d8c7c1b62 Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Thu, 7 Nov 2024 10:50:23 -0800 Subject: [PATCH 15/15] docs --- docs/docs/migrating-from-rails.md | 69 +++++++++++++++++++++++++++++++ examples/rails/.cargo/config.toml | 1 + examples/rails/src/main.rs | 2 +- rwf/src/controller/mod.rs | 7 +++- 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 docs/docs/migrating-from-rails.md create mode 120000 examples/rails/.cargo/config.toml diff --git a/docs/docs/migrating-from-rails.md b/docs/docs/migrating-from-rails.md new file mode 100644 index 00000000..a9271e04 --- /dev/null +++ b/docs/docs/migrating-from-rails.md @@ -0,0 +1,69 @@ +# Migrating from Rails + +Rwf comes with its own [Rack](https://github.com/rack/rack) server which you can run side by side with your [controllers](controllers/index.md). This allows you to run your Rails app inside Rwf, while moving traffic from Rails controllers to Rwf controllers without downtime. + +## Running Rails + +!!! note + Our Rack server is currently highly experimental. Ruby doesn't have ergonomic bindings to Rust (unlike [Python](migrating-from-python.md)), so + a lot of this had to be written specifically for this project. + +### Ruby + +Depending on your operating system, Ruby installation may differ. + +#### Linux + +On Linux, you can install Ruby using your package manager. Since Rails uses Bundler to manage dependencies, make sure you set the `GEM_HOME` environment variable to a folder writable by your UNIX user, e.g. `~/.gems`: + +```bash +mkdir -p ~/.gems +export GEM_HOME=~/.gems +``` + +Since we'll be compiling our Ruby bindings from source, make sure to install the Ruby headers as well. On most systems, that'll come from the `ruby-dev` package. + +#### Mac OS + +Mac OS comes with its own Ruby version, however it's very much out of date and won't work with modern Rails apps. You'll need to install Ruby from [homebrew](https://brew.sh/): + +```bash +brew install ruby +``` + +When building a Rwf app with Ruby bindings, you'll need to make sure the linker can find the right Ruby library. Since you'll have two versions of Ruby installed at this point, the linker will get confused and use the system one, which won't work. To get around this, create a `.cargo/config` file in your project, and add the right library path to the linker arguments: + +```toml +[build] +rustflags = ["-C", "link-args=-L/opt/homebrew/Cellar/ruby/3.3.4/lib"] +``` + +The path here will depend on the version of Ruby you have installed with homebrew. + +### Running the app + +Running a Rails app with Rwf requires only adding the built-in Rack controller to your server: + +```rust +use rwf::prelude::*; +use rwf::http::{Server, self}; +use rwf::controllers::RackController; + +#[tokio::main] +async fn main() -> Result<(), http::Error> { + Server::new(vec![ + RackController::new("path/to/your/rails/app") + .wildcard("/") + ]) + .launch("0.0.0.0:8000") + .await +} +``` + +The `RackController` takes the path (relative or absolute) to your Rails application as an argument. + + + +## Learn more + +- [examples/rails](https://github.com/levkk/rwf/tree/main/examples/rails) diff --git a/examples/rails/.cargo/config.toml b/examples/rails/.cargo/config.toml new file mode 120000 index 00000000..30fa1cea --- /dev/null +++ b/examples/rails/.cargo/config.toml @@ -0,0 +1 @@ +config \ No newline at end of file diff --git a/examples/rails/src/main.rs b/examples/rails/src/main.rs index d9b4aea5..473b081d 100644 --- a/examples/rails/src/main.rs +++ b/examples/rails/src/main.rs @@ -1,4 +1,4 @@ -use rwf::controller::rack::RackController; +use rwf::controller::RackController; use rwf::http::{self, Server}; use rwf::prelude::*; diff --git a/rwf/src/controller/mod.rs b/rwf/src/controller/mod.rs index 07f583a1..653fddb5 100644 --- a/rwf/src/controller/mod.rs +++ b/rwf/src/controller/mod.rs @@ -4,8 +4,6 @@ pub mod auth; pub mod engine; pub mod error; pub mod middleware; -#[cfg(feature = "rack")] -pub mod rack; pub mod ser; pub mod static_files; pub mod turbo_stream; @@ -16,6 +14,11 @@ pub mod wsgi; #[cfg(feature = "wsgi")] pub use wsgi::WsgiController; +#[cfg(feature = "rack")] +pub mod rack; +#[cfg(feature = "rack")] +pub use rack::RackController; + pub use auth::{AllowAll, AuthHandler, Authentication, BasicAuth, DenyAll, Session, SessionId}; pub use engine::Engine; pub use error::Error;