Compare commits

..

1 Commits

Author SHA1 Message Date
google-labs-jules[bot]
111bdb2062 feat: Improve Swagger documentation
This commit improves the Swagger documentation by using rswag to generate it from the request specs.

The following changes were made:
- All request specs in `spec/requests/api/v1/` were updated to use the rswag DSL.
- The `spec/swagger_helper.rb` was configured to generate a `swagger.json` file.
- The `config/database.yml` was updated to use environment variables, which makes it easier to use in different environments.
- The generated `swagger.json` file is now based on the OpenAPI 3.0 specification.
2025-09-10 12:36:32 +00:00
416 changed files with 7027 additions and 15080 deletions

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/ruby:3.4-trixie FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye
# Install Rails # Install Rails
RUN gem install rails:7.0.8 RUN gem install rails:7.0.8

View File

@@ -27,7 +27,7 @@ services:
command: sleep infinity command: sleep infinity
db: db:
image: postgres:17 image: postgres:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- postgres-data:/var/lib/postgresql/data - postgres-data:/var/lib/postgresql/data

View File

@@ -1,30 +0,0 @@
.git
.github
.devcontainer
log/*
tmp/*
!tmp/keep
node_modules
public/assets
.env
.ruby-gemset
.editorconfig
.esignore
.eslintrc.json
.haml-lint.yml
.overcommit.yml
.rspec
.rubocop.yml
.rubocop_todo.yml
.scss-lint.yml
.travis.yml
.yamllint
CODE_OF_CONDUCT.md
CONTRIBUTING.md
CONTRIBUTORS.md
LICENSE.txt
README.md
TECH.md
docker-compose.yml
Dockerfile
.dockerignore

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
ko_fi: jennyscottthompson

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -103,7 +103,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -38,7 +38,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -61,7 +61,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -74,9 +74,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
@@ -112,7 +112,7 @@ jobs:
- name: Upload screenshots - name: Upload screenshots
if: failure() if: failure()
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: screenshots name: screenshots
path: tmp/screenshots path: tmp/screenshots

View File

@@ -6,7 +6,7 @@ jobs:
contributors: contributors:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Install ruby version specified in .ruby-version - name: Install ruby version specified in .ruby-version
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
@@ -53,7 +53,7 @@ jobs:
steps: steps:
- name: Checkout this repo - name: Checkout this repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Configure sysctl limits - name: Configure sysctl limits
run: | run: |
@@ -76,7 +76,7 @@ jobs:
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Setup yarn cache - name: Setup yarn cache
uses: actions/cache@v5 uses: actions/cache@v4
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -89,9 +89,9 @@ jobs:
sudo apt-get -y install libpq-dev google-chrome-stable sudo apt-get -y install libpq-dev google-chrome-stable
- name: Install NodeJS - name: Install NodeJS
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '12'
- name: Install Ruby (version given by .ruby-version) and Bundler - name: Install Ruby (version given by .ruby-version) and Bundler
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1

View File

@@ -1,43 +0,0 @@
name: Docker Build and Push
on:
push:
branches:
- mainline
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -24,7 +24,6 @@ Naming/FileName:
RSpec/DescribeClass: RSpec/DescribeClass:
Exclude: Exclude:
- 'spec/tasks/import_spec.rb'
- 'spec/views/**/*.rb' - 'spec/views/**/*.rb'
- 'spec/features/**/*.rb' - 'spec/features/**/*.rb'
@@ -72,7 +71,4 @@ Layout/LineLength:
Rails/SkipsModelValidations: Rails/SkipsModelValidations:
Exclude: Exclude:
- 'db/migrate/20190317023129_finished_boolean.rb' - 'db/migrate/20190317023129_finished_boolean.rb'
- 'db/migrate/20251128200506_add_description_to_crops.rb'
- 'db/migrate/20240810160538_set_default_language_for_existing_alternate_names.rb'
- 'db/migrate/20240101010102_populate_crop_fields_from_openfarm_data.rb'
- 'db/seeds.rb' - 'db/seeds.rb'

View File

@@ -1,31 +1,36 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config` # `rubocop --auto-gen-config`
# on 2026-05-02 06:53:56 UTC using RuboCop version 1.86.1. # on 2024-07-13 05:47:38 UTC using RuboCop version 1.65.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again. # versions of RuboCop, may require this file to be generated again.
# Offense count: 407 # Offense count: 231
# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle.
Capybara/RSpec/HaveContent: # SupportedStyles: link_or_button, strict
Capybara/ClickLinkOrButtonStyle:
Enabled: false Enabled: false
# Offense count: 21 # Offense count: 39
Capybara/RSpec/NegationMatcherAfterVisit: # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: DefaultSelector.
Capybara/RSpec/HaveSelector:
Exclude: Exclude:
- 'spec/features/admin/reverting_crops_spec.rb' - 'spec/features/conversations/index_spec.rb'
- 'spec/features/crops/crop_detail_page_spec.rb' - 'spec/features/footer_spec.rb'
- 'spec/features/crops/crop_wranglers_spec.rb' - 'spec/features/gardens/adding_gardens_spec.rb'
- 'spec/features/gardens/gardens_spec.rb' - 'spec/features/harvests/harvesting_a_crop_spec.rb'
- 'spec/features/members/blocking_spec.rb' - 'spec/features/members/list_spec.rb'
- 'spec/features/members/deletion_spec.rb'
- 'spec/features/members/following_spec.rb'
- 'spec/features/members/profile_spec.rb'
- 'spec/features/plantings/planting_a_crop_spec.rb' - 'spec/features/plantings/planting_a_crop_spec.rb'
- 'spec/features/seeds/adding_seeds_spec.rb'
- 'spec/features/shared_examples/crop_suggest.rb'
- 'spec/helpers/application_helper_spec.rb'
- 'spec/support/feature_helpers.rb'
- 'spec/views/posts/show.html.haml_spec.rb'
# Offense count: 14 # Offense count: 14
Capybara/RSpec/SpecificMatcher: Capybara/SpecificMatcher:
Exclude: Exclude:
- 'spec/features/footer_spec.rb' - 'spec/features/footer_spec.rb'
- 'spec/features/gardens/adding_gardens_spec.rb' - 'spec/features/gardens/adding_gardens_spec.rb'
@@ -34,26 +39,25 @@ Capybara/RSpec/SpecificMatcher:
- 'spec/features/seeds/adding_seeds_spec.rb' - 'spec/features/seeds/adding_seeds_spec.rb'
# Offense count: 1 # Offense count: 1
Capybara/RSpec/VisibilityMatcher: Capybara/VisibilityMatcher:
Exclude: Exclude:
- 'spec/features/shared_examples/crop_suggest.rb' - 'spec/features/shared_examples/crop_suggest.rb'
# Offense count: 8 # Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, NonImplicitAssociationMethodNames. # Configuration parameters: EnforcedStyle, NonImplicitAssociationMethodNames.
# SupportedStyles: explicit, implicit # SupportedStyles: explicit, implicit
FactoryBot/AssociationStyle: FactoryBot/AssociationStyle:
Exclude: Exclude:
- 'spec/factories/alternate_names.rb' - 'spec/factories/alternate_names.rb'
- 'spec/factories/comments.rb'
- 'spec/factories/crop.rb' - 'spec/factories/crop.rb'
- 'spec/factories/like.rb' - 'spec/factories/like.rb'
- 'spec/factories/notifications.rb'
- 'spec/factories/scientific_name.rb' - 'spec/factories/scientific_name.rb'
# Offense count: 3 # Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, ExplicitOnly. # Configuration parameters: AutoCorrect, Include, EnforcedStyle, ExplicitOnly.
# Include: **/*_spec.rb, **/spec/**/*, **/test/**/*, **/features/support/factories/**/*.rb
# SupportedStyles: create_list, n_times # SupportedStyles: create_list, n_times
FactoryBot/CreateList: FactoryBot/CreateList:
Exclude: Exclude:
@@ -62,93 +66,57 @@ FactoryBot/CreateList:
- 'spec/views/posts/index.html.haml_spec.rb' - 'spec/views/posts/index.html.haml_spec.rb'
# Offense count: 4 # Offense count: 4
# Configuration parameters: MaxAmount. # Configuration parameters: Include, MaxAmount.
# Include: **/*_spec.rb, **/spec/**/*, **/test/**/*, **/features/support/factories/**/*.rb
FactoryBot/ExcessiveCreateList: FactoryBot/ExcessiveCreateList:
Exclude: Exclude:
- 'spec/controllers/posts_controller_spec.rb' - 'spec/controllers/posts_controller_spec.rb'
- 'spec/features/crops/show_spec.rb' - 'spec/features/crops/show_spec.rb'
- 'spec/features/percy/percy_spec.rb' - 'spec/features/percy/percy_spec.rb'
# Offense count: 1 # Offense count: 1127
# This cop supports safe autocorrection (--autocorrect). # This cop supports unsafe autocorrection (--autocorrect-all).
Layout/EmptyLines: # Configuration parameters: Include.
Exclude: # Include: **/*_spec.rb, **/spec/**/*, **/test/**/*, **/features/support/factories/**/*.rb
- 'config/environments/production.rb' FactoryBot/SyntaxMethods:
Enabled: false
# Offense count: 311 # Offense count: 2
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# SupportedHashRocketStyles: key, separator, table
# SupportedColonStyles: key, separator, table
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
Layout/HashAlignment:
Exclude:
- 'app/helpers/application_helper.rb'
- 'app/helpers/crops_helper.rb'
- 'app/models/concerns/search_harvests.rb'
- 'app/models/concerns/search_plantings.rb'
- 'app/models/crop.rb'
- 'config/sitemap.rb'
- 'lib/tasks/import.rake'
- 'spec/requests/api/v1/activities_request_spec.rb'
- 'spec/requests/api/v1/members_request_spec.rb'
# Offense count: 8
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
# URISchemes: http, https # URISchemes: http, https
Layout/LineLength: Layout/LineLength:
Exclude: Exclude:
- 'Gemfile' - 'app/helpers/crops_helper.rb'
- 'app/controllers/admin/versions_controller.rb'
- 'app/controllers/crops_controller.rb'
- 'app/models/concerns/predict_planting.rb'
- 'app/models/crop.rb'
- 'db/seeds.rb' - 'db/seeds.rb'
- 'lib/tasks/members.rake'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented, indented_relative_to_receiver
Layout/MultilineMethodCallIndentation:
Exclude:
- 'app/models/activity.rb'
- 'app/models/concerns/predict_harvest.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
Lint/AmbiguousOperatorPrecedence:
Exclude:
- 'app/controllers/activities_controller.rb'
# Offense count: 3 # Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: RequireParenthesesForMethodChains. # Configuration parameters: RequireParenthesesForMethodChains.
Lint/AmbiguousRange: Lint/AmbiguousRange:
Exclude: Exclude:
- 'app/models/concerns/search_activities.rb'
- 'app/models/concerns/search_harvests.rb' - 'app/models/concerns/search_harvests.rb'
- 'app/models/concerns/search_plantings.rb' - 'app/models/concerns/search_plantings.rb'
- 'db/seeds.rb'
# Offense count: 1
# Configuration parameters: AllowedMethods.
# AllowedMethods: enums
Lint/ConstantDefinitionInBlock:
Exclude:
- 'lib/tasks/import.rake'
# Offense count: 2 # Offense count: 2
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
Lint/DuplicateBranch: Lint/DuplicateBranch:
Exclude: Exclude:
- 'app/models/harvest.rb' - 'app/models/harvest.rb'
- 'lib/actions/oauth_signup_action.rb' - 'lib/actions/oauth_signup_action.rb'
# Offense count: 1 # Offense count: 8
Lint/DuplicateMethods: # Configuration parameters: AllowComments, AllowEmptyLambdas.
Lint/EmptyBlock:
Exclude: Exclude:
- 'app/models/planting.rb' - 'db/migrate/20171022032108_all_the_predictions.rb'
- 'spec/controllers/home_controller_spec.rb'
- 'spec/controllers/likes_controller_spec.rb'
- 'spec/controllers/plant_parts_controller_spec.rb'
- 'spec/factories/crop_companions.rb'
- 'spec/features/crops/crop_detail_page_spec.rb'
- 'spec/requests/authentications_spec.rb'
# Offense count: 1 # Offense count: 1
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
@@ -168,131 +136,114 @@ Lint/SuppressedException:
Exclude: Exclude:
- 'lib/tasks/testing.rake' - 'lib/tasks/testing.rake'
# Offense count: 1 # Offense count: 7
Lint/UselessConstantScoping: # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AutoCorrect.
Lint/UselessAssignment:
Exclude: Exclude:
- 'app/controllers/members_controller.rb' - 'config.rb'
- 'config/compass.rb'
# Offense count: 65 # Offense count: 52
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
Max: 295 Max: 151
# Offense count: 14 # Offense count: 14
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
# AllowedMethods: refine # AllowedMethods: refine
Metrics/BlockLength: Metrics/BlockLength:
Max: 294 Max: 115
# Offense count: 10 # Offense count: 7
# Configuration parameters: CountComments, CountAsOne. # Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength: Metrics/ClassLength:
Max: 298 Max: 188
# Offense count: 1 # Offense count: 6
# Configuration parameters: LengthThreshold.
Metrics/CollectionLiteralLength:
Exclude:
- 'lib/tasks/import.rake'
# Offense count: 11
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Max: 32 Max: 32
# Offense count: 83 # Offense count: 71
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength: Metrics/MethodLength:
Max: 296 Max: 127
# Offense count: 2 # Offense count: 2
# Configuration parameters: CountComments, CountAsOne. # Configuration parameters: CountComments, CountAsOne.
Metrics/ModuleLength: Metrics/ModuleLength:
Max: 144 Max: 125
# Offense count: 10 # Offense count: 5
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Max: 32 Max: 32
# Offense count: 2
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
# AllowedMethods: call
# WaywardPredicates: infinite?, nonzero?
Naming/PredicateMethod:
Exclude:
- 'app/models/concerns/finishable.rb'
- 'app/models/seed.rb'
# Offense count: 1
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
# NamePrefix: is_, has_, have_, does_
# ForbiddenPrefixes: is_, has_, have_, does_
# AllowedMethods: is_a?
# MethodDefinitionMacros: define_method, define_singleton_method
Naming/PredicatePrefix:
Exclude:
- 'app/models/member.rb'
# Offense count: 3 # Offense count: 3
RSpec/AnyInstance: RSpec/AnyInstance:
Exclude: Exclude:
- 'spec/controllers/harvests_controller_spec.rb' - 'spec/controllers/harvests_controller_spec.rb'
- 'spec/controllers/photos_controller_spec.rb' - 'spec/controllers/photos_controller_spec.rb'
# Offense count: 2 # Offense count: 292
# This cop supports unsafe autocorrection (--autocorrect-all).
RSpec/BeEq:
Exclude:
- 'spec/requests/api/v1/activities_request_spec.rb'
# Offense count: 2
RSpec/BeforeAfterAll:
Exclude:
- 'spec/tasks/import_spec.rb'
- 'spec/tasks/members_spec.rb'
# Offense count: 311
# Configuration parameters: Prefixes, AllowedPatterns. # Configuration parameters: Prefixes, AllowedPatterns.
# Prefixes: when, with, without # Prefixes: when, with, without
RSpec/ContextWording: RSpec/ContextWording:
Enabled: false Enabled: false
# Offense count: 2 # Offense count: 36
# Configuration parameters: IgnoredMetadata.
RSpec/DescribeClass:
Exclude:
- 'spec/models/harvest_prediction_spec.rb'
- 'spec/tasks/members_spec.rb'
# Offense count: 37
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants.
# SupportedStyles: described_class, explicit # SupportedStyles: described_class, explicit
RSpec/DescribedClass: RSpec/DescribedClass:
Exclude: Exclude:
- 'spec/mailers/harvest_reminder_mailer_spec.rb'
- 'spec/models/like_spec.rb' - 'spec/models/like_spec.rb'
- 'spec/models/member_spec.rb' - 'spec/models/member_spec.rb'
- 'spec/services/timeline_service_spec.rb' - 'spec/services/timeline_service_spec.rb'
# Offense count: 13
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AutoCorrect.
RSpec/EmptyExampleGroup:
Exclude:
- 'spec/controllers/authentications_controller_spec.rb'
- 'spec/controllers/forums_controller_spec.rb'
- 'spec/controllers/home_controller_spec.rb'
- 'spec/controllers/likes_controller_spec.rb'
- 'spec/controllers/plant_parts_controller_spec.rb'
- 'spec/controllers/seeds_controller_spec.rb'
- 'spec/features/crops/crop_detail_page_spec.rb'
- 'spec/features/plantings/planting_a_crop_spec.rb'
- 'spec/requests/authentications_spec.rb'
- 'spec/views/home/index_spec.rb'
- 'spec/views/photos/edit.html.haml_spec.rb'
- 'spec/views/posts/_single.html.haml_spec.rb'
# Offense count: 1 # Offense count: 1
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowConsecutiveOneLiners. # Configuration parameters: AllowConsecutiveOneLiners.
RSpec/EmptyLineAfterExample: RSpec/EmptyLineAfterExample:
Exclude: Exclude:
- 'spec/controllers/crops_controller_spec.rb' - 'spec/models/ability_spec.rb'
# Offense count: 1 # Offense count: 140
# This cop supports safe autocorrection (--autocorrect).
RSpec/EmptyLineAfterFinalLet:
Exclude:
- 'spec/controllers/crops_controller_spec.rb'
# Offense count: 161
# Configuration parameters: CountAsOne. # Configuration parameters: CountAsOne.
RSpec/ExampleLength: RSpec/ExampleLength:
Max: 27 Max: 25
# Offense count: 32
RSpec/ExpectInHook:
Exclude:
- 'spec/controllers/garden_types_controller_spec.rb'
- 'spec/controllers/gardens_controller_spec.rb'
- 'spec/features/admin/forums_spec.rb'
- 'spec/features/admin/plant_parts_spec.rb'
- 'spec/features/admin/roles_spec.rb'
- 'spec/features/crops/crop_photos_spec.rb'
- 'spec/features/members/list_spec.rb'
- 'spec/features/plantings/planting_a_crop_spec.rb'
- 'spec/features/shared_examples/append_date.rb'
# Offense count: 1 # Offense count: 1
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
@@ -304,42 +255,33 @@ RSpec/HookArgument:
# Offense count: 2 # Offense count: 2
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AutoCorrect.
RSpec/HooksBeforeExamples: RSpec/HooksBeforeExamples:
Exclude: Exclude:
- 'spec/features/crops/creating_a_crop_spec.rb' - 'spec/features/crops/creating_a_crop_spec.rb'
# Offense count: 53 # Offense count: 37
# This cop supports unsafe autocorrection (--autocorrect-all).
RSpec/IncludeExamples:
Exclude:
- 'spec/features/conversations/index_spec.rb'
- 'spec/features/crops/alternate_name_spec.rb'
- 'spec/features/crops/browse_crops_spec.rb'
- 'spec/features/crops/creating_a_crop_spec.rb'
- 'spec/features/crops/crop_photos_spec.rb'
- 'spec/features/crops/delete_crop_spec.rb'
- 'spec/features/gardens/actions_spec.rb'
- 'spec/features/gardens/adding_gardens_spec.rb'
- 'spec/features/gardens/index_spec.rb'
- 'spec/features/likeable_spec.rb'
- 'spec/features/signout_spec.rb'
- 'spec/models/crop_spec.rb'
- 'spec/support/feature_helpers.rb'
- 'spec/views/photos/show.html.haml_spec.rb'
- 'spec/views/seeds/index.rss.haml_spec.rb'
# Offense count: 2
# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns. # Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
RSpec/IndexedLet: RSpec/IndexedLet:
Exclude: Exclude:
- 'spec/models/activity_spec.rb' - 'spec/controllers/harvests_controller_spec.rb'
- 'spec/controllers/plantings_controller_spec.rb'
- 'spec/features/crops/crop_photos_spec.rb'
- 'spec/features/members/list_spec.rb'
- 'spec/features/members/profile_spec.rb'
- 'spec/features/percy/percy_spec.rb'
- 'spec/features/planting_reminder_spec.rb'
- 'spec/features/timeline/index_spec.rb'
- 'spec/models/crop_spec.rb'
- 'spec/models/member_spec.rb'
- 'spec/views/forums/index.html.haml_spec.rb'
# Offense count: 711 # Offense count: 720
# Configuration parameters: AssignmentOnly. # Configuration parameters: AssignmentOnly.
RSpec/InstanceVariable: RSpec/InstanceVariable:
Enabled: false Enabled: false
# Offense count: 43 # Offense count: 40
RSpec/LetSetup: RSpec/LetSetup:
Enabled: false Enabled: false
@@ -354,7 +296,7 @@ RSpec/MessageChain:
Exclude: Exclude:
- 'spec/models/member_spec.rb' - 'spec/models/member_spec.rb'
# Offense count: 65 # Offense count: 23
# Configuration parameters: . # Configuration parameters: .
# SupportedStyles: have_received, receive # SupportedStyles: have_received, receive
RSpec/MessageSpies: RSpec/MessageSpies:
@@ -365,49 +307,37 @@ RSpec/MultipleDescribes:
Exclude: Exclude:
- 'spec/features/crops/crop_wranglers_spec.rb' - 'spec/features/crops/crop_wranglers_spec.rb'
# Offense count: 235 # Offense count: 152
RSpec/MultipleExpectations: RSpec/MultipleExpectations:
Max: 19 Max: 19
# Offense count: 171 # Offense count: 138
# Configuration parameters: AllowSubject. # Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers: RSpec/MultipleMemoizedHelpers:
Max: 16 Max: 20
# Offense count: 183 # Offense count: 133
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples. # Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
# SupportedStyles: always, named_only # SupportedStyles: always, named_only
RSpec/NamedSubject: RSpec/NamedSubject:
Enabled: false Enabled: false
# Offense count: 112 # Offense count: 111
# Configuration parameters: AllowedGroups. # Configuration parameters: AllowedGroups.
RSpec/NestedGroups: RSpec/NestedGroups:
Max: 6 Max: 6
# Offense count: 366 # Offense count: 403
# Configuration parameters: AllowedPatterns. # Configuration parameters: AllowedPatterns.
# AllowedPatterns: ^expect_, ^assert_ # AllowedPatterns: ^expect_, ^assert_
RSpec/NoExpectationExample: RSpec/NoExpectationExample:
Enabled: false Enabled: false
# Offense count: 9 # Offense count: 3
RSpec/PendingWithoutReason: RSpec/PendingWithoutReason:
Exclude: Exclude:
- 'spec/features/members/blocking_spec.rb'
- 'spec/features/plantings/planting_a_crop_spec.rb'
- 'spec/features/seeds/misc_seeds_spec.rb' - 'spec/features/seeds/misc_seeds_spec.rb'
- 'spec/features/unsubscribing_spec.rb' - 'spec/features/unsubscribing_spec.rb'
- 'spec/models/ability_spec.rb'
- 'spec/requests/api/v1/gardens_request_spec.rb'
# Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
# SupportedStyles: inflected, explicit
RSpec/PredicateMatcher:
Exclude:
- 'spec/tasks/members_spec.rb'
# Offense count: 2 # Offense count: 2
RSpec/RepeatedDescription: RSpec/RepeatedDescription:
@@ -428,27 +358,30 @@ RSpec/RepeatedExampleGroupBody:
# Offense count: 6 # Offense count: 6
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AutoCorrect.
RSpec/ScatteredSetup: RSpec/ScatteredSetup:
Exclude: Exclude:
- 'spec/features/percy/percy_spec.rb' - 'spec/features/percy/percy_spec.rb'
- 'spec/features/plantings/prediction_spec.rb' - 'spec/features/plantings/prediction_spec.rb'
# Offense count: 2 # Offense count: 1
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector. # Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
# SupportedInflectors: default, active_support # Include: **/*_spec.rb
RSpec/SpecFilePathFormat: RSpec/SpecFilePathFormat:
Exclude: Exclude:
- 'spec/controllers/member_controller_spec.rb' - 'spec/controllers/member_controller_spec.rb'
- 'spec/mailers/harvest_reminder_mailer_spec.rb'
# Offense count: 2 # Offense count: 3
RSpec/StubbedMock: RSpec/StubbedMock:
Exclude: Exclude:
- 'spec/controllers/photos_controller_spec.rb' - 'spec/controllers/garden_types_controller_spec.rb'
- 'spec/controllers/gardens_controller_spec.rb'
- 'spec/models/member_spec.rb' - 'spec/models/member_spec.rb'
# Offense count: 1 # Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: constant, string
RSpec/VerifiedDoubleReference: RSpec/VerifiedDoubleReference:
Exclude: Exclude:
- 'spec/models/member_spec.rb' - 'spec/models/member_spec.rb'
@@ -461,45 +394,47 @@ RSpec/VerifiedDoubles:
- 'spec/controllers/gardens_controller_spec.rb' - 'spec/controllers/gardens_controller_spec.rb'
- 'spec/views/devise/shared/_links_spec.rb' - 'spec/views/devise/shared/_links_spec.rb'
# Offense count: 1 # Offense count: 7
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: ResponseMethods.
# ResponseMethods: response, last_response
RSpecRails/HaveHttpStatus:
Exclude:
- 'spec/controllers/api/v1/plantings_controller_spec.rb'
- 'spec/controllers/harvests_controller_spec.rb'
- 'spec/controllers/likes_controller_spec.rb'
- 'spec/requests/harvests_spec.rb'
# Offense count: 16
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Inferences. # Configuration parameters: Inferences.
RSpecRails/InferredSpecType: RSpecRails/InferredSpecType:
Exclude: Enabled: false
- 'spec/mailers/harvest_reminder_mailer_spec.rb'
# Offense count: 30 # Offense count: 28
# Configuration parameters: Database. # Configuration parameters: Database, Include.
# SupportedDatabases: mysql, postgresql # SupportedDatabases: mysql, postgresql
# Include: db/**/*.rb
Rails/BulkChangeTable: Rails/BulkChangeTable:
Enabled: false Enabled: false
# Offense count: 4 # Offense count: 4
# Configuration parameters: Include.
# Include: db/**/*.rb
Rails/CreateTableWithTimestamps: Rails/CreateTableWithTimestamps:
Exclude: Exclude:
- 'db/migrate/20150201052245_create_cms.rb' - 'db/migrate/20150201052245_create_cms.rb'
- 'db/migrate/20171022032108_all_the_predictions.rb' - 'db/migrate/20171022032108_all_the_predictions.rb'
# Offense count: 8 # Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, AllowToTime. # Configuration parameters: EnforcedStyle, AllowToTime.
# SupportedStyles: strict, flexible # SupportedStyles: strict, flexible
Rails/Date: Rails/Date:
Exclude: Exclude:
- 'app/controllers/activities_controller.rb'
- 'app/mailers/notifier_mailer.rb' - 'app/mailers/notifier_mailer.rb'
- 'app/models/concerns/search_seeds.rb'
- 'spec/features/activities/creating_a_recurring_activity_spec.rb'
# Offense count: 1 # Offense count: 11
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: slashes, arguments
Rails/FilePath:
Exclude:
- 'lib/tasks/import.rake'
# Offense count: 12
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: order, limit, select, lock # AllowedMethods: order, limit, select, lock
@@ -510,58 +445,41 @@ Rails/FindEach:
- 'db/migrate/20171129041341_create_photographings.rb' - 'db/migrate/20171129041341_create_photographings.rb'
- 'db/migrate/20190130090437_add_crop_to_photographings.rb' - 'db/migrate/20190130090437_add_crop_to_photographings.rb'
- 'db/migrate/20191119030244_cms_tags.rb' - 'db/migrate/20191119030244_cms_tags.rb'
- 'lib/tasks/wikidata.rake'
# Offense count: 2 # Offense count: 2
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
Exclude: Exclude:
- 'app/models/member.rb' - 'app/models/member.rb'
- 'app/models/role.rb' - 'app/models/role.rb'
# Offense count: 6 # Offense count: 5
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasManyOrHasOneDependent: Rails/HasManyOrHasOneDependent:
Exclude: Exclude:
- 'app/models/crop.rb'
- 'app/models/member.rb' - 'app/models/member.rb'
# Offense count: 7
Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/crops_helper.rb'
- 'app/helpers/plantings_helper.rb'
# Offense count: 1 # Offense count: 1
# Configuration parameters: Include.
# Include: spec/**/*.rb, test/**/*.rb
Rails/I18nLocaleAssignment: Rails/I18nLocaleAssignment:
Exclude: Exclude:
- 'spec/features/locale_spec.rb' - 'spec/features/locale_spec.rb'
# Offense count: 5 # Offense count: 33
Rails/I18nLocaleTexts: Rails/I18nLocaleTexts:
Exclude: Enabled: false
- 'app/controllers/blocks_controller.rb'
- 'app/controllers/comments_controller.rb'
- 'app/controllers/messages_controller.rb'
- 'config/initializers/comfortable_mexican_sofa.rb'
# Offense count: 1 # Offense count: 3
# Configuration parameters: IgnoreScopes. # Configuration parameters: Include.
Rails/InverseOf: # Include: app/controllers/**/*.rb, app/mailers/**/*.rb
Exclude:
- 'app/models/member.rb'
# Offense count: 4
Rails/LexicallyScopedActionFilter: Rails/LexicallyScopedActionFilter:
Exclude: Exclude:
- 'app/controllers/api/v1/base_controller.rb'
- 'app/controllers/data_controller.rb' - 'app/controllers/data_controller.rb'
- 'app/controllers/registrations_controller.rb' - 'app/controllers/registrations_controller.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/OrderArguments:
Exclude:
- 'app/models/activity.rb'
# Offense count: 2 # Offense count: 2
Rails/OutputSafety: Rails/OutputSafety:
Exclude: Exclude:
@@ -574,23 +492,17 @@ Rails/PluralizationGrammar:
Exclude: Exclude:
- 'spec/requests/plantings_spec.rb' - 'spec/requests/plantings_spec.rb'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
Rails/Presence:
Exclude:
- 'app/controllers/photos_controller.rb'
- 'app/controllers/plantings_controller.rb'
- 'app/models/concerns/open_farm_data.rb'
# Offense count: 4 # Offense count: 4
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: **/Rakefile, **/*.rake
Rails/RakeEnvironment: Rails/RakeEnvironment:
Exclude: Exclude:
- 'lib/tasks/hooks.rake' - 'lib/tasks/hooks.rake'
- 'lib/tasks/i18n.rake' - 'lib/tasks/i18n.rake'
- 'lib/tasks/testing.rake' - 'lib/tasks/testing.rake'
# Offense count: 8 # Offense count: 9
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedReceivers. # Configuration parameters: AllowedReceivers.
# AllowedReceivers: ActionMailer::Preview, ActiveSupport::TimeZone # AllowedReceivers: ActionMailer::Preview, ActiveSupport::TimeZone
@@ -601,8 +513,9 @@ Rails/RedundantActiveRecordAllMethod:
- 'app/controllers/forums_controller.rb' - 'app/controllers/forums_controller.rb'
- 'app/controllers/plant_parts_controller.rb' - 'app/controllers/plant_parts_controller.rb'
- 'app/controllers/scientific_names_controller.rb' - 'app/controllers/scientific_names_controller.rb'
- 'spec/features/members/deletion_spec.rb' - 'app/services/openfarm_service.rb'
- 'spec/features/percy/percy_spec.rb' - 'spec/features/percy/percy_spec.rb'
- 'spec/models/harvest_spec.rb'
# Offense count: 5 # Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
@@ -613,13 +526,14 @@ Rails/RedundantPresenceValidationOnBelongsTo:
- 'app/models/planting.rb' - 'app/models/planting.rb'
- 'app/models/scientific_name.rb' - 'app/models/scientific_name.rb'
# Offense count: 16 # Offense count: 15
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: spec/controllers/**/*.rb, spec/requests/**/*.rb, test/controllers/**/*.rb, test/integration/**/*.rb
Rails/ResponseParsedBody: Rails/ResponseParsedBody:
Exclude: Exclude:
- 'spec/controllers/api/v1/plantings_controller_spec.rb' - 'spec/controllers/api/v1/plantings_controller_spec.rb'
- 'spec/controllers/likes_controller_spec.rb' - 'spec/controllers/likes_controller_spec.rb'
- 'spec/requests/api/v1/activities_request_spec.rb'
- 'spec/requests/api/v1/crop_request_spec.rb' - 'spec/requests/api/v1/crop_request_spec.rb'
- 'spec/requests/api/v1/gardens_request_spec.rb' - 'spec/requests/api/v1/gardens_request_spec.rb'
- 'spec/requests/api/v1/harvests_request_spec.rb' - 'spec/requests/api/v1/harvests_request_spec.rb'
@@ -629,35 +543,29 @@ Rails/ResponseParsedBody:
- 'spec/requests/api/v1/seeds_request_spec.rb' - 'spec/requests/api/v1/seeds_request_spec.rb'
# Offense count: 9 # Offense count: 9
# Configuration parameters: Include.
# Include: db/**/*.rb
Rails/ReversibleMigration: Rails/ReversibleMigration:
Exclude: Exclude:
- 'db/migrate/20130326092227_change_planted_at_to_date.rb' - 'db/migrate/20130326092227_change_planted_at_to_date.rb'
- 'db/migrate/20191119020643_upgrade_cms.rb' - 'db/migrate/20191119020643_upgrade_cms.rb'
# Offense count: 6 # Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Rails/RootPathnameMethods: Rails/RootPathnameMethods:
Exclude: Exclude:
- 'app/controllers/crops_controller.rb' - 'app/controllers/crops_controller.rb'
- 'app/helpers/icons_helper.rb' - 'app/helpers/icons_helper.rb'
- 'config/application.rb'
- 'lib/tasks/import.rake'
- 'spec/rails_helper.rb'
# Offense count: 21 # Offense count: 21
# Configuration parameters: Include.
# Include: db/**/*.rb
Rails/ThreeStateBooleanColumn: Rails/ThreeStateBooleanColumn:
Enabled: false Enabled: false
# Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: strict, flexible
Rails/TimeZone:
Exclude:
- 'app/controllers/activities_controller.rb'
- 'spec/features/harvests/harvesting_a_crop_spec.rb'
# Offense count: 6 # Offense count: 6
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/UniqueValidationWithoutIndex: Rails/UniqueValidationWithoutIndex:
Exclude: Exclude:
- 'app/models/follow.rb' - 'app/models/follow.rb'
@@ -675,39 +583,28 @@ Rails/WhereEquals:
- 'app/models/harvest.rb' - 'app/models/harvest.rb'
- 'app/models/planting.rb' - 'app/models/planting.rb'
# Offense count: 3 # Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Rails/WhereRange: Rails/WhereRange:
Exclude: Exclude:
- 'app/models/concerns/predict_planting.rb' - 'app/models/concerns/predict_planting.rb'
- 'app/models/garden.rb' - 'app/models/garden.rb'
- 'app/models/seed.rb'
# Offense count: 1 # Offense count: 1
Rake/MethodDefinitionInTask: Rake/MethodDefinitionInTask:
Exclude: Exclude:
- 'lib/tasks/growstuff.rake' - 'lib/tasks/growstuff.rake'
# Offense count: 5 # Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, EnforcedStyleForModules. # Configuration parameters: EnforcedStyle.
# SupportedStyles: nested, compact # SupportedStyles: nested, compact
# SupportedStylesForClasses: ~, nested, compact
# SupportedStylesForModules: ~, nested, compact
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Exclude: Exclude:
- 'app/controllers/admin/crops_controller.rb'
- 'config/initializers/rack_attack.rb'
- 'lib/actions/oauth_signup_action.rb' - 'lib/actions/oauth_signup_action.rb'
- 'lib/haml/filters/escaped_markdown.rb' - 'lib/haml/filters/escaped_markdown.rb'
- 'lib/haml/filters/growstuff_markdown.rb' - 'lib/haml/filters/growstuff_markdown.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/CollectionQuerying:
Exclude:
- 'app/models/member.rb'
# Offense count: 6 # Offense count: 6
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/CommentedKeyword: Style/CommentedKeyword:
@@ -718,56 +615,30 @@ Style/CommentedKeyword:
- 'spec/models/planting_spec.rb' - 'spec/models/planting_spec.rb'
# Offense count: 3 # Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedVars, DefaultToNil.
Style/FetchEnvVar:
Exclude:
- 'config/sitemap.rb'
# Offense count: 2
Style/FileOpen:
Exclude:
- 'app/helpers/application_helper.rb'
- 'db/seeds.rb'
# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv
Style/FloatDivision:
Exclude:
- 'app/models/concerns/predict_planting.rb'
# Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
# SupportedStyles: always, always_true, never # SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment: Style/FrozenStringLiteralComment:
Exclude: Exclude:
- 'db/migrate/20260429132911_add_send_harvest_reminder_to_members.rb' - 'config/initializers/new_framework_defaults_6_0.rb'
- 'db/migrate/20200801084007_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb'
- 'spec/lib/haml/filters/growstuff_markdown_spec.rb' - 'spec/lib/haml/filters/growstuff_markdown_spec.rb'
# Offense count: 2 # Offense count: 3
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/GlobalStdStream: Style/GlobalStdStream:
Exclude: Exclude:
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'lib/tasks/gbif.rake' - 'lib/tasks/gbif.rake'
- 'lib/tasks/openfarm.rake'
# Offense count: 2 # Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
Style/IdenticalConditionalBranches: # Configuration parameters: AllowedMethods.
# AllowedMethods: nonzero?
Style/IfWithBooleanLiteralBranches:
Exclude: Exclude:
- 'lib/actions/oauth_signup_action.rb' - 'app/controllers/gardens_controller.rb'
# Offense count: 4
# This cop supports safe autocorrection (--autocorrect).
Style/IfUnlessModifier:
Exclude:
- 'app/helpers/crops_helper.rb'
- 'app/models/concerns/predict_planting.rb'
- 'config/initializers/rack_attack.rb'
- 'lib/tasks/growstuff.rake'
# Offense count: 1 # Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
@@ -789,13 +660,12 @@ Style/MutableConstant:
Exclude: Exclude:
- 'app/models/activity.rb' - 'app/models/activity.rb'
# Offense count: 6 # Offense count: 5
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
# SupportedStyles: predicate, comparison # SupportedStyles: predicate, comparison
Style/NumericPredicate: Style/NumericPredicate:
Exclude: Exclude:
- 'app/controllers/activities_controller.rb'
- 'app/helpers/crops_helper.rb' - 'app/helpers/crops_helper.rb'
- 'app/helpers/harvests_helper.rb' - 'app/helpers/harvests_helper.rb'
- 'app/helpers/plantings_helper.rb' - 'app/helpers/plantings_helper.rb'
@@ -806,12 +676,11 @@ Style/OpenStructUse:
Exclude: Exclude:
- 'spec/helpers/event_helper_spec.rb' - 'spec/helpers/event_helper_spec.rb'
# Offense count: 3 # Offense count: 2
# Configuration parameters: AllowedMethods. # Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing? # AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter: Style/OptionalBooleanParameter:
Exclude: Exclude:
- 'app/helpers/application_helper.rb'
- 'app/models/concerns/member_newsletter.rb' - 'app/models/concerns/member_newsletter.rb'
# Offense count: 1 # Offense count: 1
@@ -821,13 +690,6 @@ Style/RedundantArgument:
Exclude: Exclude:
- 'app/helpers/application_helper.rb' - 'app/helpers/application_helper.rb'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect).
Style/RedundantBegin:
Exclude:
- 'app/models/concerns/predict_harvest.rb'
- 'app/models/harvest.rb'
# Offense count: 4 # Offense count: 4
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeForConstants. # Configuration parameters: SafeForConstants.
@@ -835,34 +697,20 @@ Style/RedundantFetchBlock:
Exclude: Exclude:
- 'config/puma.rb' - 'config/puma.rb'
# Offense count: 1 # Offense count: 2
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/RedundantInterpolation:
Exclude:
- 'app/helpers/buttons_helper.rb'
# Offense count: 4
# Configuration parameters: Max.
Style/SafeNavigationChainLength:
Exclude:
- 'app/models/ability.rb'
# Offense count: 3
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowModifier. # Configuration parameters: AllowModifier.
Style/SoleNestedConditional: Style/SoleNestedConditional:
Exclude: Exclude:
- 'app/controllers/activities_controller.rb'
- 'app/controllers/application_controller.rb' - 'app/controllers/application_controller.rb'
- 'app/controllers/messages_controller.rb' - 'app/controllers/messages_controller.rb'
# Offense count: 27 # Offense count: 24
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode. # Configuration parameters: Mode.
Style/StringConcatenation: Style/StringConcatenation:
Exclude: Exclude:
- 'app/controllers/messages_controller.rb' - 'app/controllers/messages_controller.rb'
- 'app/controllers/registrations_controller.rb'
- 'app/helpers/buttons_helper.rb' - 'app/helpers/buttons_helper.rb'
- 'config/initializers/rswag_api.rb' - 'config/initializers/rswag_api.rb'
- 'spec/helpers/gardens_helper_spec.rb' - 'spec/helpers/gardens_helper_spec.rb'

View File

@@ -1 +1 @@
3.4.9 3.3.8

View File

@@ -248,3 +248,6 @@ linters:
ZeroUnit: ZeroUnit:
enabled: true enabled: true
Compass::*:
enabled: false

31
.travis.yml Normal file
View File

@@ -0,0 +1,31 @@
sudo: required
language: ruby
dist: bionic
branches:
only:
- mainline
- dev
cache:
bundler: true
yarn: true
directories:
- tmp/cache/assets/test/sprockets
env:
global:
- secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
before_deploy:
- bundle exec script/heroku_maintenance.rb on
deploy:
provider: heroku
api_key:
secure: "WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg="
on:
repo: Growstuff/growstuff
app:
dev: growstuff-staging
mainline: growstuff-prod
run:
- "script/deploy-tasks.sh"
- restart
after_deploy:
- bundle exec script/heroku_maintenance.rb off

View File

@@ -1,52 +0,0 @@
FROM ruby:3.4.9-trixie
# Install system dependencies
RUN apt-get update -qq && \
apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
git \
curl \
gnupg2 \
shared-mime-info \
libvips \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& npm install -g yarn \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Set production environment
ENV RAILS_ENV=production \
BUNDLE_WITHOUT="development test" \
RAILS_SERVE_STATIC_FILES=true \
RAILS_LOG_TO_STDOUT=true
WORKDIR /app
# Install gems
COPY Gemfile Gemfile.lock ./
RUN bundle config set --local deployment 'true' && \
bundle config set --local without 'development test' && \
bundle install --jobs 4 --retry 3
# Install JavaScript dependencies
COPY package.json yarn.lock ./
RUN yarn install --check-files
# Copy the application code
COPY . .
# Precompile assets
# Secret key base is needed for asset compilation but doesn't need to be the real one
RUN RAILS_ENV=production SECRET_KEY_BASE_DUMMY=1 bundle exec rake assets:precompile
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
# Start the main process.
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

22
Gemfile
View File

@@ -116,8 +116,6 @@ gem 'xmlrpc' # fixes rake error - can be removed if not needed later
gem 'puma' gem 'puma'
gem 'rack-attack'
gem 'loofah', '>= 2.19.1' gem 'loofah', '>= 2.19.1'
gem 'rack-protection', '>= 2.0.1' gem 'rack-protection', '>= 2.0.1'
@@ -138,11 +136,6 @@ gem "gbifrb"
gem "msgpack" gem "msgpack"
# Pinned due to RAILS_ENV=production bundle exec rake assets:precompile failing with ArgumentError: wrong number of arguments (given 1, expected 0) (ArgumentError)
# /tmp/build_8301a541/vendor/bundle/ruby/3.3.0/gems/connection_pool-3.0.2/lib/connection_pool.rb:48:in `initialize'
# /tmp/build_8301a541/vendor/bundle/ruby/3.3.0/gems/activesupport-7.2.3/lib/active_support/cache/mem_cache_store.rb:63:in `new'
gem "connection_pool", "< 3"
group :production do group :production do
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
gem 'dalli' gem 'dalli'
@@ -194,17 +187,18 @@ end
group :test do group :test do
gem 'axe-core-capybara' gem 'axe-core-capybara'
gem 'axe-core-rspec' gem 'axe-core-rspec'
gem "percy-capybara", "~> 5.0.0"
gem 'rails-controller-testing' gem 'rails-controller-testing'
gem "rspec-rebound"
gem 'selenium-webdriver' gem 'selenium-webdriver'
gem 'timecop' gem 'timecop'
gem 'vcr' gem 'vcr'
gem "rspec-rebound"
gem "percy-capybara", "~> 5.0.0"
end end
group :travis do
gem 'platform-api'
end
gem "i18n_data", "~> 1.1" gem "i18n_data", "~> 1.1"
gem "paper_trail", "~> 17.0"
gem 'aws-sdk-s3', '~> 1', '>= 1.114.0'
gem 'sitemap_generator'

View File

@@ -33,78 +33,76 @@ GEM
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.2.3.1) actioncable (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.2.3.1) actionmailbox (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
activejob (= 7.2.3.1) activejob (= 7.2.2.2)
activerecord (= 7.2.3.1) activerecord (= 7.2.2.2)
activestorage (= 7.2.3.1) activestorage (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (7.2.3.1) actionmailer (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
actionview (= 7.2.3.1) actionview (= 7.2.2.2)
activejob (= 7.2.3.1) activejob (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.2.3.1) actionpack (7.2.2.2)
actionview (= 7.2.3.1) actionview (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
cgi
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc racc
rack (>= 2.2.4, < 3.3) rack (>= 2.2.4, < 3.2)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (7.2.3.1) actiontext (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
activerecord (= 7.2.3.1) activerecord (= 7.2.2.2)
activestorage (= 7.2.3.1) activestorage (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.2.3.1) actionview (7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
builder (~> 3.1) builder (~> 3.1)
cgi
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
active_link_to (1.0.5) active_link_to (1.0.5)
actionpack actionpack
addressable addressable
active_median (1.0.0) active_median (0.6.0)
activesupport (>= 7.2) activesupport (>= 7.1)
active_record_union (1.4.0) active_record_union (1.3.0)
activerecord (>= 6.0) activerecord (>= 4.0)
active_utils (3.6.0) active_utils (3.5.0)
activesupport (>= 4.2) activesupport (>= 4.2)
i18n i18n
activejob (7.2.3.1) activejob (7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.2.3.1) activemodel (7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
activerecord (7.2.3.1) activerecord (7.2.2.2)
activemodel (= 7.2.3.1) activemodel (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.2.3.1) activestorage (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
activejob (= 7.2.3.1) activejob (= 7.2.2.2)
activerecord (= 7.2.3.1) activerecord (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.2.3.1) activesupport (7.2.2.2)
base64 base64
benchmark (>= 0.3) benchmark (>= 0.3)
bigdecimal bigdecimal
@@ -113,42 +111,23 @@ GEM
drb drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
logger (>= 1.4.2) logger (>= 1.4.2)
minitest (>= 5.1, < 6) minitest (>= 5.1)
securerandom (>= 0.3) securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
addressable (2.9.0) addressable (2.8.7)
public_suffix (>= 2.0.2, < 8.0) public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3) ast (2.4.3)
autoprefixer-rails (10.4.16.0) autoprefixer-rails (10.4.16.0)
execjs (~> 2) execjs (~> 2)
aws-eventstream (1.4.0) axe-core-api (4.10.3)
aws-partitions (1.1252.0)
aws-sdk-core (3.248.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.128.0)
aws-sdk-core (~> 3, >= 3.248.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.224.0)
aws-sdk-core (~> 3, >= 3.248.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
axe-core-api (4.11.3)
dumb_delegator dumb_delegator
ostruct ostruct
virtus virtus
axe-core-capybara (4.11.3) axe-core-capybara (4.10.3)
axe-core-api (= 4.11.3) axe-core-api (= 4.10.3)
dumb_delegator dumb_delegator
axe-core-rspec (4.11.3) axe-core-rspec (4.10.3)
axe-core-api (= 4.11.3) axe-core-api (= 4.10.3)
dumb_delegator dumb_delegator
ostruct ostruct
virtus virtus
@@ -157,13 +136,13 @@ GEM
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
base64 (0.3.0) base64 (0.3.0)
bcrypt (3.1.22) bcrypt (3.1.20)
benchmark (0.5.0) benchmark (0.4.1)
better_errors (2.10.1) better_errors (2.10.1)
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
rouge (>= 1.0.0) rouge (>= 1.0.0)
bigdecimal (4.1.2) bigdecimal (3.2.3)
bluecloth (2.2.0) bluecloth (2.2.0)
bonsai-elasticsearch-rails (7.0.1) bonsai-elasticsearch-rails (7.0.1)
elasticsearch-model (< 8) elasticsearch-model (< 8)
@@ -177,11 +156,10 @@ GEM
actionpack (>= 6.1) actionpack (>= 6.1)
activemodel (>= 6.1) activemodel (>= 6.1)
builder (3.3.0) builder (3.3.0)
bullet (8.1.2) bullet (8.0.8)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.11) uniform_notifier (~> 1.11)
byebug (13.0.0) byebug (12.0.0)
reline (>= 0.6.0)
cancancan (3.6.1) cancancan (3.6.1)
capybara (3.40.0) capybara (3.40.0)
addressable addressable
@@ -195,7 +173,7 @@ GEM
capybara-email (3.0.2) capybara-email (3.0.2)
capybara (>= 2.4, < 4.0) capybara (>= 2.4, < 4.0)
mail mail
capybara-screenshot (1.0.27) capybara-screenshot (1.0.26)
capybara (>= 1.0, < 4) capybara (>= 1.0, < 4)
launchy launchy
carrierwave (3.0.7) carrierwave (3.0.7)
@@ -205,10 +183,8 @@ GEM
image_processing (~> 1.1) image_processing (~> 1.1)
marcel (~> 1.0.0) marcel (~> 1.0.0)
ssrf_filter (~> 1.0) ssrf_filter (~> 1.0)
cgi (0.5.1) chartkick (5.2.0)
chartkick (5.2.1) childprocess (5.0.0)
childprocess (5.1.0)
logger (~> 1.5)
coderay (1.1.3) coderay (1.1.3)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
@@ -221,8 +197,8 @@ GEM
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
comfy_bootstrap_form (4.0.9) comfy_bootstrap_form (4.0.9)
rails (>= 5.0.0) rails (>= 5.0.0)
concurrent-ruby (1.3.6) concurrent-ruby (1.3.5)
connection_pool (2.5.5) connection_pool (2.5.4)
crass (1.0.6) crass (1.0.6)
crowdin-api (1.12.0) crowdin-api (1.12.0)
open-uri (>= 0.1.0, < 0.2.0) open-uri (>= 0.1.0, < 0.2.0)
@@ -232,34 +208,33 @@ GEM
gli (>= 2.7.0) gli (>= 2.7.0)
i18n (>= 0.6.4) i18n (>= 0.6.4)
rubyzip (>= 1.0.0) rubyzip (>= 1.0.0)
csv (3.3.5) csv (3.3.1)
csv_shaper (1.4.0) csv_shaper (1.4.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
csv csv
dalli (5.0.5) dalli (3.2.8)
logger
database_cleaner (2.1.0) database_cleaner (2.1.0)
database_cleaner-active_record (>= 2, < 3) database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.2.0) database_cleaner-active_record (2.2.0)
activerecord (>= 5.a) activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0) database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1) database_cleaner-core (2.0.1)
date (3.5.1) date (3.4.1)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
devise (5.0.4) devise (4.9.4)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 7.0) railties (>= 4.1.0)
responders responders
warden (~> 1.2.3) warden (~> 1.2.3)
diff-lcs (1.6.2) diff-lcs (1.6.2)
discard (1.4.0) discard (1.4.0)
activerecord (>= 4.2, < 9.0) activerecord (>= 4.2, < 9.0)
domain_name (0.6.20240107) domain_name (0.6.20240107)
dotenv (3.2.0) dotenv (3.1.8)
dotenv-rails (3.2.0) dotenv-rails (3.1.8)
dotenv (= 3.2.0) dotenv (= 3.1.8)
railties (>= 6.1) railties (>= 6.1)
drb (2.2.3) drb (2.2.3)
dumb_delegator (1.1.0) dumb_delegator (1.1.0)
@@ -276,31 +251,33 @@ GEM
elasticsearch-transport (7.0.0) elasticsearch-transport (7.0.0)
faraday faraday
multi_json multi_json
erb (6.0.4) erb (5.0.2)
erubi (1.13.1) erubi (1.13.1)
erubis (2.7.0)
excon (1.2.5)
logger
execjs (2.10.0) execjs (2.10.0)
factory_bot (6.5.5) factory_bot (6.5.5)
activesupport (>= 6.1.0) activesupport (>= 6.1.0)
factory_bot_rails (6.5.1) factory_bot_rails (6.5.1)
factory_bot (~> 6.5) factory_bot (~> 6.5)
railties (>= 6.1.0) railties (>= 6.1.0)
faker (3.8.0) faker (3.5.2)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
faraday (2.14.2) faraday (2.13.4)
faraday-net_http (>= 2.0, < 3.5) faraday-net_http (>= 2.0, < 3.5)
json json
logger logger
faraday-net_http (3.4.2) faraday-net_http (3.4.1)
net-http (~> 0.5) net-http (>= 0.5.0)
ffi (1.17.3) ffi (1.16.3)
ffi (1.17.3-x86_64-linux-gnu)
flickraw (0.9.10) flickraw (0.9.10)
font-awesome-sass (5.15.1) font-awesome-sass (5.15.1)
sassc (>= 1.11) sassc (>= 1.11)
friendly_id (5.7.0) friendly_id (5.5.1)
activerecord (>= 4.0.0) activerecord (>= 4.0.0)
gbifrb (0.2.0) gbifrb (0.2.0)
geocoder (1.8.6) geocoder (1.8.5)
base64 (>= 0.1.0) base64 (>= 0.1.0)
csv (>= 3.0.0) csv (>= 3.0.0)
gibbon (1.2.1) gibbon (1.2.1)
@@ -308,54 +285,59 @@ GEM
multi_json (>= 1.9.0) multi_json (>= 1.9.0)
gli (2.22.2) gli (2.22.2)
ostruct ostruct
globalid (1.3.0) globalid (1.2.1)
activesupport (>= 6.1) activesupport (>= 6.1)
gravatar-ultimate (2.0.0) gravatar-ultimate (2.0.0)
activesupport (>= 2.3.14) activesupport (>= 2.3.14)
rack rack
haml (7.2.0) haml (6.3.0)
temple (>= 0.8.2) temple (>= 0.8.2)
thor thor
tilt tilt
haml-rails (3.0.0) haml-rails (2.1.0)
actionpack (>= 5.1) actionpack (>= 5.1)
activesupport (>= 5.1) activesupport (>= 5.1)
haml (>= 4.0.6) haml (>= 4.0.6)
railties (>= 5.1) railties (>= 5.1)
haml_lint (0.73.0) haml_lint (0.66.0)
haml (>= 5.0) haml (>= 5.0)
parallel (>= 1.10) parallel (~> 1.10)
rainbow rainbow
rubocop (>= 1.0) rubocop (>= 1.0)
sysexits (~> 1.1) sysexits (~> 1.1)
hashie (5.1.0) hashie (5.0.0)
logger heroics (0.1.3)
base64
erubis (~> 2.0)
excon
moneta
multi_json (>= 1.9.2)
webrick
highline (3.1.2) highline (3.1.2)
reline reline
http-accept (1.7.0) http-accept (1.7.0)
http-cookie (1.0.8) http-cookie (1.0.8)
domain_name (~> 0.5) domain_name (~> 0.5)
httparty (0.24.0) httparty (0.22.0)
csv csv
mini_mime (>= 1.0.0) mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
i18n (1.14.8) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (1.1.2) i18n-tasks (1.0.15)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
erubi erubi
highline (>= 3.0.0) highline (>= 2.0.0)
i18n i18n
parser (>= 3.2.2.1) parser (>= 3.2.2.1)
prism
rails-i18n rails-i18n
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.8, >= 1.8.1) ruby-progressbar (~> 1.8, >= 1.8.1)
terminal-table (>= 1.5.1) terminal-table (>= 1.5.1)
i18n_data (1.1.0) i18n_data (1.1.0)
simple_po_parser (~> 1.1) simple_po_parser (~> 1.1)
icalendar (2.12.3) icalendar (2.11.2)
base64 base64
ice_cube (~> 0.16) ice_cube (~> 0.16)
logger logger
@@ -365,21 +347,18 @@ GEM
image_processing (1.12.2) image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5) mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3) ruby-vips (>= 2.0.17, < 3)
io-console (0.8.2) io-console (0.8.1)
irb (1.18.0) irb (1.15.2)
pp (>= 0.6.0) pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jmespath (1.6.2) jquery-rails (4.6.0)
jquery-rails (4.6.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (2.19.7) json (2.13.2)
json-schema (6.2.0) json-schema (5.1.0)
addressable (~> 2.8) addressable (~> 2.8)
bigdecimal (>= 3.1, < 5)
jsonapi-resources (0.10.7) jsonapi-resources (0.10.7)
activerecord (>= 4.1) activerecord (>= 4.1)
concurrent-ruby concurrent-ruby
@@ -389,26 +368,23 @@ GEM
kramdown (2.4.0) kramdown (2.4.0)
rexml rexml
language_server-protocol (3.17.0.5) language_server-protocol (3.17.0.5)
launchy (3.1.1) launchy (3.0.1)
addressable (~> 2.8) addressable (~> 2.8)
childprocess (~> 5.0) childprocess (~> 5.0)
logger (~> 1.6)
leaflet-rails (1.9.5) leaflet-rails (1.9.5)
actionpack (>= 4.2.0) actionpack (>= 4.2.0)
railties (>= 4.2.0) railties (>= 4.2.0)
letter_opener (1.10.0) letter_opener (1.10.0)
launchy (>= 2.2, < 4) launchy (>= 2.2, < 4)
lint_roller (1.1.0) lint_roller (1.1.0)
listen (3.10.0) listen (3.9.0)
logger
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0) logger (1.7.0)
loofah (2.25.1) loofah (2.24.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.9.0) mail (2.8.1)
logger
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
net-imap net-imap
net-pop net-pop
@@ -422,7 +398,7 @@ GEM
sass (>= 3.5.2) sass (>= 3.5.2)
material_icons (4.0.0) material_icons (4.0.0)
railties (>= 3.2) railties (>= 3.2)
matrix (0.4.3) matrix (0.4.2)
memcachier (0.0.2) memcachier (0.0.2)
method_source (1.1.0) method_source (1.1.0)
mime-types (3.7.0) mime-types (3.7.0)
@@ -435,14 +411,15 @@ GEM
mini_magick (4.12.0) mini_magick (4.12.0)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.9) mini_portile2 (2.8.9)
minitest (5.27.0) minitest (5.25.5)
moneta (1.0.0)
msgpack (1.8.0) msgpack (1.8.0)
multi_json (1.19.1) multi_json (1.15.0)
multi_xml (0.8.1) multi_xml (0.7.1)
bigdecimal (>= 3.1, < 5) bigdecimal (~> 3.1)
net-http (0.9.1) net-http (0.6.0)
uri (>= 0.11.1) uri
net-imap (0.5.12) net-imap (0.5.9)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@@ -452,14 +429,14 @@ GEM
net-smtp (0.5.1) net-smtp (0.5.1)
net-protocol net-protocol
netrc (0.11.0) netrc (0.11.0)
nio4r (2.7.5) nio4r (2.7.4)
nokogiri (1.19.3) nokogiri (1.18.9)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.19.3-x86_64-linux-gnu) nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
oauth (0.5.6) oauth (0.5.6)
oj (3.17.1) oj (3.16.11)
bigdecimal (>= 3.0) bigdecimal (>= 3.0)
ostruct (>= 0.2) ostruct (>= 0.2)
omniauth (1.9.2) omniauth (1.9.2)
@@ -474,37 +451,35 @@ GEM
open-uri (0.1.0) open-uri (0.1.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ostruct (0.6.3) ostruct (0.6.3)
paper_trail (17.0.0) parallel (1.27.0)
activerecord (>= 7.1) parser (3.3.9.0)
request_store (~> 1.4)
parallel (2.1.0)
parser (3.3.11.1)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
percy-capybara (5.0.0) percy-capybara (5.0.0)
capybara (>= 3) capybara (>= 3)
pg (1.6.3) pg (1.6.2)
pg (1.6.3-x86_64-linux) pg (1.6.2-x86_64-linux)
platform-api (3.8.0)
heroics (~> 0.1.1)
moneta (~> 1.0.0)
rate_throttle_client (~> 0.1.0)
popper_js (2.11.8) popper_js (2.11.8)
pp (0.6.3) pp (0.6.2)
prettyprint prettyprint
prettyprint (0.2.0) prettyprint (0.2.0)
prism (1.9.0) prism (1.4.0)
pry (0.16.0) pry (0.15.2)
coderay (~> 1.1) coderay (~> 1.1)
method_source (~> 1.0) method_source (~> 1.0)
reline (>= 0.6.0) psych (5.2.6)
psych (5.3.1)
date date
stringio stringio
public_suffix (7.0.5) public_suffix (6.0.1)
puma (8.0.2) puma (7.0.2)
nio4r (~> 2.0) nio4r (~> 2.0)
query_diet (0.7.3) query_diet (0.7.2)
racc (1.8.1) racc (1.8.1)
rack (2.2.23) rack (2.2.17)
rack-attack (6.8.0)
rack (>= 1.0, < 4)
rack-cors (2.0.2) rack-cors (2.0.2)
rack (>= 2.0.0) rack (>= 2.0.0)
rack-protection (3.2.0) rack-protection (3.2.0)
@@ -517,20 +492,20 @@ GEM
rackup (1.0.1) rackup (1.0.1)
rack (< 3) rack (< 3)
webrick webrick
rails (7.2.3.1) rails (7.2.2.2)
actioncable (= 7.2.3.1) actioncable (= 7.2.2.2)
actionmailbox (= 7.2.3.1) actionmailbox (= 7.2.2.2)
actionmailer (= 7.2.3.1) actionmailer (= 7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
actiontext (= 7.2.3.1) actiontext (= 7.2.2.2)
actionview (= 7.2.3.1) actionview (= 7.2.2.2)
activejob (= 7.2.3.1) activejob (= 7.2.2.2)
activemodel (= 7.2.3.1) activemodel (= 7.2.2.2)
activerecord (= 7.2.3.1) activerecord (= 7.2.2.2)
activestorage (= 7.2.3.1) activestorage (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.2.3.1) railties (= 7.2.2.2)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@@ -539,8 +514,8 @@ GEM
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.7.0) rails-html-sanitizer (1.6.2)
loofah (~> 2.25) 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) 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-i18n (7.0.10) rails-i18n (7.0.10)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
@@ -550,43 +525,39 @@ GEM
rails_stdout_logging rails_stdout_logging
rails_serve_static_assets (0.0.5) rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5) rails_stdout_logging (0.0.5)
railties (7.2.3.1) railties (7.2.2.2)
actionpack (= 7.2.3.1) actionpack (= 7.2.2.2)
activesupport (= 7.2.3.1) activesupport (= 7.2.2.2)
cgi
irb (~> 1.13) irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
tsort (>= 0.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
raindrops (0.20.1) raindrops (0.20.1)
rake (13.4.2) rake (13.3.0)
rate_throttle_client (0.1.2)
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.11.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
rdoc (7.2.0) rdoc (6.14.2)
erb erb
psych (>= 4.0.0) psych (>= 4.0.0)
tsort recaptcha (5.20.1)
recaptcha (5.21.2) redis-client (0.23.2)
redis-client (0.26.2)
connection_pool connection_pool
regexp_parser (2.12.0) regexp_parser (2.11.2)
reline (0.6.3) reline (0.6.2)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.7.0) responders (3.1.1)
rack (>= 1.4) actionpack (>= 5.2)
responders (3.2.0) railties (>= 5.2)
actionpack (>= 7.0)
railties (>= 7.0)
rest-client (2.1.0) rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0) http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
rexml (3.4.4) rexml (3.4.1)
rouge (4.1.2) rouge (4.1.2)
rspec (3.13.0) rspec (3.13.0)
rspec-core (~> 3.13.0) rspec-core (~> 3.13.0)
@@ -596,61 +567,61 @@ GEM
activemodel (>= 3.0) activemodel (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
rspec-mocks (>= 2.99, < 4.0) rspec-mocks (>= 2.99, < 4.0)
rspec-core (3.13.6) rspec-core (3.13.5)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-expectations (3.13.5) rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-mocks (3.13.8) rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-rails (8.0.4) rspec-rails (8.0.2)
actionpack (>= 7.2) actionpack (>= 7.2)
activesupport (>= 7.2) activesupport (>= 7.2)
railties (>= 7.2) railties (>= 7.2)
rspec-core (>= 3.13.0, < 5.0.0) rspec-core (~> 3.13)
rspec-expectations (>= 3.13.0, < 5.0.0) rspec-expectations (~> 3.13)
rspec-mocks (>= 3.13.0, < 5.0.0) rspec-mocks (~> 3.13)
rspec-support (>= 3.13.0, < 5.0.0) rspec-support (~> 3.13)
rspec-rebound (0.2.1) rspec-rebound (0.2.1)
rspec-core (~> 3.3) rspec-core (~> 3.3)
rspec-support (3.13.7) rspec-support (3.13.4)
rspectre (0.2.0) rspectre (0.2.0)
parser (>= 3.3.7.1) parser (>= 3.3.7.1)
prism (~> 1.3) prism (~> 1.3)
rspec (~> 3.10) rspec (~> 3.10)
rswag-api (2.17.0) rswag-api (2.16.0)
activesupport (>= 5.2, < 8.2) activesupport (>= 5.2, < 8.1)
railties (>= 5.2, < 8.2) railties (>= 5.2, < 8.1)
rswag-specs (2.17.0) rswag-specs (2.16.0)
activesupport (>= 5.2, < 8.2) activesupport (>= 5.2, < 8.1)
json-schema (>= 2.2, < 7.0) json-schema (>= 2.2, < 6.0)
railties (>= 5.2, < 8.2) railties (>= 5.2, < 8.1)
rspec-core (>= 2.14) rspec-core (>= 2.14)
rswag-ui (2.17.0) rswag-ui (2.16.0)
actionpack (>= 5.2, < 8.2) actionpack (>= 5.2, < 8.1)
railties (>= 5.2, < 8.2) railties (>= 5.2, < 8.1)
rubocop (1.86.2) rubocop (1.80.2)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0) lint_roller (~> 1.1.0)
parallel (>= 1.10) parallel (~> 1.10)
parser (>= 3.3.0.2) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.49.0, < 2.0) rubocop-ast (>= 1.46.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.49.1) rubocop-ast (1.46.0)
parser (>= 3.3.7.2) parser (>= 3.3.7.2)
prism (~> 1.7) prism (~> 1.4)
rubocop-capybara (2.23.0) rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.81)
rubocop-factory_bot (2.28.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1) rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.35.3) rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.33.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
@@ -659,10 +630,10 @@ GEM
rubocop-rake (0.7.1) rubocop-rake (0.7.1)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (>= 1.72.1) rubocop (>= 1.72.1)
rubocop-rspec (3.9.0) rubocop-rspec (3.7.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (~> 1.81) rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec_rails (2.32.0) rubocop-rspec_rails (2.31.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1) rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5) rubocop-rspec (~> 3.5)
@@ -670,7 +641,7 @@ GEM
ruby-units (4.1.0) ruby-units (4.1.0)
ruby-vips (2.2.1) ruby-vips (2.2.1)
ffi (~> 1.12) ffi (~> 1.12)
rubyzip (3.3.0) rubyzip (3.0.1)
sass (3.7.4) sass (3.7.4)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
@@ -684,27 +655,25 @@ GEM
sprockets (> 3.0) sprockets (> 3.0)
sprockets-rails sprockets-rails
tilt tilt
scout_apm (6.2.0) scout_apm (5.7.1)
parser parser
searchkick (5.3.1) searchkick (5.3.1)
activemodel (>= 6.1) activemodel (>= 6.1)
hashie hashie
securerandom (0.4.1) securerandom (0.4.1)
selenium-webdriver (4.44.0) selenium-webdriver (4.35.0)
base64 (~> 0.2) base64 (~> 0.2)
logger (~> 1.4) logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 4.0) rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0) websocket (~> 1.0)
sidekiq (7.3.10) sidekiq (7.3.9)
base64 base64
connection_pool (>= 2.3.0, < 3) connection_pool (>= 2.3.0)
logger logger
rack (>= 2.2.4, < 3.3) rack (>= 2.2.4)
redis-client (>= 0.23.0, < 1) redis-client (>= 0.22.2)
simple_po_parser (1.1.6) simple_po_parser (1.1.6)
sitemap_generator (6.3.0)
builder (~> 3.0)
sprockets (3.7.5) sprockets (3.7.5)
base64 base64
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@@ -714,34 +683,34 @@ GEM
activesupport (>= 5.2) activesupport (>= 5.2)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
ssrf_filter (1.1.2) ssrf_filter (1.1.2)
stringio (3.2.0) stringio (3.1.7)
sysexits (1.2.0) sysexits (1.2.0)
temple (0.10.4) temple (0.10.4)
terminal-table (4.0.0) terminal-table (4.0.0)
unicode-display_width (>= 1.1.1, < 4) unicode-display_width (>= 1.1.1, < 4)
terser (1.2.7) terser (1.2.6)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
thor (1.5.0) thor (1.4.0)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.7.0) tilt (2.6.1)
timecop (0.9.11) timecop (0.9.10)
timeout (0.6.1) timeout (0.4.3)
tsort (0.2.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (3.2.0) unicode-display_width (3.1.5)
unicode-emoji (~> 4.1) unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.2.0) unicode-emoji (4.0.4)
unicorn (6.1.0) unicorn (6.1.0)
kgio (~> 2.6) kgio (~> 2.6)
raindrops (~> 0.7) raindrops (~> 0.7)
uniform_notifier (1.18.0) uniform_notifier (1.17.0)
uri (1.1.1) uri (1.0.3)
useragent (0.16.11) useragent (0.16.11)
validate_url (1.0.15) validate_url (1.0.15)
activemodel (>= 3.0.0) activemodel (>= 3.0.0)
public_suffix public_suffix
vcr (6.4.0) vcr (6.3.1)
base64
virtus (2.0.0) virtus (2.0.0)
axiom-types (~> 0.1) axiom-types (~> 0.1)
coercible (~> 1.0) coercible (~> 1.0)
@@ -752,7 +721,7 @@ GEM
nokogiri (>= 1.2.0) nokogiri (>= 1.2.0)
rack (>= 1.0) rack (>= 1.0)
rack-test (>= 0.5.3) rack-test (>= 0.5.3)
webrick (1.9.2) webrick (1.9.1)
websocket (1.2.11) websocket (1.2.11)
websocket-driver (0.8.0) websocket-driver (0.8.0)
base64 base64
@@ -761,12 +730,11 @@ GEM
will_paginate (4.0.1) will_paginate (4.0.1)
will_paginate-bootstrap-style (0.3.0) will_paginate-bootstrap-style (0.3.0)
will_paginate (~> 4.0, >= 4.0.0) will_paginate (~> 4.0, >= 4.0.0)
xmlrpc (0.3.4) xmlrpc (0.3.3)
rexml
webrick webrick
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.7.5) zeitwerk (2.7.3)
PLATFORMS PLATFORMS
ruby ruby
@@ -776,7 +744,6 @@ DEPENDENCIES
active_median active_median
active_record_union active_record_union
active_utils active_utils
aws-sdk-s3 (~> 1, >= 1.114.0)
axe-core-capybara axe-core-capybara
axe-core-rspec axe-core-rspec
better_errors better_errors
@@ -795,7 +762,6 @@ DEPENDENCIES
chartkick chartkick
coffee-rails coffee-rails
comfortable_mexican_sofa! comfortable_mexican_sofa!
connection_pool (< 3)
crowdin-cli crowdin-cli
csv_shaper csv_shaper
dalli dalli
@@ -837,13 +803,12 @@ DEPENDENCIES
oj oj
omniauth (~> 1.3) omniauth (~> 1.3)
omniauth-flickr (>= 0.0.15) omniauth-flickr (>= 0.0.15)
paper_trail (~> 17.0)
percy-capybara (~> 5.0.0) percy-capybara (~> 5.0.0)
pg pg
platform-api
pry pry
puma puma
query_diet query_diet
rack-attack
rack-cors rack-cors
rack-protection (>= 2.0.1) rack-protection (>= 2.0.1)
rails (~> 7.2.0) rails (~> 7.2.0)
@@ -873,7 +838,6 @@ DEPENDENCIES
searchkick searchkick
selenium-webdriver selenium-webdriver
sidekiq sidekiq
sitemap_generator
sprockets (< 4) sprockets (< 4)
terser terser
timecop timecop
@@ -886,7 +850,7 @@ DEPENDENCIES
xmlrpc xmlrpc
RUBY VERSION RUBY VERSION
ruby 3.4.9 ruby 3.3.8p144
BUNDLED WITH BUNDLED WITH
2.4.22 2.4.22

View File

@@ -25,6 +25,7 @@ Vibe Coding is more than okay, just make sure you indicate if you have done so a
* [Issues](https://github.com/orgs/Growstuff/projects/1) (features we're * [Issues](https://github.com/orgs/Growstuff/projects/1) (features we're
working on, known bugs, etc) working on, known bugs, etc)
* [![Gitter](https://badges.gitter.im/Growstuff/growstuff.svg)](https://gitter.im/Growstuff/growstuff?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
* [Wiki](https://github.com/Growstuff/growstuff/wiki) (general documentation, etc.) * [Wiki](https://github.com/Growstuff/growstuff/wiki) (general documentation, etc.)
## For coders ## For coders

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 266 KiB

View File

@@ -16,27 +16,3 @@
.homepage--list-item { .homepage--list-item {
height: 100px; height: 100px;
} }
.releases {
.card {
.card-header {
}
.card-body {
h2 {
background-color: transparent;
color: black;
box-shadow: none;
}
img {
border: 0.5em solid #111;
margin-left: 5%;
margin-right: 5%;
width: 90%;
}
ul {
margin-bottom: 1em;
margin-top: 1em;
}
}
}
}

View File

@@ -33,4 +33,4 @@
@view-transition { @view-transition {
navigation: auto; navigation: auto;
} }

View File

@@ -4,32 +4,26 @@ class ActivitiesController < DataController
def index def index
@show_all = params[:all] == '1' @show_all = params[:all] == '1'
@activities = Activity.includes(:owner).order(created_at: :desc) where = {}
@activities = @activities.active unless @show_all where['active'] = true unless @show_all
if params[:member_slug].present? if params[:member_slug]
@owner = Member.find_by!(slug: params[:member_slug]) @owner = Member.find_by(slug: params[:member_slug])
@activities = @activities.where(owner_id: @owner.id) where['owner_id'] = @owner.id unless @owner.nil?
end end
@activities = @activities.paginate(page: params[:page], per_page: 30) @activities = Activity.search(
where:,
page: params[:page],
limit: 30,
boost_by: [:created_at],
load: false
)
@filename = "Growstuff-#{specifics}Activities-#{Time.zone.now.to_fs(:number)}.csv" @filename = "Growstuff-#{specifics}Activities-#{Time.zone.now.to_fs(:number)}.csv"
respond_with(@activities) respond_with(@activities)
end end
def show def show
if @activity.finished? && @activity.owner == current_member && (@activity.updated_at + 2.weeks) > Time.now
@repeat_link = new_activity_path(
name: @activity.name,
garden_id: @activity.garden_id,
planting_id: @activity.planting_id,
category: @activity.category,
description: @activity.description,
due_date: 2.weeks.from_now.to_date
)
end
respond_with @activity respond_with @activity
end end
@@ -38,10 +32,6 @@ class ActivitiesController < DataController
owner: current_member, owner: current_member,
due_date: Date.today due_date: Date.today
) )
@activity.name = params[:name] if params[:name]
@activity.description = params[:description] if params[:description]
@activity.category = params[:category] if params[:category]
@activity.due_date = params[:due_date] if params[:due_date]
if params[:garden_id] if params[:garden_id]
@activity.garden = Garden.find_by( @activity.garden = Garden.find_by(
owner: current_member, owner: current_member,
@@ -68,21 +58,7 @@ class ActivitiesController < DataController
def create def create
@activity = Activity.new(activity_params) @activity = Activity.new(activity_params)
@activity.owner = current_member @activity.owner = current_member
@activity.due_date ||= Date.today @activity.save
if @activity.save
if params[:repeat_times].to_i > 0
repeat_times = params[:repeat_times].to_i
repeat_weeks = params[:repeat_weeks].to_i
repeat_times.times do |i|
new_activity = @activity.dup
new_activity.due_date = @activity.due_date + (i + 1) * repeat_weeks.weeks
new_activity.save
end
end
end
respond_with @activity respond_with @activity
end end
@@ -101,8 +77,7 @@ class ActivitiesController < DataController
def activity_params def activity_params
params.require(:activity).permit( params.require(:activity).permit(
:name, :description, :category, :finished, :name, :description, :category, :finished,
:garden_id, :planting_id, :due_date, :garden_id, :planting_id, :due_date
:repeat_times, :repeat_weeks
) )
end end

View File

@@ -1,40 +0,0 @@
# frozen_string_literal: true
module Admin
class CropCompanionsController < AdminController
before_action :set_crop
def index
@crop_companions = @crop.crop_companions
end
def new
@crop_companion = @crop.crop_companions.new
end
def create
@crop_companion = @crop.crop_companions.new(crop_companion_params)
if @crop_companion.save
redirect_to admin_crop_crop_companions_path(@crop), notice: t('crop_companions.created')
else
render :new
end
end
def destroy
@crop_companion = @crop.crop_companions.find(params[:id])
@crop_companion.destroy
redirect_to admin_crop_crop_companions_path(@crop), notice: t('crop_companions.deleted')
end
private
def set_crop
@crop = Crop.find_by!(slug: params[:crop_slug])
end
def crop_companion_params
params.require(:crop_companion).permit(:crop_b_id, :source_url)
end
end
end

View File

@@ -1,19 +0,0 @@
# frozen_string_literal: true
class Admin::CropsController < ApplicationController
before_action :authenticate_member!
before_action :authorize_admin!
def index
@versions = PaperTrail::Version.where(item_type: 'Crop').order(created_at: :desc).limit(100)
member_ids = @versions.map(&:whodunnit).compact.map(&:to_i)
@members = Member.where(id: member_ids).index_by(&:id)
@crop_wranglers = Role.crop_wranglers
end
private
def authorize_admin!
authorize! :wrangle, Crop
end
end

View File

@@ -8,9 +8,9 @@ module Admin
responders :flash responders :flash
def index def index
@members = Member.order(:login_name) @members = Member.all
@members = @members.where("login_name ILIKE ?", "%#{search_term}%") if search_term.present? @members = @members.where("login_name ILIKE ?", "%#{search_term}%") unless search_term.nil?
@members = @members.paginate(page: params[:page]) @members = @members.order(:login_name).paginate(page: params[:page])
end end
def edit def edit

View File

@@ -1,24 +0,0 @@
# frozen_string_literal: true
module Admin
class VersionsController < ApplicationController
before_action :authenticate_member!
before_action :authorize_admin!
def revert
@version = PaperTrail::Version.find(params[:id])
@object = @version.reify
if @object.save
redirect_to admin_crops_path, notice: t('messages.revert_success', date: @version.created_at.strftime('%B %d, %Y'))
else
redirect_to admin_crops_path, alert: t('messages.revert_error', date: @version.created_at.strftime('%B %d, %Y'), errors: @object.errors.full_messages.to_sentence)
end
end
private
def authorize_admin!
authorize! :wrangle, Crop
end
end
end

View File

@@ -30,7 +30,7 @@ class AlternateNamesController < ApplicationController
@alternate_name = AlternateName.new(alternate_name_params) @alternate_name = AlternateName.new(alternate_name_params)
if @alternate_name.save if @alternate_name.save
redirect_to @alternate_name.crop, notice: t('alternate_names.created') redirect_to @alternate_name.crop, notice: 'Alternate name was successfully created.'
else else
render action: "new" render action: "new"
end end
@@ -40,7 +40,7 @@ class AlternateNamesController < ApplicationController
# PUT /alternate_names/1.json # PUT /alternate_names/1.json
def update def update
if @alternate_name.update(alternate_name_params) if @alternate_name.update(alternate_name_params)
redirect_to @alternate_name.crop, notice: t('alternate_names.updated') redirect_to @alternate_name.crop, notice: 'Alternate name was successfully updated.'
else else
render action: "edit" render action: "edit"
end end
@@ -51,7 +51,7 @@ class AlternateNamesController < ApplicationController
def destroy def destroy
@crop = @alternate_name.crop @crop = @alternate_name.crop
@alternate_name.destroy @alternate_name.destroy
redirect_to @crop, notice: t('alternate_names.deleted') redirect_to @crop, notice: 'Alternate name was successfully deleted.'
end end
private private

View File

@@ -3,36 +3,6 @@
module Api module Api
module V1 module V1
class CropsController < BaseController class CropsController < BaseController
def search
term = params[:term]
page = params.dig(:page, :number) || 1
per_page = params.dig(:page, :size) || Crop.per_page
search_results = CropSearchService.search(
term,
page: page,
per_page: per_page,
load: true
)
resources = search_results.map do |crop|
Api::V1::CropResource.new(crop, context)
end
serializer = JSONAPI::ResourceSerializer.new(Api::V1::CropResource)
data = resources.map do |resource|
serializer.object_hash(resource, {})
end
render json: {
data: data,
meta: {
record_count: search_results.total_count,
page_count: search_results.total_pages
}
}
end
end end
end end
end end

View File

@@ -68,7 +68,7 @@ class ApplicationController < ActionController::Base
# profile stuff # profile stuff
:bio, :location, :latitude, :longitude, :bio, :location, :latitude, :longitude,
# email settings # email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder, :send_harvest_reminder) :show_email, :newsletter, :send_notification_email, :send_planting_reminder)
end end
devise_parameter_sanitizer.permit(:account_update) do |member| devise_parameter_sanitizer.permit(:account_update) do |member|
@@ -80,7 +80,7 @@ class ApplicationController < ActionController::Base
:bio, :location, :latitude, :longitude, :bio, :location, :latitude, :longitude,
:website_url, :instagram_handle, :facebook_handle, :bluesky_handle, :other_url, :website_url, :instagram_handle, :facebook_handle, :bluesky_handle, :other_url,
# email settings # email settings
:show_email, :newsletter, :send_notification_email, :send_planting_reminder, :send_harvest_reminder, :show_email, :newsletter, :send_notification_email, :send_planting_reminder,
# update password # update password
:current_password) :current_password)
end end

View File

@@ -24,9 +24,9 @@ class AuthenticationsController < ApplicationController
name: name:
) )
flash[:notice] = t('messages.auth_success') flash[:notice] = "Authentication successful."
else else
flash[:notice] = t('messages.auth_failed') flash[:notice] = "Authentication failed."
end end
redirect_to request.env['omniauth.origin'] || edit_member_registration_path redirect_to request.env['omniauth.origin'] || edit_member_registration_path
end end

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
class BlocksController < ApplicationController
load_and_authorize_resource
skip_load_resource only: :create
def create
@block = current_member.blocks.build(blocked: Member.find(params[:blocked]))
if @block.save
flash[:notice] = "Blocked #{@block.blocked.login_name}"
else
flash[:error] = "Already blocking or error while blocking."
end
redirect_back_or_to(root_path)
end
def destroy
@block = current_member.blocks.find(params[:id])
@unblocked = @block.blocked
@block.destroy
flash[:notice] = "Unblocked #{@unblocked.login_name}"
redirect_to @unblocked
end
private
def block_params
params.permit(:id, :blocked)
end
end

View File

@@ -3,7 +3,6 @@
module Charts module Charts
class CropsController < ApplicationController class CropsController < ApplicationController
respond_to :json respond_to :json
before_action :set_crop
def sunniness def sunniness
pie_chart_query 'sunniness' pie_chart_query 'sunniness'
@@ -14,28 +13,20 @@ module Charts
end end
def harvested_for def harvested_for
data = Rails.cache.fetch("#{@crop.cache_key_with_version}/harvested_for", expires_in: 1.day) do @crop = Crop.find_by!(slug: params[:crop_slug])
Harvest.joins(:plant_part) render json: Harvest.joins(:plant_part)
.where(crop: @crop) .where(crop: @crop)
.group("plant_parts.name").count(:id) .group("plant_parts.name").count(:id)
end
render json: data
end end
private private
def set_crop
@crop = Crop.find_by!(slug: params[:crop_slug])
end
def pie_chart_query(field) def pie_chart_query(field)
data = Rails.cache.fetch("#{@crop.cache_key_with_version}/#{field}", expires_in: 1.day) do @crop = Crop.find_by!(slug: params[:crop_slug])
Planting.where(crop: @crop) render json: Planting.where(crop: @crop)
.where.not(field.to_sym => nil) .where.not(field.to_sym => nil)
.where.not(field.to_sym => '') .where.not(field.to_sym => '')
.group(field.to_sym).count(:id) .group(field.to_sym).count(:id)
end
render json: data
end end
end end
end end

View File

@@ -13,7 +13,7 @@ class CropsController < ApplicationController
@crops = Crop.search('*', boost_by: %i(plantings_count harvests_count), @crops = Crop.search('*', boost_by: %i(plantings_count harvests_count),
limit: 100, limit: 100,
page: params[:page], page: params[:page],
load: (request.format.csv? || request.format.rss? ? { include: %i(parent scientific_names seeds harvests creator plantings) } : false)) load: false)
@num_requested_crops = requested_crops.size if current_member @num_requested_crops = requested_crops.size if current_member
@filename = filename @filename = filename
respond_with @crops respond_with @crops
@@ -73,8 +73,6 @@ class CropsController < ApplicationController
format.html do format.html do
@posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page])
@companions = @crop.companions.approved @companions = @crop.companions.approved
member_ids = @crop.versions.map(&:whodunnit).compact.map(&:to_i)
@version_members = Member.where(id: member_ids).index_by(&:id)
end end
format.svg do format.svg do
icon_data = @crop.svg_icon.presence || File.read(Rails.root.join("app/assets/images/icons/sprout.svg")) icon_data = @crop.svg_icon.presence || File.read(Rails.root.join("app/assets/images/icons/sprout.svg"))
@@ -149,34 +147,6 @@ class CropsController < ApplicationController
respond_with @crop respond_with @crop
end end
def data_improvement
@active_tab = params[:tab] || 'photos'
@crops = case @active_tab
when 'photos'
Crop.approved.where(photo_associations_count: 0).order(plantings_count: :desc)
when 'descriptions'
Crop.approved.where(description: [nil, '']).order(plantings_count: :desc)
when 'youtube'
Crop.approved.where(en_youtube_url: [nil, '']).order(plantings_count: :desc)
when 'alternate_names'
Crop.approved.where.missing(:alternate_names).order(plantings_count: :desc)
when 'wikidata'
crops_with_wikidata = Crop.joins(:scientific_names).where.not(scientific_names: { wikidata_id: nil }).distinct
Crop.approved.where.not(id: crops_with_wikidata).order(plantings_count: :desc)
when 'row_spacing'
Crop.approved.where(row_spacing: nil).order(plantings_count: :desc)
when 'sun_requirements'
Crop.approved.where(sun_requirements: [nil, '']).order(plantings_count: :desc)
when 'height'
Crop.approved.where(height: nil).order(plantings_count: :desc)
when 'public_food_key'
Crop.approved.where(public_food_key: [nil, '']).order(plantings_count: :desc)
else
Crop.none
end
end
private private
def notifier def notifier
@@ -218,12 +188,10 @@ class CropsController < ApplicationController
def crop_params def crop_params
params.require(:crop).permit( params.require(:crop).permit(
:name, :en_wikipedia_url, :en_youtube_url, :name, :en_wikipedia_url,
:parent_id, :perennial, :parent_id, :perennial,
:request_notes, :reason_for_rejection, :request_notes, :reason_for_rejection,
:rejection_notes, :rejection_notes,
:description,
:public_food_key,
:row_spacing, :spread, :height, :row_spacing, :spread, :height,
:sowing_method, :sun_requirements, :growing_degree_days, :sowing_method, :sun_requirements, :growing_degree_days,
scientific_names_attributes: %i(scientific_name _destroy id) scientific_names_attributes: %i(scientific_name _destroy id)

View File

@@ -13,11 +13,11 @@ class FollowsController < ApplicationController
@follow = current_member.follows.build(followed: Member.find(params[:followed])) @follow = current_member.follows.build(followed: Member.find(params[:followed]))
if @follow.save if @follow.save
flash[:notice] = t('messages.followed', name: @follow.followed.login_name) flash[:notice] = "Followed #{@follow.followed.login_name}"
else else
flash[:error] = t('messages.follow_error') flash[:error] = "Already following or error while following."
end end
redirect_back_or_to(root_path) redirect_back fallback_location: root_path
end end
def destroy def destroy
@@ -25,7 +25,7 @@ class FollowsController < ApplicationController
@unfollowed = @follow.followed @unfollowed = @follow.followed
@follow.destroy @follow.destroy
flash[:notice] = t('messages.unfollowed', name: @unfollowed.login_name) flash[:notice] = "Unfollowed #{@unfollowed.login_name}"
redirect_to @unfollowed redirect_to @unfollowed
end end

View File

@@ -32,14 +32,14 @@ class ForumsController < ApplicationController
# POST /forums.json # POST /forums.json
def create def create
@forum = Forum.new(forum_params) @forum = Forum.new(forum_params)
flash[:notice] = t('forums.created') if @forum.save flash[:notice] = 'Forum was successfully created.' if @forum.save
respond_with(@forum) respond_with(@forum)
end end
# PUT /forums/1 # PUT /forums/1
# PUT /forums/1.json # PUT /forums/1.json
def update def update
flash[:notice] = t('forums.updated') if @forum.update(forum_params) flash[:notice] = 'Forum was successfully updated.' if @forum.update(forum_params)
respond_with(@forum) respond_with(@forum)
end end
@@ -47,7 +47,7 @@ class ForumsController < ApplicationController
# DELETE /forums/1.json # DELETE /forums/1.json
def destroy def destroy
@forum.destroy @forum.destroy
flash[:notice] = t('forums.deleted') flash[:notice] = 'Forum was successfully deleted'
redirect_to forums_url redirect_to forums_url
end end

View File

@@ -2,7 +2,7 @@
class GardensController < DataController class GardensController < DataController
def index def index
@owner = Member.find_by!(slug: params[:member_slug]) if params[:member_slug].present? @owner = Member.find_by(slug: params[:member_slug])
@show_all = params[:all] == '1' @show_all = params[:all] == '1'
@show_jump_to = params[:member_slug].present? || false @show_jump_to = params[:member_slug].present? || false
@@ -39,10 +39,7 @@ class GardensController < DataController
def create def create
@garden.owner_id = current_member.id @garden.owner_id = current_member.id
if @garden.save flash[:notice] = I18n.t('gardens.created') if @garden.save
link = new_activity_path(name: 'Weed the garden bed', garden_id: @garden.id, due_date: 2.weeks.from_now.to_date)
flash[:notice] = t('gardens.created_prompt_html', link: link).html_safe
end
respond_with(@garden) respond_with(@garden)
end end
@@ -57,23 +54,12 @@ class GardensController < DataController
redirect_to(member_gardens_path(@garden.owner)) redirect_to(member_gardens_path(@garden.owner))
end end
def fetch_wikidata
if @garden.populate_wikidata_info
@garden.save
flash[:notice] = "Wikidata information updated."
else
flash[:alert] = "Could not find Wikidata information for this location."
end
redirect_to @garden
end
private private
def garden_params def garden_params
params.require(:garden).permit( params.require(:garden).permit(
:name, :slug, :description, :active, :name, :slug, :description, :active,
:location, :latitude, :longitude, :area, :area_unit, :garden_type_id, :location, :latitude, :longitude, :area, :area_unit, :garden_type_id
:location_wikidata_id, :lowest_temp_c, :highest_temp_c
) )
end end
end end

View File

@@ -5,8 +5,8 @@ class HarvestsController < DataController
def index def index
where = {} where = {}
if params[:member_slug].present? if params[:member_slug]
@owner = Member.find_by!(slug: params[:member_slug]) @owner = Member.find_by(slug: params[:member_slug])
where['owner_id'] = @owner.id where['owner_id'] = @owner.id
end end
@@ -23,7 +23,7 @@ class HarvestsController < DataController
@harvests = Harvest.search('*', where:, @harvests = Harvest.search('*', where:,
limit: 100, limit: 100,
page: params[:page], page: params[:page],
load: (request.format.csv? ? { include: %i(crop owner plant_part) } : false), load: false,
boost_by: [:created_at]) boost_by: [:created_at])
@filename = csv_filename @filename = csv_filename
@@ -38,9 +38,9 @@ class HarvestsController < DataController
end end
def new def new
@harvest = Harvest.new(new_harvest_params.merge(harvested_at: Time.zone.today)) @harvest = Harvest.new(harvested_at: Time.zone.today)
@planting = @harvest.planting @planting = Planting.find_by(slug: params[:planting_slug]) if params[:planting_slug]
@crop = @harvest.crop @crop = Crop.find_by(id: params[:crop_id])
respond_with(@harvest) respond_with(@harvest)
end end
@@ -52,7 +52,7 @@ class HarvestsController < DataController
def create def create
@harvest.crop_id = @harvest.planting.crop_id if @harvest.planting_id @harvest.crop_id = @harvest.planting.crop_id if @harvest.planting_id
@harvest.harvested_at = Time.zone.now if @harvest.harvested_at.blank? @harvest.harvested_at = Time.zone.now if @harvest.harvested_at.blank?
update_planting_rating if @harvest.save @harvest.save
if params[:return] == 'planting' if params[:return] == 'planting'
respond_with(@harvest, location: @harvest.planting) respond_with(@harvest, location: @harvest.planting)
else else
@@ -61,7 +61,7 @@ class HarvestsController < DataController
end end
def update def update
update_planting_rating if @harvest.update(harvest_params) @harvest.update(harvest_params)
respond_with(@harvest) respond_with(@harvest)
end end
@@ -76,17 +76,7 @@ class HarvestsController < DataController
params.require(:harvest) params.require(:harvest)
.permit(:planting_id, :crop_id, :harvested_at, :description, .permit(:planting_id, :crop_id, :harvested_at, :description,
:quantity, :unit, :weight_quantity, :weight_unit, :quantity, :unit, :weight_quantity, :weight_unit,
:plant_part_id, :slug, :si_weight, :overall_rating) :plant_part_id, :slug, :si_weight)
.merge(owner_id: current_member.id)
end
def new_harvest_params
return {} unless params[:harvest]
params.require(:harvest)
.permit(:planting_id, :crop_id, :harvested_at, :description,
:quantity, :unit, :weight_quantity, :weight_unit,
:plant_part_id, :slug, :si_weight, :overall_rating)
.merge(owner_id: current_member.id) .merge(owner_id: current_member.id)
end end
@@ -113,10 +103,4 @@ class HarvestsController < DataController
@harvest.planting.update_harvest_days! @harvest.planting.update_harvest_days!
@harvest.crop.update_harvest_medians @harvest.crop.update_harvest_medians
end end
def update_planting_rating
return if @harvest.planting.nil? || params[:harvest][:overall_rating].blank?
@harvest.planting.update(overall_rating: params[:harvest][:overall_rating])
end
end end

View File

@@ -10,6 +10,4 @@ class HomeController < ApplicationController
# the relevant class methods directly in the view, so that fragment # the relevant class methods directly in the view, so that fragment
# caching will be effective. # caching will be effective.
end end
def community_gardens; end
end end

View File

@@ -14,7 +14,7 @@ class LikesController < ApplicationController
@like.likeable.reindex(refresh: true) @like.likeable.reindex(refresh: true)
success(@like, liked_by_member: true, status_code: :created) success(@like, liked_by_member: true, status_code: :created)
else else
failed(@like, message: t('messages.unable_to_like')) failed(@like, message: 'Unable to like')
end end
end end
@@ -29,7 +29,7 @@ class LikesController < ApplicationController
@like.likeable.reindex(refresh: true) @like.likeable.reindex(refresh: true)
success(@like, liked_by_member: false, status_code: :ok) success(@like, liked_by_member: false, status_code: :ok)
else else
failed(@like, message: t('messages.unable_to_unlike')) failed(@like, message: 'Unable to unlike')
end end
end end

View File

@@ -90,12 +90,11 @@ class MembersController < ApplicationController
EMAIL_TYPE_STRING = { EMAIL_TYPE_STRING = {
send_notification_email: "direct message notifications", send_notification_email: "direct message notifications",
send_planting_reminder: "planting reminders", send_planting_reminder: "planting reminders"
send_harvest_reminder: "harvest reminders"
}.freeze }.freeze
def member_params def member_params
params.require(:member).permit(:login_name, :tos_agreement, :email, :newsletter, :send_harvest_reminder) params.require(:member).permit(:login_name, :tos_agreement, :email, :newsletter)
end end
def member_json_fields def member_json_fields

View File

@@ -27,21 +27,10 @@ class MessagesController < ApplicationController
def create def create
if params[:conversation_id].present? if params[:conversation_id].present?
@conversation = Mailboxer::Conversation.find(params[:conversation_id]) @conversation = Mailboxer::Conversation.find(params[:conversation_id])
# Check if any of the recipients have blocked the sender
if @conversation.recipients.any? { |recipient| recipient.already_blocking?(current_member) }
flash[:error] = "You cannot reply to this conversation because one of the recipients has blocked you."
redirect_to conversation_path(@conversation)
return
end
current_member.reply_to_conversation(@conversation, params[:body]) current_member.reply_to_conversation(@conversation, params[:body])
redirect_to conversation_path(@conversation) redirect_to conversation_path(@conversation)
else else
recipient = Member.find(params[:recipient_id]) recipient = Member.find(params[:recipient_id])
if recipient.already_blocking?(current_member)
flash[:error] = "You cannot send a message to a member who has blocked you."
redirect_back_or_to(root_path)
return
end
body = params[:body] body = params[:body]
subject = params[:subject] subject = params[:subject]
@conversation = current_member.send_message(recipient, body, subject) @conversation = current_member.send_message(recipient, body, subject)

View File

@@ -10,7 +10,7 @@ require './lib/actions/oauth_signup_action'
# #
class OmniauthCallbacksController < Devise::OmniauthCallbacksController class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def failure def failure
flash[:alert] = t('messages.auth_failed') flash[:alert] = "Authentication failed."
redirect_to request.env['omniauth.origin'] || "/" redirect_to request.env['omniauth.origin'] || "/"
end end

View File

@@ -102,12 +102,11 @@ class PhotosController < ApplicationController
end end
@current_set = params[:set] @current_set = params[:set]
@current_tag = params[:tag]
page = params[:page] || 1 page = params[:page] || 1
@sets = current_member.flickr_sets @sets = current_member.flickr_sets
photos, total = current_member.flickr_photos(page, @current_set, @current_tag) photos, total = current_member.flickr_photos(page, @current_set)
@photos = WillPaginate::Collection.create(page, 30, total) do |pager| @photos = WillPaginate::Collection.create(page, 30, total) do |pager|
pager.replace photos pager.replace photos
@@ -119,8 +118,6 @@ class PhotosController < ApplicationController
{ crops: @crop.id } { crops: @crop.id }
elsif params[:planting_id] elsif params[:planting_id]
{ planting_id: @planting.id } { planting_id: @planting.id }
elsif params[:planting_slug]
{ plantings: @planting.id }
else else
{} {}
end end
@@ -129,6 +126,5 @@ class PhotosController < ApplicationController
def set_crop_and_planting def set_crop_and_planting
@crop = Crop.find params[:crop_slug] if params[:crop_slug] @crop = Crop.find params[:crop_slug] if params[:crop_slug]
@planting = Planting.find params[:planting_id] if params[:planting_id] @planting = Planting.find params[:planting_id] if params[:planting_id]
@planting ||= Planting.find params[:planting_slug] if params[:planting_slug]
end end
end end

View File

@@ -33,7 +33,7 @@ class PlacesController < ApplicationController
def search def search
if params[:new_place].empty? if params[:new_place].empty?
redirect_to places_path, alert: t('messages.invalid_location') redirect_to places_path, alert: 'Please enter a valid location'
else else
redirect_to place_path(params[:new_place]) redirect_to place_path(params[:new_place])
end end

View File

@@ -11,9 +11,9 @@ class PlantingsController < DataController
where = {} where = {}
where['active'] = true unless @show_all where['active'] = true unless @show_all
if params[:member_slug].present? if params[:member_slug]
@owner = Member.find_by!(slug: params[:member_slug]) @owner = Member.find_by(slug: params[:member_slug])
where['owner_id'] = @owner.id where['owner_id'] = @owner.id unless @owner.nil?
end end
if params[:crop_slug] if params[:crop_slug]
@@ -46,12 +46,6 @@ class PlantingsController < DataController
.where.not(id: @planting.id) .where.not(id: @planting.id)
.includes(:owner, :crop, :garden) .includes(:owner, :crop, :garden)
.limit(6) .limit(6)
if @planting.finished? && @planting.garden.plantings.current.none? && (@planting.updated_at + 2.weeks) > Time.zone.now
@cultivate_soil_link = new_activity_path(name: 'Cultivate soil', garden_id: @planting.garden_id, category: "Soil Cultivation",
description: "Recently finished #{@planting.crop.name} planting. Prepare for next planting.")
end
respond_with @planting respond_with @planting
end end
@@ -116,11 +110,11 @@ class PlantingsController < DataController
new_planting.finished_at = nil new_planting.finished_at = nil
if new_planting.save if new_planting.save
redirect_to edit_planting_path(new_planting), notice: t('messages.transplant_success') redirect_to edit_planting_path(new_planting), notice: 'Planting was successfully transplanted.'
else else
# if the save fails, we should probably roll back the finishing of the original planting # if the save fails, we should probably roll back the finishing of the original planting
@planting.update(finished: false, finished_at: nil) @planting.update(finished: false, finished_at: nil)
redirect_to @planting, alert: t('messages.transplant_error', errors: new_planting.errors.full_messages.to_sentence) redirect_to @planting, alert: "There was an error transplanting the planting: #{new_planting.errors.full_messages.to_sentence}"
end end
end end
@@ -160,7 +154,7 @@ class PlantingsController < DataController
end end
def matching_seeds def matching_seeds
@matching_seeds ||= Seed.where(crop: @planting.crop, owner: @planting.owner) Seed.where(crop: @planting.crop, owner: @planting.owner)
.where('(finished_at IS NULL OR finished_at >= ?)', @planting.planted_at) .where('(finished_at IS NULL OR finished_at >= ?)', @planting.planted_at)
.where('(saved_at IS NULL OR saved_at <= ?)', @planting.planted_at) .where('(saved_at IS NULL OR saved_at <= ?)', @planting.planted_at)
end end

View File

@@ -8,7 +8,7 @@ class PostsController < ApplicationController
respond_to :rss, only: %i(index show) respond_to :rss, only: %i(index show)
def index def index
@author = Member.find_by!(slug: params[:member_slug]) if params[:member_slug].present? @author = Member.find_by(slug: params[:member_slug])
@posts = posts @posts = posts
respond_with(@posts) respond_with(@posts)
end end
@@ -21,10 +21,6 @@ class PostsController < ApplicationController
def new def new
@post = Post.new @post = Post.new
@forum = Forum.find_by(id: params[:forum_id]) @forum = Forum.find_by(id: params[:forum_id])
if params[:crop_id]
@crop = Crop.friendly.find(params[:crop_id])
@post.body = "[#{@crop.name}](crop)"
end
respond_with(@post) respond_with(@post)
end end
@@ -33,17 +29,17 @@ class PostsController < ApplicationController
def create def create
params[:post][:author_id] = current_member.id params[:post][:author_id] = current_member.id
@post = Post.new(post_params) @post = Post.new(post_params)
flash[:notice] = t('posts.created') if @post.save flash[:notice] = 'Post was successfully created.' if @post.save
respond_with(@post) respond_with(@post)
end end
def update def update
flash[:notice] = t('posts.updated') if @post.update(post_params) flash[:notice] = 'Post was successfully updated.' if @post.update(post_params)
respond_with(@post) respond_with(@post)
end end
def destroy def destroy
flash[:notice] = t('posts.deleted') if @post.destroy flash[:notice] = 'Post was deleted.' if @post.destroy
respond_with(@post) respond_with(@post)
end end

View File

@@ -54,7 +54,7 @@ class ScientificNamesController < ApplicationController
def destroy def destroy
@crop = @scientific_name.crop @crop = @scientific_name.crop
@scientific_name.destroy @scientific_name.destroy
flash[:notice] = t('scientific_names.deleted') flash[:notice] = 'Scientific name was successfully deleted.'
respond_with(@crop) respond_with(@crop)
end end
@@ -74,7 +74,7 @@ class ScientificNamesController < ApplicationController
end end
def scientific_name_params def scientific_name_params
params.require(:scientific_name).permit(:crop_id, :name, :gbif_key, :wikidata_id) params.require(:scientific_name).permit(:crop_id, :name, :gbif_key)
end end
def gbif_service def gbif_service

View File

@@ -5,7 +5,7 @@ class SeedsController < DataController
where = {} where = {}
if params[:member_slug].present? if params[:member_slug].present?
@owner = Member.find_by!(slug: params[:member_slug]) @owner = Member.find_by(slug: params[:member_slug])
where['owner_id'] = @owner.id where['owner_id'] = @owner.id
end end
@@ -30,7 +30,7 @@ class SeedsController < DataController
page: params[:page], page: params[:page],
limit: 30, limit: 30,
boost_by: [:created_at], boost_by: [:created_at],
load: (request.format.csv? ? { include: %i(crop owner) } : false) load: false
) )
respond_with(@seeds) respond_with(@seeds)
@@ -61,7 +61,7 @@ class SeedsController < DataController
@seed.finished ||= false @seed.finished ||= false
@seed.owner = current_member @seed.owner = current_member
@seed.crop = @seed.parent_planting.crop if @seed.parent_planting @seed.crop = @seed.parent_planting.crop if @seed.parent_planting
flash[:notice] = t('seeds.added_to_stash', crop: @seed.crop) if @seed.save flash[:notice] = "Successfully added #{@seed.crop} seed to your stash." if @seed.save
if params[:return] == 'planting' if params[:return] == 'planting'
respond_with(@seed, location: @seed.parent_planting) respond_with(@seed, location: @seed.parent_planting)
else else
@@ -70,7 +70,7 @@ class SeedsController < DataController
end end
def update def update
flash[:notice] = t('seeds.updated') if @seed.update(seed_params) flash[:notice] = 'Seed was successfully updated.' if @seed.update(seed_params)
respond_with(@seed) respond_with(@seed)
end end

View File

@@ -5,7 +5,7 @@ class SessionsController < Devise::SessionsController
def create def create
super do |_resource| super do |_resource|
flash[:alert] = t('messages.crops_waiting') if Crop.pending_approval.present? && current_member.role?(:crop_wrangler) flash[:alert] = "There are crops waiting to be wrangled." if Crop.pending_approval.present? && current_member.role?(:crop_wrangler)
end end
end end
end end

View File

@@ -1,6 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'nokogiri'
module ApplicationHelper module ApplicationHelper
def parse_date(str) def parse_date(str)
str ||= '' # Date.parse barfs on nil str ||= '' # Date.parse barfs on nil
@@ -120,22 +119,4 @@ module ApplicationHelper
def og_description(description) def og_description(description)
strip_tags(description).split(' ')[0..20].join(' ') strip_tags(description).split(' ')[0..20].join(' ')
end end
def github_releases
return [] if Rails.env.test?
feed_url = 'https://github.com/Growstuff/growstuff/releases.atom'
Rails.cache.fetch(feed_url, expires_in: 1.day) do
response = Faraday.get(feed_url)
doc = Nokogiri::XML(response.body)
doc.xpath('//xmlns:entry').first(2).map do |entry|
{
title: entry.xpath('xmlns:title').text,
content: entry.xpath('xmlns:content').text,
link: entry.xpath('xmlns:link/@href').text,
updated: entry.xpath('xmlns:updated').text
}
end
end
end
end end

View File

@@ -13,7 +13,7 @@ module AutoSuggestHelper
resource = resource.class.name.downcase resource = resource.class.name.downcase
source_path = Rails.application.routes.url_helpers.send("search_#{source}s_path", format: :json) source_path = Rails.application.routes.url_helpers.send("search_#{source}s_path", format: :json)
%( %(
<input id="#{source}" class="auto-suggest #{options[:class]}" #{'required="required"' if options[:required]} <input id="#{source}" class="auto-suggest #{options[:class]}" #{options[:required] ? 'required="required"' : ''}
type="text" value="#{default}" data-source-url="#{source_path}", type="text" value="#{default}" data-source-url="#{source_path}",
placeholder="e.g. lettuce"> placeholder="e.g. lettuce">
<noscript class="text-warning"> <noscript class="text-warning">

View File

@@ -85,20 +85,7 @@ module ButtonsHelper
end end
def activity_edit_button(activity, classes: "btn btn-raised btn-info") def activity_edit_button(activity, classes: "btn btn-raised btn-info")
edit_button(edit_activity_path(slug: activity.slug), classes:) edit_button(edit_activity_path(activity), classes:)
end
def activity_copy_button(activity, classes: 'btn')
link_to new_activity_path(
name: activity.name,
description: activity.description,
category: activity.category,
garden_id: activity.garden_id,
planting_id: activity.planting_id,
due_date: activity.due_date
), class: classes do
copy_icon + ' ' + t('buttons.copy')
end
end end
def activity_finish_button(activity, classes: 'btn btn-default btn-secondary') def activity_finish_button(activity, classes: 'btn btn-default btn-secondary')

View File

@@ -1,115 +1,20 @@
# frozen_string_literal: true # frozen_string_literal: true
module CropsHelper module CropsHelper
def crop_or_parent(crop, attribute)
@crop_or_parent_cache ||= {}
cache_key = [crop.persisted? ? crop.id : crop.object_id, attribute]
return @crop_or_parent_cache[cache_key] if @crop_or_parent_cache.key?(cache_key)
@crop_or_parent_cache[cache_key] = begin
value = crop.send(attribute)
if value.blank?
parent = crop
while (parent = parent.parent)
parent_value = parent.send(attribute)
if parent_value.present?
value = parent_value
break
end
end
end
value
end
end
def display_seed_availability(member, crop) def display_seed_availability(member, crop)
@seed_availability_cache ||= {} seeds = member.seeds.where(crop:)
cache_key = [ total_quantity = seeds.where.not(quantity: nil).sum(:quantity)
member.persisted? ? member.id : member.object_id,
crop.persisted? ? crop.id : crop.object_id
]
return @seed_availability_cache[cache_key] if @seed_availability_cache.key?(cache_key)
@seed_availability_cache[cache_key] = begin return "You don't have any seeds of this crop." if seeds.none?
seeds = member.seeds.where(crop:)
if seeds.none? if total_quantity == 0
"You don't have any seeds of this crop." "You have an unknown quantity of seeds of this crop."
else else
total_quantity = seeds.where.not(quantity: nil).sum(:quantity) "You have #{total_quantity} #{Seed.model_name.human(count: total_quantity)} of this crop."
if total_quantity == 0
"You have an unknown quantity of seeds of this crop."
else
"You have #{total_quantity} #{Seed.model_name.human(count: total_quantity)} of this crop."
end
end
end end
end end
def crop_ebay_seeds_url(crop) def crop_ebay_seeds_url(crop)
"https://www.ebay.com/sch/i.html?_nkw=#{CGI.escape crop.name}" "https://www.ebay.com/sch/i.html?_nkw=#{CGI.escape crop.name}"
end end
def youtube_video_id(url)
return unless url
regex = %r{(?:youtube(?:-nocookie)?\.com/(?:[^/\n\s]+/\S+/|(?:v|e(?:mbed)?)/|\S*?[?&]v=)|youtu\.be/)([a-zA-Z0-9_-]{11})}
match = url.match(regex)
match[1] if match
end
def crop_jsonld_data(crop, full_attributes: true)
Rails.cache.fetch([crop.cache_key_with_version, "jsonld", full_attributes]) do
same_as_urls = [crop.en_wikipedia_url]
crop.scientific_names.each do |scientific_name|
if scientific_name.wikidata_id.present?
same_as_urls << "https://www.wikidata.org/wiki/#{scientific_name.wikidata_id}"
end
end
subject_of_entities = []
images = []
if full_attributes
if crop.en_youtube_url.present?
subject_of_entities << {
'@type': "VideoObject",
url: crop.en_youtube_url
}
end
crop.posts.each do |post|
subject_of_entities << {
'@type': "SocialMediaPosting",
url: post_url(post),
author: {
'@type': 'Person',
name: post.author.login_name
},
datePublished: post.created_at
}
end
crop.photos.each do |photo|
images << photo.fullsize_url
end
end
# TODO: Review plantings, seeds, harvests as a subtype of social media post or event that ended? Or creative work?
# has_many :plantings, dependent: :destroy
# has_many :seeds, dependent: :destroy
# has_many :harvests, dependent: :destroy
{
'@context': "https://schema.org",
'@type': "BioChemEntity",
name: crop.name,
taxonomicRange: crop.scientific_names.map(&:name),
description: crop.description,
sameAs: same_as_urls,
alternateName: crop.alternate_names.map(&:name),
subjectOf: subject_of_entities,
image: images
}.compact
end
end
end end

View File

@@ -7,8 +7,8 @@ module EventHelper
def event_description(event) def event_description(event)
render "#{event.event_type.pluralize}/description", event_model: resolve_model(event) render "#{event.event_type.pluralize}/description", event_model: resolve_model(event)
rescue ActionView::MissingTemplate rescue ActionView::MissingTemplate
"#{event.event_type.humanize.downcase}d" "#{event.event_type.humanize.downcase}d"
end end
def resolve_model(event) def resolve_model(event)

View File

@@ -59,10 +59,6 @@ module IconsHelper
image_icon 'delete' image_icon 'delete'
end end
def copy_icon
icon('far', 'copy')
end
def add_photo_icon def add_photo_icon
image_icon 'add-photo' image_icon 'add-photo'
end end

View File

@@ -46,13 +46,9 @@ module PlantingsHelper
# Returns a list of gardens the planting can be transplanted to # Returns a list of gardens the planting can be transplanted to
# based on the planting's owner. # based on the planting's owner.
def transplantable_gardens_by_owner(planting) def transplantable_gardens_by_owner(planting)
@transplantable_gardens ||= {} garden_ids = planting.owner.gardens.select(:id).to_a + GardenCollaborator.where(member_id: planting.owner.id).select(:garden_id).to_a
cache_key = planting.id || planting.object_id
@transplantable_gardens[cache_key] ||= begin
garden_ids = planting.owner.gardens.select(:id).to_a + GardenCollaborator.where(member_id: planting.owner.id).select(:garden_id).to_a
Garden.active.where.not(id: planting.garden_id).where(id: garden_ids) Garden.active.where.not(id: planting.garden_id).where(id: garden_ids)
end
end end
def days_from_now_to_last_harvest(planting) def days_from_now_to_last_harvest(planting)

View File

@@ -57,19 +57,6 @@ class NotifierMailer < ApplicationMailer
mail(to: @member.email, subject: @subject) if @member.send_planting_reminder mail(to: @member.email, subject: @subject) if @member.send_planting_reminder
end end
def harvest_reminder(member)
@member = member
@plantings = @member.plantings.active.select(&:harvest_in_next_week?)
@sitename = ENV.fetch('GROWSTUFF_SITE_NAME', nil)
@subject = I18n.t('notifier_mailer.harvest_reminder.subject', sitename: @sitename)
# Encrypting
message = { member_id: @member.id, type: :send_harvest_reminder }
@signed_message = verifier.generate(message)
mail(to: @member.email, subject: @subject) if @member.send_harvest_reminder
end
def new_crop_request(member, request) def new_crop_request(member, request)
@member = member @member = member
@request = request @request = request

View File

@@ -76,7 +76,6 @@ class Ability
if member.role? :crop_wrangler if member.role? :crop_wrangler
can :wrangle, Crop can :wrangle, Crop
can :manage, Crop can :manage, Crop
can :manage, CropCompanion
can :manage, ScientificName can :manage, ScientificName
can :manage, AlternateName can :manage, AlternateName
can :openfarm, Crop can :openfarm, Crop
@@ -123,7 +122,6 @@ class Ability
can :create, GardenCollaborator, garden: { owner_id: member.id } can :create, GardenCollaborator, garden: { owner_id: member.id }
can :update, GardenCollaborator, garden: { owner_id: member.id } can :update, GardenCollaborator, garden: { owner_id: member.id }
can :destroy, GardenCollaborator, garden: { owner_id: member.id } can :destroy, GardenCollaborator, garden: { owner_id: member.id }
can :destroy, GardenCollaborator, member_id: member.id
can :create, Activity can :create, Activity
can :update, Activity, owner_id: member.id can :update, Activity, owner_id: member.id
@@ -165,12 +163,6 @@ class Ability
can :destroy, Follow can :destroy, Follow
cannot :destroy, Follow, followed_id: member.id # can't unfollow yourself cannot :destroy, Follow, followed_id: member.id # can't unfollow yourself
# blocking/unblocking permissions
can :create, Block
cannot :create, Block, blocked_id: member.id # can't block yourself
can :destroy, Block, blocker_id: member.id # can only unblock your own blocks
cannot :create, GardenType cannot :create, GardenType
cannot :update, GardenType cannot :update, GardenType
cannot :destroy, GardenType cannot :destroy, GardenType

View File

@@ -4,6 +4,7 @@ class Activity < ApplicationRecord
extend FriendlyId extend FriendlyId
include Ownable include Ownable
include Finishable include Finishable
include SearchActivities
include Likeable include Likeable
belongs_to :garden, optional: true belongs_to :garden, optional: true
@@ -45,17 +46,4 @@ class Activity < ApplicationRecord
def planting_slug def planting_slug
planting&.crop&.slug planting&.crop&.slug
end end
scope :active, -> { where(finished: [false, nil]) }
def self.homepage_records(limit)
# Get the latest activity for each owner, then return the latest 'limit' of those
Activity.where(id: Activity.unscoped.select("DISTINCT ON (owner_id) id").order("owner_id, created_at DESC"))
.order(created_at: :desc)
.limit(limit)
end
def self.reindex(refresh: false); end
def reindex(refresh: false); end
end end

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: true
class AustralianFoodClassificationData < ApplicationRecord
belongs_to :crop,
foreign_key: :public_food_key,
primary_key: :public_food_key,
inverse_of: :australian_food_classification_data
end

View File

@@ -1,18 +0,0 @@
# frozen_string_literal: true
class Block < ApplicationRecord
belongs_to :blocker, class_name: "Member"
belongs_to :blocked, class_name: "Member"
validates :blocker_id, uniqueness: { scope: :blocked_id }
after_create :destroy_follow_relationship
private
def destroy_follow_relationship
# Destroy the follow relationship in both directions
Follow.where(follower: blocker, followed: blocked).destroy_all
Follow.where(follower: blocked, followed: blocker).destroy_all
end
end

View File

@@ -4,7 +4,6 @@ class Comment < ApplicationRecord
belongs_to :author, class_name: 'Member', inverse_of: :comments belongs_to :author, class_name: 'Member', inverse_of: :comments
belongs_to :commentable, polymorphic: true, counter_cache: true belongs_to :commentable, polymorphic: true, counter_cache: true
# validates :body, presence: true # validates :body, presence: true
validate :author_is_not_blocked
scope :post_order, -> { order(created_at: :asc) } # for display on post page scope :post_order, -> { order(created_at: :asc) } # for display on post page
@@ -26,14 +25,4 @@ class Comment < ApplicationRecord
def to_s def to_s
"#{author.login_name} commented on #{commentable.subject}" "#{author.login_name} commented on #{commentable.subject}"
end end
private
def author_is_not_blocked
return unless author
return unless commentable.author.already_blocking?(author)
errors.add(:base, "You cannot comment on a post of a member who has blocked you.")
end
end end

View File

@@ -40,15 +40,8 @@ module MemberFlickr
# Fetches a collection of photos from Flickr # Fetches a collection of photos from Flickr
# Returns a [[page of photos], total] pair. # Returns a [[page of photos], total] pair.
# Total is needed for pagination. # Total is needed for pagination.
def flickr_photos(page_num = 1, set = nil, tags = nil) def flickr_photos(page_num = 1, set = nil)
result = if tags.present? result = if set
flickr.photos.search(
user_id: 'me',
tags: tags,
page: page_num,
per_page: 30
)
elsif set.present?
flickr.photosets.getPhotos( flickr.photosets.getPhotos(
photoset_id: set, photoset_id: set,
page: page_num, page: page_num,

View File

@@ -19,6 +19,10 @@ module OpenFarmData
fetch_attr('tags_array') fetch_attr('tags_array')
end end
def description
fetch_attr('description')
end
def common_names def common_names
fetch_attr('common_names') fetch_attr('common_names')
end end
@@ -39,6 +43,6 @@ module OpenFarmData
def fetch_attr(key) def fetch_attr(key)
return if openfarm_data.blank? return if openfarm_data.blank?
openfarm_data.dig('attributes', key) openfarm_data.fetch('attributes', {}).fetch(key, nil)
end end
end end

View File

@@ -6,31 +6,23 @@ module PredictHarvest
included do included do
# dates # dates
def first_harvest_date def first_harvest_date
return @first_harvest_date if defined?(@first_harvest_date) harvests_with_dates.minimum(:harvested_at)
@first_harvest_date = harvests_with_dates.minimum(:harvested_at)
end end
def last_harvest_date def last_harvest_date
return @last_harvest_date if defined?(@last_harvest_date) harvests_with_dates.maximum(:harvested_at)
@last_harvest_date = harvests_with_dates.maximum(:harvested_at)
end end
def first_harvest_predicted_at def first_harvest_predicted_at
return @first_harvest_predicted_at if defined?(@first_harvest_predicted_at) return unless crop.median_days_to_first_harvest.present? && planted_at.present?
@first_harvest_predicted_at = if crop.median_days_to_first_harvest.present? && planted_at.present? planted_at + crop.median_days_to_first_harvest.days
planted_at + crop.median_days_to_first_harvest.days
end
end end
def last_harvest_predicted_at def last_harvest_predicted_at
return @last_harvest_predicted_at if defined?(@last_harvest_predicted_at) return unless crop.median_days_to_last_harvest.present? && planted_at.present?
@last_harvest_predicted_at = if crop.median_days_to_last_harvest.present? && planted_at.present? planted_at + crop.median_days_to_last_harvest.days
planted_at + crop.median_days_to_last_harvest.days
end
end end
# actions # actions
@@ -60,16 +52,10 @@ module PredictHarvest
def before_harvest_time? def before_harvest_time?
first_harvest_predicted_at.present? && first_harvest_predicted_at.present? &&
harvests.empty? && harvests.empty? &&
first_harvest_predicted_at.present? &&
first_harvest_predicted_at > Time.zone.today first_harvest_predicted_at > Time.zone.today
end end
def harvest_in_next_week?
first_harvest_predicted_at.present? &&
harvests.empty? &&
first_harvest_predicted_at >= Time.zone.today &&
first_harvest_predicted_at <= Time.zone.today + 7.days
end
def harvest_months def harvest_months
Rails.cache.fetch("#{cache_key_with_version}/harvest_months", expires_in: 5.minutes) do Rails.cache.fetch("#{cache_key_with_version}/harvest_months", expires_in: 5.minutes) do
neighbours_for_harvest_predictions.where.not(harvested_at: nil) neighbours_for_harvest_predictions.where.not(harvested_at: nil)
@@ -79,18 +65,16 @@ module PredictHarvest
end end
def neighbours_for_harvest_predictions def neighbours_for_harvest_predictions
@neighbours_for_harvest_predictions ||= begin # use this planting's harvest if any
# use this planting's harvest if any return harvests if harvests.size.positive?
if harvests.size.positive?
harvests # otherwise use nearby plantings
# otherwise use nearby plantings if location
elsif location return Harvest.where(planting: nearby_same_crop.has_harvests)
Harvest.where(planting: nearby_same_crop.has_harvests) .where.not(planting_id: nil)
.where.not(planting_id: nil)
else
Harvest.none
end
end end
Harvest.none
end end
private private

View File

@@ -13,49 +13,40 @@ module PredictPlanting
# dates # dates
def finish_predicted_at def finish_predicted_at
return @finish_predicted_at if defined?(@finish_predicted_at) if planted_at.blank? || failed?
nil
@finish_predicted_at = if planted_at.blank? || failed? elsif crop.median_lifespan.present?
nil planted_at + crop.median_lifespan.days
elsif crop.median_lifespan.present? elsif crop.parent.present? && crop.parent.median_lifespan.present?
planted_at + crop.median_lifespan.days planted_at + crop.parent.median_lifespan.days
elsif crop.parent.present? && crop.parent.median_lifespan.present? end
planted_at + crop.parent.median_lifespan.days
end
end end
# days # days
def expected_lifespan def expected_lifespan
return @expected_lifespan if defined?(@expected_lifespan) if actual_lifespan.present?
actual_lifespan
@expected_lifespan = if actual_lifespan.present? elsif crop.median_lifespan.present?
actual_lifespan crop.median_lifespan
elsif crop.median_lifespan.present? elsif crop.parent.present? && crop.parent.median_lifespan.present?
crop.median_lifespan crop.parent.median_lifespan
elsif crop.parent.present? && crop.parent.median_lifespan.present? end
crop.parent.median_lifespan
end
end end
def actual_lifespan def actual_lifespan
return @actual_lifespan if defined?(@actual_lifespan) return unless planted_at.present? && finished_at.present? && !failed?
@actual_lifespan = if planted_at.present? && finished_at.present? && !failed? (finished_at - planted_at).to_i
(finished_at - planted_at).to_i
end
end end
def age_in_days def age_in_days
return @age_in_days if defined?(@age_in_days) return if planted_at.blank?
return if failed?
@age_in_days = if planted_at.blank? || failed? known_last_day ||= finished_at || Time.zone.today
nil known_last_day = Time.zone.today if known_last_day > Time.zone.today
else
known_last_day = finished_at || Time.zone.today
known_last_day = Time.zone.today if known_last_day > Time.zone.today
(known_last_day - planted_at).to_i (known_last_day - planted_at).to_i
end
end end
def percentage_grown def percentage_grown

View File

@@ -0,0 +1,66 @@
# frozen_string_literal: true
module SearchActivities
extend ActiveSupport::Concern
included do
searchkick merge_mappings: true,
settings: { number_of_shards: 1, number_of_replicas: 0 },
mappings: {
properties: {
active: { type: :boolean },
created_at: { type: :integer },
updated_at: { type: :integer },
due_date: { type: :date }
}
}
def search_data
{
slug:,
active:,
finished: finished?,
name:,
due_date:,
category:,
garden_id:,
garden_name: garden&.name,
garden_slug: garden&.garden_slug,
planting_id:,
planting_name: planting&.crop&.name,
planting_slug: planting&.slug,
description:,
# owner
owner_id:,
owner_login_name:,
owner_slug:,
# timestamps
created_at: created_at.to_i,
updated_at: updated_at.to_i
}
end
def self.homepage_records(limit)
records = []
owners = []
1..limit.times do
where = {
# photos_count: { gt: 0 },
owner_id: { not: owners }
}
one_record = search('*',
limit: 1,
where:,
boost_by: [:created_at],
load: false).first
return records if one_record.nil?
owners << one_record.owner_id
records << one_record
end
records
end
end
end

View File

@@ -53,9 +53,7 @@ module SearchHarvests
owners = [] owners = []
1..limit.times do 1..limit.times do
where = { where = {
# Disabled for now so that more relevant harvests are photos_count: { gt: 0 },
# surfaced; even if we're falling back to crop photos.
# photos_count: { gt: 0 },
owner_id: { not: owners } owner_id: { not: owners }
} }
one_record = search('*', one_record = search('*',

View File

@@ -66,9 +66,7 @@ module SearchPlantings
owners = [] owners = []
1..limit.times do 1..limit.times do
where = { where = {
# Disabled for now so that more relevant plantings are photos_count: { gt: 0 },
# surfaced; even if we're falling back to crop photos.
# photos_count: { gt: 0 },
owner_id: { not: owners } owner_id: { not: owners }
} }
one_record = search('*', one_record = search('*',

View File

@@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class Crop < ApplicationRecord class Crop < ApplicationRecord
has_paper_trail
extend FriendlyId extend FriendlyId
include PhotoCapable include PhotoCapable
include OpenFarmData include OpenFarmData
@@ -28,10 +27,6 @@ class Crop < ApplicationRecord
has_many :companions, through: :crop_companions, source: :crop_b, class_name: 'Crop' has_many :companions, through: :crop_companions, source: :crop_b, class_name: 'Crop'
has_many :crop_posts, dependent: :delete_all has_many :crop_posts, dependent: :delete_all
has_many :posts, through: :crop_posts, dependent: :delete_all has_many :posts, through: :crop_posts, dependent: :delete_all
has_one :australian_food_classification_data,
foreign_key: :public_food_key,
primary_key: :public_food_key,
inverse_of: :crop
accepts_nested_attributes_for :scientific_names, allow_destroy: true, reject_if: :all_blank accepts_nested_attributes_for :scientific_names, allow_destroy: true, reject_if: :all_blank
@@ -57,15 +52,9 @@ class Crop < ApplicationRecord
validates :en_wikipedia_url, validates :en_wikipedia_url,
format: { format: {
with: %r{\Ahttps?://en\.wikipedia\.org/wiki/[[:alnum:]%_.()-]+\z}, with: %r{\Ahttps?://en\.wikipedia\.org/wiki/[[:alnum:]%_.()-]+\z},
message: :not_a_valid_wikipedia_url message: 'is not a valid English Wikipedia URL'
}, },
if: :approved? if: :approved?
validates :en_youtube_url,
format: {
with: %r{\A(?:https?://)?(?:www\.)?(?:youtube(?:-nocookie)?\.com/(?:(?:v|e(?:mbed)?)/|\S*?[?&]v=)|youtu\.be/)[a-zA-Z0-9_-]{11}(?:[?&]\S*)?\z},
message: :not_a_valid_youtube_url
},
allow_blank: true
validates :name, uniqueness: { scope: :approval_status }, if: :pending? validates :name, uniqueness: { scope: :approval_status }, if: :pending?
def to_s def to_s
@@ -101,7 +90,7 @@ class Crop < ApplicationRecord
def popular_plant_parts def popular_plant_parts
PlantPart.joins(:harvests) PlantPart.joins(:harvests)
.where("crop_id = ?", id) .where("crop_id = ?", id)
.order(count_harvests_id: :desc) .order("count_harvests_id DESC")
.group("plant_parts.id", "plant_parts.name") .group("plant_parts.id", "plant_parts.name")
.count("harvests.id") .count("harvests.id")
end end
@@ -164,22 +153,8 @@ class Crop < ApplicationRecord
where(["lower(crops.name) = :value", { value: name.downcase }]) where(["lower(crops.name) = :value", { value: name.downcase }])
end end
def all_companions
@all_companions ||= if parent
(companions + parent.all_companions).uniq
else
companions
end
end
before_destroy :destroy_reverse_companionships
private private
def destroy_reverse_companionships
CropCompanion.where(crop_b: self).destroy_all
end
def count_uses_of_property(col_name) def count_uses_of_property(col_name)
plantings.unscoped plantings.unscoped
.where(crop_id: id) .where(crop_id: id)
@@ -192,12 +167,12 @@ class Crop < ApplicationRecord
return if rejected? return if rejected?
return unless reason_for_rejection.present? || rejection_notes.present? return unless reason_for_rejection.present? || rejection_notes.present?
errors.add(:approval_status, :rejection_reason_required) errors.add(:approval_status, "must be rejected if a reason for rejection is present")
end end
def must_have_meaningful_reason_for_rejection def must_have_meaningful_reason_for_rejection
return unless reason_for_rejection == "other" && rejection_notes.blank? return unless reason_for_rejection == "other" && rejection_notes.blank?
errors.add(:rejection_notes, :rejection_notes_required) errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
end end
end end

View File

@@ -4,7 +4,6 @@ class Follow < ApplicationRecord
belongs_to :follower, class_name: "Member", inverse_of: :follows belongs_to :follower, class_name: "Member", inverse_of: :follows
belongs_to :followed, class_name: "Member", inverse_of: :inverse_follows belongs_to :followed, class_name: "Member", inverse_of: :inverse_follows
validates :follower_id, uniqueness: { scope: :followed_id } validates :follower_id, uniqueness: { scope: :followed_id }
validate :follower_is_not_blocked
after_create do after_create do
Notification.create( Notification.create(
@@ -15,14 +14,4 @@ class Follow < ApplicationRecord
notifiable: self notifiable: self
) )
end end
private
def follower_is_not_blocked
return unless follower
return unless followed.already_blocking?(follower)
errors.add(:base, "You cannot follow a member who has blocked you.")
end
end end

View File

@@ -3,7 +3,6 @@
class Forum < ApplicationRecord class Forum < ApplicationRecord
extend FriendlyId extend FriendlyId
include Ownable include Ownable
validates :name, presence: true validates :name, presence: true
validates :description, presence: true validates :description, presence: true
friendly_id :name, use: %i(slugged finders) friendly_id :name, use: %i(slugged finders)

View File

@@ -21,7 +21,6 @@ class Garden < ApplicationRecord
after_validation :cleanup_area after_validation :cleanup_area
after_validation :geocode after_validation :geocode
after_validation :empty_unwanted_geocodes after_validation :empty_unwanted_geocodes
after_validation :populate_wikidata_info, if: :will_save_change_to_location?
after_save :mark_inactive_garden_plantings_as_finished after_save :mark_inactive_garden_plantings_as_finished
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
@@ -33,7 +32,7 @@ class Garden < ApplicationRecord
validates :name, uniqueness: { scope: :owner_id } validates :name, uniqueness: { scope: :owner_id }
validates :name, validates :name,
format: { without: /\n/, message: :no_newlines }, format: { without: /\n/, message: "must contain no newlines" },
allow_blank: false, presence: true, allow_blank: false, presence: true,
length: { maximum: 255 } length: { maximum: 255 }
@@ -54,7 +53,7 @@ class Garden < ApplicationRecord
"acres" => "acre" "acres" => "acre"
}.freeze }.freeze
validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values, validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values,
message: :not_a_valid_area_unit }, message: "%<value>s is not a valid area unit" },
allow_blank: true allow_blank: true
def cleanup_area def cleanup_area
@@ -93,19 +92,6 @@ class Garden < ApplicationRecord
end end
end end
def populate_wikidata_info
return false if location.blank?
wd_id = WikidataService.find_wikidata_id(location)
return false if wd_id.blank?
self.location_wikidata_id = wd_id
temps = WikidataService.fetch_temps(wd_id)
self.highest_temp_c = temps[:highest_temp_c]
self.lowest_temp_c = temps[:lowest_temp_c]
true
end
protected protected
def strip_blanks def strip_blanks

View File

@@ -11,7 +11,7 @@ class GardenCollaborator < ApplicationRecord
return unless member return unless member
return unless garden return unless garden
errors.add(:member_id, :cannot_be_garden_owner) if garden.owner == member errors.add(:member_id, "cannot be the garden owner") if garden.owner == member
end end
def member_slug def member_slug

View File

@@ -2,7 +2,6 @@
class GardenType < ApplicationRecord class GardenType < ApplicationRecord
extend FriendlyId extend FriendlyId
friendly_id :name, use: %i(slugged finders) friendly_id :name, use: %i(slugged finders)
has_many :gardens, dependent: :nullify has_many :gardens, dependent: :nullify

View File

@@ -8,8 +8,6 @@ class Harvest < ApplicationRecord
include SearchHarvests include SearchHarvests
include Likeable include Likeable
attr_accessor :overall_rating
friendly_id :harvest_slug, use: %i(slugged finders) friendly_id :harvest_slug, use: %i(slugged finders)
# Constants # Constants
@@ -60,18 +58,18 @@ class Harvest < ApplicationRecord
## ##
## Validations ## Validations
validates :crop, approved: true validates :crop, approved: true
validates :crop, presence: { message: :crop_not_found } validates :crop, presence: { message: "must be present and exist in our database" }
validates :plant_part, presence: { message: :crop_not_found } validates :plant_part, presence: { message: "must be present and exist in our database" }
validates :harvested_at, presence: true validates :harvested_at, presence: true
validates :quantity, allow_nil: true, numericality: { validates :quantity, allow_nil: true, numericality: {
only_integer: false, greater_than_or_equal_to: 0 only_integer: false, greater_than_or_equal_to: 0
} }
validates :unit, allow_blank: true, inclusion: { validates :unit, allow_blank: true, inclusion: {
in: UNITS_VALUES.values, message: :not_a_valid_unit in: UNITS_VALUES.values, message: "%<value>s is not a valid unit"
} }
validates :weight_quantity, allow_nil: true, numericality: { only_integer: false } validates :weight_quantity, allow_nil: true, numericality: { only_integer: false }
validates :weight_unit, allow_blank: true, inclusion: { validates :weight_unit, allow_blank: true, inclusion: {
in: WEIGHT_UNITS_VALUES.values, message: :not_a_valid_unit in: WEIGHT_UNITS_VALUES.values, message: "%<value>s is not a valid unit"
} }
validate :crop_must_match_planting validate :crop_must_match_planting
validate :owner_must_match_planting validate :owner_must_match_planting
@@ -111,49 +109,37 @@ class Harvest < ApplicationRecord
def to_s def to_s
# 50 individual apples, weighing 3lb # 50 individual apples, weighing 3lb
# 2 buckets of apricots, weighing 10kg # 2 buckets of apricots, weighing 10kg
@to_s ||= "#{quantity_to_human} #{unit_to_human} #{crop_name_to_human} #{weight_to_human}".strip "#{quantity_to_human} #{unit_to_human} #{crop_name_to_human} #{weight_to_human}".strip
end end
def quantity_to_human def quantity_to_human
@quantity_to_human ||= if quantity return number_to_human(quantity.to_s, strip_insignificant_zeros: true) if quantity
number_to_human(quantity.to_s, strip_insignificant_zeros: true)
else ""
""
end
end end
def unit_to_human def unit_to_human
@unit_to_human ||= begin return "" unless quantity && unit
if !quantity || !unit return 'individual' if unit == 'individual'
"" return "#{unit} of" if quantity == 1
elsif unit == 'individual'
'individual' "#{unit.pluralize} of"
elsif quantity == 1
"#{unit} of"
else
"#{unit.pluralize} of"
end
end
end end
def weight_to_human def weight_to_human
@weight_to_human ||= if weight_quantity return "" unless weight_quantity
"weighing #{number_to_human(weight_quantity, strip_insignificant_zeros: true)} #{weight_unit}"
else "weighing #{number_to_human(weight_quantity, strip_insignificant_zeros: true)} #{weight_unit}"
""
end
end end
def crop_name_to_human def crop_name_to_human
@crop_name_to_human ||= begin if unit != 'individual' # buckets of apricot*s*
if unit != 'individual' # buckets of apricot*s* crop.name.pluralize
crop.name.pluralize elsif quantity == 1
elsif quantity == 1 crop.name
crop.name else
else crop.name.pluralize
crop.name.pluralize end.to_s
end.to_s
end
end end
private private
@@ -161,7 +147,7 @@ class Harvest < ApplicationRecord
def crop_must_match_planting def crop_must_match_planting
return if planting.blank? # only check if we are linked to a planting return if planting.blank? # only check if we are linked to a planting
errors.add(:planting, :same_crop_required) unless crop == planting.crop errors.add(:planting, "must be the same crop") unless crop == planting.crop
end end
def owner_must_match_planting def owner_must_match_planting
@@ -169,13 +155,14 @@ class Harvest < ApplicationRecord
return if owner == planting.owner || planting.garden.garden_collaborators.where(member_id: owner).any? return if owner == planting.owner || planting.garden.garden_collaborators.where(member_id: owner).any?
errors.add(:owner, :same_owner_required) errors.add(:owner,
"of harvest must be the same as planting, or a collaborator on that garden")
end end
def harvest_must_be_after_planting def harvest_must_be_after_planting
# only check if we are linked to a planting # only check if we are linked to a planting
return unless harvested_at.present? && planting.present? && planting.planted_at.present? return unless harvested_at.present? && planting.present? && planting.planted_at.present?
errors.add(:planting, :harvest_after_planted) unless harvested_at > planting.planted_at errors.add(:planting, "cannot be harvested before planting") unless harvested_at > planting.planted_at
end end
end end

View File

@@ -5,24 +5,4 @@ class Like < ApplicationRecord
belongs_to :likeable, polymorphic: true, counter_cache: true, touch: true belongs_to :likeable, polymorphic: true, counter_cache: true, touch: true
validates :member, :likeable, presence: true validates :member, :likeable, presence: true
validates :member, uniqueness: { scope: :likeable } validates :member, uniqueness: { scope: :likeable }
validate :member_is_not_blocked
def likeable_author
if likeable.respond_to?(:author)
likeable.author
elsif likeable.respond_to?(:owner)
likeable.owner
end
end
private
def member_is_not_blocked
return unless member
author = likeable_author
return unless author&.already_blocking?(member)
errors.add(:base, "You cannot like content of a member who has blocked you.")
end
end end

View File

@@ -2,14 +2,12 @@
class Member < ApplicationRecord class Member < ApplicationRecord
include Discard::Model include Discard::Model
acts_as_messageable # messages can be sent to this model acts_as_messageable # messages can be sent to this model
include Geocodable include Geocodable
include MemberFlickr include MemberFlickr
include MemberNewsletter include MemberNewsletter
extend FriendlyId extend FriendlyId
friendly_id :login_name, use: %i(slugged finders) friendly_id :login_name, use: %i(slugged finders)
# #
@@ -52,15 +50,6 @@ class Member < ApplicationRecord
has_many :followed, through: :follows has_many :followed, through: :follows
has_many :followers, through: :inverse_follows, source: :follower has_many :followers, through: :inverse_follows, source: :follower
#
# Blocking other members
has_many :blocks, class_name: "Block", foreign_key: "blocker_id", dependent: :destroy,
inverse_of: :blocker
has_many :inverse_blocks, class_name: "Block", foreign_key: "blocked_id",
dependent: :destroy, inverse_of: :blocked
has_many :blocked_members, through: :blocks, source: :blocked
has_many :blockers, through: :inverse_blocks, source: :blocker
# #
# Global data records this member created # Global data records this member created
has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify, has_many :requested_crops, class_name: 'Crop', foreign_key: 'requester_id', dependent: :nullify,
@@ -79,7 +68,6 @@ class Member < ApplicationRecord
scope :interesting, -> { confirmed.located.recently_signed_in.has_plantings } scope :interesting, -> { confirmed.located.recently_signed_in.has_plantings }
scope :has_plantings, -> { joins(:plantings).group("members.id") } scope :has_plantings, -> { joins(:plantings).group("members.id") }
scope :wants_reminders, -> { where(send_planting_reminder: true) } scope :wants_reminders, -> { where(send_planting_reminder: true) }
scope :wants_harvest_reminders, -> { where(send_harvest_reminder: true) }
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, # :token_authenticatable, :confirmable,
@@ -106,21 +94,20 @@ class Member < ApplicationRecord
validates :tos_agreement, acceptance: { allow_nil: true, accept: true } validates :tos_agreement, acceptance: { allow_nil: true, accept: true }
validates :login_name, validates :login_name,
length: { length: {
minimum: 2, maximum: 25, message: :login_name_length minimum: 2, maximum: 25, message: "should be between 2 and 25 characters long"
}, },
exclusion: { exclusion: {
in: %w(growstuff admin moderator staff nearby), message: :login_name_reserved in: %w(growstuff admin moderator staff nearby), message: "name is reserved"
}, },
format: { format: {
with: /\A\w+\z/, message: :login_name_format with: /\A\w+\z/, message: "may only include letters, numbers, or underscores"
}, },
uniqueness: { uniqueness: {
case_sensitive: false case_sensitive: false
} }
validates :website_url, format: { with: %r{\Ahttps?://}, message: :url_format }, allow_blank: true validates :website_url, format: { with: /\Ahttps?:\/\//, message: "must start with http:// or https://" }, allow_blank: true
validates :other_url, format: { with: %r{\Ahttps?://}, message: :url_format }, allow_blank: true validates :other_url, format: { with: /\Ahttps?:\/\//, message: "must start with http:// or https://" }, allow_blank: true
validates :instagram_handle, :facebook_handle, :bluesky_handle, validates :instagram_handle, :facebook_handle, :bluesky_handle, format: { without: %r{\Ahttps?:\/\/|\/}, message: "should be a handle, not a URL" }, allow_blank: true
format: { without: %r{\Ahttps?://|/}, message: :handle_format }, allow_blank: true
# #
# Triggers # Triggers
@@ -162,7 +149,7 @@ class Member < ApplicationRecord
end end
def unread_count def unread_count
@unread_count ||= receipts.where(is_read: false).count receipts.where(is_read: false).count
end end
def self.login_name_or_email(login) def self.login_name_or_email(login)
@@ -174,12 +161,12 @@ class Member < ApplicationRecord
end end
def self.nearest_to(place) def self.nearest_to(place)
return [] if place.blank? nearby_members = []
if place
latitude, longitude = Geocoder.coordinates(place, params: { limit: 1 }) latitude, longitude = Geocoder.coordinates(place, params: { limit: 1 })
return [] unless latitude && longitude nearby_members = Member.located.sort_by { |x| x.distance_from([latitude, longitude]) } if latitude && longitude
end
Member.located.near([latitude, longitude], 1000) nearby_members
end end
def already_following?(member) def already_following?(member)
@@ -189,33 +176,4 @@ class Member < ApplicationRecord
def get_follow(member) def get_follow(member)
follows.find_by(followed_id: member.id) if already_following?(member) follows.find_by(followed_id: member.id) if already_following?(member)
end end
def already_blocking?(member)
blocks.exists?(blocked_id: member.id)
end
def get_block(member)
blocks.find_by(blocked_id: member.id) if already_blocking?(member)
end
def has_activity?
(gardens.exists? && gardens.count > 1) ||
plantings.exists? ||
harvests.exists? ||
seeds.exists? ||
photos.exists? ||
forums.exists? ||
activities.exists? ||
posts.exists? ||
comments.exists? ||
requested_crops.exists? ||
created_crops.exists? ||
likes.exists? ||
created_alternate_names.exists? ||
created_scientific_names.exists? ||
follows.exists? ||
inverse_follows.exists? ||
blocks.exists? ||
inverse_blocks.exists?
end
end end

View File

@@ -47,7 +47,7 @@ class Photo < ApplicationRecord
info = flickr.photos.getInfo(photo_id: source_id) info = flickr.photos.getInfo(photo_id: source_id)
licenses = flickr.photos.licenses.getInfo licenses = flickr.photos.licenses.getInfo
license = licenses.find { |l| l.id.to_i == info.license.to_i } license = licenses.find { |l| l.id.to_i == info.license.to_i }
Rails.logger.error("Cannot find license: #{[info.license, licenses].inspect}") unless license Rails.logger.error("Cannot find license: " + [info.license, licenses].inspect) unless license
{ {
title: calculate_title(info), title: calculate_title(info),
license_name: license.name, license_name: license.name,

View File

@@ -29,12 +29,12 @@ class PhotoAssociation < ApplicationRecord
def photo_and_item_have_same_owner def photo_and_item_have_same_owner
return if photographable_type == 'Crop' return if photographable_type == 'Crop'
errors.add(:photo, :photo_owner_mismatch) unless photographable.owner_id == photo.owner_id errors.add(:photo, "must have same owner as item it links to") unless photographable.owner_id == photo.owner_id
end end
def crop_present def crop_present
return unless %w(Planting Seed Harvest).include?(photographable_type) return unless %w(Planting Seed Harvest).include?(photographable_type)
errors.add(:crop_id, :calculate_crop_failed) if crop_id.blank? errors.add(:crop_id, "failed to calculate crop") if crop_id.blank?
end end
end end

View File

@@ -2,7 +2,6 @@
class PlantPart < ApplicationRecord class PlantPart < ApplicationRecord
extend FriendlyId extend FriendlyId
friendly_id :name, use: %i(slugged finders) friendly_id :name, use: %i(slugged finders)
has_many :harvests, dependent: :destroy has_many :harvests, dependent: :destroy

View File

@@ -25,8 +25,6 @@ class Planting < ApplicationRecord
has_many :harvests, dependent: :destroy has_many :harvests, dependent: :destroy
has_many :activities, dependent: :destroy has_many :activities, dependent: :destroy
scope :current, -> { where.not(finished: true).where.not(failed: true) }
# #
# Ancestry of food # Ancestry of food
belongs_to :parent_seed, class_name: 'Seed', # parent, belongs_to :parent_seed, class_name: 'Seed', # parent,
@@ -72,7 +70,7 @@ class Planting < ApplicationRecord
## ##
## Validations ## Validations
validates :garden, presence: true validates :garden, presence: true
validates :crop, presence: true, approved: { message: :crop_must_be_approved } validates :crop, presence: true, approved: { message: "must be present and exist in our database" }
validate :finished_must_be_after_planted validate :finished_must_be_after_planted
validate :owner_must_match_garden_owner validate :owner_must_match_garden_owner
validate :cannot_be_finished_and_failed validate :cannot_be_finished_and_failed
@@ -80,10 +78,10 @@ class Planting < ApplicationRecord
only_integer: true, greater_than_or_equal_to: 0 only_integer: true, greater_than_or_equal_to: 0
} }
validates :sunniness, allow_blank: true, inclusion: { validates :sunniness, allow_blank: true, inclusion: {
in: SUNNINESS_VALUES, message: :not_a_valid_sunniness in: SUNNINESS_VALUES, message: "%<value>s is not a valid sunniness value"
} }
validates :planted_from, allow_blank: true, inclusion: { validates :planted_from, allow_blank: true, inclusion: {
in: PLANTED_FROM_VALUES, message: :not_a_valid_planting_method in: PLANTED_FROM_VALUES, message: "%<value>s is not a valid planting method"
} }
validates :overall_rating, allow_blank: true, numericality: { validates :overall_rating, allow_blank: true, numericality: {
only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5
@@ -119,36 +117,33 @@ class Planting < ApplicationRecord
end end
def nearby_same_crop def nearby_same_crop
return @nearby_same_crop if defined?(@nearby_same_crop) return Planting.none if location.blank? || latitude.blank? || longitude.blank?
@nearby_same_crop = if location.blank? || latitude.blank? || longitude.blank? # latitude, longitude = Geocoder.coordinates(location, params: { limit: 1 })
Planting.none Planting.joins(:garden)
else .where(crop:)
# latitude, longitude = Geocoder.coordinates(location, params: { limit: 1 }) .located
Planting.joins(:garden) .where('gardens.latitude < ? AND gardens.latitude > ?',
.where(crop:) latitude + 10, latitude - 10)
.located
.where('gardens.latitude < ? AND gardens.latitude > ?',
latitude + 10, latitude - 10)
end
end end
private private
def cannot_be_finished_and_failed def cannot_be_finished_and_failed
errors.add(:failed, :failed_and_finished) if finished && failed errors.add(:failed, "can't be true if planting is also finished") if finished && failed
end end
# check that any finished_at date occurs after planted_at # check that any finished_at date occurs after planted_at
def finished_must_be_after_planted def finished_must_be_after_planted
return unless planted_at && finished_at # only check if we have both return unless planted_at && finished_at # only check if we have both
errors.add(:finished_at, :finished_after_planted) unless planted_at < finished_at errors.add(:finished_at, "must be after the planting date") unless planted_at < finished_at
end end
def owner_must_match_garden_owner def owner_must_match_garden_owner
return if owner == garden.owner || garden.garden_collaborators.where(member_id: owner).any? return if owner == garden.owner || garden.garden_collaborators.where(member_id: owner).any?
errors.add(:owner, :same_owner_required) errors.add(:owner,
"must be the same as garden, or a collaborator on that garden")
end end
end end

View File

@@ -49,10 +49,9 @@ class Post < ApplicationRecord
# return posts sorted by recent activity # return posts sorted by recent activity
def self.recently_active def self.recently_active
left_joins(:comments) Post.order(created_at: :desc).sort do |a, b|
.select('posts.*, COALESCE(MAX(comments.created_at), posts.created_at) AS last_activity_at') b.recent_activity <=> a.recent_activity
.group('posts.id') end
.order(Arel.sql('last_activity_at DESC'))
end end
def owner_id def owner_id

View File

@@ -2,7 +2,6 @@
class Role < ApplicationRecord class Role < ApplicationRecord
extend FriendlyId extend FriendlyId
friendly_id :name, use: %i(slugged finders) friendly_id :name, use: %i(slugged finders)
validates :name, uniqueness: true, presence: true validates :name, uniqueness: true, presence: true

View File

@@ -6,7 +6,6 @@ class Seed < ApplicationRecord
include Finishable include Finishable
include Ownable include Ownable
include SearchSeeds include SearchSeeds
friendly_id :seed_slug, use: %i(slugged finders) friendly_id :seed_slug, use: %i(slugged finders)
TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally).freeze TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally).freeze
@@ -28,7 +27,7 @@ class Seed < ApplicationRecord
# #
# Validations # Validations
validates :crop, approved: true validates :crop, approved: true
validates :crop, presence: { message: :crop_not_found } validates :crop, presence: { message: "must be present and exist in our database" }
validates :quantity, allow_nil: true, validates :quantity, allow_nil: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 } numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :days_until_maturity_min, allow_nil: true, validates :days_until_maturity_min, allow_nil: true,
@@ -36,15 +35,20 @@ class Seed < ApplicationRecord
validates :days_until_maturity_max, allow_nil: true, validates :days_until_maturity_max, allow_nil: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 } numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :tradable_to, allow_blank: false, validates :tradable_to, allow_blank: false,
inclusion: { in: TRADABLE_TO_VALUES, message: :tradable_to_inclusion } inclusion: { in: TRADABLE_TO_VALUES, message: "You may only trade seed nowhere, " \
"locally, nationally, or internationally" }
validates :organic, allow_blank: false, validates :organic, allow_blank: false,
inclusion: { in: ORGANIC_VALUES, message: :organic_inclusion } inclusion: { in: ORGANIC_VALUES, message: "You must say whether the seeds " \
"are organic or not, or that you don't know" }
validates :gmo, allow_blank: false, validates :gmo, allow_blank: false,
inclusion: { in: GMO_VALUES, message: :gmo_inclusion } inclusion: { in: GMO_VALUES, message: "You must say whether the seeds are " \
"genetically modified or not, or that you don't know" }
validates :heirloom, allow_blank: false, validates :heirloom, allow_blank: false,
inclusion: { in: HEIRLOOM_VALUES, message: :heirloom_inclusion } inclusion: { in: HEIRLOOM_VALUES, message: "You must say whether the seeds" \
"are heirloom, hybrid, or unknown" }
validates :source, allow_blank: true, validates :source, allow_blank: true,
inclusion: { in: SOURCE_VALUES, message: :source_inclusion } inclusion: { in: SOURCE_VALUES, message: "You must say where the seeds are from," \
"or that you don't know" }
# #
# Delegations # Delegations

View File

@@ -7,9 +7,9 @@ module Api
@model.owner = context[:current_user] @model.owner = context[:current_user]
end end
has_one :owner, class_name: 'Member', always_include_linkage_data: true has_one :owner, class_name: 'Member'
has_one :garden, always_include_linkage_data: true has_one :garden
has_one :planting, always_include_linkage_data: true has_one :planting
attribute :name attribute :name
attribute :description attribute :description

View File

@@ -12,7 +12,7 @@ module Api
has_many :photos has_many :photos
has_one :parent, class_name: 'Crop', always_include_linkage_data: true has_one :parent, class_name: 'Crop'
attribute :name attribute :name
attribute :en_wikipedia_url attribute :en_wikipedia_url

View File

@@ -7,7 +7,7 @@ module Api
@model.owner = context[:current_user] @model.owner = context[:current_user]
end end
has_one :owner, class_name: 'Member', always_include_linkage_data: true has_one :owner, class_name: 'Member'
has_many :plantings has_many :plantings
has_many :photos has_many :photos

View File

@@ -10,9 +10,9 @@ module Api
@model.plant_part = PlantPart.first @model.plant_part = PlantPart.first
end end
has_one :crop, always_include_linkage_data: true has_one :crop
has_one :planting, always_include_linkage_data: true has_one :planting
has_one :owner, class_name: 'Member', always_include_linkage_data: true has_one :owner, class_name: 'Member'
# has_one :plant_part # has_one :plant_part
has_many :photos has_many :photos

Some files were not shown because too many files have changed in this diff Show More