Compare commits

..

44 Commits

Author SHA1 Message Date
Daniel O'Connor
75807ba2af Merge branch 'dev' of https://github.com/Growstuff/growstuff into datepicker 2025-09-01 13:22:47 +00:00
Daniel O'Connor
62406a6573 Merge branch 'dev' into datepicker 2025-03-29 14:24:22 +10:30
Daniel O'Connor
53e90f57dd Merge branch 'dev' into datepicker 2024-10-14 00:41:45 +10:30
Daniel O'Connor
54acc2c108 Merge branch 'dev' into datepicker 2024-10-14 00:10:34 +10:30
Daniel O'Connor
0639cd5ab8 Merge branch 'dev' into datepicker 2024-10-13 23:54:46 +10:30
Daniel O'Connor
41c4ae1448 Merge branch 'dev' into datepicker 2024-10-13 23:51:09 +10:30
Daniel O'Connor
99221d7cf3 Merge branch 'dev' into datepicker 2024-10-13 23:40:14 +10:30
Daniel O'Connor
eeb8c84c31 Merge branch 'dev' into datepicker 2024-10-13 23:30:36 +10:30
Daniel O'Connor
e65dd4ef85 Merge branch 'dev' into datepicker 2024-10-13 23:05:10 +10:30
Daniel O'Connor
40ed3c8007 Merge branch 'dev' into datepicker 2024-10-13 22:54:03 +10:30
Daniel O'Connor
035decb132 Update spec/features/plantings/planting_a_crop_spec.rb 2024-10-13 22:53:17 +10:30
Daniel O'Connor
7102552bfe Update spec/features/seeds/adding_seeds_spec.rb 2024-10-13 22:52:39 +10:30
Daniel O'Connor
e1270512dc Update app/views/seeds/_form.html.haml 2024-10-13 22:52:05 +10:30
Daniel O'Connor
8d40355355 Update app/views/harvests/_form.html.haml 2024-10-13 22:51:04 +10:30
Daniel O'Connor
919c3dbf37 Merge branch 'dev' into datepicker 2024-10-13 22:47:40 +10:30
Daniel O'Connor
3e7a58fcfc Merge branch 'dev' into datepicker 2024-10-13 22:38:28 +10:30
Daniel O'Connor
fc301558e1 Merge branch 'dev' into datepicker 2024-10-13 22:21:39 +10:30
Daniel O'Connor
2523bea154 Merge branch 'dev' into datepicker 2024-10-13 22:11:42 +10:30
Daniel O'Connor
f89d64ac3a Mark spec pending 2024-10-13 10:28:29 +00:00
Daniel O'Connor
65a0540e3d Mark spec pending 2024-10-13 10:19:51 +00:00
Daniel O'Connor
654aa318c4 Fix specs 2024-10-13 10:05:52 +00:00
Daniel O'Connor
5128c8be0e Fix specs 2024-10-13 09:55:20 +00:00
Daniel O'Connor
0e1578aeef Fix specs 2024-10-13 09:45:10 +00:00
Daniel O'Connor
c6b5cc61da Fix specs 2024-10-13 09:30:36 +00:00
Daniel O'Connor
dddc85c338 Fix specs 2024-10-13 09:19:10 +00:00
Daniel O'Connor
69e764dd63 Fix specs 2024-10-13 09:01:29 +00:00
Daniel O'Connor
8aa0f98196 Fix specs 2024-10-13 09:00:00 +00:00
Daniel O'Connor
4c8c54eadd Fix specs 2024-10-13 08:48:42 +00:00
Daniel O'Connor
9c6797e850 Swap to html5 control 2024-10-13 08:17:40 +00:00
Daniel O'Connor
a5f774f043 Spec no longer possible 2024-10-13 03:59:12 +00:00
Daniel O'Connor
fe0d4295be Spec no longer possible 2024-10-13 03:57:53 +00:00
Daniel O'Connor
c5bdac4a5c Merge branch 'dev' of https://github.com/Growstuff/growstuff into datepicker 2024-10-13 03:56:30 +00:00
Daniel O'Connor
195602288c Fix test 2024-10-13 03:29:29 +00:00
Daniel O'Connor
c78a347002 Mark required 2024-10-13 02:59:41 +00:00
Daniel O'Connor
5178e1257e Merge branch 'dev' into datepicker 2024-10-13 13:16:22 +10:30
Daniel O'Connor
03f8acdd1d Add placehikder 2024-10-13 02:41:22 +00:00
Daniel O'Connor
426cb3ed37 Remove redundant UI element 2024-10-13 02:36:16 +00:00
Daniel O'Connor
4716b33d82 Mark required 2024-10-13 02:33:35 +00:00
Daniel O'Connor
329c3ddddd Mark required 2024-10-13 02:31:59 +00:00
Daniel O'Connor
3405d224b4 Add input validations 2024-10-13 02:29:17 +00:00
Daniel O'Connor
bd858a0b23 Mark required 2024-10-13 02:26:27 +00:00
Daniel O'Connor
defc3def4f Allow autosuggests to be required 2024-10-13 02:26:16 +00:00
Daniel O'Connor
4b0e228525 Swap to native datepicker 2024-10-13 02:24:20 +00:00
Daniel O'Connor
0f9e151c15 Swap to native datepicker 2024-10-13 02:22:49 +00:00
432 changed files with 6344 additions and 14502 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (admin/) - name: Run rspec (admin/)
run: bundle exec rspec spec/features/admin/ -fd -t ~@flaky run: bundle exec rspec spec/features/admin/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (comments/) - name: Run rspec (comments/)
run: bundle exec rspec spec/features/comments/ -fd -t ~@flaky run: bundle exec rspec spec/features/comments/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -101,9 +101,3 @@ jobs:
- name: Run rspec (conversations/) - name: Run rspec (conversations/)
run: bundle exec rspec spec/features/conversations/ -fd -t ~@flaky run: bundle exec rspec spec/features/conversations/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (crops/) - name: Run rspec (crops/)
run: bundle exec rspec spec/features/crops/ -fd -t ~@flaky run: bundle exec rspec spec/features/crops/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (gardens/) - name: Run rspec (gardens/)
run: bundle exec rspec spec/features/gardens/ -fd -t ~@flaky run: bundle exec rspec spec/features/gardens/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -99,11 +99,4 @@ jobs:
run: bundle exec rails search:reindex run: bundle exec rails search:reindex
- name: Run rspec (harvests/) - name: Run rspec (harvests/)
run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -99,11 +99,4 @@ jobs:
run: bundle exec rails search:reindex run: bundle exec rails search:reindex
- name: Run rspec (home/) - name: Run rspec (home/)
run: bundle exec rspec spec/features/home/ -fd -t ~@flaky run: bundle exec rspec spec/features/home/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -99,11 +99,4 @@ jobs:
run: bundle exec rails search:reindex run: bundle exec rails search:reindex
- name: Run rspec (members/) - name: Run rspec (members/)
run: bundle exec rspec spec/features/members/ -fd -t ~@flaky run: bundle exec rspec spec/features/members/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (places/) - name: Run rspec (places/)
run: bundle exec rspec spec/features/places/ -fd run: bundle exec rspec spec/features/places/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (plantings/) - name: Run rspec (plantings/)
run: bundle exec rspec spec/features/plantings/ -fd run: bundle exec rspec spec/features/plantings/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,4 +1,4 @@
name: CI Features - Posts name: CI Features - Admin
on: [pull_request] on: [pull_request]
@@ -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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (posts/) - name: Run rspec (posts/)
run: bundle exec rspec spec/features/posts/ -fd run: bundle exec rspec spec/features/posts/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -100,10 +100,3 @@ jobs:
- name: Run rspec (seeds/) - name: Run rspec (seeds/)
run: bundle exec rspec spec/features/seeds/ -fd -t ~@flaky run: bundle exec rspec spec/features/seeds/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -99,11 +99,4 @@ jobs:
run: bundle exec rails search:reindex run: bundle exec rails search:reindex
- name: Run rspec (timeline/) - name: Run rspec (timeline/)
run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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
@@ -108,11 +108,4 @@ jobs:
run: bundle exec rspec spec/features/photos/ -fd run: bundle exec rspec spec/features/photos/ -fd
- name: Run rspec (rss/) - name: Run rspec (rss/)
run: bundle exec rspec spec/features/rss/ -fd run: bundle exec rspec spec/features/rss/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v7
with:
name: 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@v4
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

@@ -1,5 +1,5 @@
inherit_from: .rubocop_todo.yml inherit_from: .rubocop_todo.yml
plugins: require:
- rubocop-factory_bot - rubocop-factory_bot
- rubocop-capybara - rubocop-capybara
- rubocop-rails - rubocop-rails
@@ -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: 14
# 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.2)
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.3)
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.4)
activesupport (>= 6.1.0) activesupport (>= 6.1.0)
factory_bot_rails (6.5.1) factory_bot_rails (6.5.0)
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.1)
pg (1.6.3-x86_64-linux) pg (1.6.1-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 (6.6.1)
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.1)
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.6.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

@@ -17,14 +17,13 @@ encourage participation from people of all backgrounds and skill levels.
## Want to contribute? ## Want to contribute?
Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking. Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking.
Dive on in and submit your PRs! Dive on in and submit your PRs.
Vibe Coding is more than okay, just make sure you indicate if you have done so and ensure there are tests.
## Important links ## Important links
* [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
@@ -36,10 +35,6 @@ frontend features. We welcome contributions -- see
* To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide). * To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide).
* You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API). * You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API).
### For Home Automation enthusiasts
https://github.com/Growstuff/homeassistant-growstuff/
## For designers, writers, researchers, data wranglers, and other contributors ## For designers, writers, researchers, data wranglers, and other contributors
There are heaps of ways to get involved and contribute no matter what There are heaps of ways to get involved and contribute no matter what

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

@@ -132,8 +132,6 @@ section {
border-radius: 5%; border-radius: 5%;
margin: 0.5em 0.5em 0.5em 0; margin: 0.5em 0.5em 0.5em 0;
width: 200px; width: 200px;
align-items: stretch;
justify-content: space-between;
.img-card { .img-card {
border-top-left-radius: 5%; border-top-left-radius: 5%;

View File

@@ -4,44 +4,34 @@ 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
def new def new
@activity = Activity.new( @activity = Activity.new(
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

@@ -1,8 +0,0 @@
# frozen_string_literal: true
module Api
module V1
class ActivitiesController < BaseController
end
end
end

View File

@@ -4,40 +4,6 @@ module Api
module V1 module V1
class BaseController < JSONAPI::ResourceController class BaseController < JSONAPI::ResourceController
abstract abstract
protect_from_forgery with: :null_session
before_action :authenticate_member_from_token!
before_action :enforce_member_for_write_operations!, only: %i(create update destroy)
rescue_from CanCan::AccessDenied do
head :forbidden
end
def context
{
current_user: current_user,
current_ability: current_ability,
controller: self,
action: params[:action]
}
end
private
attr_reader :current_user
def enforce_member_for_write_operations!
head :unauthorized unless current_user
end
def authenticate_member_from_token!
authenticate_with_http_token do |token, _options|
auth = Authentication.find_by(token: token, provider: 'api')
if auth.present?
@current_user = auth.member
return true
end
end
end
end end
end end
end end

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,14 +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,
:sowing_method, :sun_requirements, :growing_degree_days,
scientific_names_attributes: %i(scientific_name _destroy id) scientific_names_attributes: %i(scientific_name _destroy id)
) )
end end

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
@@ -20,7 +20,6 @@ class GardensController < DataController
def show def show
@current_plantings = @garden.plantings.current.where.not(failed: true).includes(:crop, :owner).order(planted_at: :desc) @current_plantings = @garden.plantings.current.where.not(failed: true).includes(:crop, :owner).order(planted_at: :desc)
@current_activities = @garden.activities.current.includes(:owner).order(created_at: :desc) @current_activities = @garden.activities.current.includes(:owner).order(created_at: :desc)
@finished_activities = @garden.activities.finished.includes(:owner).order(created_at: :desc)
@finished_plantings = @garden.plantings.finished.includes(:crop) @finished_plantings = @garden.plantings.finished.includes(:crop)
@suggested_companions = Crop.approved.where( @suggested_companions = Crop.approved.where(
id: CropCompanion.where(crop_a_id: @current_plantings.select(:crop_id)).select(:crop_b_id) id: CropCompanion.where(crop_a_id: @current_plantings.select(:crop_id)).select(:crop_b_id)
@@ -39,10 +38,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 +53,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]
@@ -37,7 +37,6 @@ class PlantingsController < DataController
@photos = @planting.photos.includes(:owner).order(date_taken: :desc) @photos = @planting.photos.includes(:owner).order(date_taken: :desc)
@harvests = Harvest.search(where: { planting_id: @planting.id }) @harvests = Harvest.search(where: { planting_id: @planting.id })
@current_activities = @planting.activities.current.includes(:owner).order(created_at: :desc) @current_activities = @planting.activities.current.includes(:owner).order(created_at: :desc)
@finished_activities = @planting.activities.finished.includes(:owner).order(created_at: :desc)
@matching_seeds = matching_seeds @matching_seeds = matching_seeds
@crop = @planting.crop @crop = @planting.crop
@@ -46,12 +45,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 +109,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
@@ -140,7 +133,7 @@ class PlantingsController < DataController
:crop_id, :description, :garden_id, :planted_at, :crop_id, :description, :garden_id, :planted_at,
:parent_seed_id, :parent_seed_id,
:quantity, :sunniness, :planted_from, :finished, :quantity, :sunniness, :planted_from, :finished,
:finished_at, :failed, :overall_rating :finished_at, :failed
) )
end end
@@ -160,7 +153,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

@@ -6,7 +6,7 @@ class RegistrationsController < Devise::RegistrationsController
prepend_before_action :check_captcha, only: [:create] # Change this to be any actions you want to protect with recaptcha. prepend_before_action :check_captcha, only: [:create] # Change this to be any actions you want to protect with recaptcha.
def edit def edit
@flickr_auth = current_member.auth('flickr') @flickr_auth = current_member.auth('flickr')
render "edit" render "edit"
end end
@@ -38,12 +38,6 @@ class RegistrationsController < Devise::RegistrationsController
end end
end end
def regenerate_api_token
current_member.regenerate_api_token
set_flash_message :notice, :api_token_regenerated
redirect_to edit_member_registration_path + '#apps'
end
def destroy def destroy
if @member.valid_password?(params.require(:member)[:current_password]) if @member.valid_password?(params.require(:member)[:current_password])
@member.discard @member.discard

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
@@ -22,28 +21,6 @@ module ApplicationHelper
classes classes
end end
# Similar to Rails' time_ago_in_words, but gives a more standard
# output like "in 3 days" or "5 months ago".
# Also handles the case where from_time is a Date and to_time is a Date
# (in which case it just says "today" if they're the same date).
#
# NOTE: This is similar to distance_of_time_in_words but different enough
# that I think it's worth having a separate helper for it.
#
# from_time - the starting time (Time or Date)
# to_time - the ending time (Time or Date). Default: now (Time.zone.now)
# include_seconds - whether to include seconds in the calculation
#
# Returns a string like "in 3 days" or "5 months ago"
def standard_time_distance(from_time, to_time = 0, include_seconds = false)
return 'today' if from_time.is_a?(Date) && (from_time == to_time)
return 'now' if from_time == to_time
return "#{distance_of_time_in_words(from_time, to_time, include_seconds:)} ago" if from_time < to_time
"in #{distance_of_time_in_words(from_time, to_time, include_seconds:)}"
end
def count_github_contibutors def count_github_contibutors
File.open(Rails.root.join('CONTRIBUTORS.md')).readlines.grep(/^-/).size File.open(Rails.root.join('CONTRIBUTORS.md')).readlines.grep(/^-/).size
end end
@@ -120,22 +97,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
@@ -29,33 +30,4 @@ class Activity < ApplicationRecord
def to_s def to_s
name name
end end
def garden_name
garden&.name
end
def garden_slug
garden&.slug
end
def planting_name
planting&.crop&.name
end
def planting_slug
planting&.crop&.slug
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

@@ -8,6 +8,14 @@ module OpenFarmData
fetch_attr('main_image_path') fetch_attr('main_image_path')
end end
def height
fetch_attr('height')
end
def spread
fetch_attr('spread')
end
def svg_icon def svg_icon
icon = fetch_attr('svg_icon') icon = fetch_attr('svg_icon')
return icon if icon.present? return icon if icon.present?
@@ -19,6 +27,14 @@ module OpenFarmData
fetch_attr('tags_array') fetch_attr('tags_array')
end end
def description
fetch_attr('description')
end
def row_spacing
fetch_attr('row_spacing')
end
def common_names def common_names
fetch_attr('common_names') fetch_attr('common_names')
end end
@@ -27,10 +43,22 @@ module OpenFarmData
fetch_attr('binomial_name') fetch_attr('binomial_name')
end end
def sowing_method
fetch_attr('sowing_method')
end
def main_image_path def main_image_path
fetch_attr('main_image_path') fetch_attr('main_image_path')
end end
def sun_requirements
fetch_attr('sun_requirements')
end
def growing_degree_days
fetch_attr('growing_degree_days')
end
def processing_pictures def processing_pictures
fetch_attr('processing_pictures') fetch_attr('processing_pictures')
end end
@@ -39,6 +67,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,62 @@
# 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 }
}
}
def search_data
{
slug:,
active:,
finished: finished?,
name:,
due_date:,
category:,
garden_id:,
garden_name: garden&.name,
planting_id:,
planting_name: planting&.crop&.name,
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)
# #
@@ -26,20 +24,6 @@ class Member < ApplicationRecord
has_many :notifications, foreign_key: 'recipient_id', inverse_of: :recipient has_many :notifications, foreign_key: 'recipient_id', inverse_of: :recipient
has_many :sent_notifications, foreign_key: 'sender_id', inverse_of: :sender, class_name: "Notification" has_many :sent_notifications, foreign_key: 'sender_id', inverse_of: :sender, class_name: "Notification"
has_many :authentications, dependent: :destroy has_many :authentications, dependent: :destroy
has_one :api_token, -> { where(provider: 'api') }, class_name: 'Authentication', dependent: :destroy
def api_token?
api_token.present?
end
def regenerate_api_token
api_token.destroy if api_token?
create_api_token(
provider: 'api',
uid: id,
token: SecureRandom.hex(16)
)
end
has_many :photos, inverse_of: :owner has_many :photos, inverse_of: :owner
has_many :likes, dependent: :destroy has_many :likes, dependent: :destroy
@@ -52,15 +36,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 +54,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 +80,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 +135,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 +147,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 +162,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

@@ -46,8 +46,7 @@ class Photo < ApplicationRecord
flickr = owner.flickr flickr = owner.flickr
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 == info.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,13 +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: {
only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5
} }
def planting_slug def planting_slug
@@ -119,36 +114,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

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