mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-10 16:54:38 -04:00
Memoize Planting-related methods for performance optimization
This commit introduces memoization to various methods in the Planting model, PredictPlanting and PredictHarvest concerns, PlantingsHelper, and PlantingsController. Specifically: - Memoized database-intensive lookups like `nearby_same_crop`, `first_harvest_date`, and `last_harvest_date`. - Memoized calculated fields like `finish_predicted_at`, `expected_lifespan`, and `age_in_days`. - Optimized `PlantingsHelper#transplantable_gardens_by_owner` using a hash to cache results per planting instance within a request. - Applied the `defined?(@variable)` pattern where appropriate to ensure efficient handling of `nil` results. These changes reduce redundant database queries and expensive calculations, particularly during view rendering where these methods are frequently accessed. Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
This commit is contained in:
@@ -160,7 +160,7 @@ class PlantingsController < DataController
|
||||
end
|
||||
|
||||
def matching_seeds
|
||||
Seed.where(crop: @planting.crop, owner: @planting.owner)
|
||||
@matching_seeds ||= Seed.where(crop: @planting.crop, owner: @planting.owner)
|
||||
.where('(finished_at IS NULL OR finished_at >= ?)', @planting.planted_at)
|
||||
.where('(saved_at IS NULL OR saved_at <= ?)', @planting.planted_at)
|
||||
end
|
||||
|
||||
@@ -46,9 +46,13 @@ module PlantingsHelper
|
||||
# Returns a list of gardens the planting can be transplanted to
|
||||
# based on the planting's owner.
|
||||
def transplantable_gardens_by_owner(planting)
|
||||
garden_ids = planting.owner.gardens.select(:id).to_a + GardenCollaborator.where(member_id: planting.owner.id).select(:garden_id).to_a
|
||||
@transplantable_gardens ||= {}
|
||||
cache_key = planting.id || planting.object_id
|
||||
@transplantable_gardens[cache_key] ||= begin
|
||||
garden_ids = planting.owner.gardens.select(:id).to_a + GardenCollaborator.where(member_id: planting.owner.id).select(:garden_id).to_a
|
||||
|
||||
Garden.active.where.not(id: planting.garden_id).where(id: garden_ids)
|
||||
Garden.active.where.not(id: planting.garden_id).where(id: garden_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def days_from_now_to_last_harvest(planting)
|
||||
|
||||
@@ -6,23 +6,31 @@ module PredictHarvest
|
||||
included do
|
||||
# dates
|
||||
def first_harvest_date
|
||||
harvests_with_dates.minimum(:harvested_at)
|
||||
return @first_harvest_date if defined?(@first_harvest_date)
|
||||
|
||||
@first_harvest_date = harvests_with_dates.minimum(:harvested_at)
|
||||
end
|
||||
|
||||
def last_harvest_date
|
||||
harvests_with_dates.maximum(:harvested_at)
|
||||
return @last_harvest_date if defined?(@last_harvest_date)
|
||||
|
||||
@last_harvest_date = harvests_with_dates.maximum(:harvested_at)
|
||||
end
|
||||
|
||||
def first_harvest_predicted_at
|
||||
return unless crop.median_days_to_first_harvest.present? && planted_at.present?
|
||||
return @first_harvest_predicted_at if defined?(@first_harvest_predicted_at)
|
||||
|
||||
planted_at + crop.median_days_to_first_harvest.days
|
||||
@first_harvest_predicted_at = if crop.median_days_to_first_harvest.present? && planted_at.present?
|
||||
planted_at + crop.median_days_to_first_harvest.days
|
||||
end
|
||||
end
|
||||
|
||||
def last_harvest_predicted_at
|
||||
return unless crop.median_days_to_last_harvest.present? && planted_at.present?
|
||||
return @last_harvest_predicted_at if defined?(@last_harvest_predicted_at)
|
||||
|
||||
planted_at + crop.median_days_to_last_harvest.days
|
||||
@last_harvest_predicted_at = if crop.median_days_to_last_harvest.present? && planted_at.present?
|
||||
planted_at + crop.median_days_to_last_harvest.days
|
||||
end
|
||||
end
|
||||
|
||||
# actions
|
||||
@@ -65,16 +73,18 @@ module PredictHarvest
|
||||
end
|
||||
|
||||
def neighbours_for_harvest_predictions
|
||||
# use this planting's harvest if any
|
||||
return harvests if harvests.size.positive?
|
||||
|
||||
# otherwise use nearby plantings
|
||||
if location
|
||||
return Harvest.where(planting: nearby_same_crop.has_harvests)
|
||||
.where.not(planting_id: nil)
|
||||
@neighbours_for_harvest_predictions ||= begin
|
||||
# use this planting's harvest if any
|
||||
if harvests.size.positive?
|
||||
harvests
|
||||
# otherwise use nearby plantings
|
||||
elsif location
|
||||
Harvest.where(planting: nearby_same_crop.has_harvests)
|
||||
.where.not(planting_id: nil)
|
||||
else
|
||||
Harvest.none
|
||||
end
|
||||
end
|
||||
|
||||
Harvest.none
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -13,40 +13,49 @@ module PredictPlanting
|
||||
|
||||
# dates
|
||||
def finish_predicted_at
|
||||
if planted_at.blank? || failed?
|
||||
nil
|
||||
elsif crop.median_lifespan.present?
|
||||
planted_at + crop.median_lifespan.days
|
||||
elsif crop.parent.present? && crop.parent.median_lifespan.present?
|
||||
planted_at + crop.parent.median_lifespan.days
|
||||
end
|
||||
return @finish_predicted_at if defined?(@finish_predicted_at)
|
||||
|
||||
@finish_predicted_at = if planted_at.blank? || failed?
|
||||
nil
|
||||
elsif crop.median_lifespan.present?
|
||||
planted_at + crop.median_lifespan.days
|
||||
elsif crop.parent.present? && crop.parent.median_lifespan.present?
|
||||
planted_at + crop.parent.median_lifespan.days
|
||||
end
|
||||
end
|
||||
|
||||
# days
|
||||
def expected_lifespan
|
||||
if actual_lifespan.present?
|
||||
actual_lifespan
|
||||
elsif crop.median_lifespan.present?
|
||||
crop.median_lifespan
|
||||
elsif crop.parent.present? && crop.parent.median_lifespan.present?
|
||||
crop.parent.median_lifespan
|
||||
end
|
||||
return @expected_lifespan if defined?(@expected_lifespan)
|
||||
|
||||
@expected_lifespan = if actual_lifespan.present?
|
||||
actual_lifespan
|
||||
elsif crop.median_lifespan.present?
|
||||
crop.median_lifespan
|
||||
elsif crop.parent.present? && crop.parent.median_lifespan.present?
|
||||
crop.parent.median_lifespan
|
||||
end
|
||||
end
|
||||
|
||||
def actual_lifespan
|
||||
return unless planted_at.present? && finished_at.present? && !failed?
|
||||
return @actual_lifespan if defined?(@actual_lifespan)
|
||||
|
||||
(finished_at - planted_at).to_i
|
||||
@actual_lifespan = if planted_at.present? && finished_at.present? && !failed?
|
||||
(finished_at - planted_at).to_i
|
||||
end
|
||||
end
|
||||
|
||||
def age_in_days
|
||||
return if planted_at.blank?
|
||||
return if failed?
|
||||
return @age_in_days if defined?(@age_in_days)
|
||||
|
||||
known_last_day ||= finished_at || Time.zone.today
|
||||
known_last_day = Time.zone.today if known_last_day > Time.zone.today
|
||||
@age_in_days = if planted_at.blank? || failed?
|
||||
nil
|
||||
else
|
||||
known_last_day = finished_at || Time.zone.today
|
||||
known_last_day = Time.zone.today if known_last_day > Time.zone.today
|
||||
|
||||
(known_last_day - planted_at).to_i
|
||||
(known_last_day - planted_at).to_i
|
||||
end
|
||||
end
|
||||
|
||||
def percentage_grown
|
||||
|
||||
@@ -119,14 +119,18 @@ class Planting < ApplicationRecord
|
||||
end
|
||||
|
||||
def nearby_same_crop
|
||||
return Planting.none if location.blank? || latitude.blank? || longitude.blank?
|
||||
return @nearby_same_crop if defined?(@nearby_same_crop)
|
||||
|
||||
# latitude, longitude = Geocoder.coordinates(location, params: { limit: 1 })
|
||||
Planting.joins(:garden)
|
||||
.where(crop:)
|
||||
.located
|
||||
.where('gardens.latitude < ? AND gardens.latitude > ?',
|
||||
latitude + 10, latitude - 10)
|
||||
@nearby_same_crop = if location.blank? || latitude.blank? || longitude.blank?
|
||||
Planting.none
|
||||
else
|
||||
# latitude, longitude = Geocoder.coordinates(location, params: { limit: 1 })
|
||||
Planting.joins(:garden)
|
||||
.where(crop:)
|
||||
.located
|
||||
.where('gardens.latitude < ? AND gardens.latitude > ?',
|
||||
latitude + 10, latitude - 10)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
Reference in New Issue
Block a user