mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-09-08 20:20:11 -04:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bb632c3c1 | ||
|
|
80e724fefe |
No files matched your search
@@ -1,7 +1,7 @@
|
||||
FROM mcr.microsoft.com/devcontainers/ruby:3.4-trixie
|
||||
|
||||
# Install Rails
|
||||
RUN gem install rails:8.0.5
|
||||
RUN gem install rails:7.0.8
|
||||
|
||||
# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
|
||||
# The value is a comma-separated list of allowed domains
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
contributors:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install ruby version specified in .ruby-version
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout this repo
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Setup yarn cache
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v5
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
sudo apt-get -y install libpq-dev google-chrome-stable
|
||||
|
||||
- name: Install NodeJS
|
||||
uses: actions/setup-node@v7
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
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.6.0
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ ENV RAILS_ENV=production \
|
||||
WORKDIR /app
|
||||
|
||||
# Install gems
|
||||
COPY Gemfile Gemfile.lock .ruby-version ./
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
RUN bundle config set --local deployment 'true' && \
|
||||
bundle config set --local without 'development test' && \
|
||||
bundle install --jobs 4 --retry 3
|
||||
|
||||
@@ -5,7 +5,7 @@ source 'https://rubygems.org'
|
||||
# Match ruby version in .ruby-version
|
||||
ruby File.read('.ruby-version')
|
||||
|
||||
gem 'rails', '~> 8.0.0'
|
||||
gem 'rails', '~> 7.2.0'
|
||||
|
||||
# Keeping old sprockets
|
||||
# https://github.com/rails/sprockets-rails/issues/444#issuecomment-637817050
|
||||
|
||||
+158
-140
@@ -33,46 +33,49 @@ GEM
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actioncable (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
activejob (= 8.0.5.1)
|
||||
activerecord (= 8.0.5.1)
|
||||
activestorage (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actionmailbox (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
activejob (= 7.2.3.1)
|
||||
activerecord (= 7.2.3.1)
|
||||
activestorage (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
actionview (= 8.0.5.1)
|
||||
activejob (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actionmailer (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
actionview (= 7.2.3.1)
|
||||
activejob (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (8.0.5.1)
|
||||
actionview (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actionpack (7.2.3.1)
|
||||
actionview (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
cgi
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
racc
|
||||
rack (>= 2.2.4, < 3.3)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
useragent (~> 0.16)
|
||||
actiontext (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
activerecord (= 8.0.5.1)
|
||||
activestorage (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actiontext (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
activerecord (= 7.2.3.1)
|
||||
activestorage (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
actionview (7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
builder (~> 3.1)
|
||||
cgi
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
@@ -86,22 +89,22 @@ GEM
|
||||
active_utils (3.6.0)
|
||||
activesupport (>= 4.2)
|
||||
i18n
|
||||
activejob (8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
activejob (7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
activerecord (8.0.5.1)
|
||||
activemodel (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
activemodel (7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
activerecord (7.2.3.1)
|
||||
activemodel (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
activejob (= 8.0.5.1)
|
||||
activerecord (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
activestorage (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
activejob (= 7.2.3.1)
|
||||
activerecord (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
marcel (~> 1.0)
|
||||
activesupport (8.0.5.1)
|
||||
activesupport (7.2.3.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
@@ -110,18 +113,17 @@ GEM
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
minitest (>= 5.1, < 6)
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
uri (>= 0.13.1)
|
||||
addressable (2.9.0)
|
||||
public_suffix (>= 2.0.2, < 8.0)
|
||||
ast (2.4.3)
|
||||
autoprefixer-rails (10.4.16.0)
|
||||
execjs (~> 2)
|
||||
aws-eventstream (1.4.0)
|
||||
aws-partitions (1.1277.0)
|
||||
aws-sdk-core (3.254.0)
|
||||
aws-partitions (1.1240.0)
|
||||
aws-sdk-core (3.245.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
@@ -129,21 +131,31 @@ GEM
|
||||
bigdecimal
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
logger
|
||||
aws-sdk-kms (1.130.0)
|
||||
aws-sdk-core (~> 3, >= 3.254.0)
|
||||
aws-sdk-kms (1.123.0)
|
||||
aws-sdk-core (~> 3, >= 3.244.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.228.2)
|
||||
aws-sdk-core (~> 3, >= 3.254.0)
|
||||
aws-sdk-s3 (1.220.0)
|
||||
aws-sdk-core (~> 3, >= 3.244.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.12.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
axe-core-api (4.12.0)
|
||||
axe-core-api (4.11.3)
|
||||
dumb_delegator
|
||||
axe-core-capybara (4.12.0)
|
||||
axe-core-api (= 4.12.0)
|
||||
axe-core-rspec (4.12.0)
|
||||
axe-core-api (= 4.12.0)
|
||||
ostruct
|
||||
virtus
|
||||
axe-core-capybara (4.11.3)
|
||||
axe-core-api (= 4.11.3)
|
||||
dumb_delegator
|
||||
axe-core-rspec (4.11.3)
|
||||
axe-core-api (= 4.11.3)
|
||||
dumb_delegator
|
||||
ostruct
|
||||
virtus
|
||||
axiom-types (0.1.1)
|
||||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
base64 (0.3.0)
|
||||
bcrypt (3.1.22)
|
||||
benchmark (0.5.0)
|
||||
@@ -165,7 +177,7 @@ GEM
|
||||
actionpack (>= 6.1)
|
||||
activemodel (>= 6.1)
|
||||
builder (3.3.0)
|
||||
bullet (8.1.3)
|
||||
bullet (8.1.1)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
byebug (13.0.0)
|
||||
@@ -186,17 +198,20 @@ GEM
|
||||
capybara-screenshot (1.0.27)
|
||||
capybara (>= 1.0, < 4)
|
||||
launchy
|
||||
carrierwave (3.1.3)
|
||||
carrierwave (3.0.7)
|
||||
activemodel (>= 6.0.0)
|
||||
activesupport (>= 6.0.0)
|
||||
addressable (~> 2.6)
|
||||
image_processing (~> 1.1)
|
||||
marcel (~> 1.0.0)
|
||||
ssrf_filter (~> 1.0)
|
||||
cgi (0.5.1)
|
||||
chartkick (5.2.1)
|
||||
childprocess (5.1.0)
|
||||
logger (~> 1.5)
|
||||
coderay (1.1.3)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
coffee-rails (5.0.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 5.2.0)
|
||||
@@ -206,9 +221,9 @@ GEM
|
||||
coffee-script-source (1.12.2)
|
||||
comfy_bootstrap_form (4.0.9)
|
||||
rails (>= 5.0.0)
|
||||
concurrent-ruby (1.3.8)
|
||||
concurrent-ruby (1.3.6)
|
||||
connection_pool (2.5.5)
|
||||
crass (1.0.7)
|
||||
crass (1.0.6)
|
||||
crowdin-api (1.12.0)
|
||||
open-uri (>= 0.1.0, < 0.2.0)
|
||||
rest-client (>= 2.0.0, < 2.2.0)
|
||||
@@ -221,7 +236,7 @@ GEM
|
||||
csv_shaper (1.4.0)
|
||||
activesupport (>= 3.0.0)
|
||||
csv
|
||||
dalli (5.0.6)
|
||||
dalli (5.0.2)
|
||||
logger
|
||||
database_cleaner (2.1.0)
|
||||
database_cleaner-active_record (>= 2, < 3)
|
||||
@@ -230,15 +245,17 @@ GEM
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
date (3.5.1)
|
||||
devise (5.0.4)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
devise (5.0.3)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 7.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.6.2)
|
||||
discard (2.0.0)
|
||||
activerecord (>= 7.0, < 9.0)
|
||||
discard (1.4.0)
|
||||
activerecord (>= 4.2, < 9.0)
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (3.2.0)
|
||||
dotenv-rails (3.2.0)
|
||||
@@ -259,9 +276,9 @@ GEM
|
||||
elasticsearch-transport (7.0.0)
|
||||
faraday
|
||||
multi_json
|
||||
erb (6.0.6)
|
||||
erb (6.0.4)
|
||||
erubi (1.13.1)
|
||||
execjs (2.10.1)
|
||||
execjs (2.10.0)
|
||||
factory_bot (6.5.5)
|
||||
activesupport (>= 6.1.0)
|
||||
factory_bot_rails (6.5.1)
|
||||
@@ -269,18 +286,18 @@ GEM
|
||||
railties (>= 6.1.0)
|
||||
faker (3.8.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (2.14.3)
|
||||
faraday (2.14.1)
|
||||
faraday-net_http (>= 2.0, < 3.5)
|
||||
json
|
||||
logger
|
||||
faraday-net_http (3.4.4)
|
||||
faraday-net_http (3.4.2)
|
||||
net-http (~> 0.5)
|
||||
ffi (1.17.4)
|
||||
ffi (1.17.4-x86_64-linux-gnu)
|
||||
ffi (1.17.3)
|
||||
ffi (1.17.3-x86_64-linux-gnu)
|
||||
flickraw (0.9.10)
|
||||
font-awesome-sass (5.15.1)
|
||||
sassc (>= 1.11)
|
||||
friendly_id (5.7.0)
|
||||
friendly_id (5.6.0)
|
||||
activerecord (>= 4.0.0)
|
||||
gbifrb (0.2.0)
|
||||
geocoder (1.8.6)
|
||||
@@ -291,21 +308,21 @@ GEM
|
||||
multi_json (>= 1.9.0)
|
||||
gli (2.22.2)
|
||||
ostruct
|
||||
globalid (1.4.0)
|
||||
globalid (1.3.0)
|
||||
activesupport (>= 6.1)
|
||||
gravatar-ultimate (2.0.0)
|
||||
activesupport (>= 2.3.14)
|
||||
rack
|
||||
haml (7.2.2)
|
||||
haml (7.2.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
tilt
|
||||
haml-rails (3.1.0)
|
||||
haml-rails (3.0.0)
|
||||
actionpack (>= 5.1)
|
||||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
haml_lint (0.76.0)
|
||||
haml_lint (0.73.0)
|
||||
haml (>= 5.0)
|
||||
parallel (>= 1.10)
|
||||
rainbow
|
||||
@@ -322,7 +339,7 @@ GEM
|
||||
csv
|
||||
mini_mime (>= 1.0.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (1.15.2)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (1.1.2)
|
||||
activesupport (>= 4.0.2)
|
||||
@@ -338,17 +355,18 @@ GEM
|
||||
terminal-table (>= 1.5.1)
|
||||
i18n_data (1.1.0)
|
||||
simple_po_parser (~> 1.1)
|
||||
icalendar (2.12.4)
|
||||
icalendar (2.12.2)
|
||||
base64
|
||||
ice_cube (~> 0.16)
|
||||
logger
|
||||
ostruct
|
||||
ice_cube (0.17.0)
|
||||
image_processing (1.14.0)
|
||||
mini_magick (>= 4.9.5, < 6)
|
||||
ice_nine (0.11.2)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
io-console (0.8.2)
|
||||
irb (1.18.0)
|
||||
irb (1.17.0)
|
||||
pp (>= 0.6.0)
|
||||
prism (>= 1.3.0)
|
||||
rdoc (>= 4.0.0)
|
||||
@@ -358,7 +376,7 @@ GEM
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.21.2)
|
||||
json (2.19.4)
|
||||
json-schema (6.2.0)
|
||||
addressable (~> 2.8)
|
||||
bigdecimal (>= 3.1, < 5)
|
||||
@@ -370,7 +388,7 @@ GEM
|
||||
kgio (2.11.4)
|
||||
kramdown (2.4.0)
|
||||
rexml
|
||||
language_server-protocol (3.17.0.6)
|
||||
language_server-protocol (3.17.0.5)
|
||||
launchy (3.1.1)
|
||||
addressable (~> 2.8)
|
||||
childprocess (~> 5.0)
|
||||
@@ -386,10 +404,10 @@ GEM
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
logger (1.7.0)
|
||||
loofah (2.25.2)
|
||||
loofah (2.25.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.9.1)
|
||||
mail (2.9.0)
|
||||
logger
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
@@ -414,20 +432,17 @@ GEM
|
||||
mimemagic (0.4.3)
|
||||
nokogiri (~> 1)
|
||||
rake
|
||||
mini_magick (5.3.1)
|
||||
logger
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.9)
|
||||
minitest (6.0.6)
|
||||
drb (~> 2.0)
|
||||
prism (~> 1.5)
|
||||
msgpack (1.8.4)
|
||||
minitest (5.27.0)
|
||||
msgpack (1.8.0)
|
||||
multi_json (1.19.1)
|
||||
multi_xml (0.8.1)
|
||||
bigdecimal (>= 3.1, < 5)
|
||||
net-http (0.9.1)
|
||||
uri (>= 0.11.1)
|
||||
net-imap (0.6.6)
|
||||
net-imap (0.5.12)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
@@ -438,13 +453,13 @@ GEM
|
||||
net-protocol
|
||||
netrc (0.11.0)
|
||||
nio4r (2.7.5)
|
||||
nokogiri (1.19.4)
|
||||
nokogiri (1.19.2)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.19.4-x86_64-linux-gnu)
|
||||
nokogiri (1.19.2-x86_64-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
oauth (0.5.6)
|
||||
oj (3.17.5)
|
||||
oj (3.17.0)
|
||||
bigdecimal (>= 3.0)
|
||||
ostruct (>= 0.2)
|
||||
omniauth (1.9.2)
|
||||
@@ -463,15 +478,15 @@ GEM
|
||||
activerecord (>= 7.1)
|
||||
request_store (~> 1.4)
|
||||
parallel (2.1.0)
|
||||
parser (3.3.12.0)
|
||||
parser (3.3.11.1)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
percy-capybara (5.0.1)
|
||||
percy-capybara (5.0.0)
|
||||
capybara (>= 3)
|
||||
pg (1.6.3)
|
||||
pg (1.6.3-x86_64-linux)
|
||||
popper_js (2.11.8)
|
||||
pp (0.6.4)
|
||||
pp (0.6.3)
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
prism (1.9.0)
|
||||
@@ -479,8 +494,11 @@ GEM
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
reline (>= 0.6.0)
|
||||
psych (5.3.1)
|
||||
date
|
||||
stringio
|
||||
public_suffix (7.0.5)
|
||||
puma (8.0.2)
|
||||
puma (8.0.1)
|
||||
nio4r (~> 2.0)
|
||||
query_diet (0.7.3)
|
||||
racc (1.8.1)
|
||||
@@ -499,20 +517,20 @@ GEM
|
||||
rackup (1.0.1)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (8.0.5.1)
|
||||
actioncable (= 8.0.5.1)
|
||||
actionmailbox (= 8.0.5.1)
|
||||
actionmailer (= 8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
actiontext (= 8.0.5.1)
|
||||
actionview (= 8.0.5.1)
|
||||
activejob (= 8.0.5.1)
|
||||
activemodel (= 8.0.5.1)
|
||||
activerecord (= 8.0.5.1)
|
||||
activestorage (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
rails (7.2.3.1)
|
||||
actioncable (= 7.2.3.1)
|
||||
actionmailbox (= 7.2.3.1)
|
||||
actionmailer (= 7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
actiontext (= 7.2.3.1)
|
||||
actionview (= 7.2.3.1)
|
||||
activejob (= 7.2.3.1)
|
||||
activemodel (= 7.2.3.1)
|
||||
activerecord (= 7.2.3.1)
|
||||
activestorage (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 8.0.5.1)
|
||||
railties (= 7.2.3.1)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
@@ -521,20 +539,21 @@ GEM
|
||||
activesupport (>= 5.0.0)
|
||||
minitest
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.7.1)
|
||||
loofah (~> 2.25, >= 2.25.2)
|
||||
rails-html-sanitizer (1.7.0)
|
||||
loofah (~> 2.25)
|
||||
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 (8.1.0)
|
||||
rails-i18n (7.0.10)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 8.0.0, < 9)
|
||||
railties (>= 6.0.0, < 8)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (8.0.5.1)
|
||||
actionpack (= 8.0.5.1)
|
||||
activesupport (= 8.0.5.1)
|
||||
railties (7.2.3.1)
|
||||
actionpack (= 7.2.3.1)
|
||||
activesupport (= 7.2.3.1)
|
||||
cgi
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
@@ -547,14 +566,9 @@ GEM
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.11.1)
|
||||
ffi (~> 1.0)
|
||||
rbs (4.1.0)
|
||||
logger
|
||||
prism (>= 1.6.0)
|
||||
tsort
|
||||
rdoc (8.0.0)
|
||||
rdoc (7.2.0)
|
||||
erb
|
||||
prism (>= 1.6.0)
|
||||
rbs (>= 4.0.0)
|
||||
psych (>= 4.0.0)
|
||||
tsort
|
||||
recaptcha (5.21.2)
|
||||
redis-client (0.26.2)
|
||||
@@ -616,7 +630,7 @@ GEM
|
||||
rswag-ui (2.17.0)
|
||||
actionpack (>= 5.2, < 8.2)
|
||||
railties (>= 5.2, < 8.2)
|
||||
rubocop (1.88.2)
|
||||
rubocop (1.86.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -627,16 +641,16 @@ GEM
|
||||
rubocop-ast (>= 1.49.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.50.0)
|
||||
rubocop-ast (1.49.1)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.7)
|
||||
rubocop-capybara (3.0.0)
|
||||
rubocop-capybara (2.23.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.81)
|
||||
rubocop-factory_bot (2.28.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-rails (2.36.0)
|
||||
rubocop-rails (2.34.3)
|
||||
activesupport (>= 4.2.0)
|
||||
lint_roller (~> 1.1)
|
||||
rack (>= 1.1)
|
||||
@@ -645,20 +659,18 @@ GEM
|
||||
rubocop-rake (0.7.1)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1)
|
||||
rubocop-rspec (3.10.2)
|
||||
rubocop-rspec (3.9.0)
|
||||
lint_roller (~> 1.1)
|
||||
regexp_parser (>= 2.0)
|
||||
rubocop (~> 1.86, >= 1.86.2)
|
||||
rubocop (~> 1.81)
|
||||
rubocop-rspec_rails (2.32.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-rspec (~> 3.5)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-units (4.1.0)
|
||||
ruby-vips (2.3.0)
|
||||
ruby-vips (2.2.1)
|
||||
ffi (~> 1.12)
|
||||
logger
|
||||
rubyzip (3.4.1)
|
||||
rubyzip (3.2.2)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
@@ -672,13 +684,13 @@ GEM
|
||||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
scout_apm (6.2.1)
|
||||
scout_apm (6.2.0)
|
||||
parser
|
||||
searchkick (5.3.1)
|
||||
activemodel (>= 6.1)
|
||||
hashie
|
||||
securerandom (0.4.1)
|
||||
selenium-webdriver (4.46.0)
|
||||
selenium-webdriver (4.43.0)
|
||||
base64 (~> 0.2)
|
||||
logger (~> 1.4)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
@@ -691,7 +703,7 @@ GEM
|
||||
rack (>= 2.2.4, < 3.3)
|
||||
redis-client (>= 0.23.0, < 1)
|
||||
simple_po_parser (1.1.6)
|
||||
sitemap_generator (7.1.1)
|
||||
sitemap_generator (6.3.0)
|
||||
builder (~> 3.0)
|
||||
sprockets (3.7.5)
|
||||
base64
|
||||
@@ -701,17 +713,19 @@ GEM
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
ssrf_filter (1.5.0)
|
||||
ssrf_filter (1.1.2)
|
||||
stringio (3.2.0)
|
||||
sysexits (1.2.0)
|
||||
temple (0.10.4)
|
||||
terminal-table (4.0.0)
|
||||
unicode-display_width (>= 1.1.1, < 4)
|
||||
terser (1.2.8)
|
||||
terser (1.2.7)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
thor (1.5.0)
|
||||
tilt (2.8.0)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.7.0)
|
||||
timecop (0.9.11)
|
||||
timeout (0.6.1)
|
||||
timeout (0.5.0)
|
||||
tsort (0.2.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
@@ -728,6 +742,10 @@ GEM
|
||||
activemodel (>= 3.0.0)
|
||||
public_suffix
|
||||
vcr (6.4.0)
|
||||
virtus (2.0.0)
|
||||
axiom-types (~> 0.1)
|
||||
coercible (~> 1.0)
|
||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
webrat (0.7.3)
|
||||
@@ -736,7 +754,7 @@ GEM
|
||||
rack-test (>= 0.5.3)
|
||||
webrick (1.9.2)
|
||||
websocket (1.2.11)
|
||||
websocket-driver (0.8.2)
|
||||
websocket-driver (0.8.0)
|
||||
base64
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
@@ -748,7 +766,7 @@ GEM
|
||||
webrick
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.8.2)
|
||||
zeitwerk (2.7.5)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -828,7 +846,7 @@ DEPENDENCIES
|
||||
rack-attack
|
||||
rack-cors
|
||||
rack-protection (>= 2.0.1)
|
||||
rails (~> 8.0.0)
|
||||
rails (~> 7.2.0)
|
||||
rails-assets-leaflet.markercluster!
|
||||
rails-controller-testing
|
||||
rails_12factor
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "growstuff",
|
||||
"stack": "heroku-26",
|
||||
"stack": "heroku-18",
|
||||
"description": "Open data project for small-scale food growers",
|
||||
"scripts": {
|
||||
"postdeploy": "bundle exec rails db:seed"
|
||||
|
||||
@@ -30,3 +30,44 @@ if (document.getElementById("membermap") !== null) {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$(document).on('click', '#find-me', function(e) {
|
||||
e.preventDefault();
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(function(position) {
|
||||
$('#member_latitude').val(position.coords.latitude);
|
||||
$('#member_longitude').val(position.coords.longitude);
|
||||
updateMap(position.coords.latitude, position.coords.longitude);
|
||||
});
|
||||
} else {
|
||||
alert("Geolocation is not supported by this browser.");
|
||||
}
|
||||
});
|
||||
|
||||
if (document.getElementById("map") !== null) {
|
||||
var map = L.map('map').setView([0, 0], 2);
|
||||
var marker;
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
maxZoom: 18
|
||||
}).addTo(map);
|
||||
|
||||
map.on('click', function(e) {
|
||||
updateMarker(e.latlng);
|
||||
$('#member_latitude').val(e.latlng.lat);
|
||||
$('#member_longitude').val(e.latlng.lng);
|
||||
});
|
||||
|
||||
function updateMarker(latlng) {
|
||||
if (marker) {
|
||||
map.removeLayer(marker);
|
||||
}
|
||||
marker = L.marker(latlng).addTo(map);
|
||||
}
|
||||
|
||||
function updateMap(lat, lng) {
|
||||
map.setView([lat, lng], 13);
|
||||
updateMarker(L.latLng(lat, lng));
|
||||
}
|
||||
}
|
||||
@@ -3,36 +3,6 @@
|
||||
module Api
|
||||
module V1
|
||||
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
|
||||
@@ -57,23 +57,12 @@ class GardensController < DataController
|
||||
redirect_to(member_gardens_path(@garden.owner))
|
||||
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
|
||||
|
||||
def garden_params
|
||||
params.require(:garden).permit(
|
||||
:name, :slug, :description, :active,
|
||||
:location, :latitude, :longitude, :area, :area_unit, :garden_type_id,
|
||||
:location_wikidata_id, :lowest_temp_c, :highest_temp_c
|
||||
:location, :latitude, :longitude, :area, :area_unit, :garden_type_id
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -4,26 +4,27 @@ class HarvestsController < DataController
|
||||
after_action :update_crop_medians, only: %i(create update destroy)
|
||||
|
||||
def index
|
||||
@harvests = Harvest.all
|
||||
|
||||
where = {}
|
||||
if params[:member_slug].present?
|
||||
@owner = Member.find_by!(slug: params[:member_slug])
|
||||
@harvests = @harvests.where(owner_id: @owner.id)
|
||||
where['owner_id'] = @owner.id
|
||||
end
|
||||
|
||||
if params[:crop_slug]
|
||||
@crop = Crop.find_by(slug: params[:crop_slug])
|
||||
@harvests = @harvests.where(crop_id: @crop.id) if @crop
|
||||
where['crop_id'] = @crop.id
|
||||
end
|
||||
|
||||
if params[:planting_slug]
|
||||
@planting = Planting.find_by(slug: params[:planting_slug])
|
||||
@harvests = @harvests.where(planting_id: @planting.id) if @planting
|
||||
where['planting_id'] = @planting.id
|
||||
end
|
||||
|
||||
@harvests = @harvests.includes(:crop, :owner, :plant_part)
|
||||
|
||||
@harvests = @harvests.recent.paginate(page: params[:page], per_page: 100)
|
||||
@harvests = Harvest.search('*', where:,
|
||||
limit: 100,
|
||||
page: params[:page],
|
||||
load: (request.format.csv? ? { include: %i(crop owner plant_part) } : false),
|
||||
boost_by: [:created_at])
|
||||
|
||||
@filename = csv_filename
|
||||
|
||||
@@ -37,9 +38,9 @@ class HarvestsController < DataController
|
||||
end
|
||||
|
||||
def new
|
||||
@harvest = Harvest.new(new_harvest_params.merge(harvested_at: Time.zone.today))
|
||||
@planting = @harvest.planting
|
||||
@crop = @harvest.crop
|
||||
@harvest = Harvest.new(harvested_at: Time.zone.today)
|
||||
@planting = Planting.find_by(slug: params[:planting_slug]) if params[:planting_slug]
|
||||
@crop = Crop.find_by(id: params[:crop_id])
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
@@ -51,7 +52,7 @@ class HarvestsController < DataController
|
||||
def create
|
||||
@harvest.crop_id = @harvest.planting.crop_id if @harvest.planting_id
|
||||
@harvest.harvested_at = Time.zone.now if @harvest.harvested_at.blank?
|
||||
update_planting_rating if @harvest.save
|
||||
@harvest.save
|
||||
if params[:return] == 'planting'
|
||||
respond_with(@harvest, location: @harvest.planting)
|
||||
else
|
||||
@@ -60,7 +61,7 @@ class HarvestsController < DataController
|
||||
end
|
||||
|
||||
def update
|
||||
update_planting_rating if @harvest.update(harvest_params)
|
||||
@harvest.update(harvest_params)
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
@@ -75,17 +76,7 @@ class HarvestsController < DataController
|
||||
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)
|
||||
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)
|
||||
:plant_part_id, :slug, :si_weight)
|
||||
.merge(owner_id: current_member.id)
|
||||
end
|
||||
|
||||
@@ -112,10 +103,4 @@ class HarvestsController < DataController
|
||||
@harvest.planting.update_harvest_days!
|
||||
@harvest.crop.update_harvest_medians
|
||||
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
|
||||
@@ -11,7 +11,7 @@ class LikesController < ApplicationController
|
||||
likeable_id: params[:id]
|
||||
)
|
||||
if @like.likeable && @like.save
|
||||
@like.likeable.reindex(refresh: true) if @like.likeable.respond_to?(:reindex)
|
||||
@like.likeable.reindex(refresh: true)
|
||||
success(@like, liked_by_member: true, status_code: :created)
|
||||
else
|
||||
failed(@like, message: t('messages.unable_to_like'))
|
||||
@@ -26,7 +26,7 @@ class LikesController < ApplicationController
|
||||
)
|
||||
|
||||
if @like&.destroy
|
||||
@like.likeable.reindex(refresh: true) if @like.likeable.respond_to?(:reindex)
|
||||
@like.likeable.reindex(refresh: true)
|
||||
success(@like, liked_by_member: false, status_code: :ok)
|
||||
else
|
||||
failed(@like, message: t('messages.unable_to_unlike'))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MembersController < ApplicationController
|
||||
load_and_authorize_resource except: %i(finish_signup unsubscribe view_follows view_followers show)
|
||||
load_and_authorize_resource except: %i(finish_signup unsubscribe view_follows view_followers show set_location update_location)
|
||||
skip_authorize_resource only: %i(nearby unsubscribe finish_signup)
|
||||
respond_to :html, :json, :rss
|
||||
|
||||
@@ -40,9 +40,12 @@ class MembersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
@harvests = Harvest.where(owner_id: @member.id)
|
||||
.recent
|
||||
.limit(16)
|
||||
@harvests = Harvest.search(
|
||||
where: { owner_id: @member.id },
|
||||
boost_by: [:created_at],
|
||||
limit: 16,
|
||||
load: false
|
||||
)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
@@ -83,6 +86,36 @@ class MembersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def set_location
|
||||
@member = Member.find_by_slug!(params[:id])
|
||||
authorize! :update, @member
|
||||
end
|
||||
|
||||
def update_location
|
||||
@member = Member.find_by_slug!(params[:id])
|
||||
authorize! :update, @member
|
||||
|
||||
if params[:member][:latitude].present? && params[:member][:longitude].present?
|
||||
lat = params[:member][:latitude].to_f.round(2)
|
||||
lng = params[:member][:longitude].to_f.round(2)
|
||||
params[:member][:latitude] = lat
|
||||
params[:member][:longitude] = lng
|
||||
|
||||
results = Geocoder.search([lat, lng])
|
||||
if results.first
|
||||
params[:member][:location] = results.first.city || results.first.town || results.first.village || results.first.hamlet
|
||||
else
|
||||
params[:member][:location] = "Location near #{lat}, #{lng}"
|
||||
end
|
||||
end
|
||||
|
||||
if @member.update(member_params)
|
||||
redirect_to member_path(@member), notice: 'Location updated.'
|
||||
else
|
||||
render :set_location
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
EMAIL_TYPE_STRING = {
|
||||
@@ -92,7 +125,7 @@ class MembersController < ApplicationController
|
||||
}.freeze
|
||||
|
||||
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, :location, :latitude, :longitude, :send_harvest_reminder)
|
||||
end
|
||||
|
||||
def member_json_fields
|
||||
|
||||
@@ -9,17 +9,13 @@ class PhotosController < ApplicationController
|
||||
responders :flash
|
||||
|
||||
def index
|
||||
@photos = if @crop
|
||||
@crop.photos
|
||||
elsif @planting
|
||||
@planting.photos
|
||||
else
|
||||
Photo.all
|
||||
end
|
||||
|
||||
@photos = @photos.includes(:owner)
|
||||
.order(created_at: :desc)
|
||||
.paginate(page: params[:page], per_page: Photo.per_page)
|
||||
@photos = Photo.search(
|
||||
load: false,
|
||||
boost_by: [:created_at],
|
||||
where: index_where_clause,
|
||||
page: params[:page],
|
||||
limit: Photo.per_page
|
||||
)
|
||||
respond_with(@photos)
|
||||
end
|
||||
|
||||
@@ -118,6 +114,18 @@ class PhotosController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def index_where_clause
|
||||
if params[:crop_slug]
|
||||
{ crops: @crop.id }
|
||||
elsif params[:planting_id]
|
||||
{ planting_id: @planting.id }
|
||||
elsif params[:planting_slug]
|
||||
{ plantings: @planting.id }
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
def set_crop_and_planting
|
||||
@crop = Crop.find params[:crop_slug] if params[:crop_slug]
|
||||
@planting = Planting.find params[:planting_id] if params[:planting_id]
|
||||
|
||||
@@ -8,10 +8,26 @@ class PlantingsController < DataController
|
||||
def index
|
||||
@show_all = params[:all] == '1'
|
||||
|
||||
@owner = Member.find_by!(slug: params[:member_slug]) if params[:member_slug].present?
|
||||
@crop = Crop.find_by(slug: params[:crop_slug]) if params[:crop_slug]
|
||||
where = {}
|
||||
where['active'] = true unless @show_all
|
||||
|
||||
@plantings = plantings
|
||||
if params[:member_slug].present?
|
||||
@owner = Member.find_by!(slug: params[:member_slug])
|
||||
where['owner_id'] = @owner.id
|
||||
end
|
||||
|
||||
if params[:crop_slug]
|
||||
@crop = Crop.find_by(slug: params[:crop_slug])
|
||||
where['crop_id'] = @crop.id unless @crop.nil?
|
||||
end
|
||||
|
||||
@plantings = Planting.search(
|
||||
where:,
|
||||
page: params[:page],
|
||||
limit: 30,
|
||||
boost_by: [:created_at],
|
||||
load: false
|
||||
)
|
||||
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_fs(:number)}.csv"
|
||||
respond_with(@plantings)
|
||||
@@ -19,7 +35,7 @@ class PlantingsController < DataController
|
||||
|
||||
def show
|
||||
@photos = @planting.photos.includes(:owner).order(date_taken: :desc)
|
||||
@harvests = Harvest.where(planting_id: @planting.id).recent
|
||||
@harvests = Harvest.search(where: { planting_id: @planting.id })
|
||||
@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
|
||||
|
||||
@@ -2,32 +2,36 @@
|
||||
|
||||
class SeedsController < DataController
|
||||
def index
|
||||
@seeds = Seed.all
|
||||
where = {}
|
||||
|
||||
if params[:member_slug].present?
|
||||
@owner = Member.find_by!(slug: params[:member_slug])
|
||||
@seeds = @seeds.where(owner_id: @owner.id)
|
||||
where['owner_id'] = @owner.id
|
||||
end
|
||||
|
||||
if params[:crop_slug].present?
|
||||
@crop = Crop.find_by(slug: params[:crop_slug])
|
||||
@seeds = @seeds.where(crop_id: @crop.id) if @crop
|
||||
where['crop_id'] = @crop.id
|
||||
end
|
||||
|
||||
if params[:planting_id].present?
|
||||
@planting = Planting.find_by(slug: params[:planting_id])
|
||||
@seeds = @seeds.where(parent_planting_id: @planting.id) if @planting
|
||||
where['parent_planting'] = @planting.id
|
||||
end
|
||||
|
||||
@seeds = @seeds.where(tradable_to: params[:tradeable_to]) if params[:tradeable_to].present?
|
||||
where['tradeable_to'] = params[:tradeable_to] if params[:tradeable_to].present?
|
||||
|
||||
@show_all = (params[:all] == '1')
|
||||
@seeds = @seeds.current unless @show_all
|
||||
where['finished'] = false unless @show_all
|
||||
|
||||
@filename = csv_filename
|
||||
@seeds = @seeds.includes(:crop, :owner)
|
||||
.order(created_at: :desc)
|
||||
.paginate(page: params[:page], per_page: 30)
|
||||
@seeds = Seed.search(
|
||||
where:,
|
||||
page: params[:page],
|
||||
limit: 30,
|
||||
boost_by: [:created_at],
|
||||
load: (request.format.csv? ? { include: %i(crop owner) } : false)
|
||||
)
|
||||
|
||||
respond_with(@seeds)
|
||||
end
|
||||
|
||||
@@ -123,7 +123,6 @@ class Ability
|
||||
can :create, 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, member_id: member.id
|
||||
|
||||
can :create, Activity
|
||||
can :update, Activity, owner_id: member.id
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SearchHarvests
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
searchkick merge_mappings: true,
|
||||
settings: { number_of_shards: 1, number_of_replicas: 0 },
|
||||
mappings: {
|
||||
properties: {
|
||||
harvests_count: { type: :integer },
|
||||
photos_count: { type: :integer },
|
||||
created_at: { type: :integer },
|
||||
harvested_at: { type: :date }
|
||||
}
|
||||
}
|
||||
|
||||
def search_data
|
||||
{
|
||||
slug:,
|
||||
quantity:,
|
||||
|
||||
# crop
|
||||
crop_id:,
|
||||
crop_name:,
|
||||
crop_slug: crop.slug,
|
||||
|
||||
# owner
|
||||
owner_id:,
|
||||
owner_login_name:,
|
||||
owner_slug:,
|
||||
plant_part_name: plant_part&.name,
|
||||
|
||||
# planting
|
||||
planting_id:,
|
||||
planting_slug: planting&.slug,
|
||||
|
||||
# photo
|
||||
has_photos: photos.size.positive?,
|
||||
thumbnail_url: default_photo&.thumbnail_url || crop.default_photo&.thumbnail_url,
|
||||
|
||||
# counts
|
||||
photos_count: photos.count,
|
||||
|
||||
# timestamps
|
||||
harvested_at:,
|
||||
created_at: created_at.to_i
|
||||
}
|
||||
end
|
||||
|
||||
def self.homepage_records(limit)
|
||||
records = []
|
||||
owners = []
|
||||
1..limit.times do
|
||||
where = {
|
||||
# Disabled for now so that more relevant harvests are
|
||||
# surfaced; even if we're falling back to crop photos.
|
||||
# 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
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SearchPhotos
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
searchkick merge_mappings: true,
|
||||
settings: { number_of_shards: 1, number_of_replicas: 0 },
|
||||
mappings: {
|
||||
properties: {
|
||||
title: { type: :text },
|
||||
created_at: { type: :integer }
|
||||
}
|
||||
}
|
||||
|
||||
def search_data
|
||||
{
|
||||
id:,
|
||||
title:,
|
||||
thumbnail_url:,
|
||||
fullsize_url:,
|
||||
# crops
|
||||
crops: crops.pluck(:id),
|
||||
# likes
|
||||
liked_by_members_names:,
|
||||
# owner
|
||||
owner_id:,
|
||||
owner_login_name:,
|
||||
owner_slug:,
|
||||
# counts
|
||||
likes_count:,
|
||||
|
||||
created_at: created_at.to_i
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,87 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SearchPlantings
|
||||
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 },
|
||||
harvests_count: { type: :integer },
|
||||
photos_count: { type: :integer },
|
||||
owner_location: { type: :text }
|
||||
}
|
||||
}
|
||||
|
||||
def search_data
|
||||
{
|
||||
slug:,
|
||||
active:,
|
||||
finished: finished?,
|
||||
has_photos: photos.size.positive?,
|
||||
location:,
|
||||
percentage_grown: percentage_grown.to_i,
|
||||
planted_at:,
|
||||
planted_from:,
|
||||
planted_year: planted_at&.year,
|
||||
quantity:,
|
||||
sunniness:,
|
||||
garden_id:,
|
||||
garden_slug: garden&.garden_slug,
|
||||
garden_name: garden&.name,
|
||||
description:,
|
||||
|
||||
first_harvest_predicted_at:,
|
||||
finish_predicted_at:,
|
||||
|
||||
# crops
|
||||
crop_id:,
|
||||
crop_name:,
|
||||
crop_slug:,
|
||||
crop_perennial:,
|
||||
|
||||
# owner
|
||||
owner_id:,
|
||||
owner_location:,
|
||||
owner_login_name:,
|
||||
owner_slug:,
|
||||
|
||||
# photos
|
||||
thumbnail_url: default_photo&.thumbnail_url || crop.default_photo&.thumbnail_url,
|
||||
# counts
|
||||
photos_count: photos.size,
|
||||
harvests_count:,
|
||||
|
||||
# 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 = {
|
||||
# Disabled for now so that more relevant plantings are
|
||||
# surfaced; even if we're falling back to crop photos.
|
||||
# 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
|
||||
@@ -0,0 +1,69 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SearchSeeds
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
searchkick merge_mappings: true,
|
||||
settings: { number_of_shards: 1, number_of_replicas: 0 },
|
||||
mappings: {
|
||||
properties: {
|
||||
id: { type: :integer },
|
||||
created_at: { type: :integer },
|
||||
plant_before: { type: :text },
|
||||
photos_count: { type: :integer },
|
||||
tradable_to: { type: :text }
|
||||
}
|
||||
}
|
||||
|
||||
def search_data
|
||||
{
|
||||
slug:,
|
||||
finished: finished?,
|
||||
gmo:,
|
||||
active:,
|
||||
heirloom:,
|
||||
location: owner.location,
|
||||
organic:,
|
||||
quantity:,
|
||||
plant_before: plant_before&.to_fs(:ymd),
|
||||
tradable_to:,
|
||||
tradable:,
|
||||
|
||||
# crop
|
||||
crop_id:,
|
||||
crop_name: crop.name,
|
||||
crop_slug: crop.slug,
|
||||
|
||||
# owner
|
||||
owner_id:,
|
||||
owner_location:,
|
||||
owner_login_name:,
|
||||
owner_slug:,
|
||||
|
||||
# planting
|
||||
parent_planting:,
|
||||
|
||||
# counts
|
||||
photos_count: photos.size,
|
||||
|
||||
# photo
|
||||
has_photos: photos.size.positive?,
|
||||
thumbnail_url: default_photo&.thumbnail_url || crop.default_photo&.thumbnail_url,
|
||||
|
||||
created_at: created_at.to_i
|
||||
}
|
||||
end
|
||||
|
||||
def self.homepage_records(limit)
|
||||
search('*', limit:,
|
||||
where: {
|
||||
finished: false,
|
||||
tradable: true,
|
||||
_or: [{ plant_before: nil }, { plant_before: { lt: Date.today } }]
|
||||
},
|
||||
boost_by: [:created_at],
|
||||
load: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
+3
-5
@@ -165,11 +165,9 @@ class Crop < ApplicationRecord
|
||||
end
|
||||
|
||||
def all_companions
|
||||
@all_companions ||= if parent
|
||||
(companions + parent.all_companions).uniq
|
||||
else
|
||||
companions
|
||||
end
|
||||
return companions unless parent
|
||||
|
||||
(companions + parent.all_companions).uniq
|
||||
end
|
||||
|
||||
before_destroy :destroy_reverse_companionships
|
||||
|
||||
@@ -21,7 +21,6 @@ class Garden < ApplicationRecord
|
||||
after_validation :cleanup_area
|
||||
after_validation :geocode
|
||||
after_validation :empty_unwanted_geocodes
|
||||
after_validation :populate_wikidata_info, if: :will_save_change_to_location?
|
||||
after_save :mark_inactive_garden_plantings_as_finished
|
||||
|
||||
scope :active, -> { where(active: true) }
|
||||
@@ -93,19 +92,6 @@ class Garden < ApplicationRecord
|
||||
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
|
||||
|
||||
def strip_blanks
|
||||
|
||||
@@ -5,10 +5,9 @@ class Harvest < ApplicationRecord
|
||||
extend FriendlyId
|
||||
include PhotoCapable
|
||||
include Ownable
|
||||
include SearchHarvests
|
||||
include Likeable
|
||||
|
||||
attr_accessor :overall_rating
|
||||
|
||||
friendly_id :harvest_slug, use: %i(slugged finders)
|
||||
|
||||
# Constants
|
||||
@@ -155,10 +154,6 @@ class Harvest < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def self.homepage_records(limit)
|
||||
recent.one_per_owner.limit(limit)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def crop_must_match_planting
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class Photo < ApplicationRecord
|
||||
include Likeable
|
||||
include Ownable
|
||||
include SearchPhotos
|
||||
|
||||
PHOTO_CAPABLE = %w(Garden Planting Harvest Seed Post Crop).freeze
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ class Planting < ApplicationRecord
|
||||
include Ownable
|
||||
include PredictPlanting
|
||||
include PredictHarvest
|
||||
include SearchPlantings
|
||||
include Likeable
|
||||
|
||||
friendly_id :planting_slug, use: %i(slugged finders)
|
||||
@@ -132,10 +133,6 @@ class Planting < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def self.homepage_records(limit)
|
||||
recent.one_per_owner.limit(limit)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cannot_be_finished_and_failed
|
||||
|
||||
+1
-7
@@ -5,6 +5,7 @@ class Seed < ApplicationRecord
|
||||
include PhotoCapable
|
||||
include Finishable
|
||||
include Ownable
|
||||
include SearchSeeds
|
||||
|
||||
friendly_id :seed_slug, use: %i(slugged finders)
|
||||
|
||||
@@ -72,11 +73,4 @@ class Seed < ApplicationRecord
|
||||
def to_s
|
||||
I18n.t('seeds.string', crop: crop.name, owner:)
|
||||
end
|
||||
|
||||
def self.homepage_records(limit)
|
||||
current.tradable
|
||||
.where("plant_before IS NULL OR plant_before < ?", Time.zone.today)
|
||||
.order(created_at: :desc)
|
||||
.limit(limit)
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class CropSearchService
|
||||
# Crop.search(string)
|
||||
def self.search(query, page: 1, per_page: 12, current_member: nil, **options)
|
||||
def self.search(query, page: 1, per_page: 12, current_member: nil)
|
||||
search_params = {
|
||||
page:,
|
||||
per_page:,
|
||||
@@ -12,7 +12,7 @@ class CropSearchService
|
||||
includes: %i(scientific_names alternate_names),
|
||||
misspellings: { edit_distance: 2 },
|
||||
load: false
|
||||
}.merge(options)
|
||||
}
|
||||
# prioritise crops the member has planted
|
||||
search_params[:boost_where] = { planters_ids: current_member.id } if current_member
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
|
||||
class WikidataService
|
||||
CELSIUS_UNIT_ID = 'http://www.wikidata.org/entity/Q25267'
|
||||
FAHRENHEIT_UNIT_ID = 'http://www.wikidata.org/entity/Q42289'
|
||||
|
||||
def self.find_wikidata_id(location_name)
|
||||
return nil if location_name.blank?
|
||||
|
||||
uri = URI("https://www.wikidata.org/w/api.php?action=wbsearchentities&search=#{URI.encode_www_form_component(location_name)}&language=en&format=json")
|
||||
req = Net::HTTP::Get.new(uri)
|
||||
req['User-Agent'] = "Growstuff (https://www.growstuff.org; admin@growstuff.org)"
|
||||
|
||||
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
||||
http.request(req)
|
||||
end
|
||||
|
||||
data = JSON.parse(response.body)
|
||||
data.dig('search', 0, 'id')
|
||||
rescue StandardError => e
|
||||
Rails.logger.error "WikidataService.find_wikidata_id error: #{e.message}"
|
||||
nil
|
||||
end
|
||||
|
||||
def self.fetch_temps(wikidata_id)
|
||||
return {} if wikidata_id.blank?
|
||||
|
||||
uri = URI("https://www.wikidata.org/w/api.php?action=wbgetentities&ids=#{wikidata_id}&props=claims&format=json")
|
||||
req = Net::HTTP::Get.new(uri)
|
||||
req['User-Agent'] = "Growstuff (https://www.growstuff.org; admin@growstuff.org)"
|
||||
|
||||
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
||||
http.request(req)
|
||||
end
|
||||
|
||||
data = JSON.parse(response.body)
|
||||
claims = data.dig('entities', wikidata_id, 'claims') || {}
|
||||
|
||||
highest_temp = extract_temp(claims['P6591'])
|
||||
lowest_temp = extract_temp(claims['P7422'])
|
||||
|
||||
{
|
||||
highest_temp_c: highest_temp,
|
||||
lowest_temp_c: lowest_temp
|
||||
}
|
||||
rescue StandardError => e
|
||||
Rails.logger.error "WikidataService.fetch_temps error: #{e.message}"
|
||||
{}
|
||||
end
|
||||
|
||||
def self.extract_temp(claim_data)
|
||||
return nil if claim_data.blank?
|
||||
|
||||
# We take the first value
|
||||
main_snak = claim_data.first&.dig('mainsnak')
|
||||
return nil unless main_snak&.dig('datavalue', 'type') == 'quantity'
|
||||
|
||||
quantity_data = main_snak.dig('datavalue', 'value')
|
||||
amount = quantity_data['amount'].to_f
|
||||
unit = quantity_data['unit']
|
||||
|
||||
case unit
|
||||
when CELSIUS_UNIT_ID
|
||||
amount
|
||||
when FAHRENHEIT_UNIT_ID
|
||||
(amount - 32) * 5.0 / 9.0
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -17,7 +17,7 @@
|
||||
%li.list-group-item= link_to "View all #{crop.name} harvests", crop_harvests_path(crop), class: 'card-link'
|
||||
- if crop.approved?
|
||||
- if current_member
|
||||
%li.list-group-item= link_to "Harvest #{crop.name}", new_harvest_path(harvest: { crop_id: crop.id }), class: 'btn btn-block'
|
||||
%li.list-group-item= link_to "Harvest #{crop.name}", new_harvest_path(crop_id: crop.id), class: 'btn btn-block'
|
||||
- else
|
||||
%li.list-group-item.active
|
||||
= icon 'fas', 'user'
|
||||
|
||||
@@ -36,11 +36,10 @@
|
||||
= cute_icon
|
||||
= render 'predictions', crop: @crop
|
||||
- if @crop.all_companions.any?
|
||||
- cache [@crop, 'companions'] do
|
||||
%section.companions
|
||||
%h2 Companions
|
||||
- @crop.all_companions.each do |companion|
|
||||
= render 'crops/tiny', crop: companion
|
||||
%section.companions
|
||||
%h2 Companions
|
||||
- @crop.all_companions.each do |companion|
|
||||
= render 'crops/tiny', crop: companion
|
||||
|
||||
- if crop_or_parent(@crop, :en_youtube_url).present?
|
||||
%section.youtube
|
||||
|
||||
@@ -102,8 +102,6 @@
|
||||
%strong Collaborators:
|
||||
- if can?(:create, GardenCollaborator.new(garden: @garden))
|
||||
= link_to "Manage", garden_garden_collaborators_path(@garden)
|
||||
- elsif current_member.present? && (collab = @garden.garden_collaborators.find_by(member: current_member))
|
||||
= link_to "Leave garden", garden_garden_collaborator_path(@garden, collab), method: :delete, class: 'text-danger', data: { confirm: 'Are you sure you want to leave this garden?' }
|
||||
- if @garden.garden_collaborators.any?
|
||||
%ul
|
||||
- @garden.garden_collaborators.each do |collabator|
|
||||
@@ -124,31 +122,6 @@
|
||||
%strong Garden type:
|
||||
= @garden.garden_type.name
|
||||
|
||||
- if @garden.location_wikidata_id.present?
|
||||
%hr
|
||||
%p
|
||||
%small
|
||||
Data about this location from
|
||||
= link_to "wikidata", "https://www.wikidata.org/wiki/#{@garden.location_wikidata_id}", target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
%p
|
||||
%strong Highest temperature:
|
||||
- if @garden.highest_temp_c.present?
|
||||
= "#{ @garden.highest_temp_c.round(1) }°C"
|
||||
- else
|
||||
Not known
|
||||
%p
|
||||
%strong Lowest temperature:
|
||||
- if @garden.lowest_temp_c.present?
|
||||
= "#{ @garden.lowest_temp_c.round(1) }°C"
|
||||
- else
|
||||
Not known
|
||||
|
||||
- elsif can?(:edit, @garden) && @garden.location.present?
|
||||
.alert.alert-info
|
||||
%p Wikidata information is missing for this location.
|
||||
= button_to "Fetch Wikidata info", fetch_wikidata_garden_path(@garden), method: :post, class: 'btn btn-info btn-sm'
|
||||
|
||||
.card
|
||||
.card-header
|
||||
%h4 #{@garden.owner}'s gardens
|
||||
|
||||
@@ -52,17 +52,6 @@
|
||||
= f.select(:weight_unit, Harvest::WEIGHT_UNITS_VALUES, { include_blank: false }, class: 'form-control')
|
||||
= f.text_area :description, rows: 6, label: 'Notes'
|
||||
|
||||
- if @planting.present?
|
||||
.row
|
||||
.col-md-12
|
||||
= f.range_field :overall_rating, min: 1, max: 5, value: @planting.overall_rating, include_blank: 'Leave blank', label: 'Overall Rating - Planting', list: "rating-list", title: "How well is the planting going?"
|
||||
%datalist{"id": "rating-list"}
|
||||
%option{"value": "1"} Poor
|
||||
%option{"value": "2"}
|
||||
%option{"value": "3"}
|
||||
%option{"value": "4"}
|
||||
%option{"value": "5"} Great
|
||||
|
||||
.card-footer
|
||||
.text-right= f.submit 'Save'
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
.index-cards
|
||||
- harvest.crop.plant_parts.order(:name).each do |plant_part|
|
||||
.card
|
||||
= link_to new_harvest_path(harvest: {planting_id: harvest.planting_id, crop_id: harvest.crop_id, plant_part_id: plant_part.id}) do
|
||||
= link_to harvests_path(harvest: {planting_id: harvest.planting_id, crop_id: harvest.crop_id, plant_part_id: plant_part.id}), method: :post do
|
||||
.card-title.text-center
|
||||
%h3= plant_part_icon(plant_part.name)
|
||||
%h3= plant_part.name
|
||||
@@ -22,12 +22,10 @@
|
||||
%h6 All Plant parts
|
||||
%nav.nav
|
||||
- PlantPart.all.order(:name).each do |plant_part|
|
||||
= link_to new_harvest_path(harvest: {planting_id: harvest.planting_id, crop_id: harvest.crop_id, plant_part_id: plant_part.id}), class: 'nav-link border' do
|
||||
= link_to harvests_path(harvest: {planting_id: harvest.planting_id, crop_id: harvest.crop_id, plant_part_id: plant_part.id}), method: :post, class: 'nav-link border' do
|
||||
= plant_part_icon(plant_part.name)
|
||||
= plant_part
|
||||
|
||||
|
||||
|
||||
%a.btn#modalHarvestButton{"data-bs-target" => "#modelHarvestForm", "data-bs-toggle" => "modal", href: ""}
|
||||
= harvest_icon
|
||||
Record harvest
|
||||
@@ -1,12 +1,11 @@
|
||||
%h2= t('.recently_planted')
|
||||
- Planting.homepage_records(6).each do |planting|
|
||||
- next unless planting.thumbnail_url.present?
|
||||
= link_to planting_path(slug: planting.slug), class: 'list-group-item list-group-item-action flex-column align-items-start' do
|
||||
= link_to planting_path(slug: planting['slug']), class: 'list-group-item list-group-item-action flex-column align-items-start' do
|
||||
.d-flex.w-100.justify-content-between.homepage--list-item
|
||||
%p.mb-2
|
||||
= image_tag planting.thumbnail_url, width: 75, class: 'rounded shadow', alt: "Image of #{planting.crop_name} by #{planting.owner_login_name}"
|
||||
= image_tag planting['thumbnail_url'], width: 75, class: 'rounded shadow', alt: "Image of #{planting['crop_name']} by #{planting['owner_login_name']}"
|
||||
.text-right
|
||||
%h4= planting.crop_name
|
||||
- if planting.planted_from.present?
|
||||
%span.badge.badge-success= planting.planted_from.pluralize
|
||||
%small.text-muted planted by #{planting.owner_login_name}
|
||||
%h4= planting['crop_name']
|
||||
- if planting['planted_from'].present?
|
||||
%span.badge.badge-success= planting['planted_from'].pluralize
|
||||
%small.text-muted planted by #{planting['owner_login_name']}
|
||||
@@ -0,0 +1,23 @@
|
||||
- content_for :title, "Set your location"
|
||||
|
||||
%h1 Set your location
|
||||
|
||||
= form_for @member, url: update_location_member_path(@member), method: :put do |f|
|
||||
.form-group
|
||||
= f.label :location
|
||||
= f.text_field :location, class: 'form-control'
|
||||
|
||||
.form-group
|
||||
= f.label :latitude
|
||||
= f.text_field :latitude, class: 'form-control', readonly: true
|
||||
|
||||
.form-group
|
||||
= f.label :longitude
|
||||
= f.text_field :longitude, class: 'form-control', readonly: true
|
||||
|
||||
#map.set-location-map
|
||||
|
||||
.form-group
|
||||
%button#find-me.btn.btn-default Find my location
|
||||
|
||||
= f.submit 'Update location', class: 'btn btn-primary'
|
||||
@@ -65,6 +65,9 @@
|
||||
= link_to edit_member_registration_path, class: 'btn btn-block' do
|
||||
= member_icon
|
||||
= t('members.edit_profile')
|
||||
= link_to set_location_member_path(@member), class: 'btn btn-block' do
|
||||
= icon('fas', 'map-marker')
|
||||
Set location
|
||||
|
||||
- if can?(:create, Notification) && current_member != @member
|
||||
= link_to new_message_path(recipient_id: @member.id), class: 'btn btn-block' do
|
||||
|
||||
@@ -7,34 +7,34 @@ cal.description = "Plantings by #{@owner.login_name}"
|
||||
event = Icalendar::Event.new
|
||||
|
||||
lines = []
|
||||
lines << "Quantity: #{planting.quantity || 'unknown' }"
|
||||
lines << "Planted on: #{planting.planted_at || 'unknown' }"
|
||||
lines << "Sunniness: #{planting.sunniness || 'unknown' }"
|
||||
lines << "Planted from: #{planting.planted_from || 'unknown' }"
|
||||
lines << "First harvest from: #{planting.first_harvest_predicted_at || 'unknown' }"
|
||||
lines << "Last harvest from: #{planting.last_harvest_predicted_at || 'unknown' }"
|
||||
lines << "Finish predicted at: #{planting.finish_predicted_at || 'unknown'}"
|
||||
lines << "Finished at: #{planting.finished_at || 'unknown' }"
|
||||
lines << "Quantity: #{planting['quantity'] ? planting['quantity'] : 'unknown' }"
|
||||
lines << "Planted on: #{planting['planted_at'] ? planting['planted_at'] : 'unknown' }"
|
||||
lines << "Sunniness: #{planting['sunniness'] ? planting['sunniness'] : 'unknown' }"
|
||||
lines << "Planted from: #{planting['planted_from'] ? planting['planted_from'] : 'unknown' }"
|
||||
lines << "First harvest from: #{planting['first_harvest_predicted_at'] ? planting['first_harvest_predicted_at'] : 'unknown' }"
|
||||
lines << "Last harvest from: #{planting['last_harvest_predicted_at'] ? planting['last_harvest_predicted_at'] : 'unknown' }"
|
||||
lines << "Finish predicted at: #{planting['finish_predicted_at'] ? planting['finish_predicted_at'] : 'unknown'}"
|
||||
lines << "Finished at: #{planting['finished_at'] ? planting['finished_at'] : 'unknown' }"
|
||||
|
||||
lines << planting.description
|
||||
finish_date = (planting.finished_at || planting.finish_predicted_at || planting.last_harvest_predicted_at)&.to_date
|
||||
finish_date = Date.parse(planting['finished_at'] || planting['finish_predicted_at'] || planting['last_harvest_predicted_at']) rescue nil
|
||||
|
||||
event.dtstart = planting.created_at
|
||||
event.dtend = finish_date || 1.day.from_now.to_date
|
||||
event.summary = planting.crop.name
|
||||
event.dtstart = Time.at(planting['created_at'])
|
||||
event.dtend = finish_date || 1.day.from_now
|
||||
event.summary = planting['crop_name']
|
||||
event.description = lines.join("\n")
|
||||
event.ip_class = "PUBLIC"
|
||||
event.url = planting_url(slug: planting.slug)
|
||||
event.url = planting_url(slug: planting['slug'])
|
||||
|
||||
cal.add_event(event)
|
||||
|
||||
if finish_date && finish_date > Date.today
|
||||
predicted_date = planting.first_harvest_predicted_at&.to_date
|
||||
predicted_date = Date.parse(planting['first_harvest_predicted_at']) if planting['first_harvest_predicted_at']
|
||||
|
||||
todo = Icalendar::Todo.new
|
||||
todo.dtstart = predicted_date || finish_date || Date.today
|
||||
todo.due = finish_date
|
||||
todo.summary = "Harvest #{planting.crop.name}"
|
||||
todo.summary = "Harvest #{planting['crop_name']}"
|
||||
|
||||
cal.add_todo(todo)
|
||||
|
||||
@@ -42,9 +42,9 @@ cal.description = "Plantings by #{@owner.login_name}"
|
||||
event = Icalendar::Event.new
|
||||
event.dtstart = predicted_date || finish_date || Date.today
|
||||
event.dtend = finish_date
|
||||
event.summary = "Harvest #{planting.crop.name}"
|
||||
event.summary = "Harvest #{planting['crop_name']}"
|
||||
event.ip_class = "PUBLIC"
|
||||
event.url = planting_url(slug: planting.slug)
|
||||
event.url = planting_url(slug: planting['slug'])
|
||||
|
||||
cal.add_event(event)
|
||||
end
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
%link= plantings_url
|
||||
- @plantings.each do |planting|
|
||||
%item
|
||||
%title #{planting.crop_name} in #{planting.location}
|
||||
%pubdate= planting.created_at.to_fs(:rfc822)
|
||||
%title #{planting['crop_name']} in #{planting['location']}
|
||||
%pubdate= planting['created_at'].to_fs(:rfc822)
|
||||
%description
|
||||
:escaped
|
||||
<p>Quantity: #{planting.quantity ? planting.quantity : 'unknown' }</p>
|
||||
<p>Planted on: #{planting.planted_at ? planting.planted_at : 'unknown' }</p>
|
||||
<p>Sunniness: #{planting.sunniness ? planting.sunniness : 'unknown' }</p>
|
||||
<p>Planted from: #{planting.planted_from ? planting.planted_from : 'unknown' }</p>
|
||||
<p>Quantity: #{planting['quantity'] ? planting['quantity'] : 'unknown' }</p>
|
||||
<p>Planted on: #{planting['planted_at'] ? planting['planted_at'] : 'unknown' }</p>
|
||||
<p>Sunniness: #{planting['sunniness'] ? planting['sunniness'] : 'unknown' }</p>
|
||||
<p>Planted from: #{planting['planted_from'] ? planting['planted_from'] : 'unknown' }</p>
|
||||
:escaped_markdown
|
||||
#{ strip_tags markdownify(planting.description) }
|
||||
%link= planting_url(slug: planting.slug)
|
||||
%guid= planting_url(slug: planting.slug)
|
||||
#{ strip_tags markdownify(planting['description']) }
|
||||
%link= planting_url(slug: planting['slug'])
|
||||
%guid= planting_url(slug: planting['slug'])
|
||||
@@ -17,6 +17,6 @@
|
||||
<p>Heirloom? #{seed.heirloom}</p>
|
||||
- if seed.tradable
|
||||
:escaped
|
||||
<p>Will trade #{seed.tradable_to} from #{seed.location || 'unknown location'}</p>
|
||||
<p>Will trade #{seed.tradable_to} from #{seed.location ||= 'unknown location'}</p>
|
||||
%link= seed_url(slug: seed.slug)
|
||||
%guid= seed_url(slug: seed.slug)
|
||||
@@ -3,4 +3,4 @@
|
||||
%button.close{ type: "button", "data-bs-dismiss" => "alert" }
|
||||
%span{ "aria-hidden" => true } ×
|
||||
%span.sr-only Close
|
||||
= sanitize(content)
|
||||
= content
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
# explicit rubocop config increases performance slightly while avoiding config confusion.
|
||||
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
|
||||
|
||||
load Gem.bin_path("rubocop", "rubocop")
|
||||
@@ -14,11 +14,7 @@ Bundler.require(*Rails.groups)
|
||||
module Growstuff
|
||||
class Application < Rails::Application
|
||||
# Initialize configuration defaults for originally generated Rails version.
|
||||
config.load_defaults 8.0
|
||||
|
||||
# Rails 7.1+ requires a coder for `serialize`.
|
||||
# We set YAML as the default for compatibility with gems like Mexican Sofa.
|
||||
config.active_record.default_column_serializer = YAML
|
||||
config.load_defaults 7.0
|
||||
|
||||
I18n.config.enforce_available_locales = true
|
||||
|
||||
|
||||
@@ -27,11 +27,6 @@ class Rack::Attack
|
||||
end
|
||||
end
|
||||
|
||||
# Abusive services
|
||||
blocklist('block Semrush crawler') do |request|
|
||||
request.user_agent.to_s.downcase.include?('semrush')
|
||||
end
|
||||
|
||||
### Custom Response Headers ###
|
||||
|
||||
# Add Retry-After header to throttled responses
|
||||
|
||||
+4
-2
@@ -32,7 +32,6 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :gardens, concerns: :has_photos, param: :slug do
|
||||
get 'timeline' => 'charts/gardens#timeline', constraints: { format: 'json' }
|
||||
post 'fetch_wikidata' => 'gardens#fetch_wikidata', on: :member
|
||||
|
||||
resources :garden_collaborators
|
||||
end
|
||||
@@ -114,6 +113,10 @@ Rails.application.routes.draw do
|
||||
resources :timeline
|
||||
|
||||
resources :members, param: :slug do
|
||||
member do
|
||||
get :set_location
|
||||
put :update_location
|
||||
end
|
||||
resources :gardens
|
||||
resources :seeds
|
||||
resources :plantings
|
||||
@@ -158,7 +161,6 @@ Rails.application.routes.draw do
|
||||
namespace :api do
|
||||
namespace :v1 do
|
||||
jsonapi_resources :activities
|
||||
get "crops/search", to: "crops#search"
|
||||
jsonapi_resources :crops
|
||||
jsonapi_resources :gardens
|
||||
jsonapi_resources :harvests
|
||||
|
||||
@@ -4,6 +4,12 @@ class ElasticIndexing < ActiveRecord::Migration[5.2]
|
||||
def up
|
||||
say 'indexing crops'
|
||||
Crop.reindex
|
||||
say 'indexing plantings'
|
||||
Planting.reindex
|
||||
say 'indexing seeds'
|
||||
Seed.reindex
|
||||
say 'indexing harvests'
|
||||
Harvest.reindex
|
||||
say 'indexing photos'
|
||||
Photo.reindex
|
||||
end
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
class AddWikidataAndTempsToGardens < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
add_column :gardens, :location_wikidata_id, :string
|
||||
add_column :gardens, :lowest_temp_c, :float
|
||||
add_column :gardens, :highest_temp_c, :float
|
||||
end
|
||||
end
|
||||
+2
-5
@@ -10,9 +10,9 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_04_29_132911) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_04_29_132911) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
enable_extension "plpgsql"
|
||||
|
||||
create_table "active_storage_attachments", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
@@ -631,9 +631,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_29_132911) do
|
||||
t.decimal "area"
|
||||
t.string "area_unit"
|
||||
t.integer "garden_type_id"
|
||||
t.string "location_wikidata_id"
|
||||
t.float "lowest_temp_c"
|
||||
t.float "highest_temp_c"
|
||||
t.index ["garden_type_id"], name: "index_gardens_on_garden_type_id"
|
||||
t.index ["owner_id"], name: "index_gardens_on_owner_id"
|
||||
t.index ["slug"], name: "index_gardens_on_slug", unique: true
|
||||
|
||||
@@ -4,5 +4,8 @@ namespace :search do
|
||||
desc 'reindex'
|
||||
task reindex: :environment do
|
||||
Crop.reindex
|
||||
Planting.reindex
|
||||
Harvest.reindex
|
||||
Seed.reindex
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe HarvestsController do
|
||||
describe HarvestsController, :search do
|
||||
login_member
|
||||
|
||||
def valid_attributes
|
||||
@@ -22,12 +22,14 @@ describe HarvestsController do
|
||||
let!(:tomato_harvest) { create(:harvest, owner_id: first_member.id, crop_id: tomato.id) }
|
||||
let!(:maize_harvest) { create(:harvest, owner_id: second_member.id, crop_id: maize.id) }
|
||||
|
||||
before { Harvest.reindex }
|
||||
|
||||
describe "assigns all harvests as @harvests" do
|
||||
before { get :index, params: {} }
|
||||
|
||||
it { expect(assigns(:harvests).size).to eq 2 }
|
||||
it { expect(assigns(:harvests)).to include(tomato_harvest) }
|
||||
it { expect(assigns(:harvests)).to include(maize_harvest) }
|
||||
it { expect(assigns(:harvests)[0].slug).to eq tomato_harvest.slug }
|
||||
it { expect(assigns(:harvests)[1].slug).to eq maize_harvest.slug }
|
||||
end
|
||||
|
||||
describe "picks up owner from params and shows owner's harvests only" do
|
||||
@@ -113,21 +115,6 @@ describe HarvestsController do
|
||||
|
||||
it { expect(Harvest.last.planting.id).to eq(planting.id) }
|
||||
end
|
||||
|
||||
describe "updates planting rating" do
|
||||
let(:planting) { create(:planting, owner_id: member.id, garden: member.gardens.first) }
|
||||
|
||||
it "updates the planting rating when provided" do
|
||||
post :create, params: {
|
||||
harvest: valid_attributes.merge(
|
||||
planting_id: planting.id,
|
||||
crop_id: planting.crop_id,
|
||||
overall_rating: 4
|
||||
)
|
||||
}
|
||||
expect(planting.reload.overall_rating).to eq(4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with invalid params" do
|
||||
@@ -184,18 +171,6 @@ describe HarvestsController do
|
||||
|
||||
it { expect(response).to redirect_to(harvest) }
|
||||
end
|
||||
|
||||
describe "updates planting rating" do
|
||||
let(:planting) { create(:planting, owner_id: member.id, garden: member.gardens.first) }
|
||||
let(:harvest) do
|
||||
create(:harvest, valid_attributes.merge(planting_id: planting.id, crop_id: planting.crop_id))
|
||||
end
|
||||
|
||||
it "updates the planting rating when provided" do
|
||||
put :update, params: { slug: harvest.to_param, harvest: { overall_rating: 3 } }
|
||||
expect(planting.reload.overall_rating).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with invalid params" do
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe PhotosController do
|
||||
describe PhotosController, :search do
|
||||
login_member
|
||||
|
||||
describe 'GET index' do
|
||||
describe 'all photos' do
|
||||
let!(:photo) { create(:photo) }
|
||||
let!(:photo) { create(:photo, :reindex) }
|
||||
|
||||
before do
|
||||
Photo.reindex
|
||||
get :index
|
||||
end
|
||||
|
||||
@@ -20,23 +21,28 @@ describe PhotosController do
|
||||
end
|
||||
|
||||
describe '#index crop photos' do
|
||||
let!(:photo) { create(:photo, owner: member, title: 'no assocations photo') }
|
||||
let!(:crop_photo) { create(:photo, owner: member, title: 'photos of planting') }
|
||||
let!(:crop) { create(:crop) }
|
||||
let!(:planting) { create(:planting, crop:, owner: member) }
|
||||
let!(:photo) { create(:photo, :reindex, owner: member, title: 'no assocations photo') }
|
||||
let!(:crop_photo) { create(:photo, :reindex, owner: member, title: 'photos of planting') }
|
||||
let!(:planting) { create(:planting, :reindex, crop:, owner: member) }
|
||||
let!(:crop) { create(:crop, :reindex) }
|
||||
|
||||
before do
|
||||
planting.photos << crop_photo
|
||||
Photo.reindex
|
||||
get :index, params: { crop_slug: crop.to_param }
|
||||
end
|
||||
|
||||
describe "find photos by crop" do
|
||||
it "has indexed the photos of this crop" do
|
||||
expect(Photo.search).to include crop_photo
|
||||
end
|
||||
|
||||
it "assigns crop" do
|
||||
expect(assigns(:crop)).to eq crop
|
||||
end
|
||||
|
||||
it { expect(assigns(:photos).size).to eq 1 }
|
||||
it { expect(assigns(:photos).first.crops).to include crop }
|
||||
it { expect(assigns(:photos).first.crops).to include crop.id }
|
||||
it { expect(assigns(:photos).first.id).to eq crop_photo.id }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe PlantingsController do
|
||||
describe PlantingsController, :search do
|
||||
login_member
|
||||
|
||||
def valid_attributes
|
||||
@@ -12,7 +12,7 @@ describe PlantingsController do
|
||||
}
|
||||
end
|
||||
|
||||
describe "GET index" do
|
||||
describe "GET index", :search do
|
||||
let!(:first_member) { create(:member) }
|
||||
let!(:second_member) { create(:member) }
|
||||
let!(:tomato) { create(:tomato) }
|
||||
@@ -20,12 +20,16 @@ describe PlantingsController do
|
||||
let!(:tomato_planting) { create(:planting, crop: tomato, owner: first_member, created_at: 1.day.ago) }
|
||||
let!(:maize_planting) { create(:planting, crop: maize, owner: second_member, created_at: 5.days.ago) }
|
||||
|
||||
before do
|
||||
Planting.reindex
|
||||
end
|
||||
|
||||
describe "assigns all plantings as @plantings" do
|
||||
before { get :index }
|
||||
|
||||
it { expect(assigns(:plantings).size).to eq 2 }
|
||||
it { expect(assigns(:plantings)[0].slug).to eq tomato_planting.slug }
|
||||
it { expect(assigns(:plantings)[1].slug).to eq maize_planting.slug }
|
||||
it { expect(assigns(:plantings)[0]['slug']).to eq tomato_planting.slug }
|
||||
it { expect(assigns(:plantings)[1]['slug']).to eq maize_planting.slug }
|
||||
end
|
||||
|
||||
describe "picks up owner from params and shows owner's plantings only" do
|
||||
@@ -33,14 +37,14 @@ describe PlantingsController do
|
||||
|
||||
it { expect(assigns(:owner)).to eq first_member }
|
||||
it { expect(assigns(:plantings).size).to eq 1 }
|
||||
it { expect(assigns(:plantings).first.slug).to eq tomato_planting.slug }
|
||||
it { expect(assigns(:plantings).first['slug']).to eq tomato_planting.slug }
|
||||
end
|
||||
|
||||
describe "picks up crop from params and shows the plantings for the crop only" do
|
||||
before { get :index, params: { crop_slug: maize.slug } }
|
||||
|
||||
it { expect(assigns(:crop)).to eq maize }
|
||||
it { expect(assigns(:plantings).first.slug).to eq maize_planting.slug }
|
||||
it { expect(assigns(:plantings).first['slug']).to eq maize_planting.slug }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe SeedsController do
|
||||
describe SeedsController, :search do
|
||||
let(:owner) { create(:member) }
|
||||
|
||||
describe "GET index" do
|
||||
describe "picks up owner from params" do
|
||||
before do
|
||||
Seed.reindex
|
||||
get :index, params: { member_slug: owner.slug }
|
||||
end
|
||||
|
||||
@@ -24,6 +25,7 @@ describe SeedsController do
|
||||
let!(:planting) { create(:planting, owner:) }
|
||||
|
||||
before do
|
||||
Seed.reindex
|
||||
get :new, params: { planting_slug: planting.to_param }
|
||||
end
|
||||
|
||||
|
||||
@@ -29,5 +29,8 @@ FactoryBot.define do
|
||||
end
|
||||
|
||||
trait :reindex do
|
||||
after(:create) do |harvest, _evaluator|
|
||||
harvest.reindex(refresh: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -19,5 +19,10 @@ FactoryBot.define do
|
||||
license_url { nil }
|
||||
end
|
||||
|
||||
trait :reindex do
|
||||
after(:create) do |photo, _evaluator|
|
||||
photo.reindex(refresh: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -68,5 +68,11 @@ FactoryBot.define do
|
||||
planting.save
|
||||
end
|
||||
end
|
||||
|
||||
trait :reindex do
|
||||
after(:create) do |planting, _evaluator|
|
||||
planting.reindex(refresh: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -31,5 +31,11 @@ FactoryBot.define do
|
||||
factory :untradable_seed do
|
||||
tradable_to { "nowhere" }
|
||||
end
|
||||
|
||||
trait :reindex do
|
||||
after(:create) do |seed, _evaluator|
|
||||
seed.reindex(refresh: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe "browse harvests" do
|
||||
describe "browse harvests", :search do
|
||||
subject { page }
|
||||
|
||||
let!(:harvest) { create(:harvest, owner: member) }
|
||||
@@ -11,9 +11,10 @@ describe "browse harvests" do
|
||||
include_context 'signed in member'
|
||||
|
||||
describe 'blank optional fields' do
|
||||
let!(:harvest) { create(:harvest, :no_description) }
|
||||
let!(:harvest) { create(:harvest, :no_description, :reindex) }
|
||||
|
||||
before do
|
||||
Harvest.reindex
|
||||
visit harvests_path
|
||||
end
|
||||
|
||||
@@ -23,9 +24,10 @@ describe "browse harvests" do
|
||||
end
|
||||
|
||||
describe "filled in optional fields" do
|
||||
let!(:harvest) { create(:harvest, :long_description) }
|
||||
let!(:harvest) { create(:harvest, :long_description, :reindex) }
|
||||
|
||||
before do
|
||||
Harvest.reindex
|
||||
visit harvests_path
|
||||
end
|
||||
|
||||
|
||||
@@ -54,8 +54,6 @@ describe "Harvesting a crop", :js, :search do
|
||||
visit crop_path(maize)
|
||||
click_link "Record harvest"
|
||||
click_link plant_part.name
|
||||
# We then navigate to the new_harvest_path, and save.
|
||||
click_button "Save"
|
||||
end
|
||||
|
||||
it { expect(page).to have_content "harvest was successfully created." }
|
||||
@@ -71,22 +69,9 @@ describe "Harvesting a crop", :js, :search do
|
||||
click_link plant_part.name
|
||||
end
|
||||
|
||||
it "saves" do
|
||||
# We then navigate to the new_harvest_path, and save.
|
||||
click_button "Save"
|
||||
|
||||
expect(page).to have_content "harvest was successfully created."
|
||||
expect(page).to have_content planting.garden.name
|
||||
expect(page).to have_content "maize"
|
||||
end
|
||||
|
||||
it "updates the planting rating" do
|
||||
find_by_id('harvest_overall_rating').set 4
|
||||
click_button "Save"
|
||||
|
||||
expect(page).to have_content "harvest was successfully created."
|
||||
expect(planting.reload.overall_rating).to eq 4
|
||||
end
|
||||
it { expect(page).to have_content "harvest was successfully created." }
|
||||
it { expect(page).to have_content planting.garden.name }
|
||||
it { expect(page).to have_content "maize" }
|
||||
end
|
||||
|
||||
context "Editing a harvest" do
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe "home page" do
|
||||
describe "home page", :search do
|
||||
subject { page }
|
||||
|
||||
let(:member) { create(:member) }
|
||||
@@ -14,9 +14,9 @@ describe "home page" do
|
||||
let(:seed) { create(:tradable_seed, owner: member, crop:) }
|
||||
let(:harvest) { create(:harvest, owner: member, crop:) }
|
||||
|
||||
let!(:tradable_seed) { create(:tradable_seed, finished: false) }
|
||||
let!(:finished_seed) { create(:tradable_seed, finished: true) }
|
||||
let!(:untradable_seed) { create(:untradable_seed) }
|
||||
let!(:tradable_seed) { create(:tradable_seed, :reindex, finished: false) }
|
||||
let!(:finished_seed) { create(:tradable_seed, :reindex, finished: true) }
|
||||
let!(:untradable_seed) { create(:untradable_seed, :reindex) }
|
||||
|
||||
before do
|
||||
# Add photos, so they can appear on home page
|
||||
@@ -25,6 +25,10 @@ describe "home page" do
|
||||
harvest.photos << photo
|
||||
|
||||
Crop.reindex
|
||||
Planting.reindex
|
||||
Seed.reindex
|
||||
Harvest.reindex
|
||||
Photo.reindex
|
||||
|
||||
visit root_path
|
||||
end
|
||||
|
||||
@@ -6,9 +6,13 @@ describe 'Likeable', :js, :search do
|
||||
let(:another_member) { create(:london_member) }
|
||||
let!(:post) { create(:post, :reindex, author: member) }
|
||||
let!(:activity) { create(:activity, :reindex, owner: member) }
|
||||
let!(:photo) { create(:photo, owner: member) }
|
||||
let!(:photo) { create(:photo, :reindex, owner: member) }
|
||||
let!(:harvest) { create(:harvest, :reindex, owner: member) }
|
||||
let!(:planting) { create(:planting, owner: member) }
|
||||
let!(:planting) { create(:planting, :reindex, owner: member) }
|
||||
|
||||
before do
|
||||
Photo.reindex
|
||||
end
|
||||
|
||||
include_context 'signed in member'
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.feature 'Set location', type: :feature do
|
||||
let(:member) { FactoryBot.create(:member) }
|
||||
|
||||
before do
|
||||
login_as(member, scope: :member)
|
||||
end
|
||||
|
||||
scenario 'member sets their location by clicking on the map', js: true do
|
||||
visit set_location_member_path(member)
|
||||
|
||||
# Test clicking on the map
|
||||
page.execute_script("map.fire('click', { latlng: L.latLng(40.7128, -74.0060) })")
|
||||
expect(find('#member_latitude').value).to eq('40.7128')
|
||||
expect(find('#member_longitude').value).to eq('-74.006')
|
||||
|
||||
# Mock geocoding
|
||||
geocoder_result = instance_double('Geocoder::Result::Nominatim',
|
||||
city: 'New York',
|
||||
town: nil,
|
||||
village: nil,
|
||||
hamlet: nil)
|
||||
allow(Geocoder).to receive(:search).with([40.71, -74.01]).and_return([geocoder_result])
|
||||
|
||||
click_button 'Update location'
|
||||
|
||||
expect(page).to have_content('Location updated.')
|
||||
member.reload
|
||||
expect(member.location).to eq('New York')
|
||||
expect(member.latitude).to eq(40.71)
|
||||
expect(member.longitude).to eq(-74.01)
|
||||
end
|
||||
|
||||
scenario 'member uses "Find my location"', js: true do
|
||||
visit set_location_member_path(member)
|
||||
|
||||
# Mock browser's geolocation
|
||||
page.execute_script("
|
||||
navigator.geolocation.getCurrentPosition = function(success) {
|
||||
var position = { coords: { latitude: 34.0522, longitude: -118.2437 } };
|
||||
success(position);
|
||||
}
|
||||
")
|
||||
|
||||
click_button 'Find my location'
|
||||
|
||||
expect(find('#member_latitude').value).to eq('34.0522')
|
||||
expect(find('#member_longitude').value).to eq('-118.2437')
|
||||
|
||||
# Mock geocoding
|
||||
geocoder_result = instance_double('Geocoder::Result::Nominatim',
|
||||
city: 'Los Angeles',
|
||||
town: nil,
|
||||
village: nil,
|
||||
hamlet: nil)
|
||||
allow(Geocoder).to receive(:search).with([34.05, -118.24]).and_return([geocoder_result])
|
||||
|
||||
click_button 'Update location'
|
||||
|
||||
expect(page).to have_content('Location updated.')
|
||||
member.reload
|
||||
expect(member.location).to eq('Los Angeles')
|
||||
expect(member.latitude).to eq(34.05)
|
||||
expect(member.longitude).to eq(-118.24)
|
||||
end
|
||||
|
||||
scenario 'geocoding fails', js: true do
|
||||
visit set_location_member_path(member)
|
||||
|
||||
page.execute_script("map.fire('click', { latlng: L.latLng(1.2345, 6.7890) })")
|
||||
|
||||
allow(Geocoder).to receive(:search).with([1.23, 6.79]).and_return([])
|
||||
|
||||
click_button 'Update location'
|
||||
|
||||
expect(page).to have_content('Location updated.')
|
||||
member.reload
|
||||
expect(member.location).to eq('Location near 1.23, 6.79')
|
||||
expect(member.latitude).to eq(1.23)
|
||||
expect(member.longitude).to eq(6.79)
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,8 @@ describe "Planting a crop", :js, :search do
|
||||
create(:planting, garden:, owner: member, planted_at: Date.parse("2013-03-10"))
|
||||
end
|
||||
|
||||
before { Planting.reindex }
|
||||
|
||||
context 'signed in' do
|
||||
include_context 'signed in member'
|
||||
before { visit new_planting_path }
|
||||
@@ -221,6 +223,9 @@ describe "Planting a crop", :js, :search do
|
||||
expect(page).to have_content "Aug 2014"
|
||||
expect(page).to have_content "4/5"
|
||||
|
||||
# ensure we've indexed in elastic search
|
||||
planting.reindex(refresh: true)
|
||||
|
||||
# shouldn't be on the page
|
||||
visit plantings_path
|
||||
expect(page).to have_no_content "maize"
|
||||
|
||||
@@ -9,6 +9,7 @@ describe 'Plantings RSS feed' do
|
||||
end
|
||||
|
||||
it 'The index title is what we expect' do
|
||||
Planting.reindex
|
||||
visit plantings_path(format: 'rss')
|
||||
expect(page).to have_content "Recent plantings from " \
|
||||
"#{@owner || 'all members'} (#{ENV.fetch('GROWSTUFF_SITE_NAME', nil)})"
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require 'cancan/matchers'
|
||||
|
||||
describe Ability do
|
||||
let(:member) { create(:member) }
|
||||
let(:ability) { described_class.new(member) }
|
||||
|
||||
context 'garden collaborators' do
|
||||
let(:garden) { create(:garden) }
|
||||
let(:garden_collaborator) { create(:garden_collaborator, garden: garden, member: member) }
|
||||
let(:other_member) { create(:member) }
|
||||
let(:other_garden_collaborator) { create(:garden_collaborator, garden: garden, member: other_member) }
|
||||
|
||||
it 'can remove themselves as a collaborator' do
|
||||
expect(ability).to be_able_to(:destroy, garden_collaborator)
|
||||
end
|
||||
|
||||
it 'cannot remove others as a collaborator if not garden owner' do
|
||||
expect(ability).not_to be_able_to(:destroy, other_garden_collaborator)
|
||||
end
|
||||
|
||||
context 'as garden owner' do
|
||||
let(:garden) { create(:garden, owner: member) }
|
||||
|
||||
it 'can remove others as a collaborator' do
|
||||
expect(ability).to be_able_to(:destroy, other_garden_collaborator)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -148,19 +148,6 @@ describe Harvest do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.homepage_records' do
|
||||
it 'returns unique harvests per owner' do
|
||||
member1 = create(:member)
|
||||
member2 = create(:member)
|
||||
create(:harvest, owner: member1, created_at: 1.day.ago)
|
||||
h2 = create(:harvest, owner: member1, created_at: 1.hour.ago)
|
||||
h3 = create(:harvest, owner: member2, created_at: 2.hours.ago)
|
||||
|
||||
records = described_class.homepage_records(5)
|
||||
expect(records).to contain_exactly(h2, h3)
|
||||
end
|
||||
end
|
||||
|
||||
context "stringification" do
|
||||
let(:crop) { create(:crop, name: "apricot") }
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Photo do
|
||||
let(:photo) { create(:photo, owner: member) }
|
||||
let(:photo) { create(:photo, :reindex, owner: member) }
|
||||
let(:member) { create(:member) }
|
||||
|
||||
it_behaves_like "it is likeable"
|
||||
@@ -237,4 +237,36 @@ describe Photo do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Elastic search indexing', :search do
|
||||
let!(:planting) { create(:planting, :reindex, owner: photo.owner) }
|
||||
let!(:crop) { create(:crop, :reindex) }
|
||||
|
||||
before do
|
||||
planting.photos << photo
|
||||
described_class.reindex
|
||||
described_class.searchkick_index.refresh
|
||||
end
|
||||
|
||||
describe "finds all photos in search index" do
|
||||
it "finds just one" do
|
||||
expect(described_class.search.count).to eq 1
|
||||
end
|
||||
|
||||
it "finds the matching photo" do
|
||||
expect(described_class.search).to include photo
|
||||
end
|
||||
|
||||
it "retrieves crops from ES" do
|
||||
expect(described_class.search(load: false).first.crops).to eq [planting.crop.id]
|
||||
end
|
||||
end
|
||||
|
||||
it "finds photos by owner in search index" do
|
||||
expect(described_class.search(where: { owner_id: planting.owner_id })).to include photo
|
||||
end
|
||||
|
||||
it "finds photos by crop in search index" do
|
||||
expect(described_class.search(where: { crops: planting.crop.id })).to include photo
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -596,14 +596,16 @@ describe Planting do
|
||||
it { expect(member.plantings.active).not_to include(failed_planting) }
|
||||
end
|
||||
|
||||
describe 'homepage' do
|
||||
describe 'homepage', :search do
|
||||
subject { described_class.homepage_records(100) }
|
||||
|
||||
let!(:interesting_planting) { create(:planting, :with_photo) }
|
||||
let!(:finished_interesting_planting) { create(:finished_planting, :with_photo) }
|
||||
let!(:planting) { create(:planting) }
|
||||
let!(:interesting_planting) { create(:planting, :reindex, :with_photo) }
|
||||
let!(:finished_interesting_planting) { create(:finished_planting, :reindex, :with_photo) }
|
||||
let!(:planting) { create(:planting, :reindex) }
|
||||
|
||||
before { described_class.reindex }
|
||||
|
||||
it { expect(subject.count).to eq 3 }
|
||||
it { expect(subject.map(&:id)).to include(interesting_planting.id, finished_interesting_planting.id, planting.id) }
|
||||
it { expect(subject.map(&:id)).to eq([interesting_planting.id.to_s, finished_interesting_planting.id.to_s, planting.id.to_s]) }
|
||||
end
|
||||
end
|
||||
@@ -213,14 +213,16 @@ describe Seed do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'homepage' do
|
||||
describe 'homepage', :search do
|
||||
subject { described_class.homepage_records(100) }
|
||||
|
||||
let!(:tradable_seed) { create(:tradable_seed, finished: false) }
|
||||
let!(:finished_seed) { create(:tradable_seed, finished: true) }
|
||||
let!(:untradable_seed) { create(:untradable_seed) }
|
||||
let!(:tradable_seed) { create(:tradable_seed, :reindex, finished: false) }
|
||||
let!(:finished_seed) { create(:tradable_seed, :reindex, finished: true) }
|
||||
let!(:untradable_seed) { create(:untradable_seed, :reindex) }
|
||||
|
||||
before { described_class.reindex }
|
||||
|
||||
it { expect(subject.count).to eq 1 }
|
||||
it { expect(subject.first.id).to eq tradable_seed.id }
|
||||
it { expect(subject.first.id).to eq tradable_seed.id.to_s }
|
||||
end
|
||||
end
|
||||
@@ -1,35 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Crops Search' do
|
||||
subject { JSON.parse response.body }
|
||||
|
||||
let(:headers) { { 'Accept' => 'application/vnd.api+json' } }
|
||||
let!(:cabbage) { create(:crop, name: 'Cabbage', approval_status: 'approved') }
|
||||
let!(:apple) { create(:crop, name: 'Apple', approval_status: 'approved') }
|
||||
|
||||
describe 'GET /api/v1/crops/search' do
|
||||
before do
|
||||
Crop.reindex
|
||||
end
|
||||
|
||||
it 'returns crops matching the search term' do
|
||||
get '/api/v1/crops/search', params: { term: 'Cabbage' }, headers: headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(subject['data'].size).to eq(1)
|
||||
expect(subject['data'].first['attributes']['name']).to eq('Cabbage')
|
||||
end
|
||||
|
||||
it 'returns empty data if no crops match' do
|
||||
get '/api/v1/crops/search', params: { term: 'NonExistent' }, headers: headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(subject['data']).to be_empty
|
||||
end
|
||||
|
||||
it 'includes meta information' do
|
||||
get '/api/v1/crops/search', params: { term: 'Cabbage' }, headers: headers
|
||||
expect(subject['meta']).to include('record_count' => 1, 'page_count' => 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -15,7 +15,7 @@ describe "Plantings" do
|
||||
before do
|
||||
@member = create(:interesting_member)
|
||||
|
||||
@predictable_planting = create(:predictable_planting, owner: @member, planted_at: 1.day.ago, days_to_first_harvest: 10,
|
||||
@predictable_planting = create(:predictable_planting, owner: @member, planted_at: 1.days.ago, days_to_first_harvest: 10,
|
||||
days_to_last_harvest: 20)
|
||||
@predictable_planting.crop.update(median_days_to_first_harvest: 10)
|
||||
|
||||
@@ -24,6 +24,7 @@ days_to_last_harvest: 20)
|
||||
@finished_planting = create(:finished_planting, owner: @member)
|
||||
@annual_planting = create(:annual_planting, owner: @member)
|
||||
@perennial_planting = create(:perennial_planting, owner: @member)
|
||||
Planting.reindex
|
||||
end
|
||||
|
||||
describe "GET /members/x/plantings.ics" do
|
||||
@@ -33,17 +34,19 @@ days_to_last_harvest: 20)
|
||||
calendar = Icalendar::Parser.new(response.body, true).parse.first
|
||||
expect(calendar.description[0].to_s).to eq "Plantings by #{@member.login_name}"
|
||||
events = calendar.events
|
||||
expect(events.length).to eq 7
|
||||
expect(events.length).to eq 7 # There are 8, but finished plantings aren't included
|
||||
|
||||
predictable_event = events.find { |e| e.dtend.to_date == @predictable_planting.finish_predicted_at }
|
||||
expect(predictable_event.summary.to_s).to include @predictable_planting.crop.name
|
||||
expect(predictable_event.dtstart.to_datetime.to_i).to be_within(1.second).of @predictable_planting.created_at.to_i
|
||||
# TODO: Better date comparison
|
||||
# Predicted finish should be used
|
||||
expect(events[1].summary.to_s).to include @predictable_planting.crop.name
|
||||
expect(events[1].dtstart.to_datetime.to_i).to be_within(1.second).of @predictable_planting.created_at.to_i
|
||||
expect(events[1].dtend.to_date).to eq @predictable_planting.finish_predicted_at
|
||||
|
||||
# Actual finish should be used
|
||||
# expect(events[4].dtend.to_date).to be_within(1.second).of @finished_planting.finished_at
|
||||
# expect(events[4].dtend.to_date).to be_within(1.second).of @finised_planting.finished_at
|
||||
|
||||
# Otherwise, tomorrow should be used
|
||||
expect(events.map { |e| e.dtend.to_date }).to include 1.day.from_now.to_date
|
||||
expect(events[3].dtend.to_date).to eq 1.day.from_now.to_date
|
||||
|
||||
# TBA: Perennial and annual crops predictions of 'next' harvest date don't really fit
|
||||
|
||||
@@ -83,18 +86,16 @@ days_to_last_harvest: 20)
|
||||
data = CSV.parse(response.body, headers: true)
|
||||
expect(data.headers).to eq expected_headers
|
||||
|
||||
row = data.detect { |crop_name| @predictable_planting.id.to_s == crop_name['Id'] }
|
||||
|
||||
expect(row["Crop name"]).to eq @predictable_planting.crop.name
|
||||
expect(row["Owner name"]).to eq @member.to_s
|
||||
expect(row["Garden name"]).to eq @predictable_planting.garden.to_s
|
||||
expect(row["Description"]).to eq @predictable_planting.description
|
||||
expect(row["Date planted"]).to eq @predictable_planting.planted_at.to_fs(:db)
|
||||
expect(row["Quantity"].to_i).to eq @predictable_planting.quantity
|
||||
expect(row["Sunniness"]).to eq @predictable_planting.sunniness
|
||||
expect(row["Planted from"]).to eq @predictable_planting.planted_from
|
||||
expect(row["Date added"]).to eq @predictable_planting.created_at.to_fs(:db)
|
||||
expect(row["License"]).to eq "CC-BY-SA Growstuff http://growstuff.org/"
|
||||
expect(data[1]["Crop name"]).to eq @predictable_planting.crop.name
|
||||
expect(data[1]["Owner name"]).to eq @member.to_s
|
||||
expect(data[1]["Garden name"]).to eq @predictable_planting.garden.to_s
|
||||
expect(data[1]["Description"]).to eq @predictable_planting.description
|
||||
expect(data[1]["Date planted"]).to eq @predictable_planting.planted_at.to_fs(:db)
|
||||
expect(data[1]["Quantity"].to_i).to eq @predictable_planting.quantity
|
||||
expect(data[1]["Sunniness"]).to eq @predictable_planting.sunniness
|
||||
expect(data[1]["Planted from"]).to eq @predictable_planting.planted_from
|
||||
expect(data[1]["Date added"]).to eq @predictable_planting.created_at.to_fs(:db)
|
||||
expect(data[1]["License"]).to eq "CC-BY-SA Growstuff http://growstuff.org/"
|
||||
|
||||
expect(data.count).to eq 6
|
||||
end
|
||||
|
||||
@@ -46,6 +46,10 @@ RSpec.configure do |config|
|
||||
def index_everything
|
||||
# reindex models
|
||||
Crop.reindex
|
||||
Harvest.reindex
|
||||
Photo.reindex
|
||||
Planting.reindex
|
||||
Seed.reindex
|
||||
end
|
||||
|
||||
config.before(:suite) do
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'harvests/index.rss.haml' do
|
||||
describe 'harvests/index.rss.haml', :search do
|
||||
before do
|
||||
controller.stub(:current_user) { nil }
|
||||
@member = create(:member)
|
||||
@@ -12,7 +12,8 @@ describe 'harvests/index.rss.haml' do
|
||||
@harvest2 = create(:harvest, crop: @tomato)
|
||||
@harvest3 = create(:harvest, crop: @tomato)
|
||||
|
||||
assign(:harvests, Harvest.all)
|
||||
Harvest.searchkick_index.refresh
|
||||
assign(:harvests, Harvest.search(load: false))
|
||||
end
|
||||
|
||||
context 'all harvests' do
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'home/_seeds.html.haml', type: "view" do
|
||||
describe 'home/_seeds.html.haml', :search, type: "view" do
|
||||
let!(:seed) { create(:tradable_seed, owner:) }
|
||||
let(:owner) { create(:london_member) }
|
||||
|
||||
before do
|
||||
Seed.searchkick_index.refresh
|
||||
render
|
||||
end
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'plantings/index.rss.haml' do
|
||||
describe 'plantings/index.rss.haml', :search do
|
||||
before do
|
||||
allow(view).to receive(:current_user).and_return(nil)
|
||||
controller.stub(:current_user) { nil }
|
||||
end
|
||||
|
||||
context 'all plantings' do
|
||||
@@ -12,7 +12,8 @@ describe 'plantings/index.rss.haml' do
|
||||
@planting = create(:planting)
|
||||
@sunny = create(:sunny_planting)
|
||||
@seedling = create(:seedling_planting)
|
||||
assign(:plantings, Planting.all)
|
||||
Planting.searchkick_index.refresh
|
||||
assign(:plantings, Planting.search(load: false))
|
||||
render
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'seeds/index.rss.haml' do
|
||||
describe 'seeds/index.rss.haml', :search do
|
||||
before do
|
||||
controller.stub(:current_user) { nil }
|
||||
end
|
||||
@@ -26,7 +26,8 @@ describe 'seeds/index.rss.haml' do
|
||||
let!(:tradable) { create(:tradable_seed) }
|
||||
|
||||
before do
|
||||
assign(:seeds, Seed.all)
|
||||
Seed.searchkick_index.refresh
|
||||
assign(:seeds, Seed.search(load: false))
|
||||
render
|
||||
end
|
||||
|
||||
@@ -50,7 +51,8 @@ describe 'seeds/index.rss.haml' do
|
||||
|
||||
before do
|
||||
assign(:owner, seed.owner)
|
||||
assign(:seeds, Seed.all)
|
||||
Seed.searchkick_index.refresh
|
||||
assign(:seeds, Seed.search(load: false))
|
||||
render
|
||||
end
|
||||
|
||||
|
||||
@@ -9,85 +9,6 @@
|
||||
"/crops": {
|
||||
"get": {
|
||||
"summary": "crops List",
|
||||
"/crops/search": {
|
||||
"get": {
|
||||
"summary": "crops Search",
|
||||
"tags": [
|
||||
"crops"
|
||||
],
|
||||
"produces": [
|
||||
"application/vnd.api+json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "term",
|
||||
"in": "query",
|
||||
"type": "string",
|
||||
"description": "Search term",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "page[number]",
|
||||
"in": "query",
|
||||
"type": "string",
|
||||
"description": "Page num",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"name": "page[size]",
|
||||
"in": "query",
|
||||
"type": "string",
|
||||
"description": "Page size",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Get search results",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Type"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"record_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"page_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"crops"
|
||||
],
|
||||
|
||||
@@ -735,9 +735,9 @@ fast-levenshtein@^2.0.6:
|
||||
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
||||
|
||||
fast-uri@^3.0.1:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.5.tgz#610f37419a030270430cecd68d74e3d4d96725d0"
|
||||
integrity sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa"
|
||||
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.20.1"
|
||||
@@ -915,9 +915,9 @@ inherits@2, inherits@~2.0.3:
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
ip-address@^10.0.1:
|
||||
version "10.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.4.0.tgz#c5910bc541b6eae287765d1e4846be0308a05d93"
|
||||
integrity sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4"
|
||||
integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
@@ -964,9 +964,9 @@ js-tokens@^4.0.0:
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.1.tgz#01216c001d67f48e2cd560d708c7af21090a3848"
|
||||
integrity sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
|
||||
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
@@ -1396,9 +1396,9 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
systeminformation@^5.25.11:
|
||||
version "5.31.17"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.31.17.tgz#60bd1cdaeb34212b5ad2410608ee47d41d2d8560"
|
||||
integrity sha512-TvFA9iwDWlMjqZVlKIJ0Cy+Zgm9ttlMx0SMRwJDMNKyhlEKWBMb3+WRwDi/3dvHdWbexpos4Osp4U49p5WjB5g==
|
||||
version "5.31.5"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.31.5.tgz#e839fa6b40620a8bee010eb9d9d55c2d5f7042c8"
|
||||
integrity sha512-5SyLdip4/3alxD4Kh+63bUQTJmu7YMfYQTC+koZy7X73HgNqZSD2P4wOZQWtUncvPvcEmnfIjCoygN4MRoEejQ==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
@@ -1463,9 +1463,9 @@ wrappy@1:
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@^8.17.1:
|
||||
version "8.21.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.1.tgz#045650cd4b1207809e7547146223c3814a9af586"
|
||||
integrity sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==
|
||||
version "8.20.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8"
|
||||
integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==
|
||||
|
||||
y18n@^4.0.0:
|
||||
version "4.0.3"
|
||||
|
||||
Reference in new issue
Block a user