diff --git a/.devcontainer/.gitkeep b/.devcontainer/.gitkeep new file mode 100644 index 00000000..61739198 --- /dev/null +++ b/.devcontainer/.gitkeep @@ -0,0 +1 @@ +# Placeholder to ensure the .devcontainer directory is created. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..909ed9d0 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,58 @@ +# Use an official Ruby image as a parent image +FROM ruby:3.2.8-bullseye + +# Set environment variables +ENV LANG C.UTF-8 +ENV RAILS_ENV development +ENV RACK_ENV development +ENV PORT 3000 + +# Install system dependencies +# - build-essential: For compiling native extensions +# - libpq-dev: For the pg gem (PostgreSQL client library) +# - nodejs, yarn: For JavaScript runtime and package management (if needed by Rails asset pipeline) +# - postgresql-client: For psql command-line utility +RUN apt-get update -qq && apt-get install -y --no-install-recommends \ + build-essential \ + libpq-dev \ + nodejs \ + yarn \ + postgresql-client \ + && rm -rf /var/lib/apt/lists/* + +# Create a non-root user +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ + # Add user to sudoers with no password (optional, for convenience) + && apt-get update && apt-get install -y sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME \ + && rm -rf /var/lib/apt/lists/* + +# Set up work directory +WORKDIR /workspace + +# Copy Gemfile and Gemfile.lock +COPY Gemfile Gemfile.lock ./ + +# Install gems for the non-root user to avoid permission issues with host mounts +# Ensure bundle path is writable by the user +USER $USERNAME +RUN mkdir -p /home/$USERNAME/.bundle \ + && chown -R $USERNAME:$USERNAME /home/$USERNAME/.bundle \ + && bundle install --jobs $(nproc) --retry 3 + +# Copy the rest of the application code +USER root +COPY . . +RUN chown -R $USERNAME:$USERNAME /workspace + +# Switch back to non-root user +USER $USERNAME + +# Expose port 3000 and set the default command +EXPOSE 3000 +CMD ["bin/rails", "server", "-b", "0.0.0.0"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..2c577a38 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,48 @@ +{ + "name": "Arooo Dockerized Dev", + "dockerComposeFile": [ + "../docker-compose.yml" + ], + "service": "app", + "runServices": ["app", "db"], + "workspaceFolder": "/app", + // Features like node and postgres client are now handled by the Dockerfile for the 'app' service. + // We can keep common-utils if desired for the general container experience, or remove if not needed. + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": "true", + "configureZshAsDefaultShell": "true", + "installOhMyZsh": "true" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "Shopify.ruby-lsp", + "Shopify.rubocop-lsp", + "rebornix.Ruby", + "castwide.solargraph", + "KoichiSasada.vscode-rdbg", + "ms-azuretools.vscode-docker", + "GitHub.codespaces", + "GitHub.vscode-pull-request-github", + "standard.vscode-standard" + ], + "settings": { + // Since rbenv/rvm are not explicitly installed in the Dockerfile (it uses system ruby), + // 'none' is appropriate. If a version manager were added to Dockerfile, this might change. + "rubyLsp.rubyVersionManager": "none", + "editor.formatOnSave": true + } + } + }, + // Ports are now forwarded by docker-compose.yml, but listing the primary app port here is good for Codespaces UI. + "forwardPorts": [3000], + // The Dockerfile and docker-compose setup should handle gem installation and user setup. + // postCreateCommand might still be useful for other setup, or could be removed/simplified. + // For example, ensuring the database is created if it's the first time. + "postCreateCommand": "cp -n config/database.example.yml config/database.yml && cp -n config/application.example.yml config/application.yml && bundle check || bundle install && bundle exec rails db:prepare", + // The user is defined in the Dockerfile and docker-compose.yml for the app service. + // Ensure this matches if not 'vscode', or remove if service user is different and correctly set. + "remoteUser": "vscode" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..1bc810a5 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3.8' +services: + app: + build: + context: . # Use the Dockerfile in the .devcontainer directory + dockerfile: Dockerfile + volumes: + - ..:/workspace:cached # Mount the project root (one level up) to /workspace in the container + ports: + - "3000:3000" # Map port 3000 on the host to port 3000 in the container + depends_on: + - db + environment: + - DATABASE_URL=postgres://arooo_user:arooo_password@db:5432/arooo_development + - RAILS_ENV=development + - RACK_ENV=development + # Add any other necessary environment variables for the app service here + # e.g., GITHUB_CLIENT_KEY, GITHUB_CLIENT_SECRET, etc. + # These would ideally be sourced from a .env file or user secrets in Codespaces + + db: + image: postgres:15 # Or your preferred PostgreSQL version + volumes: + - postgres_data:/var/lib/postgresql/data # Persist PostgreSQL data + environment: + - POSTGRES_USER=arooo_user + - POSTGRES_PASSWORD=arooo_password + - POSTGRES_DB=arooo_development + ports: + - "5432:5432" # Optionally map PostgreSQL port to host (for external tools) + +volumes: + postgres_data: # Defines the named volume for data persistence diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fa26cc6..d95d530b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,14 +3,14 @@ name: Ruby CI on: push: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' - 'bin/*' pull_request: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' @@ -18,6 +18,7 @@ on: jobs: rspec: + name: rspec runs-on: ubuntu-latest services: @@ -29,14 +30,15 @@ jobs: ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + options: --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: '3.4.3' bundler-cache: true - name: Bundle install diff --git a/.github/workflows/ruby_lint.yml b/.github/workflows/ruby_lint.yml index bb4dbe55..23d03fbd 100644 --- a/.github/workflows/ruby_lint.yml +++ b/.github/workflows/ruby_lint.yml @@ -3,14 +3,14 @@ name: standardrb lint on: push: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' - 'bin/*' pull_request: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' @@ -22,11 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: '3.4.3' bundler-cache: true - name: lint diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 2dae5940..1852ca2b 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -3,14 +3,14 @@ name: brakeman on: push: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' - 'bin/*' pull_request: branches: - - main + - '**' paths-ignore: - 'docs/**' - '*.md' @@ -22,11 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: '3.4.3' bundler-cache: true - name: brakeman diff --git a/.ruby-version b/.ruby-version index 1f7da99d..6cb9d3dd 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.7 +3.4.3 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..a72ead61 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.4.3 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47545bad..af2ff07f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,46 @@ $ bundle exec rake spec 1. `bundle exec rails server` don't forget to use http://localhost:3000 and not https 1. `bundle exec rails console` Optional - useful for looking at and changing your local data) +### Development Container Setup (Recommended for VS Code Users) + +This project supports [VS Code Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers), which provides a fully configured development environment, including all necessary tools, extensions, and a running PostgreSQL database. This is the recommended way to get started if you use VS Code. + +**Prerequisites:** + +* [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed and running. +* [Visual Studio Code](https://code.visualstudio.com/) installed. +* The [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed in VS Code. + +**Getting Started:** + +1. **Clone the Repository:** If you haven't already, clone this repository to your local machine. + ```bash + git clone + cd arooo + ``` +2. **Open in Dev Container:** + * Open the cloned `arooo` directory in VS Code. + * VS Code should automatically detect the `.devcontainer/devcontainer.json` file and show a notification in the bottom-right corner asking if you want to "Reopen in Container." Click it. + * Alternatively, open the Command Palette (Cmd+Shift+P or Ctrl+Shift+P), type "Dev Containers: Reopen in Container", and select it. +3. **First-Time Setup:** The first time you open the project in the dev container, it will build the Docker image and set up the environment. This might take a few minutes. The `postCreateCommand` defined in `devcontainer.json` will automatically: + * Copy `config/database.example.yml` to `config/database.yml` (if it doesn't exist). + * Copy `config/application.example.yml` to `config/application.yml` (if it doesn't exist). + * Run `bundle install` to install all gem dependencies. + * Run `bundle exec rails db:prepare` to set up your development database. +4. **Start the Rails Server:** Once the container is ready and VS Code is connected, open a new terminal within VS Code (Terminal > New Terminal). This terminal is inside the dev container. Then, start the Rails server: + ```bash + bundle exec rails s -p 3000 -b '0.0.0.0' + ``` +5. **Access the Application:** Open your web browser and navigate to [http://localhost:3000](http://localhost:3000). +6. **OAuth Setup (Manual Step):** For features requiring GitHub or Google OAuth (like login), you'll still need to manually: + * Create OAuth applications on GitHub and Google as described in the "[Set up an application for local OAuth](#set-up-an-application-for-local-oauth)" section below. + * Add your `CLIENT_ID` and `CLIENT_SECRET` to the `config/application.yml` file inside the dev container. (Remember, this file is created from `application.example.yml` if it didn't exist). + * Restart the Rails server after updating `config/application.yml`. + +This setup provides a consistent environment matching the project's requirements, with Ruby, PostgreSQL, and necessary VS Code extensions pre-configured. + +--- + ### Docker setup (optional) 1. Install docker and docker compose diff --git a/Dockerfile b/Dockerfile index dc012bf9..75b2084d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,38 @@ -FROM ruby:2.7.1-slim +FROM ruby:3.4.3-slim +# Install essential packages, PostgreSQL client, and a JavaScript runtime RUN apt-get update -qq && \ - apt-get install -y build-essential curl cmake git libpq-dev tzdata + apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + git \ + libpq-dev \ + nodejs \ + npm \ + tzdata && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN mkdir /app +# Set the working directory in the container WORKDIR /app -RUN gem install bundler -v 2.0.1 +# Copy Gemfile and Gemfile.lock to leverage Docker cache +COPY Gemfile Gemfile.lock .ruby-version ./ + +# Install Bundler. Use a version compatible with your Gemfile.lock (e.g., if BUNDLED WITH is 2.6.9, this covers it) +RUN gem install bundler -v '~> 2.6' --conservative --minimal-deps + +# Configure Bundler to install gems to /usr/local/bundle (shared volume via docker-compose) +ENV BUNDLE_PATH /usr/local/bundle + +# Install gems +RUN bundle install --jobs $(nproc) --retry 3 + +# Copy the rest of the application code into the container +COPY . . + +# Expose the port Rails runs on +EXPOSE 3000 +# Default command to start the application (can be overridden) CMD ["bash"] diff --git a/Gemfile b/Gemfile index 9f10967a..e2717053 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,14 @@ source "https://rubygems.org" ruby File.read(".ruby-version").strip +gem "concurrent-ruby", "1.3.4" +gem "mutex_m" +gem "drb" +gem "benchmark" +gem "ostruct" +gem "observer" -gem "rails", "~>6.0" +gem "rails", "~> 8.0.2" gem "jquery-rails", ">= 4.3.5" gem "turbolinks" gem "jbuilder" @@ -11,25 +17,24 @@ gem "omniauth" gem "omniauth-github" gem "omniauth-google-oauth2" gem "pg" -gem "state_machine_deuxito", require: 'state_machine' -gem "protected_attributes_continued" # works w rails 5 +# # gem 'state_machine_deuxito', require: "state_machine" gem "kaminari", ">= 1.2.1" gem "rails_autolink", ">= 1.1.6" gem "redcarpet" -gem "configurable_engine" , "~> 2" -gem "bugsnag" +# gem "configurable_engine", "~> 2" +# gem "bugsnag" gem "stripe", "~> 3" # TODO upgrade this! Carefully... gem "stripe_event" gem "rack-canonical-host" gem "aws-sdk-rails", "~> 3" +gem "twitter-bootstrap-rails", "~> 3" gem "rack-cors" gem "haml-rails", ">= 1.0.0" -gem "sass-rails", ">= 5.0.7" -gem "uglifier" -gem "coffee-rails", ">= 4.2.2" -gem "bootstrap-sass" +gem "dartsass-rails" +gem "bootstrap", "~> 5.3.3" gem "jquery-datatables-rails", ">= 3.4.0" -gem 'jwt' +gem "jwt" +gem "rubyzip", "~> 2.4.1" # Avoid low-severity security issue: https://github.com/advisories/GHSA-vr8q-g5c7-m54m gem "nokogiri", ">= 1.11.0.rc4" @@ -48,8 +53,8 @@ group :development, :test do gem "rack_session_access" gem "pry-rails" gem "pry" - gem "puma", "~> 5.6" - gem "standard" + gem "brakeman", "~> 7.0", require: false + gem "standard", "~> 1.39" gem "timecop" end @@ -59,11 +64,12 @@ group :production do end group :test do + gem "minitest", ">= 5.22.0" gem "capybara" gem "webdrivers" gem "database_cleaner" gem "email_spec" - gem "factory_bot_rails", ">= 6.1.0" + gem "factory_bot_rails", ">= 6.1.0", require: false gem "launchy" gem "rspec-collection_matchers" gem "selenium-webdriver" @@ -72,5 +78,3 @@ group :test do gem "simplecov" gem "rails-controller-testing", ">= 1.0.5" end - -gem "brakeman" diff --git a/Gemfile.lock b/Gemfile.lock index d72d2e77..73df3f15 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,173 +1,193 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.0.4.7) - actionpack (= 6.0.4.7) + actioncable (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.4.7) - actionpack (= 6.0.4.7) - activejob (= 6.0.4.7) - activerecord (= 6.0.4.7) - activestorage (= 6.0.4.7) - activesupport (= 6.0.4.7) - mail (>= 2.7.1) - actionmailer (6.0.4.7) - actionpack (= 6.0.4.7) - actionview (= 6.0.4.7) - activejob (= 6.0.4.7) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.0.4.7) - actionview (= 6.0.4.7) - activesupport (= 6.0.4.7) - rack (~> 2.0, >= 2.0.8) + zeitwerk (~> 2.6) + 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 (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.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.4.7) - actionpack (= 6.0.4.7) - activerecord (= 6.0.4.7) - activestorage (= 6.0.4.7) - activesupport (= 6.0.4.7) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + 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 (6.0.4.7) - activesupport (= 6.0.4.7) + actionview (8.0.2) + activesupport (= 8.0.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.4.7) - activesupport (= 6.0.4.7) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (8.0.2) + activesupport (= 8.0.2) globalid (>= 0.3.6) - activemodel (6.0.4.7) - activesupport (= 6.0.4.7) - activerecord (6.0.4.7) - activemodel (= 6.0.4.7) - activesupport (= 6.0.4.7) - activestorage (6.0.4.7) - actionpack (= 6.0.4.7) - activejob (= 6.0.4.7) - activerecord (= 6.0.4.7) - marcel (~> 1.0.0) - activesupport (6.0.4.7) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - annotate (3.2.0) - activerecord (>= 3.2, < 8.0) - rake (>= 10.4, < 14.0) - ast (2.4.2) - autoprefixer-rails (9.6.0) - execjs + 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 (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activesupport (= 8.0.2) + marcel (~> 1.0) + activesupport (8.0.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + annotate (2.6.5) + activerecord (>= 2.3.0) + rake (>= 0.8.7) + ast (2.4.3) awesome_print (1.9.2) - aws-eventstream (1.2.0) - aws-partitions (1.607.0) - aws-record (2.7.0) - aws-sdk-dynamodb (~> 1.18) - aws-sdk-core (3.131.2) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-eventstream (1.4.0) + aws-partitions (1.1131.0) + aws-record (2.14.0) + aws-sdk-dynamodb (~> 1, >= 1.85.0) + aws-sdk-core (3.226.3) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 jmespath (~> 1, >= 1.6.1) - aws-sdk-dynamodb (1.75.0) - aws-sdk-core (~> 3, >= 3.127.0) - aws-sigv4 (~> 1.1) - aws-sdk-rails (3.6.2) + logger + aws-sdk-dynamodb (1.146.0) + aws-sdk-core (~> 3, >= 3.225.0) + aws-sigv4 (~> 1.5) + aws-sdk-rails (3.13.0) aws-record (~> 2) - aws-sdk-ses (~> 1) - aws-sdk-sqs (~> 1) + aws-sdk-ses (~> 1, >= 1.50.0) + aws-sdk-sesv2 (~> 1, >= 1.34.0) + aws-sdk-sqs (~> 1, >= 1.56.0) aws-sessionstore-dynamodb (~> 2) - concurrent-ruby (~> 1) + concurrent-ruby (>= 1.3.1) railties (>= 5.2.0) - aws-sdk-ses (1.47.0) - aws-sdk-core (~> 3, >= 3.127.0) - aws-sigv4 (~> 1.1) - aws-sdk-sqs (1.51.1) - aws-sdk-core (~> 3, >= 3.127.0) - aws-sigv4 (~> 1.1) - aws-sessionstore-dynamodb (2.0.1) - aws-sdk-dynamodb (~> 1) - rack (~> 2) - aws-sigv4 (1.5.0) + aws-sdk-ses (1.85.0) + aws-sdk-core (~> 3, >= 3.225.0) + aws-sigv4 (~> 1.5) + aws-sdk-sesv2 (1.79.0) + aws-sdk-core (~> 3, >= 3.225.0) + aws-sigv4 (~> 1.5) + aws-sdk-sqs (1.96.0) + aws-sdk-core (~> 3, >= 3.225.0) + aws-sigv4 (~> 1.5) + aws-sessionstore-dynamodb (2.2.0) + aws-sdk-dynamodb (~> 1, >= 1.85.0) + rack (>= 2, < 4) + rack-session (>= 1, < 3) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) - better_errors (2.9.1) - coderay (>= 1.0.0) + base64 (0.3.0) + benchmark (0.4.1) + better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (1.0.0) - debug_inspector (>= 0.0.1) - bootstrap-sass (3.4.1) - autoprefixer-rails (>= 5.2.1) - sassc (>= 2.0.0) - brakeman (5.4.0) - bugsnag (6.24.2) - concurrent-ruby (~> 1.0) - builder (3.2.4) - capybara (3.37.1) + rouge (>= 1.0.0) + bigdecimal (3.2.2) + binding_of_caller (1.0.1) + debug_inspector (>= 1.2.0) + bootstrap (5.3.5) + popper_js (>= 2.11.8, < 3) + brakeman (7.1.0) + racc + 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) + childprocess (5.1.0) + logger (~> 1.5) coderay (1.1.3) - coffee-rails (5.0.0) - coffee-script (>= 2.2.0) - railties (>= 5.2.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - concurrent-ruby (1.2.0) - configurable_engine (2.0.2) - rails (> 5.2.0) + commonjs (0.2.7) + concurrent-ruby (1.3.4) + connection_pool (2.5.3) crass (1.0.6) dante (0.2.0) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.1) - activerecord (>= 5.a) - database_cleaner-core (~> 2.0.0) - database_cleaner-core (2.0.1) - debug_inspector (1.1.0) - diff-lcs (1.5.0) - docile (1.4.0) - email_spec (2.2.0) + dartsass-rails (0.5.1) + railties (>= 6.0.0) + sass-embedded (~> 1.63) + date (3.4.1) + debug_inspector (1.2.0) + diff-lcs (1.6.2) + docile (1.4.1) + drb (2.2.3) + email_spec (2.3.0) htmlentities (~> 4.3.3) - launchy (~> 2.1) + launchy (>= 2.1, < 4.0) mail (~> 2.7) - erubi (1.10.0) + erb (5.0.2) + erubi (1.13.1) erubis (2.7.0) - execjs (2.8.1) - factory_bot (6.2.1) - activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) - railties (>= 5.0.0) - faker (2.23.0) + execjs (2.10.0) + factory_bot (6.5.4) + activesupport (>= 6.1.0) + factory_bot_rails (6.5.0) + factory_bot (~> 6.5) + railties (>= 6.1.0) + faker (3.5.2) i18n (>= 1.8.11, < 2) - faraday (0.17.5) + faraday (0.17.6) multipart-post (>= 1.2, < 3) - ffi (1.13.1) - figaro (1.2.0) + ffi (1.17.2-arm64-darwin) + figaro (1.3.0) thor (>= 0.14.0, < 2) - globalid (1.1.0) - activesupport (>= 5.0) - haml (5.2.2) - temple (>= 0.8.0) + globalid (1.2.1) + activesupport (>= 6.1) + google-protobuf (4.31.1-arm64-darwin) + bigdecimal + rake (>= 13) + haml (6.3.0) + temple (>= 0.8.2) + thor tilt - haml-rails (2.0.1) + haml-rails (2.1.0) actionpack (>= 5.1) activesupport (>= 5.1) - haml (>= 4.0.6, < 6.0) - html2haml (>= 1.0.1) + haml (>= 4.0.6) railties (>= 5.1) hashie (5.0.0) html2haml (2.3.0) @@ -176,23 +196,29 @@ GEM nokogiri (>= 1.6.0) ruby_parser (~> 3.5) htmlentities (4.3.4) - i18n (1.12.0) + i18n (1.14.7) concurrent-ruby (~> 1.0) - jbuilder (2.11.5) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jmespath (1.6.1) + jmespath (1.6.2) jquery-datatables-rails (3.4.0) actionpack (>= 3.1) jquery-rails railties (>= 3.1) sass-rails - jquery-rails (4.5.0) + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.6.2) - jwt (2.3.0) + json (2.13.0) + jwt (3.1.2) + base64 kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -206,241 +232,325 @@ GEM kaminari-core (= 1.2.2) kaminari-core (1.2.2) kgio (2.11.4) - launchy (2.5.0) - addressable (~> 2.7) - loofah (2.19.1) + language_server-protocol (3.17.0.5) + launchy (3.1.1) + addressable (~> 2.8) + childprocess (~> 5.0) + logger (~> 1.6) + less (2.6.0) + commonjs (~> 0.2.7) + less-rails (4.0.0) + actionpack (>= 4) + less (~> 2.6.0) + sprockets (>= 2) + lint_roller (1.1.0) + logger (1.7.0) + loofah (2.24.1) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (1.0.2) - matrix (0.4.2) - method_source (1.0.0) - mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.17.0) - multi_json (1.15.0) - multi_xml (0.6.0) - multipart-post (2.2.3) - nio4r (2.5.8) - nokogiri (1.14.1) - mini_portile2 (~> 2.8.0) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.3) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.25.5) + multi_json (1.17.0) + multi_xml (0.7.2) + bigdecimal (~> 3.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + net-imap (0.5.9) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.4) + nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) - oauth2 (1.4.9) - faraday (>= 0.17.3, < 3.0) - jwt (>= 1.0, < 3.0) - multi_json (~> 1.3) + oauth2 (2.0.12) + faraday (>= 0.17.3, < 4.0) + jwt (>= 1.0, < 4.0) + logger (~> 1.2) multi_xml (~> 0.5) - rack (>= 1.2, < 3) - omniauth (1.9.2) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0, >= 2.0.3) + version_gem (>= 1.1.8, < 3) + observer (0.1.2) + omniauth (2.1.3) hashie (>= 3.4.6) - rack (>= 1.6.2, < 3) - omniauth-github (1.4.0) - omniauth (~> 1.5) - omniauth-oauth2 (>= 1.4.0, < 2.0) - omniauth-google-oauth2 (0.8.2) - jwt (>= 2.0) - oauth2 (~> 1.1) - omniauth (~> 1.1) - omniauth-oauth2 (>= 1.6) - omniauth-oauth2 (1.7.2) - oauth2 (~> 1.4) - omniauth (>= 1.9, < 3) - parallel (1.22.1) - parser (3.1.2.1) + rack (>= 2.2.3) + rack-protection + omniauth-github (2.0.1) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.8) + omniauth-google-oauth2 (1.2.1) + jwt (>= 2.9.2) + oauth2 (~> 2.0) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.8) + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) - pg (1.4.3) - protected_attributes_continued (1.8.2) - activemodel (>= 5.0) - pry (0.14.1) + racc + pg (1.5.9) + popper_js (2.11.8) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) + pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - pry-rails (0.3.9) - pry (>= 0.10.4) - public_suffix (4.0.7) - puma (5.6.5) - nio4r (~> 2.0) - racc (1.6.2) - rack (2.2.6.2) - rack-canonical-host (1.1.0) + pry-rails (0.3.11) + pry (>= 0.13.0) + psych (5.2.6) + date + stringio + public_suffix (6.0.2) + racc (1.8.1) + rack (3.1.16) + rack-canonical-host (1.3.0) addressable (> 0, < 3) - rack (>= 1.0.0, < 3) - rack-cors (1.1.1) - rack (>= 2.0.0) - rack-test (2.0.2) + rack (>= 1.6, < 4) + rack-cors (3.0.0) + logger + rack (>= 3.0.14) + rack-protection (4.1.1) + base64 (>= 0.1.0) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) rack (>= 1.3) rack_session_access (0.2.0) builder (>= 2.0.0) rack (>= 1.0.0) - rails (6.0.4.7) - actioncable (= 6.0.4.7) - actionmailbox (= 6.0.4.7) - actionmailer (= 6.0.4.7) - actionpack (= 6.0.4.7) - actiontext (= 6.0.4.7) - actionview (= 6.0.4.7) - activejob (= 6.0.4.7) - activemodel (= 6.0.4.7) - activerecord (= 6.0.4.7) - activestorage (= 6.0.4.7) - activesupport (= 6.0.4.7) - bundler (>= 1.3.0) - railties (= 6.0.4.7) - sprockets-rails (>= 2.0.0) + rackup (2.2.1) + rack (>= 3) + 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 (= 8.0.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) - loofah (~> 2.19, >= 2.19.1) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + 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) rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging - rails_autolink (1.1.6) - rails (> 3.1) + rails_autolink (1.1.8) + actionview (> 3.1) + activesupport (> 3.1) + railties (> 3.1) rails_serve_static_assets (0.0.5) rails_stdout_logging (0.0.5) - railties (6.0.4.7) - actionpack (= 6.0.4.7) - activesupport (= 6.0.4.7) - method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.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) rainbow (3.1.1) - raindrops (0.20.0) - rake (13.0.6) - redcarpet (3.5.1) - regexp_parser (2.5.0) - rexml (3.2.5) - rspec-collection_matchers (1.2.0) + raindrops (0.20.1) + rake (13.3.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) + redcarpet (3.6.1) + regexp_parser (2.10.0) + reline (0.6.1) + io-console (~> 0.5) + rexml (3.4.1) + rouge (4.5.2) + rspec-collection_matchers (1.2.1) rspec-expectations (>= 2.99.0.beta1) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-rails (5.1.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - railties (>= 5.2) - rspec-core (~> 3.10) - rspec-expectations (~> 3.10) - rspec-mocks (~> 3.10) - rspec-support (~> 3.10) - rspec-support (3.11.0) - rubocop (1.35.1) + rspec-support (~> 3.13.0) + rspec-rails (8.0.1) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.4) + rubocop (1.75.8) json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.20.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.21.0) - parser (>= 3.1.1.0) - rubocop-performance (1.14.3) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - ruby-progressbar (1.11.0) - ruby_parser (3.19.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + ruby-progressbar (1.13.0) + ruby_parser (3.21.1) + racc (~> 1.5) sexp_processor (~> 4.16) - rubyzip (2.3.2) + rubyzip (2.4.1) + sass-embedded (1.89.2-arm64-darwin) + google-protobuf (~> 4.31) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) - sassc (2.0.1) + sassc (2.4.0) ffi (~> 1.9) - rake sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (4.8.1) + securerandom (0.4.1) + selenium-webdriver (4.34.0) + base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - sexp_processor (4.16.1) - shoulda-matchers (5.3.0) + sexp_processor (4.17.3) + shoulda-matchers (6.5.0) activesupport (>= 5.2.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) - sprockets (4.1.1) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) + sprockets (4.2.2) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + logger + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - standard (1.16.1) - rubocop (= 1.35.1) - rubocop-performance (= 1.14.3) - state_machine_deuxito (0.0.1) + standard (1.50.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.75.5) + standard-custom (~> 1.0.0) + standard-performance (~> 1.8) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.8.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.25.0) + stringio (3.1.7) stripe (3.31.1) faraday (~> 0.10) stripe-ruby-mock (2.5.8) dante (>= 0.2.0) multi_json (~> 1.0) stripe (>= 2.0.3) - stripe_event (2.6.0) + stripe_event (2.12.0) activesupport (>= 3.1) - stripe (>= 2.8, < 8) - temple (0.8.2) - thor (1.2.1) - thread_safe (0.3.6) - tilt (2.0.11) - timecop (0.9.5) + stripe (>= 2.8, < 16) + temple (0.10.3) + thor (1.4.0) + tilt (2.6.1) + timecop (0.9.10) + timeout (0.4.3) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.11) - thread_safe (~> 0.1) - uglifier (4.2.0) - execjs (>= 0.3.0, < 3) - unicode-display_width (2.2.0) + twitter-bootstrap-rails (3.2.2) + actionpack (>= 3.1) + execjs (>= 2.2.2, >= 2.2) + less-rails (>= 2.5.0) + railties (>= 3.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) + uri (1.0.3) + useragent (0.16.11) + version_gem (1.1.8) webdrivers (5.2.0) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (~> 4.0) - websocket (1.2.9) - websocket-driver (0.7.5) + websocket (1.2.11) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.6) + zeitwerk (2.7.3) PLATFORMS - ruby + arm64-darwin-24 DEPENDENCIES annotate awesome_print aws-sdk-rails (~> 3) + benchmark better_errors binding_of_caller - bootstrap-sass - brakeman - bugsnag + bootstrap (~> 5.3.3) + brakeman (~> 7.0) capybara - coffee-rails (>= 4.2.2) - configurable_engine (~> 2) - database_cleaner + concurrent-ruby (= 1.3.4) + dartsass-rails + drb email_spec factory_bot_rails (>= 6.1.0) faker @@ -453,42 +563,43 @@ DEPENDENCIES jwt kaminari (>= 1.2.1) launchy + minitest (>= 5.22.0) + mutex_m nokogiri (>= 1.11.0.rc4) + observer omniauth omniauth-github omniauth-google-oauth2 + ostruct pg - protected_attributes_continued pry pry-rails - puma (~> 5.6) rack-canonical-host rack-cors rack_session_access - rails (~> 6.0) + rails (~> 8.0.2) rails-controller-testing (>= 1.0.5) rails_12factor rails_autolink (>= 1.1.6) redcarpet rspec-collection_matchers rspec-rails (>= 4.0.0) - sass-rails (>= 5.0.7) + rubyzip (~> 2.4.1) selenium-webdriver shoulda-matchers simplecov - standard - state_machine_deuxito + standard (~> 1.39) stripe (~> 3) stripe-ruby-mock stripe_event timecop turbolinks - uglifier + twitter-bootstrap-rails (~> 3) unicorn webdrivers RUBY VERSION - ruby 2.7.7p221 + ruby 3.4.3p32 BUNDLED WITH - 2.1.4 + 2.6.9 diff --git a/UPGRADE_PLAN.md b/UPGRADE_PLAN.md new file mode 100644 index 00000000..9249c5f0 --- /dev/null +++ b/UPGRADE_PLAN.md @@ -0,0 +1,280 @@ +# Ruby and Rails Upgrade Plan + +This document outlines the steps to upgrade this project to the latest stable versions of Ruby and Rails, and how to set up the recommended Dockerized development environment. + +## 0. Dockerized Development & Testing Environment Setup + +These instructions outline how to set up and use the Docker-based environment for developing and testing the Arooo application. This is the recommended way to work on the project, especially during the upgrade process. + +### Prerequisites + +1. **Docker Desktop:** Install Docker Desktop for your operating system (Mac, Windows, or Linux). Make sure the Docker daemon is running. + * [Docker for Mac](https://docs.docker.com/docker-for-mac/install/) + * [Docker for Windows](https://docs.docker.com/docker-for-windows/install/) + * [Docker for Linux](https://docs.docker.com/engine/install/) (choose your distribution) +2. **Git:** Ensure Git is installed for cloning the repository. +3. **Code Editor:** Your preferred code editor (e.g., VS Code). + +### Getting Started + +1. **Clone the Repository:** + If you haven't already, clone your fork of the `arooo` repository: + ```bash + git clone https://github.com/YOUR_USERNAME/arooo.git + cd arooo + ``` + +2. **Review Docker Configuration:** + * `Dockerfile`: Defines the image for the Rails application, using Ruby 3.4.3. + * `docker-compose.yml`: Configures the `app` (Rails), `db` (PostgreSQL), and `test` services. + +3. **Configure Local Application Settings:** + * **Database Configuration:** + Copy the example database configuration if it doesn't exist: + ```bash + cp -n config/database.example.yml config/database.yml + ``` + Ensure your `config/database.yml` is set up to read credentials from environment variables for the `development` and `test` environments. The `docker-compose.yml` file provides these (e.g., `DATABASE_HOST`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`). It should look something like this for the relevant sections: + ```yaml + # config/database.yml + + default: &default + adapter: postgresql + encoding: unicode + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + + development: + <<: *default + database: arooo_development # Or your preferred dev DB name + username: <%= ENV['DATABASE_USERNAME'] %> + password: <%= ENV['DATABASE_PASSWORD'] %> + host: <%= ENV['DATABASE_HOST'] %> + + test: + <<: *default + database: arooo_test # This will be the name of the test database + username: <%= ENV['DATABASE_USERNAME'] %> + password: <%= ENV['DATABASE_PASSWORD'] %> + host: <%= ENV['DATABASE_HOST'] %> + ``` + + * **Application Secrets & Credentials:** + * Copy the example application configuration: + ```bash + cp -n config/application.example.yml config/application.yml + ``` + * **`SECRET_KEY_BASE`**: The `docker-compose.yml` file requires `SECRET_KEY_BASE` to be set for both `app` and `test` services. You can generate new keys by running `bundle exec rake secret` locally (if you have a Ruby environment) or `docker-compose run --rm app bundle exec rake secret`. Add these to the `environment` section of the `app` and `test` services in `docker-compose.yml` or, preferably, manage them via your `config/application.yml` if Figaro is configured to load them and `docker-compose.yml` points to it. + Example for `docker-compose.yml` (less ideal for secrets, prefer Figaro): + ```yaml + # In docker-compose.yml under app: and test: services + environment: + # ... other env vars + SECRET_KEY_BASE: "your_generated_secret_for_app_or_test" + ``` + * **OAuth Keys (GitHub/Google):** For local development requiring OAuth, follow the instructions in `CONTRIBUTING.md` ("Set up an application for local OAuth") to generate keys. Add these to your `config/application.yml` (which is gitignored). + ```yaml + # config/application.yml + GITHUB_CLIENT_KEY: "your_github_client_key" + GITHUB_CLIENT_SECRET: "your_github_client_secret" + GOOGLE_CLIENT_ID: "your_google_client_id" + GOOGLE_CLIENT_SECRET: "your_google_client_secret" + ``` + * **`RAILS_MASTER_KEY`**: If the application uses encrypted credentials (`config/credentials.yml.enc`) and they are *essential* for the test or development environment, you'll need to provide `RAILS_MASTER_KEY` as an environment variable in `docker-compose.yml`. If not essential, a dummy value or omitting it might be fine, depending on Rails version and configuration. + +4. **Build Docker Images:** + This command builds the `app` image based on the `Dockerfile`. + ```bash + docker-compose build + ``` + *(For newer Docker versions, you might use `docker compose build`)* + +5. **Set up the Development Database:** + This command runs the `db:setup` rake task inside a temporary `app` container. + ```bash + docker-compose run --rm app bundle exec rake db:setup + ``` + +### Running the Application (Development) + +1. **Start Services:** + This starts the `app` and `db` services in detached mode. + ```bash + docker-compose up -d app db + ``` + *(Or `docker compose up -d app db`)* + +2. **Access the Application:** + Open your web browser and go to [http://localhost:3000](http://localhost:3000). + +3. **View Logs:** + ```bash + docker-compose logs -f app + ``` + +4. **Run Rails Console:** + ```bash + docker-compose exec app bundle exec rails console + ``` + +5. **Stop Services:** + ```bash + docker-compose down + ``` + +### Running Tests + +1. **Ensure Test Database is Prepared:** + The test command in `docker-compose.yml` includes `db:create` and `db:schema:load`. If you need to prepare it separately or reset it: + ```bash + docker-compose run --rm test bundle exec rake db:test:prepare + ``` + +2. **Run RSpec Tests:** + This command starts the `test` service (and its `db` dependency), runs the test preparation tasks, then executes `rspec`. The `--rm` flag removes the container after tests complete. + ```bash + docker-compose run --rm test + ``` + The command configured in `docker-compose.yml` for the `test` service is: + `bundle exec rake db:create db:schema:load && bundle exec rspec` + +### Troubleshooting Common Docker Issues + +* **Port Conflicts:** If port 3000 (for Rails) or 5432 (for PostgreSQL, if you choose to expose it) is in use on your host, `docker-compose up` will fail. Stop the conflicting service or change the port mapping in `docker-compose.yml` (e.g., `"3001:3000"`). +* **Volume Permission Issues (Linux):** Ensure the user running Docker has permissions to write to the mounted volumes. Sometimes, files created inside the container might have `root` ownership. +* **Outdated Images/Gems:** If you encounter strange errors, try rebuilding images without cache (`docker-compose build --no-cache`) and removing gem volumes (`docker-compose down -v` followed by `docker volume rm arooo_bundle_cache arooo_pg_data` - **Caution**: `arooo_pg_data` removal deletes your dev database). +* **Database Connection Errors:** + * Verify `DATABASE_HOST` in `config/database.yml` points to the service name in `docker-compose.yml` (which is `db`). + * Ensure `POSTGRES_USER` and `POSTGRES_PASSWORD` in the `db` service match what the `app` and `test` services expect (via `DATABASE_USERNAME`, `DATABASE_PASSWORD`). + +--- + +## 1. Assessment + +* [x] **Determine Current Versions:** + * Ruby version: `2.7.7` (from `.ruby-version`) + * Rails version: `6.0.4.7` (from `Gemfile.lock`) +* [x] **Identify Key Dependencies:** + * Review `Gemfile` and `Gemfile.lock` for critical gems. + * Note versions of major gems (e.g., Devise, Sidekiq, database adapters). +* [ ] **Review Existing Test Coverage:** + * Ensure tests are comprehensive and passing before starting the upgrade. + * Note current test coverage percentage if available. + +## 2. Research & Planning + +* [ ] **Identify Target Versions:** + * Latest stable Ruby version: `3.4.3` (as of May 2025, per our Dockerfile) + * Latest stable Rails version: `8.0.x` (e.g., `8.0.0.1` or newer patch, as of May 2025) - current interim target is `6.1.7.10`. +* [ ] **Consult Official Documentation:** + * Read Ruby release notes for versions between current and target. + * Read Rails upgrade guides (e.g., `https://guides.rubyonrails.org/upgrading_ruby_on_rails.html`). Pay close attention to breaking changes. +* [ ] **Check Gem Compatibility:** + * Use resources like [RubyGems.org](https://rubygems.org/) or [The Ruby Toolbox](https://www.ruby-toolbox.com/) to check compatibility of key gems with target Ruby/Rails versions. + * Identify any gems that need updating or replacing. +* [ ] **Decide on Upgrade Strategy:** + * **Incremental Jumps (Recommended for large upgrades):** Upgrade Rails one minor version at a time (e.g., 6.0 -> 6.1 -> 7.0 -> 7.1 -> ...). This makes debugging easier. + * **Direct to Latest:** Possible for smaller upgrades or if time is a constraint, but carries higher risk. + * Create a new branch for the upgrade: `git checkout -b upgrade-ruby-rails` + +## 3. Execution - Ruby Upgrade (Accomplished via Dockerfile) + +* [x] **Update Local Ruby Version:** Managed by Docker image `ruby:3.4.3-slim`. +* [x] **Update Project Ruby Version:** + * `.ruby-version` should reflect `3.4.3` for local tooling if used outside Docker. + * `Gemfile` should specify `ruby '3.4.3'`. +* [x] **Install Gems:** Handled by `docker-compose build` and `bundle install` within Docker. +* [ ] **Test Ruby Upgrade:** Run tests within the Docker environment. + +## 4. Execution - Rails Upgrade + +*This section might be repeated if doing incremental Rails version upgrades.* + +* [ ] **Update Rails Version in Gemfile:** + * Change `gem 'rails', '~> X.Y.Z'` to the next target version (e.g., `~> 6.1.7.10`). +* [ ] **Update Rails Gem (within Docker):** + * Rebuild the Docker image if `Gemfile` changes: `docker-compose build app test` + * Or run `docker-compose run --rm app bundle update rails` (and for `test` service if its bundle is separate or commit `Gemfile.lock` and rebuild). +* [ ] **Run Rails Update Task (within Docker):** + * `docker-compose run --rm app bundle exec rails app:update` + * Carefully review changes to configuration files. Use `d` to see diffs and decide whether to overwrite. It's often safer to review and merge manually. +* [ ] **Address Deprecations and Breaking Changes:** + * Consult the specific Rails version upgrade guide. + * Run tests frequently within Docker. Look for deprecation warnings. +* [ ] **Update Other Gems (within Docker):** + * Address any gem incompatibilities. Update other gems as necessary using `bundle update some_gem_name` inside the container or by editing `Gemfile` and rebuilding. +* [ ] **Install Gems (within Docker):** + * Ensure `Gemfile.lock` is updated and committed. `docker-compose build` will use it. +* [ ] **Test Rails Upgrade Increment (within Docker):** + * Run the full test suite: `docker-compose run --rm test`. + * Fix any Rails-specific errors and deprecations. + * Commit changes: `git commit -m "Upgrade to Rails X.Y"` + +## 5. Testing (After Final Target Version is Reached) + +* [ ] **Run All Automated Tests (within Docker):** + * Ensure the entire test suite passes. +* [ ] **Perform Thorough Manual Testing (using Dockerized app):** + * Test all critical user flows. + * Check for UI/UX issues. + * Test background jobs, API endpoints, and integrations. +* [ ] **Address Bugs and Regressions:** + * Fix any issues identified. + +## 6. Code Refactoring & Cleanup (Optional but Recommended) + +* [ ] **Adopt New Rails Features:** + * Leverage new APIs, conventions, or features. +* [ ] **Remove Old Workarounds:** + * Delete any monkey patches or code specific to older versions. +* [ ] **Review Deprecation Warnings:** + * Ensure all deprecation warnings have been addressed. + +## 7. Deployment (Considerations for Dockerized App) + +* [ ] **Prepare Production Environment:** + * Production will need a Docker hosting solution (e.g., Heroku with Docker deploys, AWS ECS, Kubernetes). + * Ensure production Ruby version matches, or container handles it. +* [ ] **Deploy to Staging (using Docker):** + * Deploy the Dockerized application to a staging environment. + * Perform final smoke tests and UAT. +* [ ] **Production Deployment (using Docker):** + * Schedule a maintenance window if necessary. + * Deploy to production. +* [ ] **Post-Deployment Monitoring:** + * Closely monitor application logs, error tracking, and performance. + +## Debugging Upgrade Issues + +### `FrozenError: can't modify frozen Array` + +This error has been the primary blocker during the upgrade. It occurs during the Rails initialization process when running `rspec`. + +**Key Information:** +* **Ruby Version Upgrade:** From `2.7.7` to `3.4.3`. +* **Rails Version Upgrade:** From `~> 6.0` to `~> 8.0.2`. +* **Error Message:** `FrozenError: can't modify frozen Array: ["/Users/gonz/.asdf/installs/ruby/3.4.3/lib/ruby/gems/3.4.0/gems/actiontext-8.0.2/app/helpers", "/Users/gonz/.asdf/installs/ruby/3.4.3/lib/ruby/gems/3.4.0/gems/actiontext-8.0.2/app/models"]` + +**Investigation Log:** + +1. **Disabled Gems:** The following gems were disabled as initial suspects for causing the `FrozenError`: + * `state_machine_deuxito` + * `configurable_engine` + * `bugsnag` + * **Result:** The error persisted after disabling each of these. + +2. **`factory_bot_rails` Loading Order:** Investigated the possibility that `factory_bot_rails` was loading its definitions too early. + * **Action:** Modified `Gemfile` to use `require: false` and added `FactoryBot.find_definitions` to the `spec_helper.rb` `before(:suite)` block. + * **Result:** Error persisted, indicating this was not the root cause. + +3. **`simplecov`:** Identified as a likely culprit because it loads before the Rails environment in `spec_helper.rb` and could interfere with the initialization process in newer Rails/Ruby versions. + * **Action:** Commented out `simplecov` initialization in `spec_helper.rb`. + * **Next Step:** Run the test suite to confirm if the `FrozenError` is resolved. + +## Notes & Potential Issues + +* [Add any project-specific notes, known problematic gems, or areas of concern here] +* **Bundler version:** Dockerfile installs a compatible Bundler. Ensure `Gemfile.lock`'s `BUNDLED WITH` section is consistent. +* **Database considerations:** Docker setup uses PostgreSQL. Ensure any specific configurations are handled. + +--- +This plan is a general guideline. Adjust it based on your project's specific needs and complexity. diff --git a/app/controllers/admin/memberships_controller.rb b/app/controllers/admin/memberships_controller.rb index 0ad4daf0..9706e06a 100644 --- a/app/controllers/admin/memberships_controller.rb +++ b/app/controllers/admin/memberships_controller.rb @@ -35,7 +35,7 @@ def change_membership_state unless allowed_updated_state raise ArgumentError.new("Unrecognized user state: #{params.dig(:user, :updated_state)}") end - action_method = user.method("make_#{allowed_updated_state}") + action_method = user.method(:"make_#{allowed_updated_state}") flash[:message] = if action_method.call "#{user.name} is now a #{user.state.humanize.downcase}." diff --git a/app/controllers/applications_controller.rb b/app/controllers/applications_controller.rb index 36b5454e..cd46ce8e 100644 --- a/app/controllers/applications_controller.rb +++ b/app/controllers/applications_controller.rb @@ -32,7 +32,7 @@ def update begin @user.application.submit! flash[:notice] = "Application submitted!" - rescue StandardError => e + rescue => e errors = @user.application.errors.full_messages.to_sentence errors = e.inspect if errors.empty? flash[:error] = "Application not submitted: #{errors}" diff --git a/app/controllers/members/access_controller.rb b/app/controllers/members/access_controller.rb index cf8f40e3..8abc56a3 100644 --- a/app/controllers/members/access_controller.rb +++ b/app/controllers/members/access_controller.rb @@ -1,4 +1,4 @@ -require 'jwt' +require "jwt" class Members::AccessController < Members::MembersController def token @@ -10,6 +10,6 @@ def token exp: Time.now.to_i + 30 } - render json: { token: JWT.encode(payload, ENV['ACCESS_CONTROL_SIGNING_KEY'], 'HS256') } + render json: {token: JWT.encode(payload, ENV["ACCESS_CONTROL_SIGNING_KEY"], "HS256")} end end diff --git a/app/controllers/members/applications_controller.rb b/app/controllers/members/applications_controller.rb index f388d38c..057e7518 100644 --- a/app/controllers/members/applications_controller.rb +++ b/app/controllers/members/applications_controller.rb @@ -38,7 +38,7 @@ def sponsor elsif current_user.sponsorship(application) flash[:notice] = "You are already sponsoring this application!" else - flash[:error] = "Sorry, something went wrong!" + flash[:error] = "Sorry, something went wrong!" end else Sponsorship.where(user_id: current_user, application_id: application).destroy_all diff --git a/app/controllers/members/dues_controller.rb b/app/controllers/members/dues_controller.rb index e649bf8d..6c7058d2 100644 --- a/app/controllers/members/dues_controller.rb +++ b/app/controllers/members/dues_controller.rb @@ -77,16 +77,16 @@ def redirect_target end def amount_plan_name - { 10000 => "extra_large_monthly", - 7500 => "75_monthly", - 5000 => "large_monthly", - 4500 => "45_monthly", - 4000 => "40_monthly", - 3500 => "35_monthly", - 3000 => "30_monthly", - 2500 => "medium_monthly", - 2000 => "20_monthly", - 1500 => "15_monthly", - 1000 => "small_monthly" } + {10000 => "extra_large_monthly", + 7500 => "75_monthly", + 5000 => "large_monthly", + 4500 => "45_monthly", + 4000 => "40_monthly", + 3500 => "35_monthly", + 3000 => "30_monthly", + 2500 => "medium_monthly", + 2000 => "20_monthly", + 1500 => "15_monthly", + 1000 => "small_monthly"} end end diff --git a/app/controllers/members/key_members_controller.rb b/app/controllers/members/key_members_controller.rb index 4565e748..7aa1164a 100644 --- a/app/controllers/members/key_members_controller.rb +++ b/app/controllers/members/key_members_controller.rb @@ -24,7 +24,7 @@ def agreements_missing? return true if params[:agreements].nil? agreements = params[:agreements] - !(agreements.values.all?("1")) + !agreements.values.all?("1") end def send_keymember_email diff --git a/app/controllers/members/users_controller.rb b/app/controllers/members/users_controller.rb index 6ba99514..27e0610b 100644 --- a/app/controllers/members/users_controller.rb +++ b/app/controllers/members/users_controller.rb @@ -48,8 +48,8 @@ def user_params def profile_attributes [:id, :twitter, :facebook, :website, :linkedin, :blog, - :summary, :reasons, :projects, :pronouns, :skills, - :show_name_on_site, :gravatar_email, :show_reasons, :show_projects, :show_skills] + :summary, :reasons, :projects, :pronouns, :skills, + :show_name_on_site, :gravatar_email, :show_reasons, :show_projects, :show_skills] end def set_user diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index f775a0a6..71fe4329 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,4 @@ class SessionsController < ApplicationController - def login redirect_to members_root_path if current_user.try(:general_member?) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6f1496f8..7614c4d3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -65,7 +65,7 @@ def google_analytics end @@markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(hard_wrap: true, - prettify: true), + prettify: true), no_intra_emphasis: true, tables: true, fenced_code_blocks: true, diff --git a/app/helpers/stripe_event_helper.rb b/app/helpers/stripe_event_helper.rb deleted file mode 100644 index 93b28f4f..00000000 --- a/app/helpers/stripe_event_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module StripeEventHelper - class ChargeSucceeded - def call(event) - stripe_customer_id = event.data.object.customer - user = User.find_by stripe_customer_id: stripe_customer_id - user.last_stripe_charge_succeeded = Time.at(event.data.object.created).to_datetime - user.save! - end - end - - class ChargeFailed - def call(event) - email = if event.data.object.customer.present? - User.find_by_stripe_customer_id(event.data.object.customer).email - else - event.data.object.source.name - end - DuesMailer.failed(email).deliver_now - end - end -end diff --git a/app/models/application.rb b/app/models/application.rb index 1824a8d3..7f71206f 100644 --- a/app/models/application.rb +++ b/app/models/application.rb @@ -1,4 +1,4 @@ -require "state_machine" # from gem state_machine_deuxito +# require "state_machine" # from gem state_machine_deuxito class Application < ApplicationRecord belongs_to :user @@ -25,7 +25,7 @@ def self.minimum_yes_votes scope :for_applicant, -> { includes(:user) - .where('users.state': "applicant") + .where("users.state": "applicant") } scope :submitted, -> { @@ -49,9 +49,7 @@ def no_votes end def not_voted_count - @_not_voted_count ||= begin - User.voting_members.count - votes.size - end + @_not_voted_count ||= User.voting_members.count - votes.size end def stale? @@ -72,46 +70,46 @@ def votes_threshold_email end end - state_machine :state, initial: :started do - after_transition started: :submitted do |application, _| - application.touch :submitted_at - ApplicationsMailer.confirmation(application).deliver_now - - if Configurable[:send_new_application_emails] - member_emails = User.all_members.pluck(:email).compact - (member_emails << JOIN_EMAIL).each do |email| - ApplicationsMailer.notify_member_of_application(application, email).deliver_now - end - end - end - - after_transition submitted: [:approved, :rejected] do |application, transition| - application.touch :processed_at - end - - after_transition submitted: :approved do |application| - application.user.make_member - ApplicationsMailer.approved(application).deliver_now - end - - event :submit do - transition started: :submitted - transition rejected: :submitted - end - - event :approve do - transition submitted: :approved - end - - event :reject do - transition submitted: :rejected - end - - state :started - state :submitted - state :approved - state :rejected - end + # state_machine :state, initial: :started do + # after_transition started: :submitted do |application, _| + # application.touch :submitted_at + # ApplicationsMailer.confirmation(application).deliver_now + # + # if Configurable[:send_new_application_emails] + # member_emails = User.all_members.pluck(:email).compact + # (member_emails << JOIN_EMAIL).each do |email| + # ApplicationsMailer.notify_member_of_application(application, email).deliver_now + # end + # end + # end + # + # after_transition submitted: [:approved, :rejected] do |application, transition| + # application.touch :processed_at + # end + # + # after_transition submitted: :approved do |application| + # application.user.make_member + # ApplicationsMailer.approved(application).deliver_now + # end + # + # event :submit do + # transition started: :submitted + # transition rejected: :submitted + # end + # + # event :approve do + # transition submitted: :approved + # end + # + # event :reject do + # transition submitted: :rejected + # end + # + # state :started + # state :submitted + # state :approved + # state :rejected + # end def approvable? enough_yes && few_nos && sponsored @@ -126,15 +124,15 @@ def sufficient_votes? end def self.to_approve - all.map { |x| x if x.approvable? && x.state == "submitted" }.compact.sort_by { |x| x.submitted_at } + all.select { |x| x.approvable? && x.state == "submitted" }.sort_by { |x| x.submitted_at } end def self.to_reject - all.map { |x| x if x.rejectable? && x.state == "submitted" }.compact.sort_by { |x| x.submitted_at } + all.select { |x| x.rejectable? && x.state == "submitted" }.sort_by { |x| x.submitted_at } end def self.not_enough_info - all.map { |x| x if !x.rejectable? && !x.approvable? && x.state == "submitted" }.compact.sort_by { |x| x.submitted_at } + all.select { |x| !x.rejectable? && !x.approvable? && x.state == "submitted" }.sort_by { |x| x.submitted_at } end private diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 6a57d9c2..45fb2f74 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,4 +1,4 @@ -require "state_machine" # from gem state_machine_deuxito +# require "state_machine" # from gem state_machine_deuxito class ApplicationRecord < ActiveRecord::Base self.abstract_class = true diff --git a/app/models/door_code.rb b/app/models/door_code.rb index 01d48da0..62ed71dc 100644 --- a/app/models/door_code.rb +++ b/app/models/door_code.rb @@ -2,37 +2,37 @@ class DoorCode < ApplicationRecord # We don't need an "assigned to user" status: can use the user_id field to check if a code belongs to a user enum status: { # This code is not entered in the physical lock. - not_in_lock: 'not_in_lock', + not_in_lock: "not_in_lock", # This code is entered in the physical lock. - in_lock: 'in_lock', + in_lock: "in_lock", # This code was previously assigned to a user and is still available in the lock. - formerly_assigned_in_lock: 'formerly_assigned_in_lock', + formerly_assigned_in_lock: "formerly_assigned_in_lock", # This code was previously assigned to a user, and is no longer in the lock. # We keep such codes around to avoid code re-use. - formerly_assigned_not_in_lock: 'formerly_assigned_not_in_lock', + formerly_assigned_not_in_lock: "formerly_assigned_not_in_lock", # Denylisted (assumed to not be in the lock). # Used for disallowing easily guessable codes. - denylisted: 'denylisted' + denylisted: "denylisted" } # A code without a user is available for assigning to a member belongs_to :user, optional: true validates :code, presence: true - validates :code, numericality: { only_integer: true } - validates :code, length: { minimum: 6 } + validates :code, numericality: {only_integer: true} + validates :code, length: {minimum: 6} validates_uniqueness_of :code, case_sensitive: false validates_uniqueness_of :index_number, if: -> { index_number.present? } class << self # @return [String] A randomly generated number of the requested length, as a string. May be zero-padded. def make_random_code(digits: 7) - (1..digits).map{ "0123456789".chars.to_a.sample }.join + (1..digits).map { "0123456789".chars.to_a.sample }.join end end def is_assigned? - return user.present? + user.present? end def unassign diff --git a/app/models/profile.rb b/app/models/profile.rb index c178d04c..e9ebad3b 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -18,7 +18,7 @@ def github_url # hacks -- we set username as the auth provider's username, and didn't # store it on the auth. We should write a migration, instead, and remove # this once we're confident the data looks good. - return make_github_url(user.username) unless user.username.include?("@") + make_github_url(user.username) unless user.username.include?("@") end private diff --git a/app/models/user.rb b/app/models/user.rb index c5eac94e..9e7bfc1d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -53,7 +53,7 @@ class User < ApplicationRecord scope :show_public, -> { all_members .includes(:profile) - .where('profiles.show_name_on_site': true) + .where("profiles.show_name_on_site": true) .where("name IS NOT NULL") .order_by_state } @@ -62,7 +62,7 @@ class User < ApplicationRecord applicants .includes(:profile) .includes(:application) - .where('applications.state': "submitted") + .where("applications.state": "submitted") .order("applications.submitted_at DESC") } @@ -70,7 +70,7 @@ class User < ApplicationRecord applicants .includes(:profile) .includes(:application) - .where('applications.state': "started") + .where("applications.state": "started") .order("applications.submitted_at DESC") } @@ -95,42 +95,42 @@ class User < ApplicationRecord .squish)) } - state_machine :state, initial: :visitor do - event :make_applicant do - transition visitor: :applicant - end - - event :make_member do - transition [:applicant, :voting_member, :key_member, :former_member] => :member - end - - event :make_key_member do - transition [:member, :voting_member] => :key_member - end - - event :make_voting_member do - transition [:member, :key_member] => :voting_member - end - - event :make_former_member do - transition [:member, :voting_member, :key_member] => :former_member - end - - after_transition on: all - [:make_voting_member] do |user, _| - user.update(voting_policy_agreement: false) - end - - after_transition on: all - [:make_key_member, :make_voting_member] do |user, _| - user.door_code.unassign if user.door_code.present? - end - - state :visitor - state :applicant - state :member - state :key_member - state :voting_member - state :former_member - end + # state_machine :state, initial: :visitor do + # event :make_applicant do + # transition visitor: :applicant + # end + # + # event :make_member do + # transition [:applicant, :voting_member, :key_member, :former_member] => :member + # end + # + # event :make_key_member do + # transition [:member, :voting_member] => :key_member + # end + # + # event :make_voting_member do + # transition [:member, :key_member] => :voting_member + # end + # + # event :make_former_member do + # transition [:member, :voting_member, :key_member] => :former_member + # end + # + # after_transition on: all - [:make_voting_member] do |user, _| + # user.update(voting_policy_agreement: false) + # end + # + # after_transition on: all - [:make_key_member, :make_voting_member] do |user, _| + # user.door_code.unassign if user.door_code.present? + # end + # + # state :visitor + # state :applicant + # state :member + # state :key_member + # state :voting_member + # state :former_member + # end def general_member? member? || key_member? || voting_member? @@ -187,7 +187,7 @@ def vote_for(application) def number_applications_needing_vote if voting_member? n = Application.where(state: "submitted").count - Application.joins("JOIN votes ON votes.application_id = applications.id AND applications.state = 'submitted' AND votes.user_id = #{id}").count - n == 0 ? nil : n + (n == 0) ? nil : n end end diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 00000000..ace1c9ba --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/dev b/bin/dev new file mode 100755 index 00000000..5f91c205 --- /dev/null +++ b/bin/dev @@ -0,0 +1,2 @@ +#!/usr/bin/env ruby +exec "./bin/rails", "server", *ARGV diff --git a/bin/rails b/bin/rails index 8a217f6a..efc03774 100755 --- a/bin/rails +++ b/bin/rails @@ -1,10 +1,4 @@ #!/usr/bin/env ruby -if ENV["RAILS_ENV"] == "test" - require "simplecov" - SimpleCov.start "rails" - puts "required simplecov" -end - -APP_PATH = File.expand_path("../../config/application", __FILE__) +APP_PATH = File.expand_path("../config/application", __dir__) require_relative "../config/boot" require "rails/commands" diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 00000000..40330c0f --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup index 67e27f0a..c27f9c8c 100755 --- a/bin/setup +++ b/bin/setup @@ -1,29 +1,34 @@ #!/usr/bin/env ruby -require "pathname" +require "fileutils" -# path to your application root. -APP_ROOT = Pathname.new File.expand_path("../../", __FILE__) +APP_ROOT = File.expand_path("..", __dir__) -Dir.chdir APP_ROOT do - # This script is a starting point to setup your application. - # Add necessary setup steps to this file: +def system!(*) + system(*, exception: true) +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. puts "== Installing dependencies ==" - system "gem install bundler --conservative" - system "bundle check || bundle install" + system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" # unless File.exist?("config/database.yml") - # system "cp config/database.yml.sample config/database.yml" + # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end puts "\n== Preparing database ==" - system "bin/rake db:setup" + system! "bin/rails db:prepare" puts "\n== Removing old logs and tempfiles ==" - system "rm -f log/*" - system "rm -rf tmp/cache" + system! "bin/rails log:clear tmp:clear" - puts "\n== Restarting application server ==" - system "touch tmp/restart.txt" + 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/config/application.rb b/config/application.rb index a319c6aa..36d64c88 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,4 @@ -require File.expand_path("../boot", __FILE__) +require_relative "boot" require "rails/all" @@ -8,32 +8,20 @@ module Doubleunion class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.0 - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. - # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. - # config.time_zone = 'Central Time (US & Canada)' - - # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. - # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] - # config.i18n.default_locale = :de - - # explicitly loading locales so they will be available in the initializers - I18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] - - config.eager_load_paths += %W[#{config.root}/lib] - config.autoload_paths += %W[#{config.root}/lib] - - # CORS – this allows doubleunion.org to request the api from javascript - config.middleware.insert_before 0, Rack::Cors do - allow do - origins "*" - - resource "/public_members", headers: :any, methods: [:get], max_age: 0 - end - end + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") end end diff --git a/config/boot.rb b/config/boot.rb index fb24cf2e..28201161 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,3 @@ -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 00000000..b1f9fa90 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: doubleunion_production diff --git a/config/environment.rb b/config/environment.rb index 17119856..1410b7a3 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,7 @@ # Load the Rails application. -require File.expand_path("../application", __FILE__) + +require_relative "application" + # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index e1a2155a..4ba93a0c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,51 +1,72 @@ +require "active_support/core_ext/integer/time" + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false + # Make code changes take effect immediately without server restart. + config.enable_reloading = true # Do not eager load code on boot. config.eager_load = false - # Show full error reports and disable caching. + # Show full error reports. config.consider_all_requests_local = true - config.action_controller.perform_caching = false + + # Enable server timing. + config.server_timing = true + + # 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.public_file_server.headers = {"cache-control" => "public, max-age=#{2.days.to_i}"} + else + config.action_controller.perform_caching = false + 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 # 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 an error on page load if there are pending migrations. config.active_record.migration_error = :page_load - # We're in San Francisco, forever! - config.time_zone = "Pacific Time (US & Canada)" - config.active_record.default_timezone = :local + # 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 - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true - # Send email in development mode using MailCatcher: http://mailcatcher.me/. - config.action_mailer.default_url_options = {host: "localhost:3000"} - config.action_mailer.perform_deliveries = true - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = {address: "localhost", port: 1025} + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + # config.generators.apply_rubocop_autocorrect_after_generate! end diff --git a/config/environments/production.rb b/config/environments/production.rb index 211127de..aec64cdf 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,82 +1,89 @@ +require "active_support/core_ext/integer/time" + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. - config.cache_classes = true + 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. + # Full error reports are disabled. config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Enable Rack::Cache to put a simple HTTP cache in front of your application - # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like - # NGINX, varnish or squid. - # config.action_dispatch.rack_cache = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.serve_static_files = ENV["RAILS_SERVE_STATIC_FILES"].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = Uglifier.new(harmony: true) - # 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}"} - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local - # 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 + # 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 - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT with the current request id as a default log tag. + config.log_tags = [:request_id] + config.logger = ActiveSupport::TaggedLogging.logger($stdout) + + # Change to "debug" to log everything (including potentially personally-identifiable information!) + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") - # Prepend all log lines with the following tags. - # config.log_tags = [ :subdomain, :uuid ] + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" - # Use a different logger for distributed setups. - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + # Don't log any deprecations. + config.active_support.report_deprecations = false - # Use a different cache store in production. + # Replace the default in-process memory cache store with a durable alternative. # config.cache_store = :mem_cache_store - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # 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 - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - config.action_mailer.delivery_method = :ses - config.action_mailer.default_url_options = {host: ENV["HOST_URL"]} - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - # 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/config/environments/test.rb b/config/environments/test.rb index 89631f71..0fb6d30c 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,47 +1,53 @@ +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? - # Configure static file server for tests with Cache-Control for performance. - config.serve_static_files = true - config.static_cache_control = "public, max-age=3600" + # 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. + # Show full error reports. config.consider_all_requests_local = true - config.action_controller.perform_caching = false + config.cache_store = :null_store - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false + # 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 + # 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 - config.action_mailer.default_url_options = {host: "localhost:3000"} - # Randomize the order test cases are executed. - config.active_support.test_order = :random + # 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 - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = 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 - # using rack_session_access gem for auth in integration tests - # see: http://pivotallabs.com/faking-authentication-for-capybara-tests-within-a-rails-engine/ - config.middleware.use RackSessionAccess::Middleware + # 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/config/initializers/assets.rb b/config/initializers/assets.rb index 90c3ddd5..48732442 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -3,9 +3,5 @@ # Version of your assets, change this if you want to expire all your assets. Rails.application.config.assets.version = "1.0" -# Add additional assets to the asset load path +# Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -Rails.application.config.assets.precompile %w[manifest.js] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 00000000..b3076b38 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1e..c0b717f7 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,4 +1,8 @@ # Be sure to restart your server when you modify this file. -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc +] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf9..3860f659 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -4,13 +4,13 @@ # are locale specific, and you may define rules for as many different # locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" # inflect.uncountable %w( fish sheep ) # end # These inflection rules are supported but not enabled by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' +# inflect.acronym "RESTful" # end diff --git a/config/initializers/new_framework_defaults_8_0.rb b/config/initializers/new_framework_defaults_8_0.rb new file mode 100644 index 00000000..92efa951 --- /dev/null +++ b/config/initializers/new_framework_defaults_8_0.rb @@ -0,0 +1,30 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 8.0 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `8.0`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +### +# Specifies whether `to_time` methods preserve the UTC offset of their receivers or preserves the timezone. +# If set to `:zone`, `to_time` methods will use the timezone of their receivers. +# If set to `:offset`, `to_time` methods will use the UTC offset. +# If `false`, `to_time` methods will convert to the local system UTC offset instead. +#++ +# Rails.application.config.active_support.to_time_preserves_timezone = :zone + +### +# When both `If-Modified-Since` and `If-None-Match` are provided by the client +# only consider `If-None-Match` as specified by RFC 7232 Section 6. +# If set to `false` both conditions need to be satisfied. +#++ +# Rails.application.config.action_dispatch.strict_freshness = true + +### +# Set `Regexp.timeout` to `1`s by default to improve security over Regexp Denial-of-Service attacks. +#++ +# Regexp.timeout = 1 diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb index ef8d2f1c..6f58350a 100644 --- a/config/initializers/stripe.rb +++ b/config/initializers/stripe.rb @@ -1,3 +1,5 @@ +require "stripe_event_handlers/stripe_event_helper" + Rails.configuration.stripe = { event_signing_secret: ENV["STRIPE_SIGNING_SECRET"], publishable_key: ENV["STRIPE_PUBLISHABLE_KEY"], @@ -8,7 +10,7 @@ # You can verify which Stripe API version is in use, or upgrade it, through the # Stripe developer dashboard: https://dashboard.stripe.com/developers -Stripe.api_version = '2016-02-03' +Stripe.api_version = "2016-02-03" StripeEvent.signing_secret = Rails.configuration.stripe[:event_signing_secret] diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 00000000..a248513b --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,41 @@ +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. +# +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# 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. +# +# 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. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3000) + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart + +# 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/config/storage.yml b/config/storage.yml new file mode 100644 index 00000000..4942ab66 --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/20200709004913_create_door_codes.rb b/db/migrate/20200709004913_create_door_codes.rb index 3bc34ce3..56e5b3dd 100644 --- a/db/migrate/20200709004913_create_door_codes.rb +++ b/db/migrate/20200709004913_create_door_codes.rb @@ -1,8 +1,8 @@ class CreateDoorCodes < ActiveRecord::Migration[4.2] def change create_table :door_codes do |t| - t.references :user, null: false, index: { unique: true } - t.string :code, null: false, index: { unique: true } + t.references :user, null: false, index: {unique: true} + t.string :code, null: false, index: {unique: true} t.boolean :enabled, null: false, default: false t.timestamps end diff --git a/docker-compose.yml b/docker-compose.yml index ba0e6a14..32f540fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,34 +1,73 @@ -version: "3" services: db: - image: postgres:10.6-alpine - ports: - - 5432:5432 + image: postgres:14-alpine volumes: - pg_data:/var/lib/postgresql/data environment: - - POSTGRES_PASSWORD=password + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password # Change this for production! + POSTGRES_DB: arooo_development # Optional: creates DB on init + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d arooo_development"] + interval: 10s + timeout: 5s + retries: 5 app: - build: . - depends_on: - - db + build: + context: . + dockerfile: Dockerfile + command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" + volumes: + - .:/app # Mounts the current directory to /app in the container for live code changes + - bundle_cache:/usr/local/bundle # Persists installed gems ports: - - 3000:3000 - command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" + - "3000:3000" + depends_on: + db: + condition: service_healthy + environment: + RAILS_ENV: development + DATABASE_HOST: db + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password # Should match POSTGRES_PASSWORD in db service + # For Figaro, you might use these if your database.yml expects them via Figaro.env + # DB_HOST: db + # DB_USERNAME: postgres + # DB_PASSWORD: password + FIGARO_APPLICATION_YML_PATH: "config/application.yml" # If you want to point Figaro to it + SECRET_KEY_BASE: "your_development_secret_key_base_here_if_not_using_credentials_or_figaro_for_it" # Ensure this is set + + test: + build: + context: . + dockerfile: Dockerfile + command: /bin/sh -c "bundle exec rake db:create db:schema:load && bundle exec rspec" volumes: - .:/app - - bundle_cache:/bundle + - bundle_cache:/usr/local/bundle + depends_on: + db: + condition: service_healthy environment: - - BUNDLE_PATH=/bundle/vendor - - RAILS_ENV=development - - PSQL_USERNAME=postgres - - PSQL_PASSWORD=password - - PSQL_HOST=db - - SECRET_TOKEN=development - tty: true - stdin_open: true + RAILS_ENV: test + DATABASE_HOST: db + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + # Ensure your config/database.yml for the 'test' environment is configured to use these variables. + # For example: + # test: + # adapter: postgresql + # encoding: unicode + # database: arooo_test # Or read from ENV + # host: <%= ENV['DATABASE_HOST'] %> + # username: <%= ENV['DATABASE_USERNAME'] %> + # password: <%= ENV['DATABASE_PASSWORD'] %> + # pool: 5 + FIGARO_APPLICATION_YML_PATH: "config/application.yml" # If test db config is in application.yml + RAILS_MASTER_KEY: "dummy_master_key_for_tests_if_needed" # if using encrypted credentials and they are needed for tests + SECRET_KEY_BASE: "your_test_secret_key_base_here" # Ensure this is set for test env volumes: - bundle_cache: - pg_data: + pg_data: # Persists PostgreSQL data + bundle_cache: # Persists gems diff --git a/lib/stripe_event_handlers/stripe_event_helper.rb b/lib/stripe_event_handlers/stripe_event_helper.rb new file mode 100644 index 00000000..ae514eb2 --- /dev/null +++ b/lib/stripe_event_handlers/stripe_event_helper.rb @@ -0,0 +1,25 @@ +module StripeEventHandlers + module StripeEventHelper + class ChargeSucceeded + def call(event) + stripe_customer_id = event.data.object.customer + user = User.find_by stripe_customer_id: stripe_customer_id + user.last_stripe_charge_succeeded = Time.at(event.data.object.created).to_datetime + user.save! + end + end + + class ChargeFailed + def call(event) + customer = User.find_by_stripe_customer_id(event.data.object.customer) + charge = event.data.object + email = if customer.respond_to?(:email) && customer.email.present? + customer.email + else + charge.receipt_email + end + DuesMailer.failed(email).deliver_now + end + end + end +end diff --git a/lib/tasks/populate.rake b/lib/tasks/populate.rake index 09ab53b9..5a35cff2 100644 --- a/lib/tasks/populate.rake +++ b/lib/tasks/populate.rake @@ -17,7 +17,7 @@ namespace :populate do # Some parts of the app expect members to have an application with a valid processed_at date. if %w[member key_member voting_member].include? user.state - user.application.processed_at = (1 + rand(5)).days.ago + user.application.processed_at = rand(1..5).days.ago user.save! end end diff --git a/public/400.html b/public/400.html new file mode 100644 index 00000000..282dbc8c --- /dev/null +++ b/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/public/404.html b/public/404.html index a0daa0c1..c0670bc8 100644 --- a/public/404.html +++ b/public/404.html @@ -1,58 +1,114 @@ - - - - The page you were looking for doesn't exist (404) - - - - - -
-

The page you were looking for doesn't exist.

-

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

-
-

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

- + + + + + + + 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/public/406-unsupported-browser.html b/public/406-unsupported-browser.html new file mode 100644 index 00000000..9532a9cc --- /dev/null +++ b/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/public/422.html b/public/422.html index fbb4b84d..8bcf0601 100644 --- a/public/422.html +++ b/public/422.html @@ -1,58 +1,114 @@ - - - - The change you wanted was rejected (422) - - - - - -
-

The change you wanted was rejected.

-

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

-
-

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

- + + + + + + + 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/public/500.html b/public/500.html index e9052d35..d77718c3 100644 --- a/public/500.html +++ b/public/500.html @@ -1,57 +1,114 @@ - - - - We're sorry, but something went wrong (500) - - - - - -
-

We're sorry, but something went wrong.

-
-

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

- + + + + + + + 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/public/icon.png b/public/icon.png new file mode 100644 index 00000000..c4c9dbfb Binary files /dev/null and b/public/icon.png differ diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 00000000..04b34bf8 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/robots.txt b/public/robots.txt index 1a3a5e4d..c19f78ab 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,5 +1 @@ -# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file -# -# To ban all spiders from the entire site uncomment the next two lines: -# User-agent: * -# Disallow: / +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/spec/controllers/admin/door_codes_controller_spec.rb b/spec/controllers/admin/door_codes_controller_spec.rb index 5c81102c..256c23d4 100644 --- a/spec/controllers/admin/door_codes_controller_spec.rb +++ b/spec/controllers/admin/door_codes_controller_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require "spec_helper" describe Admin::DoorCodesController do include AuthHelper @@ -8,7 +8,7 @@ render_views let(:door_code) { create(:door_code) } - let(:params) { { id: door_code.id } } + let(:params) { {id: door_code.id} } describe "GET :index" do context "logged in as a non-admin member" do @@ -21,7 +21,7 @@ end context "when logged in as an admin" do - let!(:door_codes) { create_list(:door_code, 5)} + let!(:door_codes) { create_list(:door_code, 5) } before { login_as(:voting_member, is_admin: true) } @@ -45,14 +45,14 @@ it "saves the new door code" do expect { - post :create, params: { door_code: { code: "1234567", status: "in_lock", user_id: existing_member.id } } + post :create, params: {door_code: {code: "1234567", status: "in_lock", user_id: existing_member.id}} }.to change(DoorCode, :count).from(1).to(2) expect(existing_member.door_code.code).to eq("1234567") end it "doesn't save a duplicated door code" do expect { - post :create, params: { door_code: { code: existing_door_code.code, status: "in_lock" } } + post :create, params: {door_code: {code: existing_door_code.code, status: "in_lock"}} }.not_to change(DoorCode, :count) end end @@ -67,7 +67,7 @@ it "updates the door code" do expect { - patch :update, params: { id: door_code.id, door_code: { user_id: member.id } } + patch :update, params: {id: door_code.id, door_code: {user_id: member.id}} }.not_to change(DoorCode, :count) expect(door_code.reload.user.id).to eq(member.id) end diff --git a/spec/controllers/applications_controller_spec.rb b/spec/controllers/applications_controller_spec.rb index 991d64a0..fb93c523 100644 --- a/spec/controllers/applications_controller_spec.rb +++ b/spec/controllers/applications_controller_spec.rb @@ -30,19 +30,19 @@ describe "GET show" do it "should redirect to root if not logged in" do - get :show, params: { id: 1 } + get :show, params: {id: 1} expect(response).to redirect_to :root end it "should redirect to root if logged in as visitor" do user = login_as(:visitor) - get :show, params: { id: user.application.id } + get :show, params: {id: user.application.id} expect(response).to redirect_to :root end it "should redirect to root if logged in as member" do user = login_as(:member) - get :show, params: { id: user.application.id } + get :show, params: {id: user.application.id} expect(response).to redirect_to :root end @@ -53,12 +53,12 @@ before { log_in(user) } it "should render own application" do - get :show, params: { id: user.application.id } + get :show, params: {id: user.application.id} expect(response).to render_template :show end it "should redirect to root for another user's application" do - get :show, params: { id: other_user.application.id } + get :show, params: {id: other_user.application.id} expect(response).to redirect_to :root end end @@ -66,19 +66,19 @@ describe "GET edit" do it "should redirect to root if not logged in" do - get :edit, params: { id: create(:user, state: :applicant).application.id } + get :edit, params: {id: create(:user, state: :applicant).application.id} expect(response).to redirect_to :root end it "should redirect to root if logged in as visitor" do user = login_as(:visitor) - get :edit, params: { id: user.application.id } + get :edit, params: {id: user.application.id} expect(response).to redirect_to :root end it "should redirect to root if logged in as member" do user = login_as(:member) - get :edit, params: { id: user.application.id } + get :edit, params: {id: user.application.id} expect(response).to redirect_to :root end @@ -89,12 +89,12 @@ before { log_in(user) } it "should render edit for own application" do - get :edit, params: { id: user.application.id } + get :edit, params: {id: user.application.id} expect(response).to render_template :edit end it "should redirect to root for another user's application" do - get :edit, params: { id: other_user.application.id } + get :edit, params: {id: other_user.application.id} expect(response).to redirect_to :root end end @@ -102,19 +102,19 @@ describe "POST update" do it "should redirect to root if not logged in" do - post :update, params: { id: create(:user, state: :applicant).application.id } + post :update, params: {id: create(:user, state: :applicant).application.id} expect(response).to redirect_to :root end it "should redirect to root if logged in as visitor" do user = login_as(:visitor) - post :update, params: { id: user.application.id } + post :update, params: {id: user.application.id} expect(response).to redirect_to :root end it "should redirect to root if logged in as member" do user = login_as(:member) - post :update, params: { id: user.application.id } + post :update, params: {id: user.application.id} expect(response).to redirect_to :root end @@ -144,7 +144,7 @@ it "should update the user's application" do expect { subject }.to change { application.agreement_terms }.from(false).to(true) - .and change { user.email_for_google }.from(nil).to("lemurs@gmail.com") + .and change { user.email_for_google }.from(nil).to("lemurs@gmail.com") expect(response).to redirect_to edit_application_path(application) end @@ -225,7 +225,7 @@ end context "missing required profile fields" do - let(:profile_params) { { summary: "lemurs!", reasons: "lemur reasonss!" } } + let(:profile_params) { {summary: "lemurs!", reasons: "lemur reasonss!"} } it "should add an error to the flash" do subject diff --git a/spec/controllers/members/applications_controller_spec.rb b/spec/controllers/members/applications_controller_spec.rb index ad1d100d..1bfbe354 100644 --- a/spec/controllers/members/applications_controller_spec.rb +++ b/spec/controllers/members/applications_controller_spec.rb @@ -30,26 +30,26 @@ describe "GET show" do it "redirects if not logged in" do - get :show, params: { id: @applicant.application.id } + get :show, params: {id: @applicant.application.id} expect(response).to redirect_to :root end it "redirects if logged in as visitor" do login_as(:visitor) - get :show, params: { id: @applicant.application.id } + get :show, params: {id: @applicant.application.id} expect(response).to redirect_to :root end it "redirects if logged in as applicant" do login_as(@applicant) - get :show, params: { id: @applicant.application.id } + get :show, params: {id: @applicant.application.id} expect(response).to redirect_to :root end describe "for authenticated member" do it "redirects if application is in started state" do login_as(:member) - get :show, params: { id: @applicant.application.id } + get :show, params: {id: @applicant.application.id} expect(flash[:error]).to match(/not currently visible/) expect(response).to redirect_to members_root_path end @@ -61,7 +61,7 @@ application = applicant.application application.update_attribute(:state, "submitted") - get :show, params: { id: application.id } + get :show, params: {id: application.id} expect(response).to render_template :show end @@ -72,7 +72,7 @@ application = applicant.application application.update_attribute(:state, "approved") - get :show, params: { id: application.id } + get :show, params: {id: application.id} expect(response).to redirect_to members_root_path end @@ -83,7 +83,7 @@ application = applicant.application application.update_attribute(:state, "rejected") - get :show, params: { id: application.id } + get :show, params: {id: application.id} expect(response).to redirect_to members_root_path end @@ -96,7 +96,7 @@ end it "shows all the comments" do - get :show, params: { id: submitted_application.id } + get :show, params: {id: submitted_application.id} expect(response).to render_template :show expect(assigns(:comments)).to eq(comments) end @@ -113,7 +113,7 @@ end it "shows only comments authored by the current logged in member" do - get :show, params: { id: submitted_application.id } + get :show, params: {id: submitted_application.id} expect(assigns(:comments)).to include(comment_by_self) expect(assigns(:comments)).to_not include(comment_by_someone_else) end @@ -128,14 +128,14 @@ it "does not render voting form for member" do login_as(:member) - get :show, params: { id: @submitted_application.id } + get :show, params: {id: @submitted_application.id} expect(response).to render_template :show expect(response.body).not_to have_selector(:css, "form#new_vote") end it "renders voting form for voting member" do login_as(:voting_member) - get :show, params: { id: @submitted_application.id } + get :show, params: {id: @submitted_application.id} expect(response).to render_template :show expect(response.body).to have_selector(:css, "form#new_vote") end diff --git a/spec/controllers/members/dues_controller_spec.rb b/spec/controllers/members/dues_controller_spec.rb index 0bfbe47d..091cd909 100644 --- a/spec/controllers/members/dues_controller_spec.rb +++ b/spec/controllers/members/dues_controller_spec.rb @@ -6,7 +6,7 @@ let(:member) { create(:member) } describe "GET show" do - subject { get :show, params: { user_id: member.id } } + subject { get :show, params: {user_id: member.id} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -18,12 +18,12 @@ context "when a member has an associated Stripe account without a subscription" do let(:current_user) do - login_as( - :member, - name: "Foo Bar", - email: "someone@example.com", - stripe_customer_id: 'stripe-user-id-abc123' - ) + login_as( + :member, + name: "Foo Bar", + email: "someone@example.com", + stripe_customer_id: "stripe-user-id-abc123" + ) end before do @@ -72,11 +72,11 @@ # https://github.com/rebelidealist/stripe-ruby-mock/issues/209 Stripe.api_key = "coolapikey" Stripe::Plan.create(id: "test_plan", - amount: 5000, - currency: "usd", - interval: "month", - product: "test product", - name: "test plan") + amount: 5000, + currency: "usd", + interval: "month", + product: "test product", + name: "test plan") # Must set referrer so that DuesController#redirect_target works request.env["HTTP_REFERER"] = "http://example.com/members/users/x/dues" @@ -121,11 +121,11 @@ # https://github.com/rebelidealist/stripe-ruby-mock/issues/209 Stripe.api_key = "coolapikey" Stripe::Plan.create(id: "test_plan", - amount: 5000, - currency: "usd", - interval: "month", - product: "test product", - name: "test plan") + amount: 5000, + currency: "usd", + interval: "month", + product: "test product", + name: "test plan") # Must set referrer so that DuesController#redirect_target works request.env["HTTP_REFERER"] = "http://example.com/members/users/x/dues" diff --git a/spec/controllers/members/key_members_controller_spec.rb b/spec/controllers/members/key_members_controller_spec.rb index 76a3cb4c..9fd52d21 100644 --- a/spec/controllers/members/key_members_controller_spec.rb +++ b/spec/controllers/members/key_members_controller_spec.rb @@ -6,7 +6,7 @@ let(:member) { create :member } describe "get edit" do - let(:subject) { get :edit, params: { user_id: member } } + let(:subject) { get :edit, params: {user_id: member} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -23,7 +23,7 @@ end describe "post update" do - let(:subject) { patch :update, params: { user_id: member } } + let(:subject) { patch :update, params: {user_id: member} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] diff --git a/spec/controllers/members/users_controller_spec.rb b/spec/controllers/members/users_controller_spec.rb index 11b484ce..d2d13927 100644 --- a/spec/controllers/members/users_controller_spec.rb +++ b/spec/controllers/members/users_controller_spec.rb @@ -18,7 +18,7 @@ end describe "GET show" do - subject { get :show, params: { id: someone_cool.id } } + subject { get :show, params: {id: someone_cool.id} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -30,7 +30,7 @@ end describe "GET edit" do - subject { get :edit, params: { id: someone_cool.id } } + subject { get :edit, params: {id: someone_cool.id} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -42,7 +42,7 @@ end describe "POST update" do - subject { post :update, params: { id: someone_cool.id, user: {id: someone_cool.id} } } + subject { post :update, params: {id: someone_cool.id, user: {id: someone_cool.id}} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -93,7 +93,7 @@ end describe "GET setup" do - subject { get :setup, params: { user_id: someone_cool.id } } + subject { get :setup, params: {user_id: someone_cool.id} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :voting_member] @@ -105,7 +105,7 @@ end describe "PATCH finalize" do - subject { patch :finalize, params: { user_id: someone_cool.id, user: {dues_pledge: 25} } } + subject { patch :finalize, params: {user_id: someone_cool.id, user: {dues_pledge: 25}} } it_should_behave_like "deny non-members", [:visitor, :applicant] diff --git a/spec/controllers/members/votes_controller_spec.rb b/spec/controllers/members/votes_controller_spec.rb index c44fe2a5..e91ef951 100644 --- a/spec/controllers/members/votes_controller_spec.rb +++ b/spec/controllers/members/votes_controller_spec.rb @@ -67,8 +67,8 @@ let(:params) { {id: application.id} } let(:vote) { Vote.where(application_id: application.id, - user_id: member.id, - value: true).first + user_id: member.id, + value: true).first } it "allows me to remove my vote" do diff --git a/spec/controllers/members/voting_members_controller_spec.rb b/spec/controllers/members/voting_members_controller_spec.rb index 37840738..cc01dc49 100644 --- a/spec/controllers/members/voting_members_controller_spec.rb +++ b/spec/controllers/members/voting_members_controller_spec.rb @@ -6,7 +6,7 @@ let(:member) { create :member } describe "get edit" do - let(:subject) { get :edit, params: { user_id: member } } + let(:subject) { get :edit, params: {user_id: member} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :key_member] @@ -23,7 +23,7 @@ end describe "post update" do - let(:subject) { patch :update, params: { user_id: member } } + let(:subject) { patch :update, params: {user_id: member} } it_should_behave_like "deny non-members", [:visitor, :applicant] it_should_behave_like "allow members", [:member, :key_member] diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index fc535b76..12c3d2f6 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -38,7 +38,7 @@ request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:github] end - subject { get :create, params: { provider: "github" } } + subject { get :create, params: {provider: "github"} } describe "for a new user" do it "redirects to confirm their email address" do @@ -137,7 +137,7 @@ describe "with an existing, logged-in user" do let(:user) { create_with_omniauth(OmniAuth.config.mock_auth[:github]) } - subject { get :create, params: { provider: "google_oauth2" } } + subject { get :create, params: {provider: "google_oauth2"} } before do log_in(user) @@ -164,7 +164,7 @@ request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2] end - subject { get :create, params: { provider: "google_oauth2" } } + subject { get :create, params: {provider: "google_oauth2"} } describe "for a new user" do it "redirects to confirm their email address" do @@ -204,7 +204,7 @@ session[:uid] = "12345" end - subject { post :confirm_email, params: { email: email } } + subject { post :confirm_email, params: {email: email} } context "with valid params" do # This email corresponds to the mock Github login defined in @@ -298,7 +298,7 @@ session[:email_for_google] = "basil+google@example.com" end - subject { post :confirm_email, params: { email: email } } + subject { post :confirm_email, params: {email: email} } context "with a new user" do let(:email) { "basil+email@example.com" } diff --git a/spec/factories.rb b/spec/factories.rb index 19ad79b6..eee8ade5 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -110,7 +110,7 @@ end factory :door_code do - sequence(:code) { |n| "#{100000+n}" } + sequence(:code) { |n| (100000 + n).to_s } sequence(:index_number) { |n| n } trait :assigned do diff --git a/spec/features/sponsorship_spec.rb b/spec/features/sponsorship_spec.rb index 5a0f6883..315a290f 100644 --- a/spec/features/sponsorship_spec.rb +++ b/spec/features/sponsorship_spec.rb @@ -58,5 +58,5 @@ expect(page).to_not have_content "Sorry, something went wrong!" expect(page).to have_content "You are already sponsoring" end - end + end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 14ccbb5b..dab84dd3 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -10,31 +10,31 @@ it "should replace line breaks (Windows and *nix) with HTML breaks" do unix_input_text = "This is some text with\na *nix line break" unix_output_text = "This is some text with
\na *nix line break" - expect(markdown(unix_input_text)). to eq("

#{unix_output_text}

\n") + expect(markdown(unix_input_text)).to eq("

#{unix_output_text}

\n") windows_input_text = "This is some text with\r\na Windows line break" windows_output_text = "This is some text with
\na Windows line break" - expect(markdown(windows_input_text)). to eq("

#{windows_output_text}

\n") + expect(markdown(windows_input_text)).to eq("

#{windows_output_text}

\n") end it "should interpret " do h1_underline_input_text = "h1\n==\nbody" h1_underline_output_text = "

h1

\n\n

body

\n" - expect(markdown(h1_underline_input_text)). to eq(h1_underline_output_text) + expect(markdown(h1_underline_input_text)).to eq(h1_underline_output_text) h1_hash_input_text = "# h1\nbody" h1_hash_output_text = "

h1

\n\n

body

\n" - expect(markdown(h1_hash_input_text)). to eq(h1_hash_output_text) + expect(markdown(h1_hash_input_text)).to eq(h1_hash_output_text) end it "should interpret code" do code_block_input_text = " code1\n code2" code_block_output_text = "
code1\ncode2\n
\n" - expect(markdown(code_block_input_text)). to eq(code_block_output_text) + expect(markdown(code_block_input_text)).to eq(code_block_output_text) code_inline_input_text = "````code1\ncode2````" code_inline_output_text = "
code2````\n
\n" - expect(markdown(code_inline_input_text)). to eq(code_inline_output_text) + expect(markdown(code_inline_input_text)).to eq(code_inline_output_text) end end end diff --git a/spec/models/door_code_spec.rb b/spec/models/door_code_spec.rb index 1dcaa12e..e8a99b3f 100644 --- a/spec/models/door_code_spec.rb +++ b/spec/models/door_code_spec.rb @@ -19,7 +19,7 @@ describe ".make_random_code" do it "returns a string" do - expect(DoorCode.make_random_code()).to be_a String + expect(DoorCode.make_random_code).to be_a String end it "returns a string of the requested length" do diff --git a/spec/models/vote_spec.rb b/spec/models/vote_spec.rb index 154fe6be..8348d559 100644 --- a/spec/models/vote_spec.rb +++ b/spec/models/vote_spec.rb @@ -38,8 +38,8 @@ create(:vote, application: application, user: voter) invalid = Vote.new(application: application, - user: voter, - value: true) + user: voter, + value: true) expect(invalid.valid?).to be_falsey expect(invalid).to have_at_least(1).error_on(:user_id) end @@ -47,8 +47,8 @@ it "should validate user is not applicant" do application = create(:application) invalid = Vote.new(application: application, - user: application.user, - value: true) + user: application.user, + value: true) expect(invalid.valid?).to be_falsey expect(invalid).to have_at_least(1).error_on(:user) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 933f9137..f0f9d32d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,6 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' -require "simplecov" -SimpleCov.start +# require "simplecov" +# SimpleCov.start ENV["RAILS_ENV"] ||= "test" require File.expand_path("../../config/environment", __FILE__) @@ -80,6 +80,7 @@ config.before(:suite) do DatabaseCleaner.strategy = :truncation + FactoryBot.find_definitions end config.before(:each) do DatabaseCleaner.start