diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..906dc3670 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,38 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "bootstrap_form", + + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + "dockerComposeFile": ["../compose.yml", "../compose.override.yml"], + + // The 'service' property is the name of the service for the container that VS Code should + // use. Update this value and .devcontainer/docker-compose.yml to the real service name. + "service": "shell", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/app", + // "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + "shutdownAction": "none" + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "reid" +} diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 89ec201be..d5b865374 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -3,19 +3,20 @@ name: Ruby on: push: pull_request: + workflow_dispatch: jobs: Lint: runs-on: ubuntu-latest env: - BUNDLE_GEMFILE: gemfiles/6.1.gemfile + BUNDLE_GEMFILE: gemfiles/7.1.gemfile steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 with: - ruby-version: 3.0.6 + ruby-version: 3.2.7 bundler-cache: true # Disabled since it requires access not granted by GitHub Actions for PRs # - name: Danger @@ -31,8 +32,8 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: [ '3.2', '3.1', '3.0', 'ruby-head' ] - gemfile: [ '7.1', '7.0', '6.1', 'edge' ] + ruby-version: [ '3.4', '3.3', '3.2', 'ruby-head' ] + gemfile: [ '8.0', '7.2', '7.1', 'edge' ] env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: @@ -53,6 +54,12 @@ jobs: with: working-directory: demo bundler-cache: true + ruby-version: 3.4.2 - name: Run tests working-directory: demo run: bundle exec rake test:all + # - name: Commit changed screenshots and other files + # if: failure() + # working-directory: demo + # run: bundle exec rake commit + diff --git a/.gitignore b/.gitignore index 44b16c241..0f46ab259 100644 --- a/.gitignore +++ b/.gitignore @@ -4,14 +4,22 @@ .npm/_npx .idea -log/*.log -pkg/ +/doc/ +/log/*.log +/pkg/ +/tmp/ + demo/db/*.sqlite3 demo/db/*.sqlite3-shm demo/db/*.sqlite3-wal demo/doc/screenshots/**/*.diff.png +demo/doc/screenshots/**/*.base.png demo/log/*.log +demo/storage demo/tmp/ + +test/dummy/ + *.gem .rbenv-gemsets *.swp @@ -27,10 +35,6 @@ Vagrantfile # For the demo app. -# Ignore uploaded files in development. -demo/storage/* -!demo/storage/.keep - demo/public/assets **/.byebug_history @@ -47,13 +51,14 @@ demo/.yarn-integrity demo/vendor/bundle # For stuff that gets created if using the Dockerfile image -.bundle/ +/.bundle/ .cache/ vendor/bundle # or .local/share/pry/pry_history if you need to be more exact .local/ .irb_history +.rdbg_history .byebug_history # For Debian images with Bash .bash_history @@ -61,4 +66,12 @@ vendor/bundle .ash_history .sqlite_history +compose.override.yml docker-compose.override.yml + +.vscode-server/ +.dotnet/ +.gnupg/ +.ssh/ +.gitconfig +.gk/ diff --git a/.rubocop.yml b/.rubocop.yml index f9e797b99..15e25d3ff 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,12 +1,12 @@ -require: +plugins: - rubocop-performance - rubocop-rails AllCops: DisplayCopNames: true DisplayStyleGuide: true - TargetRubyVersion: 3.0 - TargetRailsVersion: 6.1 + TargetRubyVersion: 3.2 + TargetRailsVersion: 7.1 NewCops: enable Exclude: - bin/* diff --git a/.yarnrc b/.yarnrc index 60afeac13..d95b8811b 100644 --- a/.yarnrc +++ b/.yarnrc @@ -2,4 +2,4 @@ # yarn lockfile v1 -lastUpdateCheck 1698606246341 +lastUpdateCheck 1741731093687 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6f497426..00e9f4b91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,140 +50,65 @@ You may find the [demo application](#the-demo-application) useful for developmen - If your PR fixes an issues, be sure to put "Fixes #nnn" in the description of the PR (where `nnn` is the issue number). Github will automatically close the issue when the PR is merged. - When the PR is submitted, check if GitHub Actions ran all the tests successfully, and didn't raise any issues. -### 7. Done - -Somebody will shortly review your pull request and if everything is good, it will be -merged into the main branch. Eventually the gem will be published with your changes. - -### Coding guidelines - -This project uses [RuboCop](https://github.com/bbatsov/rubocop) to enforce standard Ruby coding -guidelines. - -- Test that your contribution passes with `rake rubocop`. -- RuboCop is also run as part of the full test suite with `bundle exec rake`. -- Note the Travis build will fail and your PR cannot be merged if RuboCop finds offences. - -Note that most editors have plugins to run RuboCop as you type, or when you save a file. You may find it well worth your time to install and configure the RuboCop plugin for your editor. Read the [RuboCop documentation](https://rubocop.readthedocs.io/en/latest/integration_with_other_tools/). +When you create or update a pull request, GitHub automatically runs tests that generate the screenshots in the [`README.md`](/README.md). If any of the screenshots change, GitHub will add an additional commit to your branch, with the updated screenshots. -### Supported Versions of Ruby and Rails - -The goal of `bootstrap_form` is to support all versions of Rails currently supported for bug fixes and security issues. We do not test against versions supported for severe security issues. We test against the minimum [version of Ruby required](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#ruby-versions) for those versions of Rails. - -The Ruby on Rails support policy is [here](https://guides.rubyonrails.org/maintenance_policy.html). - -### Developing with Docker - -This repository offers experimental support support for a couple of ways to develop using Docker, if you're interested: +Normally, the screenshots should _not_ change. If the screenshots changed, please review them _carefully_. Some clear reasons why you would want to keep the changed screenshots: -- Using `docker-compose`. This way is less tested, and is an attempt to make the Docker container a more complete environment where you can conveniently develop and release the gem. -- Using just a simple Dockerfile. This way works for simple testing, but doesn't make it easy to release the gem, among other things. +- Your PR was fixing behaviour that was wrong in the screenshot. +- You added new examples in the documentation, so there are new screenshots. +- A change to the images used by GitHub in their actions changes the behaviour of Chrome, although if you think it's this you should probably prepare a separate PR that _only_ updates the screenshots, so it's clear what the change is and why we're making the change. -Docker is _not_ required to work on this gem. - -#### Using `docker-compose` - -The `docker-compose` approach should link to enough of your networking configuration that you can release the gem. -However, you have to do some of the configuration yourself, because it's dependent on your host operating system. - -First, build the image for whatever version of Ruby you need (typically either the earliest supported version or the latest): +Unless you have one of the above reasons, or you have a good explanation for why the screenshots have changed with your PR, you need to get rid of the changed screenshots (i.e. revert the last commit) and fix your PR so the screenshots don't change. (The reason you should revert the commit with the screenshots is so that the next time you push, GitHub will compare against the original screenshots, not the ones changed by your previous push.) Here's how to get rid of the changed screenshots: ```bash -RUBY_VERSION=3.2 docker-compose build +git pull # to bring the additional commit to your local branch. +git revert HEAD # to remove the changes. ``` -You can run a shell in a Docker container that pretty much should behave like a Debian distribution with: +Then fix the code and push your branch again. -```bash -docker-compose run --service-ports shell -``` - -(`--service-ports` exposes port 3000 so you can browse to the demo app on `localhost:3000`. If you just want to run a one-off command, or run the test suite, leave off the `--service-ports`.) - -The following instructions work for an Ubuntu host, and will probably work for other common Linux distributions. - -Add a `docker-compose.override.yml` in the local directory, that looks like this: - -```docker-compose.yml -version: '3.3' - -# https://blog.giovannidemizio.eu/2021/05/24/how-to-set-user-and-group-in-docker-compose/ - -services: - shell: - # You have to set the user and group for this process, because you're going to be - # creating all kinds of files from inside the container, that need to persist - # outside the container. - # Change `1000:1000` to the user and default group of your laptop user. - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - ~/.gem/credentials:/app/.gem/credentials:ro - # $HOME here is your host computer's `~`, e.g. `/home/reid`. - # `ssh` explicitly looks for its config in the home directory from `/etc/passwd`, - # so the target for this has to look like your home directory on the host. - - ~/.ssh:${HOME}/.ssh:ro - - ${SSH_AUTH_SOCK}:/ssh-agent - environment: - - SSH_AUTH_SOCK=/ssh-agent -``` +If the change was intended, a comment in the PR explaing why the change is expected would be very much appreciated. More than appreciated. It will avoid us having to ask you for an explanation. -You may have to change the `1000:1000` to the user and group IDs of your laptop. You may also have to change the `version` parameter to match the version of the `docker-compose.yml` file. - -Adapting the above `docker-compose.override.yml` for MacOS should be relatively straight-forward. Windows users, I'm afraid you're on your own. If you figure this out, a PR documenting how to do it would be most welcome. - -The above doesn't allow you to run the system tests. To keep the image small, it doesn't include Chrome or any other browser. - -There is an experimental `docker-compose-system-test.yml` file, that runs the `bootstrap_forms` docker container along with an off-the-shelf Selenium container. To start this configuration: - -```bash -RUBY_VERSION=3.2 docker-compose -f docker-compose-system-test.yml -f docker-compose.override.yml up& -RUBY_VERSION=3.2 docker-compose -f docker-compose-system-test.yml -f docker-compose.override.yml exec shell /bin/bash -``` - -(Sometimes, on shutdown, the Rails server PID file isn't removed, and so the above will fail. `rm demo/tmp/pids/server.pid` will fix it.) - -Once in the shell: +You can run the tests that generate the screenshots locally, but unless your environment is very much like the GitHub CI environment -- Ubuntu running Chrome with default fonts -- all the screenshots will be reported as having changed. To generate the screenshots: ```bash cd demo -bundle exec rails test:system +bundle exec rails test:all # or test:system ``` -Note that this system test approach is highly experimental and has some rough edges. The docker compose file and/or steps to run system tests may change. The tests currently fail, because the files with which they're being compared were generated on a Mac, but the Docker containers are running Linux. +The [Docker development environment](#using-docker compose) appears to generate screenshots that are the same as what GitHub generates. -#### Simple Dockerfile +Finally, maintainers may sometimes push changes directly to `main` or use other workflows to update the code. If pushing to `main` generates a commit for screenshot changes, please consider reverting your change immediately by executing the above `pull` and `revert` and another `push`, for the sanity of users who are using the edge (`main` branch) version of the gem. At any rate, review the changes promptly and use your judgement. -This repository includes a `Dockerfile` to build an image with the minimum `bootstrap_form`-supported Ruby environment. To build the image: +### 7. Done -```bash -docker build --tag bootstrap_form . -``` +Somebody will shortly review your pull request and if everything is good, it will be +merged into the main branch. Eventually the gem will be published with your changes. -This builds an image called `bootstrap_form`. You can change that to any tag you wish. Just make sure you use the same tag name in the `docker run` command. +### Coding guidelines -If you want to use a different Ruby version, or a smaller Linux distribution (although the distro may be missing tools you need): +This project uses [RuboCop](https://github.com/bbatsov/rubocop) to enforce standard Ruby coding +guidelines. -```bash -docker build --build-arg "RUBY_VERSION=3.0" --build-arg "DISTRO=slim-buster" --tag bootstrap_form . -``` +- Test that your contribution passes with `rake rubocop`. +- RuboCop is also run as part of the full test suite with `bundle exec rake`. +- Note the Travis build will fail and your PR cannot be merged if RuboCop finds offences. + +Note that most editors have plugins to run RuboCop as you type, or when you save a file. You may find it well worth your time to install and configure the RuboCop plugin for your editor. Read the [RuboCop documentation](https://rubocop.readthedocs.io/en/latest/integration_with_other_tools/). -Then run the container you built with the shell, and create the bundle: +### Supported Versions of Ruby and Rails -```bash -docker run --volume "$PWD:/app" --user $UID:`grep ^$USERNAME /etc/passwd | cut -d: -f4` -it bootstrap_form /bin/bash -bundle install -``` +The goal of `bootstrap_form` is to support all versions of Rails currently supported for bug fixes and security issues. We do not test against versions supported for severe security issues. We test against the minimum [version of Ruby required](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#ruby-versions) for those versions of Rails. -You can run tests in the container as normal, with `rake test`. +The Ruby on Rails support policy is [here](https://guides.rubyonrails.org/maintenance_policy.html). -(Some of that command line is need for Linux hosts, to run the container as the current user.) +### Developing with Docker -One of the disadvantages of this approach is that you can't release the gem from here, because the Docker container doesn't have access to your SSH credentials, or the right user name, or perhaps other things needed to release a gem. But for simple testing, it works. +This repository offers experimental support for development using Docker. Docker is _not_ required to do development work on this gem. -#### Troubleshooting Docker +One advantage of the Docker environment is that it allows you to generate and compare the screenshots with the expected screenshots in the repo. It also allows you to develop in an environment that is isolated from your own computer's set-up, so there's less risk of breaking other things you might be doing with your computer. -- With the above configuration, the gems are kept in `vendor/bundle` on your hosts, which is `$GEM_HOME` or `/app/vendor/bundle` in the running Docker container. If you're having permission problems when switching versions of Ruby or Rails, you can try `sudo rm -rf vendor/bundle` on the host, then run `BUNDLE_GEMFILES=gemfiles/7.0.gemfile bundle update` in the Docker container to re-install all the gems with the right permissions. +If you're intested in trying the Docker approach, read the [documentation](DOCKER.md). ### The Demo Application diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 000000000..d8b88d038 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,75 @@ +# Working With Docker + +This repository offers experimental support support for developing using Docker, if you're interested. Docker is _not_ required to work on this gem. + +The `docker compose` approach should link to enough of your networking configuration that you can release the gem. +However, you have to do some of the configuration yourself, because it's dependent on your host operating system. + +## Set-Up + +Put your personal and OS-specific configuration in a `compose.override.yml` file. + +The following instructions work for an Ubuntu host, and will probably work for other common Linux distributions. Add a `compose.override.yml` in the local directory, that looks like this: + +```compose.override.yml +version: '3.3' + +# https://blog.giovannidemizio.eu/2021/05/24/how-to-set-user-and-group-in-docker compose/ + +services: + shell: + # You have to set the user and group for this process, because you're going to be + # creating all kinds of files from inside the container, that need to persist + # outside the container. + # Change `1000:1000` to the user and default group of your laptop user. + user: 1000:1000 + volumes: + - /etc/passwd:/etc/passwd:ro + - ~/.gem/credentials:/app/.gem/credentials:ro + # $HOME here is your host computer's `~`, e.g. `/home/reid`. + # `ssh` explicitly looks for its config in the home directory from `/etc/passwd`, + # so the target for this has to look like your home directory on the host. + - ~/.ssh:${HOME}/.ssh:ro + - ${SSH_AUTH_SOCK}:/ssh-agent + environment: + - SSH_AUTH_SOCK=/ssh-agent +``` + +You may have to change the `1000:1000` to the user and group IDs of your laptop. You may also have to change the `version` parameter to match the version of the `docker compose.yml` file. + +Adapting the above `compose.override.yml` for MacOS should be relatively straight-forward. Windows users, I'm afraid you're on your own. If you figure this out, a PR documenting how to do it would be most welcome. + +## Running + +Start the containers: + +```bash +docker compose up -d +``` + +You may need to install or update the gems: + +```bash +docker compose exec -it shell bundle install +``` + +To get a shell in the container: + +```bash +docker compose exec -it shell /bin/bash +``` + +Once in the shell: + +```bash +cd demo +bundle exec rails test:system +``` + +Note that this system test approach is highly experimental and has some rough edges. The docker compose file and/or steps to run system tests may change. The tests currently fail, because the files with which they're being compared were generated on a Mac, but the Docker containers are running Linux. + +## Troubleshooting Docker + +- With the above configuration, the gems are kept in `vendor/bundle` on your hosts, which is `$GEM_HOME` or `/app/vendor/bundle` in the running Docker container. If you're having permission problems when switching versions of Ruby or Rails, you can try `sudo rm -rf vendor/bundle` on the host, then run `BUNDLE_GEMFILES=gemfiles/7.0.gemfile bundle update` in the Docker container to re-install all the gems with the right permissions. +- Sometimes, on shutdown, the Rails server PID file isn't removed, and so the above will fail. `rm demo/tmp/pids/server.pid` will fix it. + diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9a1e75891..000000000 --- a/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -ARG RUBY_VERSION=3.0 -ARG DISTRO=bullseye - -FROM ruby:$RUBY_VERSION-$DISTRO - -ARG NODE_MAJOR=18 - -RUN mkdir -p /app -ENV HOME /app -WORKDIR /app - -ENV GEM_HOME $HOME/vendor/bundle -ENV BUNDLE_APP_CONFIG="$GEM_HOME" -ENV PATH ./bin:$GEM_HOME/bin:$PATH -RUN (echo 'docker'; echo 'docker') | passwd root - -# Rails wants a newer version of node than we get with the Debian distro. -RUN curl -fsSL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash - && apt-get install -y nodejs -RUN corepack enable && corepack prepare yarn@stable --activate -RUN apt install -y -q sqlite3 - -EXPOSE 3000 diff --git a/Gemfile b/Gemfile index 70b52f6ad..71dc2ee4a 100644 --- a/Gemfile +++ b/Gemfile @@ -3,5 +3,9 @@ eval File.read(gems), binding, gems # rubocop: disable Security/Eval require "#{__dir__}/lib/bootstrap_form/version" +gem "bigdecimal" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "drb" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "mutex_m" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") gem "rails", BootstrapForm::REQUIRED_RAILS_VERSION gem "sprockets-rails", require: "sprockets/railtie" +gem "sqlite3", ">= 1.4" diff --git a/README.md b/README.md index dc39c303f..d25aac302 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ Some other nice things that `bootstrap_form` does for you are: `bootstrap_form` supports at a minimum the currently supported versions of Ruby and Rails: -* Ruby 3.0+ (https://www.ruby-lang.org/en/downloads/branches/) -* Rails 6.1+ (https://guides.rubyonrails.org/maintenance_policy.html) +* Ruby 3.2+ (https://www.ruby-lang.org/en/downloads/branches/) +* Rails 7.1+ (https://guides.rubyonrails.org/maintenance_policy.html) * Bootstrap 5.0+ ## Installation @@ -59,7 +59,7 @@ If you use Rails in the default mode without any pre-processor, you'll have to a If you followed the [official bootstrap installation guide](https://github.com/twbs/bootstrap-rubygem#a-ruby-on-rails), you'll probably have switched to SCSS. In this case add the following line to your `application.scss`: ```scss -@import "rails_bootstrap_forms"; +@import "rails_bootstrap_forms.css"; ``` ## Usage @@ -1064,7 +1064,7 @@ will be rendered as: - + ``` @@ -1586,6 +1586,7 @@ If you want to display a custom inline error for a specific attribute not repres ![Example 50](demo/doc/screenshots/bootstrap/readme/50_example.png "Example 50") ```erb <%= bootstrap_form_for @user_with_error do |f| %> + <%= f.errors_on :email %> <% end %> ``` @@ -1594,15 +1595,19 @@ Which outputs: ```html
+
Email is invalid
``` +Note that the `invalid-feedback` `div` is hidden unless there is a preceding element under the same parent that has class `is-invalid`. For the examples, we've artificially added a hidden input. + You can hide the attribute name like this: ![Example 51](demo/doc/screenshots/bootstrap/readme/51_example.png "Example 51") ```erb <%= bootstrap_form_for @user_with_error do |f| %> + <%= f.errors_on :email, hide_attribute_name: true %> <% end %> ``` @@ -1611,6 +1616,7 @@ Which outputs: ```html
+
is invalid
``` @@ -1620,6 +1626,7 @@ You can also use a custom class for the wrapping div, like this: ![Example 52](demo/doc/screenshots/bootstrap/readme/52_example.png "Example 52") ```erb <%= bootstrap_form_for @user_with_error do |f| %> + <%= f.errors_on :email, custom_class: 'custom-error' %> <% end %> ``` @@ -1628,10 +1635,13 @@ Which outputs: ```html
+
Email is invalid
``` +Note that adding the custom class removes the default `invalid-feedback` class. If you still want the default `invalid-feedback` formatting, add it to your `custom_class`es. + ## Required Fields A label that is associated with a required field is automatically annotated with diff --git a/Rakefile b/Rakefile index 49e7366cc..b8ab30407 100644 --- a/Rakefile +++ b/Rakefile @@ -28,3 +28,26 @@ desc 'Run RuboCop checks' RuboCop::RakeTask.new(:rubocop) task default: %i[test rubocop:autocorrect] + +namespace :test do + desc "Run tests for all supported Rails versions, with current Ruby version" + task :all do + original_gemfile = ENV["BUNDLE_GEMFILE"] + gemfiles = Dir.glob("gemfiles/*.gemfile").reject { |f| File.basename(f) == "common.gemfile" } + gemfiles.each do |f| + ENV["BUNDLE_GEMFILE"] = f + system("bundle check") || system("bundle install") + system("bundle exec rake test") + end + + original_directory = Dir.pwd + Dir.chdir("demo") + ENV.delete("BUNDLE_GEMFILE") + system("bundle check") || system("bundle install") + system("bundle exec rake test:all") + + ensure + original_gemfile.nil? ? ENV.delete("BUNDLE_GEMFILE") : ENV["BUNDLE_GEMFILE"] = original_gemfile + Dir.chdir(original_directory) unless original_directory.nil? + end +end diff --git a/bootstrap_form.gemspec b/bootstrap_form.gemspec index 4118aaf8e..9cfabe09b 100644 --- a/bootstrap_form.gemspec +++ b/bootstrap_form.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.bindir = "exe" s.require_paths = ["lib"] - s.required_ruby_version = ">= 3.0" + s.required_ruby_version = ">= 3.2" s.add_dependency("actionpack", BootstrapForm::REQUIRED_RAILS_VERSION) s.add_dependency("activemodel", BootstrapForm::REQUIRED_RAILS_VERSION) diff --git a/docker-compose-system-test.yml b/compose.yml similarity index 52% rename from docker-compose-system-test.yml rename to compose.yml index 250fbe273..d0e4f36dc 100644 --- a/docker-compose-system-test.yml +++ b/compose.yml @@ -1,39 +1,28 @@ -version: '3.3' - +# Set up the Selenium container based on the Selenium official: +# https://github.com/SeleniumHQ/docker-selenium/blob/trunk/docker-compose-v3.yml +# And: +# https://medium.com/@retrorubies/chrome-as-a-service-for-rails-testing-b1a45e70fec1 services: - app: &app - build: - context: . - args: - NODE_MAJOR: "12" - YARN_VERSION: "1.22.4" - RUBY_VERSION: ${RUBY_VERSION} - image: bootstrap-form:latest-$RUBY_VERSION - tmpfs: - - /tmp - shell: - <<: *app + image: lenchoreyes/jade:rails-app-${RUBY_VERSION:-3.3}-sqlite-${DISTRO:-bookworm} stdin_open: true tty: true volumes: - .:/app:cached environment: - - SSH_AUTH_SOCK=/ssh-agent - - NODE_ENV=development - - BOOTSNAP_CACHE_DIR=/usr/local/bundle/_bootsnap - - WEB_CONCURRENCY=1 - HISTFILE=/app/.bash_history - SELENIUM_HOST=selenium - SELENIUM_PORT=4444 - TEST_APP_HOST=shell - TEST_APP_PORT=3001 ports: + - "3000:3000" - "3001:3001" command: /bin/bash selenium: - image: selenium/standalone-chrome:118.0 + image: selenium/standalone-chrome:133.0 + shm_size: 2gb logging: driver: none stdin_open: true @@ -43,3 +32,4 @@ services: ports: - '4444:4444' - '5900:5900' + - '7900:7900' diff --git a/demo/.ruby-version b/demo/.ruby-version index 14dbd3772..fb72f061b 100644 --- a/demo/.ruby-version +++ b/demo/.ruby-version @@ -1 +1 @@ -ruby-3.0.6 +ruby-3.2.7 diff --git a/demo/Gemfile b/demo/Gemfile index b8e0dc784..ea6b41a55 100644 --- a/demo/Gemfile +++ b/demo/Gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" gem "bootstrap_form", path: ".." -gem "rails", "~> 7.1.1" +gem "rails", "~> 8.0.0" gem "bootsnap", require: false gem "cssbundling-rails" @@ -11,7 +11,7 @@ gem "jbuilder" gem "jsbundling-rails" gem "puma" gem "sprockets-rails", require: "sprockets/railtie" -gem "sqlite3", "~> 1.4" +gem "sqlite3" gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby] group :development, :test do @@ -24,5 +24,6 @@ end group :test do gem "capybara-screenshot-diff" + gem "chunky_png", "~> 1.4" gem "selenium-webdriver" end diff --git a/demo/Gemfile.lock b/demo/Gemfile.lock index 30e81b307..f26e41fb9 100644 --- a/demo/Gemfile.lock +++ b/demo/Gemfile.lock @@ -2,134 +2,133 @@ PATH remote: .. specs: bootstrap_form (5.4.0) - actionpack (>= 6.1) - activemodel (>= 6.1) + actionpack (>= 7.1) + activemodel (>= 7.1) GEM remote: https://rubygems.org/ specs: - actioncable (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) + actioncable (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.1) - actionpack (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activesupport (= 7.1.1) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) + mail (>= 2.8.0) + actionmailer (8.0.2) + actionpack (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activesupport (= 8.0.2) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.1) - actionview (= 7.1.1) - activesupport (= 7.1.1) + actionpack (8.0.2) + actionview (= 8.0.2) + activesupport (= 8.0.2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.1) - actionpack (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + useragent (~> 0.16) + actiontext (8.0.2) + actionpack (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.1) - activesupport (= 7.1.1) + actionview (8.0.2) + activesupport (= 8.0.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.1) - activesupport (= 7.1.1) + activejob (8.0.2) + activesupport (= 8.0.2) globalid (>= 0.3.6) - activemodel (7.1.1) - activesupport (= 7.1.1) - activerecord (7.1.1) - activemodel (= 7.1.1) - activesupport (= 7.1.1) + activemodel (8.0.2) + activesupport (= 8.0.2) + activerecord (8.0.2) + activemodel (= 8.0.2) + activesupport (= 8.0.2) timeout (>= 0.4.0) - activestorage (7.1.1) - actionpack (= 7.1.1) - activejob (= 7.1.1) - activerecord (= 7.1.1) - activesupport (= 7.1.1) + activestorage (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activesupport (= 8.0.2) marcel (~> 1.0) - activesupport (7.1.1) + activesupport (8.0.2) base64 + benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - addressable (2.8.5) - public_suffix (>= 2.0.2, < 6.0) - base64 (0.1.1) - bigdecimal (3.1.4) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) bindex (0.8.1) - bootsnap (1.16.0) + bootsnap (1.18.4) msgpack (~> 1.2) - builder (3.2.4) - capybara (3.39.2) + builder (3.3.0) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - capybara-screenshot-diff (1.8.3) - actionpack (>= 6.1, < 8) + capybara-screenshot-diff (1.10.2) + actionpack (>= 7.0, < 9) capybara (>= 2, < 4) - chunky_png (~> 1.3) chunky_png (1.4.0) - concurrent-ruby (1.2.2) - connection_pool (2.4.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) crass (1.0.6) - cssbundling-rails (1.3.3) + cssbundling-rails (1.4.3) railties (>= 6.0.0) - date (3.3.3) - debug (1.8.0) - irb (>= 1.5.0) - reline (>= 0.3.1) - drb (2.1.1) - ruby2_keywords - erubi (1.12.0) + date (3.4.1) + debug (1.10.0) + irb (~> 1.10) + reline (>= 0.3.8) + drb (2.2.1) + erubi (1.13.1) globalid (1.2.1) activesupport (>= 6.1) - htmlbeautifier (1.4.2) - i18n (1.14.1) + htmlbeautifier (1.4.3) + i18n (1.14.7) concurrent-ruby (~> 1.0) - io-console (0.6.0) - irb (1.8.3) - rdoc - reline (>= 0.3.8) - jbuilder (2.11.5) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jsbundling-rails (1.2.1) + jsbundling-rails (1.3.1) railties (>= 6.0.0) - loofah (2.21.4) + logger (1.6.6) + loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -137,137 +136,156 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) + marcel (1.0.4) matrix (0.4.2) mini_mime (1.1.5) - mini_portile2 (2.8.5) - minitest (5.20.0) - msgpack (1.7.2) - mutex_m (0.1.2) - net-imap (0.4.2) + mini_portile2 (2.8.8) + minitest (5.25.4) + msgpack (1.8.0) + net-imap (0.5.6) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.4.0) + net-smtp (0.5.1) net-protocol - nio4r (2.5.9) - nokogiri (1.15.4) + nio4r (2.7.4) + nokogiri (1.18.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.15.4-x86_64-darwin) + nokogiri (1.18.3-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.3-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.3-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.3-x86_64-darwin) racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + nokogiri (1.18.3-x86_64-linux-gnu) racc (~> 1.4) - psych (5.1.1.1) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + psych (5.2.3) + date stringio - public_suffix (5.0.3) - puma (6.4.0) + public_suffix (6.0.1) + puma (6.6.0) nio4r (~> 2.0) - racc (1.7.1) - rack (3.0.8) - rack-session (2.0.0) + racc (1.8.1) + rack (3.1.12) + rack-session (2.1.0) + base64 (>= 0.1.0) rack (>= 3.0.0) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) - rails (7.1.1) - actioncable (= 7.1.1) - actionmailbox (= 7.1.1) - actionmailer (= 7.1.1) - actionpack (= 7.1.1) - actiontext (= 7.1.1) - actionview (= 7.1.1) - activejob (= 7.1.1) - activemodel (= 7.1.1) - activerecord (= 7.1.1) - activestorage (= 7.1.1) - activesupport (= 7.1.1) + rails (8.0.2) + actioncable (= 8.0.2) + actionmailbox (= 8.0.2) + actionmailer (= 8.0.2) + actionpack (= 8.0.2) + actiontext (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activemodel (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) bundler (>= 1.15.0) - railties (= 7.1.1) + railties (= 8.0.2) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.1.1) - actionpack (= 7.1.1) - activesupport (= 7.1.1) - irb + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) - rake (13.1.0) - rdoc (6.5.0) + rake (13.2.1) + rdoc (6.12.0) psych (>= 4.0.0) - regexp_parser (2.8.2) - reline (0.3.9) + regexp_parser (2.10.0) + reline (0.6.0) io-console (~> 0.5) - rexml (3.2.6) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - selenium-webdriver (4.14.0) + rexml (3.4.1) + rubyzip (2.4.1) + securerandom (0.4.1) + selenium-webdriver (4.29.1) + 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.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sqlite3 (1.6.7) - mini_portile2 (~> 2.8.0) - sqlite3 (1.6.7-x86_64-darwin) - sqlite3 (1.6.7-x86_64-linux) - stringio (3.0.8) - thor (1.3.0) - timeout (0.4.0) + sqlite3 (2.6.0-aarch64-linux-gnu) + sqlite3 (2.6.0-arm-linux-gnu) + sqlite3 (2.6.0-arm64-darwin) + sqlite3 (2.6.0-x86-linux-gnu) + sqlite3 (2.6.0-x86_64-darwin) + sqlite3 (2.6.0-x86_64-linux-gnu) + stringio (3.1.5) + thor (1.3.2) + timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + uri (1.0.3) + useragent (0.16.11) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webrick (1.8.1) - websocket (1.2.10) - websocket-driver (0.7.6) + websocket (1.2.11) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.12) + zeitwerk (2.7.2) PLATFORMS - ruby - x86_64-darwin-21 + aarch64-linux + arm-linux + arm64-darwin + x86-linux + x86_64-darwin x86_64-linux DEPENDENCIES bootsnap bootstrap_form! capybara-screenshot-diff + chunky_png (~> 1.4) cssbundling-rails debug htmlbeautifier jbuilder jsbundling-rails puma - rails (~> 7.1.1) + rails (~> 8.0.0) selenium-webdriver sprockets-rails - sqlite3 (~> 1.4) + sqlite3 tzinfo-data web-console BUNDLED WITH - 2.4.1 + 2.5.9 diff --git a/demo/app/helpers/bootstrap_helper.rb b/demo/app/helpers/bootstrap_helper.rb index a69c77156..b28a0573d 100644 --- a/demo/app/helpers/bootstrap_helper.rb +++ b/demo/app/helpers/bootstrap_helper.rb @@ -1,6 +1,6 @@ module BootstrapHelper - def form_with_source(&block) - form_html = capture(&block) + def form_with_source(&) + form_html = capture(&) tag.div(class: "example") do concat(form_html) diff --git a/demo/app/models/faux_user.rb b/demo/app/models/faux_user.rb index 752762f02..d6485a3ff 100644 --- a/demo/app/models/faux_user.rb +++ b/demo/app/models/faux_user.rb @@ -3,7 +3,7 @@ class FauxUser def initialize(attributes={}) attributes.each do |name, value| - send("#{name}=", value) + send(:"#{name}=", value) end end end diff --git a/demo/app/models/user.rb b/demo/app/models/user.rb index 67a627e1f..342938557 100644 --- a/demo/app/models/user.rb +++ b/demo/app/models/user.rb @@ -10,12 +10,12 @@ class User < ApplicationRecord validates :status, presence: true, if: -> { age > 42 } validates :misc, presence: true, unless: -> { feet == 5 } - has_one :address + has_one :address, dependent: nil accepts_nested_attributes_for :address - has_rich_text(:life_story) if Rails::VERSION::STRING > "6" + has_rich_text(:life_story) - def always + def always # rubocop:disable Naming/PredicateMethod true end diff --git a/demo/app/views/bootstrap/form.html.erb b/demo/app/views/bootstrap/form.html.erb index 74d8bf654..105e462e4 100644 --- a/demo/app/views/bootstrap/form.html.erb +++ b/demo/app/views/bootstrap/form.html.erb @@ -48,7 +48,7 @@ <%= form.password_field :password, placeholder: "Password" %> <%= form.check_box :terms, label: "Agree to Terms" %> <%= form.collection_check_boxes :misc, @collection, :id, :street %> - <%= form.rich_text_area(:life_story) if Rails::VERSION::STRING > "6" %> + <%= form.rich_text_area(:life_story) %> <%= form.submit %> <% end %> <% end %> diff --git a/demo/app/views/layouts/application.html.erb b/demo/app/views/layouts/application.html.erb index 745064afb..bbff7eb85 100644 --- a/demo/app/views/layouts/application.html.erb +++ b/demo/app/views/layouts/application.html.erb @@ -3,8 +3,6 @@ Hello, world! - - diff --git a/demo/bin/setup b/demo/bin/setup index 0f5f3857b..be3db3c0d 100755 --- a/demo/bin/setup +++ b/demo/bin/setup @@ -1,7 +1,6 @@ #!/usr/bin/env ruby require "fileutils" -# path to your application root. APP_ROOT = File.expand_path("..", __dir__) def system!(*args) @@ -14,7 +13,6 @@ FileUtils.chdir APP_ROOT do # 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 ==" @@ -23,11 +21,14 @@ FileUtils.chdir APP_ROOT do # end puts "\n== Preparing database ==" - system! "bin/rails db:setup" + 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" + unless ARGV.include?("--skip-server") + puts "\n== Starting development server ==" + STDOUT.flush # flush the output before exec(2) so that it displays + exec "bin/dev" + end end diff --git a/demo/config/environments/development.rb b/demo/config/environments/development.rb index 456d26f88..63cf6835d 100644 --- a/demo/config/environments/development.rb +++ b/demo/config/environments/development.rb @@ -3,9 +3,7 @@ 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. + # Make code changes take effect immediately without server restart. config.enable_reloading = true # Do not eager load code on boot. @@ -14,63 +12,58 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable server timing + # Enable server timing. config.server_timing = true - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. + # Enable/disable Action Controller caching. By default Action Controller caching is disabled. + # Run rails dev:cache to toggle Action Controller 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}" - } + 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 + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local if config.respond_to?(:active_storage) # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + # Make template changes take effect immediately. config.action_mailer.perform_caching = false + # Set localhost to be used by links generated in mailer templates. + 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 + # Append comments with runtime information tags to SQL queries in logs. + config.active_record.query_log_tags_enabled = 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 + 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 + # 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/demo/config/environments/production.rb b/demo/config/environments/production.rb index 1c2922edb..d4cf79f41 100644 --- a/demo/config/environments/production.rb +++ b/demo/config/environments/production.rb @@ -6,88 +6,84 @@ # 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. + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). 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 + # Full error reports are disabled. + config.consider_all_requests_local = false - # Enable static file serving from the `/public` folder (turn off if using NGINX/Apache for it). - config.public_file_server.enabled = true - - # Compress CSS using a preprocessor. - # config.assets.css_compressor = :sass + # Turn on fragment caching in view templates. + config.action_controller.perform_caching = true - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + # Cache assets for far-future expiry since they are all digest stamped. + config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" } # 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. + config.assume_ssl = true # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true - # Log to STDOUT by default - config.logger = ActiveSupport::Logger.new($stdout) - .tap { |logger| logger.formatter = Logger::Formatter.new } - .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } - # Prepend all log lines with the following tags. + # Log to STDOUT with the current request id as a default log tag. config.log_tags = [:request_id] + config.logger = ActiveSupport::TaggedLogging.logger($stdout) - # Info include 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". + # Change to "debug" to log everything (including potentially personally-identifiable information!) config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") - # Use a different cache store in production. - # config.cache_store = :mem_cache_store + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" + + # Don't log any deprecations. + config.active_support.report_deprecations = false - # 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 = "demo_production" + # Replace the default in-process memory cache store with a durable alternative. + # config.cache_store = :mem_cache_store - config.action_mailer.perform_caching = false + # Replace the default in-process and non-durable queuing backend for Active Job. + # config.active_job.queue_adapter = :resque # 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 + # Set host to be used by links generated in mailer templates. + config.action_mailer.default_url_options = { host: "example.com" } + + # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit. + # config.action_mailer.smtp_settings = { + # user_name: Rails.application.credentials.dig(:smtp, :user_name), + # password: Rails.application.credentials.dig(:smtp, :password), + # address: "smtp.example.com", + # port: 587, + # authentication: :plain + # } + # 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/demo/config/environments/test.rb b/demo/config/environments/test.rb index d1e70ecee..c2095b117 100644 --- a/demo/config/environments/test.rb +++ b/demo/config/environments/test.rb @@ -1,5 +1,3 @@ -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 @@ -17,48 +15,39 @@ # 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.enabled = true - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{1.hour.to_i}" - } + # Configure public file server for tests with cache-control for performance. + config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false + # Show full error reports. + config.consider_all_requests_local = true config.cache_store = :null_store - # Raise exceptions instead of rendering exception templates. + # 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 if config.respond_to?(:active_storage) - - config.action_mailer.perform_caching = false + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test # 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 + # Set host to be used by links generated in mailer templates. + config.action_mailer.default_url_options = { host: "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 + # 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/demo/config/initializers/assets.rb b/demo/config/initializers/assets.rb index 58275b40d..d5d27eca1 100644 --- a/demo/config/initializers/assets.rb +++ b/demo/config/initializers/assets.rb @@ -6,8 +6,3 @@ # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path Rails.application.config.assets.paths << Rails.root.join("node_modules/bootstrap-icons/font") - -# 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/demo/config/initializers/filter_parameter_logging.rb b/demo/config/initializers/filter_parameter_logging.rb index 262e86202..f72dcdfaa 100644 --- a/demo/config/initializers/filter_parameter_logging.rb +++ b/demo/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += %i[ - passw secret token _key crypt salt certificate otp ssn + passw email secret token _key crypt salt certificate otp ssn cvv cvc ] diff --git a/demo/config/puma.rb b/demo/config/puma.rb index 44256ac09..a248513b2 100644 --- a/demo/config/puma.rb +++ b/demo/config/puma.rb @@ -1,56 +1,41 @@ -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. +# 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. # -threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. # -port ENV.fetch("PORT", 3000) - -# Specifies the `environment` that Puma will run in. +# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You +# should only set this value when you want to run 2 or more workers. The +# default is already 1. # -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). +# 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 +# prioritize throughput over latency. # -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. +# 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. # -# preload_app! - -# If you are preloading your application and using Active Record, it's -# recommended that you close any connections to the database before workers -# are forked to prevent connection leakage. +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. # -# before_fork do -# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) -# end +# 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 -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted, this block will be run. If you are using the `preload_app!` -# option, you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, as Ruby -# cannot share connections between processes. -# -# on_worker_boot do -# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -# end -# +# 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 `rails restart` command. +# Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart + +# Run the Solid Queue supervisor inside of Puma for single-server deployments +plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] + +# 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/demo/db/migrate/20241013183910_add_service_name_to_active_storage_blobs.active_storage.rb b/demo/db/migrate/20241013183910_add_service_name_to_active_storage_blobs.active_storage.rb new file mode 100644 index 000000000..b10e40e8a --- /dev/null +++ b/demo/db/migrate/20241013183910_add_service_name_to_active_storage_blobs.active_storage.rb @@ -0,0 +1,22 @@ +# This migration comes from active_storage (originally 20190112182829) +class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0] + def up + return unless table_exists?(:active_storage_blobs) + + return if column_exists?(:active_storage_blobs, :service_name) + + add_column :active_storage_blobs, :service_name, :string + + if (configured_service = ActiveStorage::Blob.service.name) + ActiveStorage::Blob.unscoped.update_all(service_name: configured_service) # rubocop:disable Rails/SkipsModelValidations + end + + change_column :active_storage_blobs, :service_name, :string, null: false + end + + def down + return unless table_exists?(:active_storage_blobs) + + remove_column :active_storage_blobs, :service_name + end +end diff --git a/demo/db/migrate/20241013183911_create_active_storage_variant_records.active_storage.rb b/demo/db/migrate/20241013183911_create_active_storage_variant_records.active_storage.rb new file mode 100644 index 000000000..95fd27fa6 --- /dev/null +++ b/demo/db/migrate/20241013183911_create_active_storage_variant_records.active_storage.rb @@ -0,0 +1,28 @@ +# This migration comes from active_storage (originally 20191206030411) +class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + # Use Active Record's configured type for primary key + create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t| + t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type + t.string :variation_digest, null: false + + t.index %i[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end + + private + + def primary_key_type + config = Rails.configuration.generators + config.options[config.orm][:primary_key_type] || :primary_key + end + + def blobs_primary_key_type + pkey_name = connection.primary_key(:active_storage_blobs) + pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name } + pkey_column.bigint? ? :bigint : pkey_column.type + end +end diff --git a/demo/db/migrate/20241013183912_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb b/demo/db/migrate/20241013183912_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb new file mode 100644 index 000000000..93c8b85ad --- /dev/null +++ b/demo/db/migrate/20241013183912_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb @@ -0,0 +1,8 @@ +# This migration comes from active_storage (originally 20211119233751) +class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + change_column_null(:active_storage_blobs, :checksum, true) + end +end diff --git a/demo/db/schema.rb b/demo/db/schema.rb index c96edeb37..a98d9bb92 100644 --- a/demo/db/schema.rb +++ b/demo/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20_220_109_230_956) do +ActiveRecord::Schema.define(version: 2024_10_13_183912) do create_table "action_mailbox_inbound_emails", force: :cascade do |t| t.integer "status", default: 0, null: false t.string "message_id", null: false @@ -25,9 +25,9 @@ t.text "body" t.string "record_type", null: false t.bigint "record_id", null: false - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index %w[record_type record_id name], name: "index_action_text_rich_texts_uniqueness", unique: true + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true end create_table "active_storage_attachments", force: :cascade do |t| @@ -35,9 +35,9 @@ t.string "record_type", null: false t.bigint "record_id", null: false t.bigint "blob_id", null: false - t.datetime "created_at", precision: 6, null: false + t.datetime "created_at", null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" - t.index %w[record_type record_id name blob_id], name: "index_active_storage_attachments_uniqueness", unique: true + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end create_table "active_storage_blobs", force: :cascade do |t| @@ -48,14 +48,14 @@ t.string "service_name", null: false t.bigint "byte_size", null: false t.string "checksum" - t.datetime "created_at", precision: 6, null: false + t.datetime "created_at", null: false t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end create_table "active_storage_variant_records", force: :cascade do |t| t.bigint "blob_id", null: false t.string "variation_digest", null: false - t.index %w[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end create_table "addresses", force: :cascade do |t| @@ -64,13 +64,13 @@ t.string "city" t.string "state" t.string "zip_code" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "bogons", force: :cascade do |t| - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "food_waste_main" end @@ -83,8 +83,8 @@ t.text "preferences" t.boolean "terms", default: false t.string "type" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" diff --git a/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png b/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png index 532bff724..5d65f427e 100644 Binary files a/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png and b/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png differ diff --git a/demo/doc/screenshots/bootstrap/index/01_with_validation_error.png b/demo/doc/screenshots/bootstrap/index/01_with_validation_error.png index 864dd678c..5572357de 100644 Binary files a/demo/doc/screenshots/bootstrap/index/01_with_validation_error.png and b/demo/doc/screenshots/bootstrap/index/01_with_validation_error.png differ diff --git a/demo/doc/screenshots/bootstrap/index/02_inline_form.png b/demo/doc/screenshots/bootstrap/index/02_inline_form.png index f07c87564..f648a7d67 100644 Binary files a/demo/doc/screenshots/bootstrap/index/02_inline_form.png and b/demo/doc/screenshots/bootstrap/index/02_inline_form.png differ diff --git a/demo/doc/screenshots/bootstrap/index/03_simple_action_text_example.png b/demo/doc/screenshots/bootstrap/index/03_simple_action_text_example.png index 18a1f9245..62a99ea7c 100644 Binary files a/demo/doc/screenshots/bootstrap/index/03_simple_action_text_example.png and b/demo/doc/screenshots/bootstrap/index/03_simple_action_text_example.png differ diff --git a/demo/doc/screenshots/bootstrap/index/04_floating_labels.png b/demo/doc/screenshots/bootstrap/index/04_floating_labels.png index c30039bd0..6d2c4b8ea 100644 Binary files a/demo/doc/screenshots/bootstrap/index/04_floating_labels.png and b/demo/doc/screenshots/bootstrap/index/04_floating_labels.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/00_example.png b/demo/doc/screenshots/bootstrap/readme/00_example.png index 2a0c955b3..22df0b467 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/00_example.png and b/demo/doc/screenshots/bootstrap/readme/00_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/01_example.png b/demo/doc/screenshots/bootstrap/readme/01_example.png index 78f8611a8..ee6af64b8 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/01_example.png and b/demo/doc/screenshots/bootstrap/readme/01_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/02_example.png b/demo/doc/screenshots/bootstrap/readme/02_example.png index 268dcb18f..90d23a482 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/02_example.png and b/demo/doc/screenshots/bootstrap/readme/02_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/03_example.png b/demo/doc/screenshots/bootstrap/readme/03_example.png index ba465fdb1..3fc8489f4 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/03_example.png and b/demo/doc/screenshots/bootstrap/readme/03_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/04_example.png b/demo/doc/screenshots/bootstrap/readme/04_example.png index 638697625..c7f4e4ff9 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/04_example.png and b/demo/doc/screenshots/bootstrap/readme/04_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/05_example.png b/demo/doc/screenshots/bootstrap/readme/05_example.png index f37aeee2b..0f2f293b7 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/05_example.png and b/demo/doc/screenshots/bootstrap/readme/05_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/06_example.png b/demo/doc/screenshots/bootstrap/readme/06_example.png index ea01ff3a4..df624a876 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/06_example.png and b/demo/doc/screenshots/bootstrap/readme/06_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/07_example.png b/demo/doc/screenshots/bootstrap/readme/07_example.png index 2e2738ea0..488c287e6 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/07_example.png and b/demo/doc/screenshots/bootstrap/readme/07_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/08_example.png b/demo/doc/screenshots/bootstrap/readme/08_example.png index 9b64b1b03..c86ac27a9 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/08_example.png and b/demo/doc/screenshots/bootstrap/readme/08_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/09_example.png b/demo/doc/screenshots/bootstrap/readme/09_example.png index 2aa352b7d..bb3e5011d 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/09_example.png and b/demo/doc/screenshots/bootstrap/readme/09_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/10_example.png b/demo/doc/screenshots/bootstrap/readme/10_example.png index 750322145..2530ebb1f 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/10_example.png and b/demo/doc/screenshots/bootstrap/readme/10_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/11_example.png b/demo/doc/screenshots/bootstrap/readme/11_example.png index b16c2634e..4c7f780bb 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/11_example.png and b/demo/doc/screenshots/bootstrap/readme/11_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/12_example.png b/demo/doc/screenshots/bootstrap/readme/12_example.png index 922620f1b..c20ef42f8 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/12_example.png and b/demo/doc/screenshots/bootstrap/readme/12_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/13_example.png b/demo/doc/screenshots/bootstrap/readme/13_example.png index 577787737..507ef4e2e 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/13_example.png and b/demo/doc/screenshots/bootstrap/readme/13_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/14_example.png b/demo/doc/screenshots/bootstrap/readme/14_example.png index b994080c9..ecea01443 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/14_example.png and b/demo/doc/screenshots/bootstrap/readme/14_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/15_example.png b/demo/doc/screenshots/bootstrap/readme/15_example.png index 133771556..821fbe73e 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/15_example.png and b/demo/doc/screenshots/bootstrap/readme/15_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/16_example.png b/demo/doc/screenshots/bootstrap/readme/16_example.png index d3ebd2fa7..379bc3f4b 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/16_example.png and b/demo/doc/screenshots/bootstrap/readme/16_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/17_example.png b/demo/doc/screenshots/bootstrap/readme/17_example.png index 896075bfa..586e26a18 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/17_example.png and b/demo/doc/screenshots/bootstrap/readme/17_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/18_example.png b/demo/doc/screenshots/bootstrap/readme/18_example.png index c7cd229c1..45426a416 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/18_example.png and b/demo/doc/screenshots/bootstrap/readme/18_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/19_example.png b/demo/doc/screenshots/bootstrap/readme/19_example.png index c30eaa69d..8cf822aa2 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/19_example.png and b/demo/doc/screenshots/bootstrap/readme/19_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/20_example.png b/demo/doc/screenshots/bootstrap/readme/20_example.png index 2c9f0c09d..d38bbf0ed 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/20_example.png and b/demo/doc/screenshots/bootstrap/readme/20_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/21_example.png b/demo/doc/screenshots/bootstrap/readme/21_example.png index e4b8dd649..5e20c6325 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/21_example.png and b/demo/doc/screenshots/bootstrap/readme/21_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/22_example.png b/demo/doc/screenshots/bootstrap/readme/22_example.png index 71306897d..54c7d85bf 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/22_example.png and b/demo/doc/screenshots/bootstrap/readme/22_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/23_example.png b/demo/doc/screenshots/bootstrap/readme/23_example.png index 9d456ef8e..61ef9c01e 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/23_example.png and b/demo/doc/screenshots/bootstrap/readme/23_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/24_example.png b/demo/doc/screenshots/bootstrap/readme/24_example.png index a1f24eb25..da055a518 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/24_example.png and b/demo/doc/screenshots/bootstrap/readme/24_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/25_example.png b/demo/doc/screenshots/bootstrap/readme/25_example.png index c8c1bca07..5408da930 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/25_example.png and b/demo/doc/screenshots/bootstrap/readme/25_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/26_example.png b/demo/doc/screenshots/bootstrap/readme/26_example.png index 03cf4f86b..a864ec646 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/26_example.png and b/demo/doc/screenshots/bootstrap/readme/26_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/27_example.png b/demo/doc/screenshots/bootstrap/readme/27_example.png index 8a7af9ec9..1df0a867a 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/27_example.png and b/demo/doc/screenshots/bootstrap/readme/27_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/28_example.png b/demo/doc/screenshots/bootstrap/readme/28_example.png index 650b2aec3..68a8a7080 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/28_example.png and b/demo/doc/screenshots/bootstrap/readme/28_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/29_example.png b/demo/doc/screenshots/bootstrap/readme/29_example.png index 7570ce7a8..eda667772 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/29_example.png and b/demo/doc/screenshots/bootstrap/readme/29_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/30_example.png b/demo/doc/screenshots/bootstrap/readme/30_example.png index 7570ce7a8..eda667772 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/30_example.png and b/demo/doc/screenshots/bootstrap/readme/30_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/31_example.png b/demo/doc/screenshots/bootstrap/readme/31_example.png index b3153872a..0dbd943fd 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/31_example.png and b/demo/doc/screenshots/bootstrap/readme/31_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/32_example.png b/demo/doc/screenshots/bootstrap/readme/32_example.png index 9e83ec134..c2e9ac3ee 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/32_example.png and b/demo/doc/screenshots/bootstrap/readme/32_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/33_example.png b/demo/doc/screenshots/bootstrap/readme/33_example.png index 30edc299e..cd7f81181 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/33_example.png and b/demo/doc/screenshots/bootstrap/readme/33_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/34_example.png b/demo/doc/screenshots/bootstrap/readme/34_example.png index d804a412a..63b851c08 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/34_example.png and b/demo/doc/screenshots/bootstrap/readme/34_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/35_example.png b/demo/doc/screenshots/bootstrap/readme/35_example.png index ee42af4bc..1bc461689 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/35_example.png and b/demo/doc/screenshots/bootstrap/readme/35_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/36_example.png b/demo/doc/screenshots/bootstrap/readme/36_example.png index e261e0469..f4d76bb76 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/36_example.png and b/demo/doc/screenshots/bootstrap/readme/36_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/37_example.png b/demo/doc/screenshots/bootstrap/readme/37_example.png index b7217522d..993f43d6a 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/37_example.png and b/demo/doc/screenshots/bootstrap/readme/37_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/38_example.png b/demo/doc/screenshots/bootstrap/readme/38_example.png index 2dd87500a..a51825ebf 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/38_example.png and b/demo/doc/screenshots/bootstrap/readme/38_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/39_example.png b/demo/doc/screenshots/bootstrap/readme/39_example.png index 74ebae279..bcb761e90 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/39_example.png and b/demo/doc/screenshots/bootstrap/readme/39_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/40_example.png b/demo/doc/screenshots/bootstrap/readme/40_example.png index 7dff6588c..433d10a51 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/40_example.png and b/demo/doc/screenshots/bootstrap/readme/40_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/41_example.png b/demo/doc/screenshots/bootstrap/readme/41_example.png index b2e63ab3d..9d870ab7f 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/41_example.png and b/demo/doc/screenshots/bootstrap/readme/41_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/42_example.png b/demo/doc/screenshots/bootstrap/readme/42_example.png index 39042fa1e..07ba29be7 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/42_example.png and b/demo/doc/screenshots/bootstrap/readme/42_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/43_example.png b/demo/doc/screenshots/bootstrap/readme/43_example.png index 985394761..4c45e77af 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/43_example.png and b/demo/doc/screenshots/bootstrap/readme/43_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/44_example.png b/demo/doc/screenshots/bootstrap/readme/44_example.png index 113e3acb6..1a804a984 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/44_example.png and b/demo/doc/screenshots/bootstrap/readme/44_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/45_example.png b/demo/doc/screenshots/bootstrap/readme/45_example.png index b786657c5..1116bc523 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/45_example.png and b/demo/doc/screenshots/bootstrap/readme/45_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/46_example.png b/demo/doc/screenshots/bootstrap/readme/46_example.png index bee9737c1..f57ab1b9d 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/46_example.png and b/demo/doc/screenshots/bootstrap/readme/46_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/47_example.png b/demo/doc/screenshots/bootstrap/readme/47_example.png index 10eef24e3..de9655e7c 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/47_example.png and b/demo/doc/screenshots/bootstrap/readme/47_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/48_example.png b/demo/doc/screenshots/bootstrap/readme/48_example.png index 07e458690..dd04773c4 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/48_example.png and b/demo/doc/screenshots/bootstrap/readme/48_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/49_example.png b/demo/doc/screenshots/bootstrap/readme/49_example.png index 0fb21c6d1..b065278c9 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/49_example.png and b/demo/doc/screenshots/bootstrap/readme/49_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/50_example.png b/demo/doc/screenshots/bootstrap/readme/50_example.png index 8748b832f..07dc83a07 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/50_example.png and b/demo/doc/screenshots/bootstrap/readme/50_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/51_example.png b/demo/doc/screenshots/bootstrap/readme/51_example.png index 8748b832f..d0c6f864b 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/51_example.png and b/demo/doc/screenshots/bootstrap/readme/51_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/52_example.png b/demo/doc/screenshots/bootstrap/readme/52_example.png index 5fe3d9f0d..340e9244f 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/52_example.png and b/demo/doc/screenshots/bootstrap/readme/52_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/53_example.png b/demo/doc/screenshots/bootstrap/readme/53_example.png index 4a0f562b5..a64518273 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/53_example.png and b/demo/doc/screenshots/bootstrap/readme/53_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/54_example.png b/demo/doc/screenshots/bootstrap/readme/54_example.png index 140d21049..b461c59f6 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/54_example.png and b/demo/doc/screenshots/bootstrap/readme/54_example.png differ diff --git a/demo/doc/screenshots/bootstrap/readme/55_example.png b/demo/doc/screenshots/bootstrap/readme/55_example.png index 776ffa92b..f116d753b 100644 Binary files a/demo/doc/screenshots/bootstrap/readme/55_example.png and b/demo/doc/screenshots/bootstrap/readme/55_example.png differ diff --git a/demo/lib/tasks/commit.rake b/demo/lib/tasks/commit.rake new file mode 100644 index 000000000..bab35e029 --- /dev/null +++ b/demo/lib/tasks/commit.rake @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +desc <<~DESC + Commit changed files, if any. Meant to be used in CI to commit automatically created files + (e.g. screenshots). +DESC +task :commit do # rubocop:disable Rails/RakeEnvironment + msg = <<~MSG + Changed in CI + Please review the changes in the files in this commit + carefully, as they were automatically generated during CI. + Run `git pull` to bring the changes into your local branch. + Then, if you do not want the changes, run `git revert HEAD`. + MSG + system("git config user.name github-actions") + system("git config user.email github-actions@github.com") + system("git diff --exit-code -s || git commit --all -m '#{msg}' && git push") +end diff --git a/demo/package.json b/demo/package.json index 1d68f51a6..e9359675e 100644 --- a/demo/package.json +++ b/demo/package.json @@ -3,9 +3,9 @@ "private": true, "dependencies": { "@popperjs/core": "^2.11.8", - "@rails/actioncable": "^7.1.0", - "@rails/actiontext": "^7.1.0", - "@rails/activestorage": "^7.1.0", + "@rails/actioncable": "^8.0.0", + "@rails/actiontext": "^8.0.0", + "@rails/activestorage": "^8.0.0", "@rails/ujs": "^7.1.0", "bootstrap": "^5.3.2", "bootstrap-icons": "^1.11.1", @@ -18,5 +18,6 @@ "scripts": { "build": "node_modules/.bin/esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets", "build:css": "node_modules/.bin/sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/demo/public/400.html b/demo/public/400.html new file mode 100644 index 000000000..282dbc8cc --- /dev/null +++ b/demo/public/400.html @@ -0,0 +1,114 @@ + + + + + + + The server cannot process the request due to a client error (400 Bad Request) + + + + + + + + + + + + + +
+
+ +
+
+

The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.

+
+
+ + + + diff --git a/demo/public/404.html b/demo/public/404.html new file mode 100644 index 000000000..c0670bc87 --- /dev/null +++ b/demo/public/404.html @@ -0,0 +1,114 @@ + + + + + + + The page you were looking for doesn’t exist (404 Not found) + + + + + + + + + + + + + +
+
+ +
+
+

The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.

+
+
+ + + + diff --git a/demo/public/406-unsupported-browser.html b/demo/public/406-unsupported-browser.html new file mode 100644 index 000000000..9532a9ccd --- /dev/null +++ b/demo/public/406-unsupported-browser.html @@ -0,0 +1,114 @@ + + + + + + + Your browser is not supported (406 Not Acceptable) + + + + + + + + + + + + + +
+
+ +
+
+

Your browser is not supported.
Please upgrade your browser to continue.

+
+
+ + + + diff --git a/demo/public/422.html b/demo/public/422.html new file mode 100644 index 000000000..8bcf06014 --- /dev/null +++ b/demo/public/422.html @@ -0,0 +1,114 @@ + + + + + + + The change you wanted was rejected (422 Unprocessable Entity) + + + + + + + + + + + + + +
+
+ +
+
+

The change you wanted was rejected. Maybe you tried to change something you didn’t have access to. If you’re the application owner check the logs for more information.

+
+
+ + + + diff --git a/demo/public/500.html b/demo/public/500.html new file mode 100644 index 000000000..d77718c3a --- /dev/null +++ b/demo/public/500.html @@ -0,0 +1,114 @@ + + + + + + + We’re sorry, but something went wrong (500 Internal Server Error) + + + + + + + + + + + + + +
+
+ +
+
+

We’re sorry, but something went wrong.
If you’re the application owner check the logs for more information.

+
+
+ + + + diff --git a/demo/public/icon.png b/demo/public/icon.png new file mode 100644 index 000000000..c4c9dbfbb Binary files /dev/null and b/demo/public/icon.png differ diff --git a/demo/public/icon.svg b/demo/public/icon.svg new file mode 100644 index 000000000..04b34bf83 --- /dev/null +++ b/demo/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/demo/public/robots.txt b/demo/public/robots.txt new file mode 100644 index 000000000..c19f78ab6 --- /dev/null +++ b/demo/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/demo/test/application_system_test_case.rb b/demo/test/application_system_test_case.rb index 842f0d850..7127aabda 100644 --- a/demo/test/application_system_test_case.rb +++ b/demo/test/application_system_test_case.rb @@ -1,7 +1,9 @@ require "test_helper" +require "capybara_screenshot_diff/minitest" class ApplicationSystemTestCase < ActionDispatch::SystemTestCase include Capybara::Screenshot::Diff + include CapybaraScreenshotDiff::DSL class << self def remote_selenium? = @remote_selenium ||= ENV["SELENIUM_HOST"].present? || ENV["SELENIUM_PORT"].present? @@ -16,9 +18,11 @@ def remote_selenium? = @remote_selenium ||= ENV["SELENIUM_HOST"].present? || ENV {} end - driven_by :selenium, using: :headless_chrome, screen_size: [960, 720], options: options do |capabilities| - capabilities.add_argument "force-device-scale-factor=1" - capabilities.add_argument "lang=#{ENV.fetch('LANG', 'en_CA')}" + # Debugging tip: Change to `chrome` (not headless) and then browse to: + # http://localhost:7900/?autoconnect=1&resize=scale&password=secret. You can watch the fun there. + driven_by :selenium, using: :headless_chrome, screen_size: [960, 800], options: options do |capabilities| + capabilities.add_argument("force-device-scale-factor=1") + capabilities.add_argument("lang=#{ENV.fetch('LANG', 'en_CA')}") end if remote_selenium? @@ -27,6 +31,8 @@ def remote_selenium? = @remote_selenium ||= ENV["SELENIUM_HOST"].present? || ENV Capybara.app_host = "http://#{ENV.fetch('TEST_APP_HOST', 'shell')}:#{ENV.fetch('TEST_APP_PORT', Capybara.server_port)}" end - Capybara::Screenshot.enabled = ENV["CI"].blank? + Capybara::Screenshot.enabled = true + Capybara::Screenshot::Diff.driver = ENV.fetch("SCREENSHOT_DRIVER", "chunky_png").to_sym + Capybara.server = :puma, { Silent: true } end diff --git a/demo/test/system/bootstrap_test.rb b/demo/test/system/bootstrap_test.rb index b9247ed25..c5c0d3006 100644 --- a/demo/test/system/bootstrap_test.rb +++ b/demo/test/system/bootstrap_test.rb @@ -1,4 +1,5 @@ require "application_system_test_case" +require "capybara_screenshot_diff/minitest" class BootstrapTest < ApplicationSystemTestCase setup { screenshot_section :bootstrap } @@ -15,6 +16,8 @@ class BootstrapTest < ApplicationSystemTestCase sleep 0.5 screenshot header.text.downcase.tr(" ", "_"), crop: bounds(example), color_distance_limit: 2 end + + self.assertions += 1 # To avoid a warning message. end HEADERS = ["Generated HTML", "This generates", "Which outputs", "will be rendered as"].join("|").freeze @@ -71,7 +74,7 @@ class BootstrapTest < ApplicationSystemTestCase #{html} MD end - augmented_readme.gsub!(/127.0.0.1:\d+/, "test.host") + augmented_readme.gsub!(/(127.0.0.1:\d+|shell:3001)/, "test.host") File.write(File.expand_path("../../../README.md", __dir__), augmented_readme) end diff --git a/demo/yarn.lock b/demo/yarn.lock index 738a102c7..a1217656b 100644 --- a/demo/yarn.lock +++ b/demo/yarn.lock @@ -117,29 +117,29 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@rails/actioncable@^7.1.0": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.1.1.tgz#e8c49769d41f35a4473133c259cc98adc04dddf8" - integrity sha512-ZRJ9rdwFQQjRbtgJnweY0/4UQyxN6ojEGRdib0JkjnuIciv+4ok/aAeZmBJqNreTMaBqS0eHyA9hCArwN58opg== - -"@rails/actiontext@^7.1.0": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-7.1.1.tgz#01ac458df5eff06c971b48001b03d73c67b50462" - integrity sha512-AdDK+gUa+akys1kFNgZXB6+rz3n60Of+c4TyJzUe6p5cBIjTimMaMWPSr76fyORXEBFKsCUH9bigXSAoUZKU0A== +"@rails/actioncable@^8.0.0": + version "8.0.200" + resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-8.0.200.tgz#1d27d9d55e45266e061190db045925e0b4d53d6b" + integrity sha512-EDqWyxck22BHmv1e+mD8Kl6GmtNkhEPdRfGFT7kvsv1yoXd9iYrqHDVAaR8bKmU/syC5eEZ2I5aWWxtB73ukMw== + +"@rails/actiontext@^8.0.0": + version "8.0.200" + resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-8.0.200.tgz#b0ed8ba50ec31dd8fcc7a8885403c58b2b4f378d" + integrity sha512-p9SVulDmWKMChQpNYFrRBGa2aIfMSw8vyCRW9bwHzihFB8eAZ1NE6ak88nr037gwurbCBv4UVdnwndtuh2piAA== dependencies: - "@rails/activestorage" ">= 7.1.0-alpha" + "@rails/activestorage" ">= 8.0.0-alpha" -"@rails/activestorage@>= 7.1.0-alpha", "@rails/activestorage@^7.1.0": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-7.1.1.tgz#3f12e8ac784f460f6a9d205744354abd79a525f2" - integrity sha512-QGBj+y4fbZt/QMMpjqnpKlzCKpDGTYrvJ+qc0QLis34AfbBLVgRo7kPzmdmeOTtwvWqpcivB9CrjTcV/C/7ruA== +"@rails/activestorage@>= 8.0.0-alpha", "@rails/activestorage@^8.0.0": + version "8.0.200" + resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-8.0.200.tgz#147c088e2b4167d6d49292431bdbdf10b118d5bd" + integrity sha512-V7GnZXsAMPDWVOBv4/XpHwj5sOw5bWjidWCuUbK3Zx1xt2pOfFaeJDUG7fEWb1MwP4aW1oVVlGkJBdXVyvru0A== dependencies: spark-md5 "^3.0.1" "@rails/ujs@^7.1.0": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.1.1.tgz#f8df96e406a2a824084b637880e57c257073cb05" - integrity sha512-ywGwWNiqXN3Bb1BifVQTrkWEWcAGLHW3D0JNQMQeu57LsoluRzvnenNLPsmdoDPkrmSIASDXNsJiCIpUzFj8CA== + version "7.1.400" + resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.1.400.tgz#b2a76bdccb5197b9e866954536106386c87cfab5" + integrity sha512-YwvXm3BR5tn+VCAKYGycLejMRVZE3Ionj5gFjEeGXCZnI0Rpi+7dKpmyu90kdUY7dRUFpHTdu9zZceEzFLl38w== anymatch@~3.1.2: version "3.1.3" diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index fced7ad87..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: '3.3' - -services: - app: &app - build: - context: . - args: - NODE_MAJOR: "18" - RUBY_VERSION: ${RUBY_VERSION} - image: bootstrap-form:latest-$RUBY_VERSION - tmpfs: - - /tmp - - shell: &shell - <<: *app - stdin_open: true - tty: true - volumes: - - .:/app:cached - environment: - - SSH_AUTH_SOCK=/ssh-agent - - NODE_ENV=development - - RAILS_ENV=${RAILS_ENV:-development} - - BOOTSNAP_CACHE_DIR=/usr/local/bundle/_bootsnap - - WEB_CONCURRENCY=1 - - HISTFILE=/app/.bash_history - ports: - - "3000:3000" - command: /bin/bash diff --git a/gemfiles/6.1.gemfile b/gemfiles/6.1.gemfile deleted file mode 100644 index 3571c2224..000000000 --- a/gemfiles/6.1.gemfile +++ /dev/null @@ -1,4 +0,0 @@ -gems = "#{__dir__}/common.gemfile" -eval File.read(gems), binding, gems # rubocop: disable Security/Eval - -gem "rails", "~> 6.1.0" diff --git a/gemfiles/7.0.gemfile b/gemfiles/7.0.gemfile deleted file mode 100644 index 5de6fe5fd..000000000 --- a/gemfiles/7.0.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -gems = "#{__dir__}/common.gemfile" -eval File.read(gems), binding, gems # rubocop: disable Security/Eval - -gem "rails", "~> 7.0.2" -gem "sprockets-rails", require: "sprockets/railtie" diff --git a/gemfiles/7.1.gemfile b/gemfiles/7.1.gemfile index 43ce7b7f3..903f18780 100644 --- a/gemfiles/7.1.gemfile +++ b/gemfiles/7.1.gemfile @@ -1,5 +1,9 @@ gems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval +gem "bigdecimal" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "drb" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "mutex_m" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") gem "rails", "~> 7.1.0" gem "sprockets-rails", require: "sprockets/railtie" +gem "sqlite3", "~> 1.4" diff --git a/gemfiles/7.2.gemfile b/gemfiles/7.2.gemfile new file mode 100644 index 000000000..db3479f26 --- /dev/null +++ b/gemfiles/7.2.gemfile @@ -0,0 +1,9 @@ +gems = "#{__dir__}/common.gemfile" +eval File.read(gems), binding, gems # rubocop: disable Security/Eval + +gem "bigdecimal" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "drb" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "mutex_m" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "rails", "~> 7.2.0" +gem "sprockets-rails", require: "sprockets/railtie" +gem "sqlite3" diff --git a/gemfiles/8.0.gemfile b/gemfiles/8.0.gemfile new file mode 100644 index 000000000..8ed38009f --- /dev/null +++ b/gemfiles/8.0.gemfile @@ -0,0 +1,9 @@ +gems = "#{__dir__}/common.gemfile" +eval File.read(gems), binding, gems # rubocop: disable Security/Eval + +gem "bigdecimal" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "drb" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "mutex_m" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "rails", "~> 8.0.1" +gem "sprockets-rails", require: "sprockets/railtie" +gem "sqlite3" diff --git a/gemfiles/common.gemfile b/gemfiles/common.gemfile index 5b838b739..f65c6ff5f 100644 --- a/gemfiles/common.gemfile +++ b/gemfiles/common.gemfile @@ -15,12 +15,11 @@ group :test do gem "diffy" gem "equivalent-xml" gem "mocha" - gem "sqlite3" end group :development, :test do gem "debug" - gem "pry-byebug" + gem "ostruct" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.5.0") end group :ci do diff --git a/gemfiles/edge.gemfile b/gemfiles/edge.gemfile index 7f1498dc0..9d2de7c4c 100644 --- a/gemfiles/edge.gemfile +++ b/gemfiles/edge.gemfile @@ -1,5 +1,9 @@ gems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval +gem "bigdecimal" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "drb" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") +gem "mutex_m" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") gem "rails", git: "https://github.com/rails/rails.git", branch: "main" gem "sprockets-rails", require: "sprockets/railtie" +gem "sqlite3" diff --git a/lib/bootstrap_form.rb b/lib/bootstrap_form.rb index 23607fe07..61cc6066f 100644 --- a/lib/bootstrap_form.rb +++ b/lib/bootstrap_form.rb @@ -1,13 +1,3 @@ -# NOTE: The rich_text_area and rich_text_area_tag helpers are defined in a file -# with a different name and not in the usual autoload-reachable way. -# The following line is definitely need to make `bootstrap_form` work. -# rubocop:disable Lint/SuppressedException -begin - require "#{Gem::Specification.find_by_name('actiontext').gem_dir}/app/helpers/action_text/tag_helper" -rescue Gem::MissingSpecError -end -# rubocop:enable Lint/SuppressedException - require "action_view" require "action_pack" require "bootstrap_form/action_view_extensions/form_helper" diff --git a/lib/bootstrap_form/action_view_extensions/form_helper.rb b/lib/bootstrap_form/action_view_extensions/form_helper.rb index c60b8bcf3..a0ebf1620 100644 --- a/lib/bootstrap_form/action_view_extensions/form_helper.rb +++ b/lib/bootstrap_form/action_view_extensions/form_helper.rb @@ -33,20 +33,20 @@ def bootstrap_form_with(options={}, &block) end end - def bootstrap_form_tag(options={}, &block) + def bootstrap_form_tag(options={}, &) options[:acts_like_form_tag] = true - bootstrap_form_for("", options, &block) + bootstrap_form_for("", options, &) end - def bootstrap_fields_for(record_name, record_object=nil, options={}, &block) + def bootstrap_fields_for(record_name, record_object=nil, options={}, &) options[:builder] = BootstrapForm::FormBuilder - fields_for(record_name, record_object, options, &block) + fields_for(record_name, record_object, options, &) end - def bootstrap_fields(scope=nil, model: nil, **options, &block) + def bootstrap_fields(scope=nil, model: nil, **options, &) options[:builder] = BootstrapForm::FormBuilder - fields(scope, model: model, **options, &block) + fields(scope, model: model, **options, &) end private diff --git a/lib/bootstrap_form/components/labels.rb b/lib/bootstrap_form/components/labels.rb index c3ffad4f6..579957ed8 100644 --- a/lib/bootstrap_form/components/labels.rb +++ b/lib/bootstrap_form/components/labels.rb @@ -34,7 +34,7 @@ def label_classes(name, options, custom_label_col, group_layout) def label_layout_classes(custom_label_col, group_layout) if layout_horizontal?(group_layout) - ["col-form-label", (custom_label_col || label_col)] + ["col-form-label", custom_label_col || label_col] elsif layout_inline?(group_layout) %w[form-label me-sm-2] else @@ -43,7 +43,7 @@ def label_layout_classes(custom_label_col, group_layout) end def label_text(name, options) - label = options[:text] || object&.class&.try(:human_attribute_name, name)&.html_safe # rubocop:disable Rails/OutputSafety + label = options[:text] || object&.class&.try(:human_attribute_name, name)&.html_safe # rubocop:disable Rails/OutputSafety, Style/SafeNavigationChainLength if label_errors && error?(name) (" ".html_safe + get_error_messages(name)).prepend(label) else diff --git a/lib/bootstrap_form/form_builder.rb b/lib/bootstrap_form/form_builder.rb index fbc2fcf94..c4b425479 100644 --- a/lib/bootstrap_form/form_builder.rb +++ b/lib/bootstrap_form/form_builder.rb @@ -72,10 +72,10 @@ def add_default_form_attributes_and_form_inline(options) .compact.uniq, " ") end - def fields_for_with_bootstrap(record_name, record_object=nil, fields_options={}, &block) + def fields_for_with_bootstrap(record_name, record_object=nil, fields_options={}, &) fields_options = fields_for_options(record_object, fields_options) record_object = nil if record_object.is_a?(Hash) && record_object.extractable_options? - fields_for_without_bootstrap(record_name, record_object, fields_options, &block) + fields_for_without_bootstrap(record_name, record_object, fields_options, &) end bootstrap_alias :fields_for @@ -92,7 +92,7 @@ def fields_for_options(record_object, fields_options) %i[layout control_col inline_errors label_errors].each do |option| field_options[option] ||= options[option] end - field_options[:label_col] = field_options[:label_col].present? ? (field_options[:label_col]).to_s : options[:label_col] + field_options[:label_col] = field_options[:label_col].present? ? field_options[:label_col].to_s : options[:label_col] field_options end diff --git a/lib/bootstrap_form/form_group.rb b/lib/bootstrap_form/form_group.rb index c06a7e9c9..5039aa71a 100644 --- a/lib/bootstrap_form/form_group.rb +++ b/lib/bootstrap_form/form_group.rb @@ -29,15 +29,15 @@ def form_group_content_tag(name, field_name, without_field_name, options, html_o end end - def form_group_content(label, help_text, options, &block) # rubocop:disable Metrics/AbcSize + def form_group_content(label, help_text, options, &) # rubocop:disable Metrics/AbcSize label ||= ActiveSupport::SafeBuffer.new if group_layout_horizontal?(options[:layout]) - label + tag.div(capture(&block) + help_text, class: form_group_control_class(options)) + label + tag.div(capture(&) + help_text, class: form_group_control_class(options)) else content = ActiveSupport::SafeBuffer.new # Floating labels need to be rendered after the field content << label unless options[:floating] - content << capture(&block) + content << capture(&) content << label if options[:floating] content << help_text if help_text content diff --git a/lib/bootstrap_form/form_group_builder.rb b/lib/bootstrap_form/form_group_builder.rb index c465c1d7c..79eff7e56 100644 --- a/lib/bootstrap_form/form_group_builder.rb +++ b/lib/bootstrap_form/form_group_builder.rb @@ -6,7 +6,7 @@ module FormGroupBuilder private - def form_group_builder(method, options, html_options=nil, &block) + def form_group_builder(method, options, html_options=nil, &) no_wrapper = options[:wrapper] == false options = form_group_builder_options(options, method) @@ -21,7 +21,7 @@ def form_group_builder(method, options, html_options=nil, &block) if no_wrapper yield else - form_group(method, form_group_options, &block) + form_group(method, form_group_options, &) end end diff --git a/lib/bootstrap_form/helpers/bootstrap.rb b/lib/bootstrap_form/helpers/bootstrap.rb index 38934b235..3ed6bf13c 100644 --- a/lib/bootstrap_form/helpers/bootstrap.rb +++ b/lib/bootstrap_form/helpers/bootstrap.rb @@ -56,17 +56,17 @@ def static_control(*args) text_field_with_bootstrap(name, static_options) end - def custom_control(*args, &block) + def custom_control(*args, &) options = args.extract_options! name = args.first - form_group_builder(name, options, &block) + form_group_builder(name, options, &) end - def prepend_and_append_input(name, options, &block) + def prepend_and_append_input(name, options, &) options = options.extract!(:prepend, :append, :input_group_class).compact - input = capture(&block) || ActiveSupport::SafeBuffer.new + input = capture(&) || ActiveSupport::SafeBuffer.new input = attach_input(options, :prepend) + input + attach_input(options, :append) input << generate_error(name) @@ -75,8 +75,8 @@ def prepend_and_append_input(name, options, &block) input end - def input_with_error(name, &block) - input = capture(&block) + def input_with_error(name, &) + input = capture(&) input << generate_error(name) end diff --git a/lib/bootstrap_form/inputs/base.rb b/lib/bootstrap_form/inputs/base.rb index adf85a781..94833bd97 100644 --- a/lib/bootstrap_form/inputs/base.rb +++ b/lib/bootstrap_form/inputs/base.rb @@ -6,13 +6,15 @@ module Base extend ActiveSupport::Concern class_methods do - def bootstrap_field(field_name, control_class: nil) - define_method "#{field_name}_with_bootstrap" do |name, options={ control_class: control_class }.compact| + def bootstrap_field(field_name) + define_method :"#{field_name}_with_bootstrap" do |name, options={}| warn_deprecated_layout_value(options) + options = options.reverse_merge(control_class: "form-range") if field_name == :range_field + options = options.reverse_merge(control_class: "form-control form-control-color") if field_name == :color_field form_group_builder(name, options) do prepend_and_append_input(name, options) do options[:placeholder] ||= name if options[:floating] - send("#{field_name}_without_bootstrap".to_sym, name, options.except(:floating)) + send(:"#{field_name}_without_bootstrap", name, options.except(:floating)) end end end @@ -21,7 +23,7 @@ def bootstrap_field(field_name, control_class: nil) end def bootstrap_select_group(field_name) - define_method("#{field_name}_with_bootstrap") do |name, options={}, html_options={}| + define_method(:"#{field_name}_with_bootstrap") do |name, options={}, html_options={}| html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(name, options, html_options) do form_group_content_tag(name, field_name, "#{field_name}_without_bootstrap", options, html_options) @@ -38,8 +40,8 @@ def bootstrap_select_group(field_name) # if your application does not include the actiontext dependency due to # `rich_text_area` not being defined. def bootstrap_alias(field_name) - alias_method "#{field_name}_without_bootstrap".to_sym, field_name - alias_method field_name, "#{field_name}_with_bootstrap".to_sym + alias_method :"#{field_name}_without_bootstrap", field_name + alias_method field_name, :"#{field_name}_with_bootstrap" rescue NameError # rubocop:disable Lint/SuppressedException end end diff --git a/lib/bootstrap_form/inputs/check_box.rb b/lib/bootstrap_form/inputs/check_box.rb index 97698fdca..3c44ec6cb 100644 --- a/lib/bootstrap_form/inputs/check_box.rb +++ b/lib/bootstrap_form/inputs/check_box.rb @@ -42,7 +42,7 @@ def check_box_options(name, options) check_box_options.merge!(required_field_options(options, name)) end - def check_box_label(name, options, checked_value, &block) + def check_box_label(name, options, checked_value, &) label_name = if options[:multiple] check_box_value(name, checked_value) else @@ -50,19 +50,20 @@ def check_box_label(name, options, checked_value, &block) end label_options = { class: check_box_label_class(options) } label_options[:for] = options[:id] if options[:id].present? - label(label_name, check_box_description(name, options, &block), label_options) + label(label_name, check_box_description(name, options, &), label_options) end def check_box_description(name, options, &block) content = block ? capture(&block) : options[:label] - content || object&.class&.human_attribute_name(name) || name.to_s.humanize + # Ugh. Next Rails after 7.1 passes `false` when there's no object. + content || (object && object.class.human_attribute_name(name)) || name.to_s.humanize # rubocop:disable Style/SafeNavigation end def check_box_value(name, value) # label's `for` attribute needs to match checkbox tag's id, # IE sanitized value, IE # https://github.com/rails/rails/blob/5-0-stable/actionview/lib/action_view/helpers/tags/base.rb#L123-L125 - "#{name}_#{value.to_s.gsub(/\s/, '_').gsub(/[^-[[:word:]]]/, '').mb_chars.downcase}" + "#{name}_#{value.to_s.gsub(/\s/, '_').gsub(/[^-[[:word:]]]/, '').downcase}" end def check_box_classes(name, options) diff --git a/lib/bootstrap_form/inputs/collection_check_boxes.rb b/lib/bootstrap_form/inputs/collection_check_boxes.rb index e70a4156d..659911862 100644 --- a/lib/bootstrap_form/inputs/collection_check_boxes.rb +++ b/lib/bootstrap_form/inputs/collection_check_boxes.rb @@ -7,7 +7,7 @@ module CollectionCheckBoxes include Base include InputsCollection - included do + included do # rubocop:disable Metrics/BlockLength def collection_check_boxes_with_bootstrap(*args) html = inputs_collection(*args) do |name, value, options| options[:multiple] = true @@ -26,7 +26,20 @@ def collection_check_boxes_with_bootstrap(*args) def field_name(method, *methods, multiple: false, index: @options[:index]) object_name = @options.fetch(:as) { @object_name } - @template.field_name(object_name, method, *methods, index: index, multiple: multiple) + field_name_shim(object_name, method, *methods, index: index, multiple: multiple) + end + + private + + def field_name_shim(object_name, method_name, *method_names, multiple: false, index: nil) + names = method_names.map! { |name| "[#{name}]" }.join + if object_name.blank? + "#{method_name}#{names}#{'[]' if multiple}" + elsif index + "#{object_name}[#{index}][#{method_name}]#{names}#{'[]' if multiple}" + else + "#{object_name}[#{method_name}]#{names}#{'[]' if multiple}" + end end end end diff --git a/lib/bootstrap_form/inputs/file_field.rb b/lib/bootstrap_form/inputs/file_field.rb index 92b195179..6bfb71de7 100644 --- a/lib/bootstrap_form/inputs/file_field.rb +++ b/lib/bootstrap_form/inputs/file_field.rb @@ -10,7 +10,7 @@ module FileField def file_field_with_bootstrap(name, options={}) options = options.reverse_merge(control_class: "form-control") form_group_builder(name, options) do - input_with_error(name) do + prepend_and_append_input(name, options) do file_field_without_bootstrap(name, options) end end diff --git a/lib/bootstrap_form/inputs/range_field.rb b/lib/bootstrap_form/inputs/range_field.rb index 67453b714..125eba5b7 100644 --- a/lib/bootstrap_form/inputs/range_field.rb +++ b/lib/bootstrap_form/inputs/range_field.rb @@ -7,7 +7,7 @@ module RangeField include Base included do - bootstrap_field :range_field, control_class: "form-range" + bootstrap_field :range_field end end end diff --git a/lib/bootstrap_form/inputs/submit.rb b/lib/bootstrap_form/inputs/submit.rb index 272b0795b..cddad6636 100644 --- a/lib/bootstrap_form/inputs/submit.rb +++ b/lib/bootstrap_form/inputs/submit.rb @@ -1,7 +1,7 @@ module BootstrapForm module Inputs module Submit - def button(value=nil, options={}, &block) + def button(value=nil, options={}, &) value = setup_css_class "btn btn-secondary", value, options super end diff --git a/lib/bootstrap_form/version.rb b/lib/bootstrap_form/version.rb index 309980fa2..2b3d295c5 100644 --- a/lib/bootstrap_form/version.rb +++ b/lib/bootstrap_form/version.rb @@ -1,4 +1,4 @@ module BootstrapForm VERSION = "5.4.0".freeze - REQUIRED_RAILS_VERSION = ">= 6.1".freeze + REQUIRED_RAILS_VERSION = ">= 7.1".freeze end diff --git a/test/bootstrap_checkbox_test.rb b/test/bootstrap_checkbox_test.rb index c727da7d3..6fdfd4549 100644 --- a/test/bootstrap_checkbox_test.rb +++ b/test/bootstrap_checkbox_test.rb @@ -124,7 +124,6 @@ class BootstrapCheckboxTest < ActionView::TestCase test "inline checkboxes from form layout" do expected = <<~HTML
- #{'' unless ::Rails::VERSION::STRING >= '6'}
@@ -473,19 +472,6 @@ class BootstrapCheckboxTest < ActionView::TestCase :street, checked: collection) end - if Rails::VERSION::MAJOR < 7 - def field_name(object_name, method_name, *method_names, multiple: false, index: nil) - names = method_names.map! { |name| "[#{name}]" }.join - if object_name.blank? - "#{method_name}#{names}#{multiple ? '[]' : ''}" - elsif index - "#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? '[]' : ''}" - else - "#{object_name}[#{method_name}]#{names}#{multiple ? '[]' : ''}" - end - end - end - test "collection_check_boxes renders with include_hidden options correctly" do collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")] expected = <<~HTML @@ -532,8 +518,7 @@ def field_name(object_name, method_name, *method_names, multiple: false, index: expected = <<~HTML - #{'' unless ::Rails::VERSION::STRING >= '6'} - +
@@ -583,8 +568,7 @@ def field_name(object_name, method_name, *method_names, multiple: false, index: collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")] expected = <<~HTML - #{'' unless ::Rails::VERSION::STRING >= '6'} - +
@@ -610,8 +594,7 @@ def field_name(object_name, method_name, *method_names, multiple: false, index: @user.errors.add(:terms, "You must accept the terms.") expected = <<~HTML - #{'' unless ::Rails::VERSION::STRING >= '6'} -
+