Compare commits

...
15 Commits
Author SHA1 Message Date
Daniel O'Connor c0eb49aebb Merge pull request #4678 from Growstuff/fix-photo-reindex-error-4629481995101950332
Fix NoMethodError: undefined method 'reindex' for class Photo in specs
2026-06-24 01:45:40 +09:30
google-labs-jules[bot]andCloCkWeRX abd5df606e Fix Photo.reindex errors in specs and controller
- Remove `Photo.reindex` calls from `spec/spec_helper.rb` and feature specs.
- Modify `LikesController` to conditionally call `reindex` only if the likeable object responds to it, preventing `NoMethodError` for non-indexed models like `Photo`.
- Remove unnecessary `:reindex` trait from photo factory call in `likeable_spec.rb`.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-06-23 14:22:47 +00:00
google-labs-jules[bot]andCloCkWeRX d6b9ededec Remove Photo.reindex calls from test suite
The Photo model is no longer using Searchkick/Elasticsearch, so calling
`Photo.reindex` causes a NoMethodError. This commit removes these calls
from `spec/spec_helper.rb` and feature specs, and removes the unused
`:reindex` trait from the photo factory call in `likeable_spec.rb`.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-06-23 14:00:08 +00:00
Daniel O'Connor 118e50ccfc Merge pull request #4677 from Growstuff/fix-docker-build-missing-ruby-version-8858247702805966576
Fix Docker build by copying .ruby-version before bundle install
2026-06-23 22:50:58 +09:30
google-labs-jules[bot]andCloCkWeRX 75fa206ff7 Copy .ruby-version before bundle install in Dockerfile
The Gemfile reads the .ruby-version file to determine the Ruby version.
If this file is not present during `bundle install`, the build fails.
This change ensures that .ruby-version is copied along with the Gemfile
and Gemfile.lock.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-06-23 13:06:53 +00:00
Daniel O'Connor 452b0c4eba Merge pull request #4674 from Growstuff/security-2026-06-23
Security fixes
2026-06-23 22:34:46 +09:30
Daniel O'Connor 7dd78359aa Merge pull request #4676 from Growstuff/dependabot/bundler/net-imap-0.6.4.1
Bump net-imap from 0.6.4 to 0.6.4.1
2026-06-23 22:34:33 +09:30
Daniel O'Connor 3af51f18dc Merge pull request #4675 from Growstuff/convert-photos-to-sql-4865766049333411151
Convert Photos controller to avoid elasticsearch
2026-06-23 21:51:39 +09:30
Daniel O'Connor b2248ee4ca Merge branch 'dev' into convert-photos-to-sql-4865766049333411151 2026-06-23 21:47:45 +09:30
dependabot[bot] bda4d98a1b Bump net-imap from 0.6.4 to 0.6.4.1
Bumps [net-imap](https://github.com/ruby/net-imap) from 0.6.4 to 0.6.4.1.
- [Release notes](https://github.com/ruby/net-imap/releases)
- [Commits](https://github.com/ruby/net-imap/compare/v0.6.4...v0.6.4.1)

---
updated-dependencies:
- dependency-name: net-imap
  dependency-version: 0.6.4.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 12:17:25 +00:00
google-labs-jules[bot]andCloCkWeRX 97e236f7e8 Convert Photos controller to plain SQL queries
- Refactored `PhotosController#index` to use ActiveRecord queries instead of Searchkick/Elasticsearch.
- Handled filtering by crop and planting via associations.
- Added eager loading for the photo owner to prevent N+1 queries.
- Decoupled the `Photo` model from Elasticsearch by removing the `SearchPhotos` concern.
- Deleted `app/models/concerns/search_photos.rb`.
- Updated controller and model specs to use standard ActiveRecord expectations.
- Cleaned up the photo factory by removing the now-obsolete `:reindex` trait.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-06-23 12:16:50 +00:00
Daniel O'Connor 92faca98fa Security fixes 2026-06-23 12:14:57 +00:00
Daniel O'Connor a9179618f5 Merge pull request #4673 from Growstuff/heroku-26
Heroku 26
2026-06-23 21:43:48 +09:30
Daniel O'Connor 217b3953ac Update docker rails version 2026-06-23 11:59:57 +00:00
Daniel O'Connor 9fe2294c7b Update to heroku-26 2026-06-23 11:54:38 +00:00
14 changed files with 28 additions and 123 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/devcontainers/ruby:3.4-trixie
# Install Rails
RUN gem install rails:7.0.8
RUN gem install rails:8.0.5
# 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
+1 -1
View File
@@ -25,7 +25,7 @@ ENV RAILS_ENV=production \
WORKDIR /app
# Install gems
COPY Gemfile Gemfile.lock ./
COPY Gemfile Gemfile.lock .ruby-version ./
RUN bundle config set --local deployment 'true' && \
bundle config set --local without 'development test' && \
bundle install --jobs 4 --retry 3
+3 -3
View File
@@ -441,7 +441,7 @@ GEM
bigdecimal (>= 3.1, < 5)
net-http (0.9.1)
uri (>= 0.11.1)
net-imap (0.6.4)
net-imap (0.6.4.1)
date
net-protocol
net-pop (0.1.2)
@@ -452,10 +452,10 @@ GEM
net-protocol
netrc (0.11.0)
nio4r (2.7.5)
nokogiri (1.19.3)
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri (1.19.3-x86_64-linux-gnu)
nokogiri (1.19.4-x86_64-linux-gnu)
racc (~> 1.4)
oauth (0.5.6)
oj (3.17.3)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "growstuff",
"stack": "heroku-18",
"stack": "heroku-26",
"description": "Open data project for small-scale food growers",
"scripts": {
"postdeploy": "bundle exec rails db:seed"
+2 -2
View File
@@ -11,7 +11,7 @@ class LikesController < ApplicationController
likeable_id: params[:id]
)
if @like.likeable && @like.save
@like.likeable.reindex(refresh: true)
@like.likeable.reindex(refresh: true) if @like.likeable.respond_to?(:reindex)
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)
@like.likeable.reindex(refresh: true) if @like.likeable.respond_to?(:reindex)
success(@like, liked_by_member: false, status_code: :ok)
else
failed(@like, message: t('messages.unable_to_unlike'))
+11 -19
View File
@@ -9,13 +9,17 @@ class PhotosController < ApplicationController
responders :flash
def index
@photos = Photo.search(
load: false,
boost_by: [:created_at],
where: index_where_clause,
page: params[:page],
limit: Photo.per_page
)
@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)
respond_with(@photos)
end
@@ -114,18 +118,6 @@ 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]
-37
View File
@@ -1,37 +0,0 @@
# 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
-1
View File
@@ -3,7 +3,6 @@
class Photo < ApplicationRecord
include Likeable
include Ownable
include SearchPhotos
PHOTO_CAPABLE = %w(Garden Planting Harvest Seed Post Crop).freeze
+7 -13
View File
@@ -2,15 +2,14 @@
require 'rails_helper'
describe PhotosController, :search do
describe PhotosController do
login_member
describe 'GET index' do
describe 'all photos' do
let!(:photo) { create(:photo, :reindex) }
let!(:photo) { create(:photo) }
before do
Photo.reindex
get :index
end
@@ -21,28 +20,23 @@ describe PhotosController, :search do
end
describe '#index crop photos' do
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) }
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) }
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.id }
it { expect(assigns(:photos).first.crops).to include crop }
it { expect(assigns(:photos).first.id).to eq crop_photo.id }
end
end
-5
View File
@@ -19,10 +19,5 @@ FactoryBot.define do
license_url { nil }
end
trait :reindex do
after(:create) do |photo, _evaluator|
photo.reindex(refresh: true)
end
end
end
end
-1
View File
@@ -28,7 +28,6 @@ describe "home page", :search do
Planting.reindex
Seed.reindex
Harvest.reindex
Photo.reindex
visit root_path
end
+1 -5
View File
@@ -6,14 +6,10 @@ 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, :reindex, owner: member) }
let!(:photo) { create(:photo, owner: member) }
let!(:harvest) { create(:harvest, :reindex, owner: member) }
let!(:planting) { create(:planting, :reindex, owner: member) }
before do
Photo.reindex
end
include_context 'signed in member'
shared_examples 'object can be liked' do
+1 -33
View File
@@ -3,7 +3,7 @@
require 'rails_helper'
describe Photo do
let(:photo) { create(:photo, :reindex, owner: member) }
let(:photo) { create(:photo, owner: member) }
let(:member) { create(:member) }
it_behaves_like "it is likeable"
@@ -237,36 +237,4 @@ 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
-1
View File
@@ -47,7 +47,6 @@ RSpec.configure do |config|
# reindex models
Crop.reindex
Harvest.reindex
Photo.reindex
Planting.reindex
Seed.reindex
end