diff --git a/app/assets/javascripts/likes.js b/app/assets/javascripts/likes.js index 60b3e57e7..801617ff9 100644 --- a/app/assets/javascripts/likes.js +++ b/app/assets/javascripts/likes.js @@ -14,7 +14,7 @@ $(document).ready(function() { } else { likeBadge.removeClass('liked'); likeButton.data('method', 'post'); - likeButton.attr('href', '/likes.json?post_id=' + data.id); + likeButton.attr('href', '/likes.json?type=Post&id=' + data.id); likeButton.text('Like'); } }); @@ -34,7 +34,7 @@ $(document).ready(function() { likeBadge.removeClass('liked'); // Turn the button into an *like* button likeButton.data('method', 'post'); - likeButton.attr('href', '/likes.json?photo_id=' + data.id); + likeButton.attr('href', '/likes.json?type=Photo&id=' + data.id); } }); }); diff --git a/app/controllers/crops_controller.rb b/app/controllers/crops_controller.rb index 4c6f7c0a1..baf56aa1a 100644 --- a/app/controllers/crops_controller.rb +++ b/app/controllers/crops_controller.rb @@ -11,7 +11,10 @@ class CropsController < ApplicationController def index @sort = params[:sort] - @crops = crops + @crops = Crop.search('*', boost_by: %i(plantings_count harvests_count), + limit: 100, + page: params[:page], + load: false) @num_requested_crops = requested_crops.size if current_member @filename = filename respond_with @crops @@ -51,19 +54,19 @@ class CropsController < ApplicationController def search @term = params[:term] - @crops = CropSearchService.search( - @term, page: params[:page], - per_page: 36, - current_member: current_member - ) + @crops = CropSearchService.search(@term, + page: params[:page], + per_page: Crop.per_page, + current_member: current_member) respond_with @crops end def show - @crop = Crop.includes(:scientific_names, :alternate_names, :parent, :varieties).find_by!(slug: params[:slug]) + @crop = Crop.includes( + :scientific_names, :alternate_names, :parent, :varieties + ).find_by!(slug: params[:slug]) respond_to do |format| format.html do - @photos = Photo.by_crop(@crop) @posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page]) @companions = @crop.companions.approved end @@ -204,24 +207,17 @@ class CropsController < ApplicationController def crop_json_fields { include: { - plantings: { + plantings: { include: { owner: { only: %i(id login_name location latitude longitude) } } }, scientific_names: { only: [:name] }, - alternate_names: { only: [:name] } + alternate_names: { only: [:name] } } } end - def crops - q = Crop.approved.includes(:scientific_names, plantings: :photos) - q = q.popular unless @sort == 'alpha' - q.order(Arel.sql("LOWER(crops.name)")) - .includes(:photos).paginate(page: params[:page]) - end - def requested_crops current_member.requested_crops.pending_approval end diff --git a/app/controllers/gardens_controller.rb b/app/controllers/gardens_controller.rb index 696377186..25acefe0f 100644 --- a/app/controllers/gardens_controller.rb +++ b/app/controllers/gardens_controller.rb @@ -52,7 +52,9 @@ class GardensController < DataController private def garden_params - params.require(:garden).permit(:name, :slug, :description, :active, - :location, :latitude, :longitude, :area, :area_unit, :garden_type_id) + params.require(:garden).permit( + :name, :slug, :description, :active, + :location, :latitude, :longitude, :area, :area_unit, :garden_type_id + ) end end diff --git a/app/controllers/harvests_controller.rb b/app/controllers/harvests_controller.rb index 76cb3afe5..4b6624136 100644 --- a/app/controllers/harvests_controller.rb +++ b/app/controllers/harvests_controller.rb @@ -4,14 +4,27 @@ class HarvestsController < DataController after_action :update_crop_medians, only: %i(create update destroy) def index - @owner = Member.find_by(slug: params[:member_slug]) - @crop = Crop.find_by(slug: params[:crop_slug]) - @planting = Planting.find_by(slug: params[:planting_id]) + where = {} + if params[:member_slug] + @owner = Member.find_by(slug: params[:member_slug]) + where['owner_id'] = @owner.id + end - @harvests = @harvests.where(owner: @owner) if @owner.present? - @harvests = @harvests.where(crop: @crop) if @crop.present? - @harvests = @harvests.where(planting: @planting) if @planting.present? - @harvests = @harvests.order(harvested_at: :desc).joins(:owner, :crop).paginate(page: params[:page]) + if params[:crop_slug] + @crop = Crop.find_by(slug: params[:crop_slug]) + where['crop_id'] = @crop.id + end + + if params[:planting_slug] + @planting = Planting.find_by(slug: params[:planting_slug]) + where['planting_id'] = @planting.id + end + + @harvests = Harvest.search('*', where: where, + limit: 100, + page: params[:page], + load: false, + boost_by: [:created_at]) @filename = csv_filename @@ -26,7 +39,7 @@ class HarvestsController < DataController def new @harvest = Harvest.new(harvested_at: Time.zone.today) - @planting = Planting.find_by(slug: params[:planting_id]) if params[:planting_id] + @planting = Planting.find_by(slug: params[:planting_slug]) if params[:planting_slug] @crop = Crop.find_by(id: params[:crop_id]) respond_with(@harvest) end diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index 37d302fcb..953baac35 100644 --- a/app/controllers/likes_controller.rb +++ b/app/controllers/likes_controller.rb @@ -5,36 +5,42 @@ class LikesController < ApplicationController respond_to :html, :json def create - @like = Like.new(member: current_member, likeable: find_likeable) - return failed(@like, message: 'Unable to like') unless @like.likeable && @like.save - - success(@like, liked_by_member: true, status_code: :created) + @like = Like.new( + member: current_member, + likeable_type: params[:type], + likeable_id: params[:id] + ) + if @like.likeable && @like.save + @like.likeable.reindex(refresh: true) + success(@like, liked_by_member: true, status_code: :created) + else + failed(@like, message: 'Unable to like') + end end def destroy - @like = Like.find_by(id: params[:id], member: current_member) - return failed(@like, message: 'Unable to unlike') unless @like&.destroy + @like = Like.find_by( + likeable_type: params[:type], + likeable_id: params[:id], + member: current_member + ) - success(@like, liked_by_member: false, status_code: :ok) + if @like&.destroy + @like.likeable.reindex(refresh: true) + success(@like, liked_by_member: false, status_code: :ok) + else + failed(@like, message: 'Unable to unlike') + end end private - def find_likeable - if params[:post_id] - Post.find(params[:post_id]) - elsif params[:photo_id] - Photo.find(params[:photo_id]) - end - end - def render_json(like, liked_by_member: true) { - id: like.likeable.id, - like_count: like.likeable.likes.count, + id: like.likeable.id, + like_count: like.likeable.likes.count, liked_by_member: liked_by_member, - description: ActionController::Base.helpers.pluralize(like.likeable.likes.count, "like"), - url: like_path(like, format: :json) + description: ActionController::Base.helpers.pluralize(like.likeable.likes.count, "like") } end @@ -42,9 +48,10 @@ class LikesController < ApplicationController respond_to do |format| format.html { redirect_to like.likeable } format.json do - render(json: render_json(like, - liked_by_member: liked_by_member), - status: status_code) + render(json: render_json( + like, + liked_by_member: liked_by_member + ), status: status_code) end end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 9b65068d0..9f83044b0 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -71,7 +71,7 @@ class MembersController < ApplicationController EMAIL_TYPE_STRING = { send_notification_email: "direct message notifications", - send_planting_reminder: "planting reminders" + send_planting_reminder: "planting reminders" }.freeze def member_params diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 9b7fb7c1b..322c5a66e 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -13,18 +13,22 @@ class PhotosController < ApplicationController end def index + where = {} if params[:crop_slug] @crop = Crop.find params[:crop_slug] - @photos = Photo.by_crop(@crop) + where = { crops: @crop.id } elsif params[:planting_id] @planting = Planting.find params[:planting_id] - @photos = @planting.photos - else - @photos = Photo.all + where = { planting_id: @planting.id } end - @photos = @photos.order(created_at: :desc) - .includes(:owner) - .paginate(page: params[:page]) + + @photos = Photo.search( + load: false, + boost_by: [:created_at], + where: where, + page: params[:page], + limit: Photo.per_page + ) respond_with(@photos) end @@ -86,7 +90,7 @@ class PhotosController < ApplicationController def find_or_create_photo_from_flickr_photo photo = Photo.find_or_initialize_by( source_id: photo_params[:source_id], - source: 'flickr' + source: 'flickr' ) photo.update(photo_params) photo.owner_id = current_member.id diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index 47919e95e..983fb13ff 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -5,20 +5,28 @@ class PlantingsController < DataController after_action :update_planting_medians, only: :update def index - @owner = Member.find_by(slug: params[:member_slug]) if params[:member_slug] - @crop = Crop.find_by(slug: params[:crop_slug]) if params[:crop_slug] - @show_all = params[:all] == '1' - @plantings = @plantings.where(owner: @owner) if @owner.present? - @plantings = @plantings.where(crop: @crop) if @crop.present? + where = {} + where['active'] = true unless @show_all - @plantings = @plantings.active unless params[:all] == '1' + if params[:member_slug] + @owner = Member.find_by(slug: params[:member_slug]) + where['owner_id'] = @owner.id + end - @plantings = @plantings.joins(:owner, :crop, :garden) - .order(created_at: :desc) - .includes(:owner, :garden, crop: :parent) - .paginate(page: params[:page]) + if params[:crop_slug] + @crop = Crop.find_by(slug: params[:crop_slug]) + where['crop_id'] = @crop.id + end + + @plantings = Planting.search( + where: where, + page: params[:page], + limit: 30, + boost_by: [:created_at], + load: false + ) @filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv" @@ -27,9 +35,13 @@ class PlantingsController < DataController def show @photos = @planting.photos.includes(:owner).order(date_taken: :desc) + @harvests = Harvest.search(where: { planting_id: @planting.id }) @matching_seeds = matching_seeds + + # TODO: use elastic search long/lat @neighbours = @planting.nearby_same_crop .where.not(id: @planting.id) + .includes(:owner, :crop, :garden) .limit(6) respond_with @planting end @@ -37,15 +49,15 @@ class PlantingsController < DataController def new @planting = Planting.new( planted_at: Time.zone.today, - owner: current_member, - garden: current_member.gardens.first + owner: current_member, + garden: current_member.gardens.first ) @seed = Seed.find_by(slug: params[:seed_id]) if params[:seed_id] @crop = Crop.approved.find_by(id: params[:crop_id]) || Crop.new if params[:garden_id] @planting.garden = Garden.find_by( owner: current_member, - id: params[:garden_id] + id: params[:garden_id] ) end diff --git a/app/controllers/seeds_controller.rb b/app/controllers/seeds_controller.rb index 2294e2840..566edfc4a 100644 --- a/app/controllers/seeds_controller.rb +++ b/app/controllers/seeds_controller.rb @@ -2,14 +2,34 @@ class SeedsController < DataController def index - @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].present? - @planting = Planting.find_by(slug: params[:planting_id]) if params[:planting_id].present? + where = {} + + if params[:member_slug].present? + @owner = Member.find_by(slug: params[:member_slug]) + where['owner_id'] = @owner.id + end + + if params[:crop_slug].present? + @crop = Crop.find_by(slug: params[:crop_slug]) + where['crop_id'] = @crop.id + end + + if params[:planting_id].present? + @planting = Planting.find_by(slug: params[:planting_id]) + where['parent_planting'] = @planting.id + end @show_all = (params[:all] == '1') + where['finished'] = false unless @show_all @filename = csv_filename - @seeds = seeds.order(created_at: :desc).includes(:owner, :crop).paginate(page: params[:page]) + @seeds = Seed.search( + where: where, + page: params[:page], + limit: 30, + boost_by: [:created_at], + load: false + ) respond_with(@seeds) end @@ -22,8 +42,8 @@ class SeedsController < DataController def new @seed = Seed.new - if params[:planting_id] - @planting = Planting.find_by(slug: params[:planting_id]) + if params[:planting_slug] + @planting = Planting.find_by(slug: params[:planting_slug]) else @crop = Crop.find_or_initialize_by(id: params[:crop_id]) end @@ -56,15 +76,6 @@ class SeedsController < DataController private - def seeds - records = Seed.all - records = records.where(owner: @owner) if @owner.present? - records = records.where(crop: @crop) if @crop.present? - records = records.where(parent_planting: @planting) if @planting.present? - records = records.active unless @show_all - records - end - def seed_params params.require(:seed).permit( :crop_id, :description, :quantity, :plant_before, diff --git a/app/helpers/buttons_helper.rb b/app/helpers/buttons_helper.rb index ca5cc5d78..7e822e78b 100644 --- a/app/helpers/buttons_helper.rb +++ b/app/helpers/buttons_helper.rb @@ -74,14 +74,14 @@ module ButtonsHelper def planting_finish_button(planting, classes: 'btn btn-default btn-secondary') return unless can?(:edit, planting) || planting.finished - link_to planting_path(planting, planting: { finished: 1 }), + link_to planting_path(slug: planting.slug, planting: { finished: 1 }), method: :put, class: "#{classes} append-date" do finished_icon + ' ' + t('buttons.mark_as_finished') end end def seed_finish_button(seed, classes: 'btn btn-default') - return unless can?(:create, Planting) && seed.active? + return unless can?(:create, Planting) && seed.active link_to seed_path(seed, seed: { finished: 1 }), method: :put, class: "#{classes} append-date" do finished_icon + ' ' + t('buttons.mark_as_finished') @@ -89,9 +89,9 @@ module ButtonsHelper end def planting_harvest_button(planting, classes: 'btn btn-default') - return unless planting.active? && can?(:create, Harvest) && can?(:edit, planting) + return unless planting.active && can?(:create, Harvest) && can?(:edit, planting) - link_to new_planting_harvest_path(planting), class: classes do + link_to new_planting_harvest_path(planting_slug: planting.slug), class: classes do harvest_icon + ' ' + t('buttons.record_harvest') end end @@ -99,7 +99,7 @@ module ButtonsHelper def planting_save_seeds_button(planting, classes: 'btn btn-default') return unless can?(:edit, planting) - link_to new_planting_seed_path(planting), class: classes do + link_to new_planting_seed_path(planting_slug: planting.slug), class: classes do seed_icon + ' ' + t('buttons.save_seeds') end end diff --git a/app/helpers/crops_helper.rb b/app/helpers/crops_helper.rb index 7d2187e51..ead50b24d 100644 --- a/app/helpers/crops_helper.rb +++ b/app/helpers/crops_helper.rb @@ -15,6 +15,6 @@ module CropsHelper end def crop_ebay_seeds_url(crop) - "https://rover.ebay.com/rover/1/705-53470-19255-0/1?icep_ff3=9&pub=5575213277&toolid=10001&campid=5337940151&customid=&icep_uq=#{CGI.escape crop.name}&icep_sellerId=&icep_ex_kw=&icep_sortBy=12&icep_catId=181003&icep_minPrice=&icep_maxPrice=&ipn=psmain&icep_vectorid=229515&kwid=902099&mtid=824&kw=lg" # rubocop:disable Metrics/LineLength + "https://rover.ebay.com/rover/1/705-53470-19255-0/1?icep_ff3=9&pub=5575213277&toolid=10001&campid=5337940151&customid=&icep_uq=#{CGI.escape crop.name}&icep_sellerId=&icep_ex_kw=&icep_sortBy=12&icep_catId=181003&icep_minPrice=&icep_maxPrice=&ipn=psmain&icep_vectorid=229515&kwid=902099&mtid=824&kw=lg" # rubocop:disable Layout/LineLength end end diff --git a/app/models/concerns/crop_search.rb b/app/models/concerns/crop_search.rb deleted file mode 100644 index edeffe36b..000000000 --- a/app/models/concerns/crop_search.rb +++ /dev/null @@ -1,41 +0,0 @@ -module CropSearch - extend ActiveSupport::Concern - - included do - #################################### - # Elastic search configuration - searchkick word_start: %i(name alternate_names scientific_names), - case_sensitive: false, - merge_mappings: true, - mappings: { - properties: { - created_at: { type: :integer } - } - } - - # Special scope to control if it's in the search index - scope :search_import, -> { includes(:scientific_names, :photos) } - - def should_index? - approved? - end - - def search_data - { - name: name, - slug: slug, - alternate_names: alternate_names.pluck(:name), - scientific_names: scientific_names.pluck(:name), - # boost the crops that are planted the most - plantings_count: plantings_count, - # boost this crop for these members - planters_ids: plantings.pluck(:owner_id), - has_photos: photos.size.positive?, - photo: default_photo&.thumbnail_url, - scientific_name: default_scientific_name&.name, - description: description, - created_at: created_at.to_i - } - end - end -end diff --git a/app/models/concerns/finishable.rb b/app/models/concerns/finishable.rb index 6ff68e9c5..d4a4488dc 100644 --- a/app/models/concerns/finishable.rb +++ b/app/models/concerns/finishable.rb @@ -7,7 +7,7 @@ module Finishable scope :finished, -> { where(finished: true) } scope :current, -> { where.not(finished: true) } - def active? + def active !finished end end diff --git a/app/models/concerns/likeable.rb b/app/models/concerns/likeable.rb index 911317a6e..c1927a126 100644 --- a/app/models/concerns/likeable.rb +++ b/app/models/concerns/likeable.rb @@ -9,6 +9,10 @@ module Likeable end def liked_by?(member) - member && members.include?(member) + liked_by_members_names.include?(member.login_name) + end + + def liked_by_members_names + Member.where(id: likes.pluck(:member_id)).pluck(:login_name) end end diff --git a/app/models/concerns/photo_capable.rb b/app/models/concerns/photo_capable.rb index b585d4b6b..c1433a7fc 100644 --- a/app/models/concerns/photo_capable.rb +++ b/app/models/concerns/photo_capable.rb @@ -15,6 +15,14 @@ module PhotoCapable end end + def thumbnail_url + df = default_photo + + return unless df + + df.source == 'flickr' ? df.fullsize_url : df.thumbnail_url + end + def most_liked_photo photos.order(likes_count: :desc, created_at: :desc).first end diff --git a/app/models/concerns/search_crops.rb b/app/models/concerns/search_crops.rb new file mode 100644 index 000000000..959627436 --- /dev/null +++ b/app/models/concerns/search_crops.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module SearchCrops + extend ActiveSupport::Concern + + included do + #################################### + # Elastic search configuration + searchkick word_start: %i(name description alternate_names scientific_names), + case_sensitive: false, + searchable: %i(name descriptions alternate_names scientific_names), + merge_mappings: true, + mappings: { + properties: { + created_at: { type: :integer }, + plantings_count: { type: :integer }, + harvests_count: { type: :integer }, + photos_count: { type: :integer } + } + } + + # Special scope to control if it's in the search index + scope :search_import, -> { includes(:scientific_names, :photos) } + + def should_index? + approved? + end + + def search_data + { + name: name, + description: description, + slug: slug, + alternate_names: alternate_names.pluck(:name), + scientific_names: scientific_names.pluck(:name), + photos_count: photo_associations_count, + # boost the crops that are planted the most + plantings_count: plantings_count, + harvests_count: harvests_count, + # boost this crop for these members + planters_ids: plantings.pluck(:owner_id), + has_photos: photos.size.positive?, + thumbnail_url: thumbnail_url, + scientific_name: default_scientific_name&.name, + created_at: created_at.to_i + } + end + end +end diff --git a/app/models/concerns/search_harvests.rb b/app/models/concerns/search_harvests.rb new file mode 100644 index 000000000..75fb67cdb --- /dev/null +++ b/app/models/concerns/search_harvests.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module SearchHarvests + extend ActiveSupport::Concern + + included do + searchkick merge_mappings: true, mappings: { + properties: { + harvests_count: { type: :integer }, + photos_count: { type: :integer }, + created_at: { type: :integer }, + harvested_at: { type: :date } + } + } + + scope :search_import, -> { includes(:owner, :crop, :plant_part) } + + def search_data + { + slug: slug, + quantity: quantity, + + # crop + crop_id: crop_id, + crop_name: crop_name, + crop_slug: crop.slug, + + # owner + owner_id: owner_id, + owner_login_name: owner_login_name, + owner_slug: owner_slug, + plant_part_name: plant_part&.name, + + # planting + planting_id: 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: harvested_at, + created_at: created_at.to_i + } + end + + def self.homepage_records(limit) + search('*', limit: limit, + where: { + photos_count: { gt: 0 } + }, + boost_by: [:created_at], + load: false) + end + end +end diff --git a/app/models/concerns/search_photos.rb b/app/models/concerns/search_photos.rb new file mode 100644 index 000000000..5bf48ec67 --- /dev/null +++ b/app/models/concerns/search_photos.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module SearchPhotos + extend ActiveSupport::Concern + + included do + searchkick merge_mappings: true, mappings: { + properties: { + title: { type: :text }, + created_at: { type: :integer } + } + } + + def search_data + { + id: id, + title: title, + thumbnail_url: thumbnail_url, + fullsize_url: fullsize_url, + # crops + crops: crops.pluck(:id), + # likes + liked_by_members_names: liked_by_members_names, + # owner + owner_id: owner_id, + owner_login_name: owner.login_name, + # counts + likes_count: likes_count, + + created_at: created_at.to_i + } + end + end +end diff --git a/app/models/concerns/search_plantings.rb b/app/models/concerns/search_plantings.rb new file mode 100644 index 000000000..bb29e07f9 --- /dev/null +++ b/app/models/concerns/search_plantings.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module SearchPlantings + extend ActiveSupport::Concern + + included do + searchkick merge_mappings: true, mappings: { + properties: { + active: { type: :boolean }, + created_at: { type: :integer }, + harvests_count: { type: :integer }, + photos_count: { type: :integer }, + owner_location: { type: :text } + } + } + + scope :search_import, -> { includes(:owner, :crop) } + + def search_data + { + slug: slug, + active: active, + finished: finished?, + has_photos: photos.size.positive?, + location: location, + percentage_grown: percentage_grown.to_i, + planted_at: planted_at, + planted_from: planted_from, + quantity: quantity, + sunniness: sunniness, + + # crops + crop_id: crop_id, + crop_name: crop.name, + crop_slug: crop.slug, + + # owner + owner_id: owner_id, + owner_location: owner_location, + owner_login_name: owner_login_name, + owner_slug: owner_slug, + + # photos + thumbnail_url: default_photo&.thumbnail_url || crop.default_photo&.thumbnail_url, + # counts + photos_count: photos.size, + harvests_count: harvests_count, + + # timestamps + created_at: created_at.to_i + } + end + + def self.homepage_records(limit) + search('*', limit: limit, + where: { + photos_count: { gt: 0 } + }, + boost_by: [:created_at], + load: false) + end + end +end diff --git a/app/models/concerns/search_seeds.rb b/app/models/concerns/search_seeds.rb new file mode 100644 index 000000000..4c3c3d685 --- /dev/null +++ b/app/models/concerns/search_seeds.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module SearchSeeds + extend ActiveSupport::Concern + + included do + searchkick merge_mappings: true, 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: slug, + finished: finished?, + gmo: gmo, + active: active, + heirloom: heirloom, + location: owner.location, + organic: organic, + quantity: quantity, + plant_before: plant_before&.to_s(:ymd), + tradable_to: tradable_to, + tradable: tradable, + + # crop + crop_id: crop_id, + crop_name: crop.name, + crop_slug: crop.slug, + + # owner + owner_id: owner_id, + owner_location: owner_location, + owner_login_name: owner_login_name, + owner_slug: owner_slug, + + # planting + parent_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: limit, + where: { + finished: false, + tradable: true + }, + boost_by: [:created_at], + load: false) + end + end +end diff --git a/app/models/crop.rb b/app/models/crop.rb index 703576e5d..0df63cf85 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -4,7 +4,7 @@ class Crop < ApplicationRecord extend FriendlyId include PhotoCapable include OpenFarmData - include CropSearch + include SearchCrops friendly_id :name, use: %i(slugged finders) @@ -18,7 +18,7 @@ class Crop < ApplicationRecord has_many :plantings, dependent: :destroy has_many :seeds, dependent: :destroy has_many :harvests, dependent: :destroy - has_many :photo_associations, dependent: :delete_all + has_many :photo_associations, dependent: :delete_all, inverse_of: :crop has_many :photos, through: :photo_associations has_many :plant_parts, -> { joins_members.distinct.order("plant_parts.name") }, through: :harvests has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id', dependent: :nullify, inverse_of: :parent diff --git a/app/models/garden.rb b/app/models/garden.rb index 95c619dbe..6490bb411 100644 --- a/app/models/garden.rb +++ b/app/models/garden.rb @@ -77,6 +77,8 @@ class Garden < ApplicationRecord end end + def reindex(refresh: false); end + protected def strip_blanks diff --git a/app/models/harvest.rb b/app/models/harvest.rb index a38241c4e..338984dd2 100644 --- a/app/models/harvest.rb +++ b/app/models/harvest.rb @@ -5,6 +5,7 @@ class Harvest < ApplicationRecord extend FriendlyId include PhotoCapable include Ownable + include SearchHarvests friendly_id :harvest_slug, use: %i(slugged finders) @@ -45,10 +46,14 @@ class Harvest < ApplicationRecord scope :recent, -> { order(created_at: :desc) } scope :one_per_owner, lambda { joins("JOIN members m ON (m.id=harvests.owner_id) - LEFT OUTER JOIN harvests h2 - ON (m.id=h2.owner_id AND harvests.id < h2.id)").where("h2 IS NULL") + LEFT OUTER JOIN harvests h2 + ON (m.id=h2.owner_id AND harvests.id < h2.id)").where("h2 IS NULL") } + delegate :name, :slug, to: :crop, prefix: true + delegate :login_name, :slug, to: :owner, prefix: true + delegate :name, to: :plant_part, prefix: true + ## ## Validations validates :crop, approved: true diff --git a/app/models/photo.rb b/app/models/photo.rb index 38359732c..3e0254800 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -3,11 +3,18 @@ class Photo < ApplicationRecord include Likeable include Ownable + include SearchPhotos PHOTO_CAPABLE = %w(Garden Planting Harvest Seed Post Crop).freeze has_many :photo_associations, foreign_key: :photo_id, dependent: :delete_all, inverse_of: :photo - has_many :crops, through: :photo_associations, counter_cache: true + + # This doesn't work, ActiveRecord tries to use the polymoriphinc photographable + # relationship instead. + # has_many :crops, through: :photo_associations + def crops + Crop.distinct.joins(:photo_associations).where(photo_associations: { photo: self }) + end validates :fullsize_url, url: true validates :thumbnail_url, url: true @@ -25,6 +32,8 @@ class Photo < ApplicationRecord joins(:photo_associations).where(photo_associations: { photographable_type: model_name.to_s }) } + delegate :login_name, to: :owner, prefix: true + # This is split into a side-effect free method and a side-effecting method # for easier stubbing and testing. def flickr_metadata diff --git a/app/models/photo_association.rb b/app/models/photo_association.rb index 1fe576902..e253a3a50 100644 --- a/app/models/photo_association.rb +++ b/app/models/photo_association.rb @@ -1,24 +1,23 @@ # frozen_string_literal: true class PhotoAssociation < ApplicationRecord - belongs_to :photo, inverse_of: :photo_associations + belongs_to :photo, touch: true + belongs_to :crop, optional: true, touch: true # , counter_cache: true belongs_to :photographable, polymorphic: true, touch: true - belongs_to :crop, optional: true, counter_cache: true validate :photo_and_item_have_same_owner + validate :crop_present ## ## Triggers - before_save :set_crop - - def item - find_by!(photographable_id: photographable_id, photographable_type: photographable_type).photographable - end + before_validation :set_crop def self.item(item_id, item_type) find_by!(photographable_id: item_id, photographable_type: item_type).photographable end + private + def set_crop if %w(Planting Seed Harvest).include?(photographable_type) self.crop_id = photographable.crop_id @@ -27,11 +26,15 @@ class PhotoAssociation < ApplicationRecord end end - private - def photo_and_item_have_same_owner - return unless photographable_type != 'Crop' + return if photographable_type == 'Crop' errors.add(:photo, "must have same owner as item it links to") unless photographable.owner_id == photo.owner_id end + + def crop_present + if %w(Planting Seed Harvest).include?(photographable_type) + errors.add(:crop_id, "failed to calculate crop") if crop_id.blank? + end + end end diff --git a/app/models/planting.rb b/app/models/planting.rb index a61c1d773..9dcad1fca 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -7,6 +7,8 @@ class Planting < ApplicationRecord include Ownable include PredictPlanting include PredictHarvest + include SearchPlantings + friendly_id :planting_slug, use: %i(slugged finders) # Constants @@ -56,6 +58,8 @@ class Planting < ApplicationRecord ## Delegations delegate :name, :slug, :en_wikipedia_url, :default_scientific_name, :plantings_count, to: :crop, prefix: true + delegate :login_name, :slug, :location, to: :owner, prefix: true + delegate :slug, to: :planting, prefix: true delegate :annual?, :perennial?, :svg_icon, to: :crop delegate :location, :longitude, :latitude, to: :garden diff --git a/app/models/post.rb b/app/models/post.rb index 659291584..0fbdf3533 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -57,6 +57,8 @@ class Post < ApplicationRecord subject end + def reindex(refresh: false); end + private def update_crop_posts_association diff --git a/app/models/seed.rb b/app/models/seed.rb index e902a8a29..b212d6acb 100644 --- a/app/models/seed.rb +++ b/app/models/seed.rb @@ -5,6 +5,7 @@ class Seed < ApplicationRecord include PhotoCapable include Finishable include Ownable + include SearchSeeds friendly_id :seed_slug, use: %i(slugged finders) TRADABLE_TO_VALUES = %w(nowhere locally nationally internationally).freeze @@ -46,7 +47,9 @@ class Seed < ApplicationRecord # # Delegations - delegate :name, to: :crop + delegate :name, to: :crop, prefix: true + delegate :location, :latitude, :longitude, to: :owner + delegate :login_name, :slug, :location, to: :owner, prefix: true # # Scopes @@ -57,7 +60,7 @@ class Seed < ApplicationRecord scope :recent, -> { order(created_at: :desc) } scope :active, -> { where('finished <> true').where('finished_at IS NULL OR finished_at < ?', Time.zone.now) } - def tradable? + def tradable tradable_to != 'nowhere' end diff --git a/app/services/crop_search_service.rb b/app/services/crop_search_service.rb index 274d5a3c0..12bbae811 100644 --- a/app/services/crop_search_service.rb +++ b/app/services/crop_search_service.rb @@ -4,13 +4,14 @@ class CropSearchService # Crop.search(string) def self.search(query, page: 1, per_page: 12, current_member: nil) search_params = { - page: page, - per_page: per_page, - fields: %i(name^5 alternate_names scientific_names), - match: :word_start, - boost_by: [:plantings_count], - includes: %i(scientific_names alternate_names), - misspellings: { edit_distance: 2 } + page: page, + per_page: per_page, + fields: %i(name^5 alternate_names scientific_names), + match: :word_start, + boost_by: [:plantings_count], + includes: %i(scientific_names alternate_names), + misspellings: { edit_distance: 2 }, + load: false } # prioritise crops the member has planted search_params[:boost_where] = { planters_ids: current_member.id } if current_member @@ -22,22 +23,22 @@ class CropSearchService body = { "query": { "function_score": { - "query": { "query_string": { "query": 'has_photos:true' } }, + "query": { "query_string": { "query": 'has_photos:true' } }, "random_score": { "seed": DateTime.now.to_i } } } } Crop.search( limit: limit, - load: false, - body: body + load: false, + body: body ) end def self.recent(limit) Crop.search( - limit: limit, - load: false, + limit: limit, + load: false, boost_by: { created_at: { factor: 100 } } # default factor is 1 ) end diff --git a/app/views/crops/_crop.html.haml b/app/views/crops/_crop.html.haml index 9c763518d..e32a933a3 100644 --- a/app/views/crops/_crop.html.haml +++ b/app/views/crops/_crop.html.haml @@ -6,7 +6,7 @@ crop .card-body %h3.card-title - %strong= link_to crop, crop + %strong= link_to crop.name, crop_path(slug: crop.slug) = crop.default_scientific_name .d-flex.justify-content-between - if crop.annual? && crop.median_lifespan.present? diff --git a/app/views/crops/_photos.html.haml b/app/views/crops/_photos.html.haml index ba27113f6..42ee6eef7 100644 --- a/app/views/crops/_photos.html.haml +++ b/app/views/crops/_photos.html.haml @@ -1,7 +1,7 @@ -- if photos.size.positive? +- if crop.photos.count.positive? %h2 #{photo_icon} Photos - [Crop, Planting, Harvest, Seed].each do |model_name| - - if photos.by_model(model_name).size.positive? + - if crop.photos.by_model(model_name).size.positive? %h3 #{@crop.name.capitalize} #{t("activerecord.models.#{model_name.to_s.downcase}.other")} - = render 'photos/gallery', photos: photos.by_model(model_name).includes(:owner).order(likes_count: :desc).limit(5) + = render 'photos/gallery', photos: crop.photos.by_model(model_name).includes(:owner).order(likes_count: :desc).limit(5) = link_to 'more photos »', crop_photos_path(@crop), class: 'btn' diff --git a/app/views/crops/_thumbnail.html.haml b/app/views/crops/_thumbnail.html.haml index 948c59bbc..29617b768 100644 --- a/app/views/crops/_thumbnail.html.haml +++ b/app/views/crops/_thumbnail.html.haml @@ -1,12 +1,11 @@ - cache crop do .card.crop-thumbnail - = link_to image_tag(crop_image_path(crop), + = link_to image_tag(crop.thumbnail_url ? crop.thumbnail_url : placeholder_image, alt: crop.name, class: 'img img-card'), - crop + crop_path(slug: crop.slug) .text - %h3.crop-name= link_to crop, crop + %h3.crop-name= link_to crop.name, crop_path(slug: crop.slug) %h5.crop-sci-name - - = crop.scientific_names.first&.name + = crop.scientific_names.first diff --git a/app/views/crops/index.html.haml b/app/views/crops/index.html.haml index 7fc088750..427412019 100644 --- a/app/views/crops/index.html.haml +++ b/app/views/crops/index.html.haml @@ -22,8 +22,8 @@ %h2= t('.title') = will_paginate @crops .index-cards - - @crops.each do |crop| - = render 'crops/thumbnail', crop: crop + - @crops.each do |c| + = render 'crops/thumbnail', crop: c = will_paginate @crops diff --git a/app/views/crops/show.html.haml b/app/views/crops/show.html.haml index 7acefc6bb..f4dd34744 100644 --- a/app/views/crops/show.html.haml +++ b/app/views/crops/show.html.haml @@ -32,7 +32,7 @@ %section.photos = cute_icon - = render 'crops/photos', photos: @photos, crop: @crop + = render 'crops/photos', crop: @crop - if @crop.plantings.any? %section.charts diff --git a/app/views/harvests/_card.html.haml b/app/views/harvests/_card.html.haml index 0d51fbb40..7cfedccc8 100644 --- a/app/views/harvests/_card.html.haml +++ b/app/views/harvests/_card.html.haml @@ -1,11 +1,13 @@ -.card - = link_to harvest do - = image_tag harvest_image_path(harvest), alt: harvest, class: 'img-card' - .card-body - %h5 - = crop_icon(harvest.crop) - %strong - = link_to harvest.crop, harvest - %span.badge.badge-pill= harvest.plant_part - .card-footer - .float-right=render 'members/tiny', member: harvest.owner \ No newline at end of file +- cache harvest do + .card + = link_to harvest do + = image_tag harvest.thumbnail_url ? harvest.thumbnail_url : placeholder_image, alt: harvest.crop_name, class: 'img-card' + .card-body + %h5 + %strong= link_to harvest.crop_name, harvest_path(slug: harvest.slug) + %span.badge.badge-pill= harvest.plant_part_name + .card-footer + .float-right + %span.chip.member-chip + = link_to member_path(slug: harvest.owner_slug) do + = harvest.owner_login_name \ No newline at end of file diff --git a/app/views/harvests/_harvest.haml b/app/views/harvests/_harvest.haml index 7341875cf..f67725164 100644 --- a/app/views/harvests/_harvest.haml +++ b/app/views/harvests/_harvest.haml @@ -1,6 +1,4 @@ - if local_assigns[:full] - - cache harvest do - = render 'harvests/card', harvest: harvest + = render 'harvests/card', harvest: harvest - else - - cache harvest do - = render 'harvests/thumbnail', harvest: harvest \ No newline at end of file + = render 'harvests/thumbnail', harvest: harvest \ No newline at end of file diff --git a/app/views/harvests/_thumbnail.html.haml b/app/views/harvests/_thumbnail.html.haml index 8539d72d8..5dfe125c3 100644 --- a/app/views/harvests/_thumbnail.html.haml +++ b/app/views/harvests/_thumbnail.html.haml @@ -1,9 +1,10 @@ -.card.harvest-thumbnail - = link_to image_tag(harvest_image_path(harvest), - alt: harvest, - class: 'img img-card'), - harvest +- cache harvest do + .card.harvest-thumbnail + = link_to image_tag(harvest_image_path(harvest), + alt: harvest, + class: 'img img-card'), + harvest - .text - %h3.harvest-plant-part= link_to harvest.plant_part, harvest - %h5.harvest-crop= harvest.crop + .text + %h3.harvest-plant-part= link_to harvest.plant_part, harvest + %h5.harvest-crop= harvest.crop diff --git a/app/views/harvests/index.html.haml b/app/views/harvests/index.html.haml index f2a5cb044..71c724c68 100644 --- a/app/views/harvests/index.html.haml +++ b/app/views/harvests/index.html.haml @@ -29,8 +29,9 @@ .badge.badge-success= link_to 'API Methods', '/api-docs' .col-md-10 %section - %h2 - = page_entries_info @harvests + %h2= page_entries_info @harvests = will_paginate @harvests - .index-cards=render @harvests, full: true + .index-cards + - @harvests.each do |h| + = render 'harvests/card', harvest: h = will_paginate @harvests diff --git a/app/views/harvests/index.rss.haml b/app/views/harvests/index.rss.haml index f5682edf1..10f226d47 100644 --- a/app/views/harvests/index.rss.haml +++ b/app/views/harvests/index.rss.haml @@ -2,18 +2,17 @@ %rss{ version: 2.0 } %channel %title - Recent harvests from #{@owner ? @owner : 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']}) + Recent harvests from #{@owner ||= 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']}) %link= harvests_url - @harvests.each do |harvest| %item - %title #{harvest.owner.login_name}'s #{harvest.crop.name} - %pubdate= harvest.harvested_at.to_s(:rfc822) + %title #{harvest.owner_login_name}'s #{harvest.crop_name} + %pubdate= harvest.harvested_at %description :escaped -
Crop: #{harvest.crop ? harvest.crop : 'unknown' }
-Quantity: #{harvest.quantity ? harvest.quantity : 'unknown' }
-Harvested on: #{harvest.harvested_at ? harvest.harvested_at : 'unknown' }
- :escaped_markdown - #{ strip_tags harvest.description } - %link= harvest_url(harvest) - %guid= harvest_url(harvest) +Crop: #{harvest.crop_name}
+Plant path: #{harvest.plant_part_name}
+Quantity: #{harvest.quantity ||= 'unknown' }
+Harvested on: #{harvest.harvested_at ||= 'unknown' }
+ %link= harvest_url(slug: harvest.slug) + %guid= harvest_url(slug: harvest.slug) diff --git a/app/views/home/_crops.html.haml b/app/views/home/_crops.html.haml index fe4b1492a..17c25cd4d 100644 --- a/app/views/home/_crops.html.haml +++ b/app/views/home/_crops.html.haml @@ -1,16 +1,4 @@ - cache cache_key_for(Crop, 'homepage'), expires_in: 1.day do .index-cards - - CropSearchService.random_with_photos(16).each do |crop| - .card.crop-thumbnail - = link_to crop_path(slug: crop['slug']) do - = image_tag(crop['photo'], - alt: crop['name'], - class: 'img img-card') - - .text - %h3.crop-name - = link_to crop['name'], crop_path(slug: crop['slug']) - %h5.crop-sci-name - - = crop['scientific_name'] - + - CropSearchService.random_with_photos(16).each do |c| + = render 'crops/thumbnail', crop: c \ No newline at end of file diff --git a/app/views/home/_discuss.html.haml b/app/views/home/_discuss.html.haml index 7954b536a..96983207b 100644 --- a/app/views/home/_discuss.html.haml +++ b/app/views/home/_discuss.html.haml @@ -12,7 +12,7 @@ %p.mb-2 = truncate(strip_tags(post.body), length: 200) %small - = post.comments.size + = post.comments_count comments %p.text-right = link_to "#{t('.view_all')} »", posts_path, class: 'btn btn-block' diff --git a/app/views/home/_harvests.html.haml b/app/views/home/_harvests.html.haml index cdf7aa589..b8d23f9be 100644 --- a/app/views/home/_harvests.html.haml +++ b/app/views/home/_harvests.html.haml @@ -1,11 +1,12 @@ %h2= t('.recently_harvested') -- Harvest.has_photos.recent.includes(:crop, :owner, :photos, :plant_part).limit(6).each do |harvest| +- Harvest.homepage_records(6).each do |harvest| - cache harvest do - = link_to harvest, class: 'list-group-item list-group-item-action flex-column align-items-start' do + = link_to harvest_path(slug: harvest.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 %div - %h5= harvest.crop.name + %h5= harvest.crop_name %span.badge.badge-success=harvest.plant_part %small.text-muted - harvested by #{harvest.owner} - %p.mb-2= image_tag harvest_image_path(harvest), width: 75, class: 'rounded shadow' \ No newline at end of file + harvested by #{harvest.owner_name} + %p.mb-2 + = image_tag harvest.thumbnail_url, width: 75, class: 'rounded shadow' \ No newline at end of file diff --git a/app/views/home/_plantings.html.haml b/app/views/home/_plantings.html.haml index 8d26fe455..a3bb79f75 100644 --- a/app/views/home/_plantings.html.haml +++ b/app/views/home/_plantings.html.haml @@ -1,12 +1,11 @@ %h2= t('.recently_planted') -- Planting.has_photos.recent.includes(:owner, :crop).limit(6).each do |planting| - - cache planting do - = link_to planting, 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_image_path(planting), width: 75, class: 'rounded shadow' - .text-right - %h5= planting.crop.name - - if planting.planted_from.present? - %span.badge.badge-success= planting.planted_from.pluralize - %small.text-muted planted by #{planting.owner} +- Planting.homepage_records(6).each do |planting| + = 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' + .text-right + %h5= planting['crop_name'] + - if planting['planted_from'].present? + %span.badge.badge-success= planting['planted_from'].pluralize + %small.text-muted planted by #{planting['owner_name']} diff --git a/app/views/home/_seeds.html.haml b/app/views/home/_seeds.html.haml index 38ae29fd9..bfb56295b 100644 --- a/app/views/home/_seeds.html.haml +++ b/app/views/home/_seeds.html.haml @@ -1,5 +1,5 @@ - cache cache_key_for(Seed) do %h2.text-center= t('home.seeds.title') .index-cards - - Seed.current.tradable.includes(:owner, :crop).order(created_at: :desc).limit(6).each do |seed| - = render 'seeds/card', seed: seed + - Seed.homepage_records(6).each do |s| + = render 'seeds/card', seed: s diff --git a/app/views/layouts/_menu.haml b/app/views/layouts/_menu.haml index eea460e0d..3703f969b 100644 --- a/app/views/layouts/_menu.haml +++ b/app/views/layouts/_menu.haml @@ -5,7 +5,7 @@ = link_to timeline_index_path, method: :get, class: 'nav-link text-white' do = image_tag 'icons/notification.svg', class: 'img img-icon' %li.nav-item - = link_to member_gardens_path(member_slug: current_member.slug), class: 'nav-link text-white' do + = link_to member_gardens_path(current_member), class: 'nav-link text-white' do = image_icon 'gardens' %li.nav-item.dropdown %a.nav-link.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", href: "#", role: "button"} diff --git a/app/views/likes/_count.haml b/app/views/likes/_count.haml index 79fece151..61920e259 100644 --- a/app/views/likes/_count.haml +++ b/app/views/likes/_count.haml @@ -1,4 +1,4 @@ -%span.badge.like-badge{class: (likeable.liked_by?(current_member)) ? 'liked' : ''} +%span.badge.like-badge{class: liked ? 'liked' : ''} = like_icon %span.like-count= likeable.likes_count diff --git a/app/views/photos/_card.html.haml b/app/views/photos/_card.html.haml index f073cbef1..4b606c636 100644 --- a/app/views/photos/_card.html.haml +++ b/app/views/photos/_card.html.haml @@ -1,10 +1,11 @@ .card.photo-card{id: "photo-#{photo.id}"} - = link_to image_tag(photo.source == 'flickr' ? photo.fullsize_url : photo.thumbnail_url, alt: photo.title, class: 'img img-card'), photo + = link_to photo_path(id: photo.id) do + = image_tag(photo.source == 'flickr' ? photo.fullsize_url : photo.thumbnail_url, alt: photo.title, class: 'img img-card') .card-body %h5.ellipsis = photo_icon - = link_to photo.title, photo - %i by #{link_to photo.owner, photo.owner} + = link_to photo.title, photo_path(id: photo.id) + %i by #{link_to photo.owner_login_name, member_path(slug: photo.owner_login_name)} - if photo.date_taken.present? %small.text-muted %time{datetime: photo.date_taken}= I18n.l(photo.date_taken.to_date) diff --git a/app/views/photos/_likes.html.haml b/app/views/photos/_likes.html.haml index fb0b0e918..4268e05cf 100644 --- a/app/views/photos/_likes.html.haml +++ b/app/views/photos/_likes.html.haml @@ -1,14 +1,13 @@ %span.likes - - if member_signed_in? - - if can?(:new, Like) && !photo.liked_by?(current_member) - = link_to likes_path(photo_id: photo.id, format: :json), - method: :post, remote: true, class: 'photo-like like-btn' do - = render 'likes/count', likeable: photo + - if member_signed_in? && can?(:new, Like) + - if !photo.liked_by_members_names.include?(current_member.login_name) + = link_to likes_path(type: 'Photo', id: photo.id, format: :json), + method: :post, remote: true, class: 'photo-like like-btn ' do + = render 'likes/count', likeable: photo, liked: false - else - - like = photo.likes.find_by(member: current_member) - - if like && can?(:destroy, like) - = link_to like_path(id: like.id, format: :json), - method: :delete, remote: true, class: 'photo-like like-btn' do - = render 'likes/count', likeable: photo + = link_to likes_path(type: 'Photo', id: photo.id, format: :json), + method: :delete, remote: true, class: 'photo-like like-btn' do + = render 'likes/count', likeable: photo, liked: true - else - = render 'likes/count', likeable: photo \ No newline at end of file + = render 'likes/count', likeable: photo, liked: member_signed_in? && photo.liked_by_members_names.include?(current_member.login_name) + diff --git a/app/views/plantings/_actions.html.haml b/app/views/plantings/_actions.html.haml index e974526af..aa58542e3 100644 --- a/app/views/plantings/_actions.html.haml +++ b/app/views/plantings/_actions.html.haml @@ -4,7 +4,7 @@ .dropdown-menu.dropdown-menu-xs{"aria-labelledby" => "planting-actions-button"} = planting_edit_button(planting, classes: 'dropdown-item') = add_photo_button(planting, classes: 'dropdown-item') - - if planting.active? + - if planting.active = planting_finish_button(planting, classes: 'dropdown-item') = planting_harvest_button(planting, classes: 'dropdown-item') = planting_save_seeds_button(planting, classes: 'dropdown-item') diff --git a/app/views/plantings/_card.html.haml b/app/views/plantings/_card.html.haml index e875b2154..1c593fbd8 100644 --- a/app/views/plantings/_card.html.haml +++ b/app/views/plantings/_card.html.haml @@ -1,27 +1,37 @@ - cache planting do - .card.planting{class: planting.active? ? '' : 'card-finished'} - = link_to planting do - = image_tag planting_image_path(planting), class: 'img-card', alt: planting - - if can? :edit, planting - .planting-quick-actions - .dropdown - %a.planting-menu.btn.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", type: "button", href: '#'} - .dropdown-menu{"aria-labelledby" => "planting-menu"} - = planting_edit_button(planting, classes: 'dropdown-item') - = add_photo_button(planting, classes: 'dropdown-item') + .card.planting{class: planting.active ? '' : 'card-finished'} + = link_to planting_path(slug: planting.slug) do + = image_tag planting.thumbnail_url ? planting.thumbnail_url : placeholder_image, class: 'img-card', alt: planting.crop_name - - if planting.active? - = planting_finish_button(planting, classes: 'dropdown-item') - = planting_harvest_button(planting, classes: 'dropdown-item') - = planting_save_seeds_button(planting, classes: 'dropdown-item') + - if member_signed_in? && current_member.id == planting.owner_id + = link_to planting_path(slug: planting.slug) do + .planting-quick-actions + .dropdown + %a.planting-menu.btn.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", type: "button", href: '#'} - - if can? :destroy, planting - .dropdown-divider - = delete_button(planting, classes: 'dropdown-item text-danger') - = link_to planting do + .dropdown-menu{"aria-labelledby" => "planting-menu"} + = link_to edit_planting_path(slug: planting.slug), class: 'dropdown-item' do + = edit_icon + = t('buttons.edit') + = link_to new_photo_path(id: planting.id, type: 'planting'), class: 'dropdown-item' do + = add_photo_icon + = t('buttons.add_photo') + + - if planting.active + = planting_finish_button(planting, classes: 'dropdown-item') + = planting_harvest_button(planting, classes: 'dropdown-item') + = planting_save_seeds_button(planting, classes: 'dropdown-item') + + - if can? :destroy, planting + .dropdown-divider + = delete_button(planting, classes: 'dropdown-item text-danger') + = link_to planting_path(slug: planting.slug) do .card-body.text-center - %h4= planting.crop + %h4= planting.crop_name .text-center= render 'plantings/badges', planting: planting = render 'plantings/progress', planting: planting .card-footer - .float-right=render 'members/tiny', member: planting.owner \ No newline at end of file + .float-right + %span.chip.member-chip + = link_to member_path(slug: planting.owner_slug) do + = planting.owner_login_name \ No newline at end of file diff --git a/app/views/plantings/_facts.haml b/app/views/plantings/_facts.haml index b0f2a440b..428c9f6d8 100644 --- a/app/views/plantings/_facts.haml +++ b/app/views/plantings/_facts.haml @@ -16,14 +16,14 @@ unknown - if planting.planted_at.present? %span.planted_at - =planting.planted_at.year + = planting.planted_at.year - if planting.finish_is_predicatable? .card.fact-card %h3 Progress %strong #{planting.age_in_days}/#{planting.expected_lifespan} %span days - + .card.fact-card{class: planting.quantity.present? ? '' : 'text-muted'} %h3 Quantity diff --git a/app/views/plantings/_harvests.html.haml b/app/views/plantings/_harvests.html.haml index aa3a0fd40..920c553c7 100644 --- a/app/views/plantings/_harvests.html.haml +++ b/app/views/plantings/_harvests.html.haml @@ -1,13 +1,13 @@ %h2 Harvests -- if can? :edit, @planting +- if can? :edit, planting %a.btn.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", :href => "#", :role => "button"} = harvest_icon Record harvest .dropdown-menu.dropdown-secondary - PlantPart.all.each do |plant_part| - = link_to harvests_path(return: 'planting', harvest: {crop_id: @planting.crop_id, planting_id: @planting.id, plant_part_id: plant_part.id}), method: :post, class: 'dropdown-item' do + = link_to harvests_path(return: 'planting', harvest: {crop_id: planting.crop_id, planting_id: planting.id, plant_part_id: plant_part.id}), method: :post, class: 'dropdown-item' do = plant_part.name - if planting.harvests.empty? @@ -16,5 +16,5 @@ %p Record your harvests here to improve crop predictions, and you'll be able to compare with your garden next season. - else .index-cards - - planting.harvests.order(created_at: :desc).includes(:crop).each do |harvest| + - planting.harvests.each do |harvest| = render 'harvests/thumbnail', harvest: harvest diff --git a/app/views/plantings/_progress.html.haml b/app/views/plantings/_progress.html.haml index a61233f3e..692488457 100644 --- a/app/views/plantings/_progress.html.haml +++ b/app/views/plantings/_progress.html.haml @@ -1,4 +1,4 @@ -- if planting.active? && planting.annual? && planting.percentage_grown.present? +- if planting.active && planting.annual? && planting.percentage_grown.present? .progress .progress-bar.bg-success{"aria-valuemax" => "100", "aria-valuemin" => "0", "aria-valuenow" => planting.percentage_grown, role: "progressbar", style: "width: #{planting.percentage_grown}%"} diff --git a/app/views/plantings/_quick_actions.haml b/app/views/plantings/_quick_actions.haml index 61d173ef2..74ac39314 100644 --- a/app/views/plantings/_quick_actions.haml +++ b/app/views/plantings/_quick_actions.haml @@ -6,7 +6,7 @@ %li= planting_edit_button(planting, classes: 'dropdown-item') %li= add_photo_button(planting, classes: 'dropdown-item') - - if planting.active? + - if planting.active %li= planting_finish_button(planting, classes: 'dropdown-item') %li= planting_harvest_button(planting, classes: 'dropdown-item') %li= planting_save_seeds_button(planting, classes: 'dropdown-item') diff --git a/app/views/plantings/index.html.haml b/app/views/plantings/index.html.haml index c1f21c44b..b7f3a4689 100644 --- a/app/views/plantings/index.html.haml +++ b/app/views/plantings/index.html.haml @@ -10,7 +10,6 @@ %h1.display-2.text-center = planting_icon = title('plantings', @owner, @crop, @planting) - .row .col-md-2 = render 'layouts/nav', model: Planting @@ -20,9 +19,9 @@ include finished plantings %hr - if @owner.present? - = render @owner + = render @owner, cached: true - if @crop.present? - = render @crop + = render @crop, cached: true %section.open-data %h2 Open Data @@ -40,7 +39,6 @@ %h2= page_entries_info @plantings = will_paginate @plantings .index-cards - - @plantings.each do |planting| - = render planting, full: true - + - @plantings.each do |p| + = render 'plantings/card', planting: p = will_paginate @plantings diff --git a/app/views/plantings/index.rss.haml b/app/views/plantings/index.rss.haml index 94eb026da..fd7a467e8 100644 --- a/app/views/plantings/index.rss.haml +++ b/app/views/plantings/index.rss.haml @@ -6,15 +6,15 @@ %link= plantings_url - @plantings.each do |planting| %item - %title #{planting.crop} in #{planting.location} - %pubdate= planting.created_at.to_s(:rfc822) + %title #{planting['crop_name']} in #{planting['location']} + %pubdate= planting['created_at'].to_s(:rfc822) %description :escaped -Quantity: #{planting.quantity ? planting.quantity : 'unknown' }
-Planted on: #{planting.planted_at ? planting.planted_at : 'unknown' }
-Sunniness: #{planting.sunniness ? planting.sunniness : 'unknown' }
-Planted from: #{planting.planted_from ? planting.planted_from : 'unknown' }
+Quantity: #{planting['quantity'] ? planting['quantity'] : 'unknown' }
+Planted on: #{planting['planted_at'] ? planting['planted_at'] : 'unknown' }
+Sunniness: #{planting['sunniness'] ? planting['sunniness'] : 'unknown' }
+Planted from: #{planting['planted_from'] ? planting['planted_from'] : 'unknown' }
:escaped_markdown - #{ strip_tags planting.description } - %link= planting_url(planting) - %guid= planting_url(planting) + #{ strip_tags planting['description'] } + %link= planting_url(slug: planting['slug']) + %guid= planting_url(slug: planting['slug']) diff --git a/app/views/posts/_likes.html.haml b/app/views/posts/_likes.html.haml index 18112c308..7c00d1bfd 100644 --- a/app/views/posts/_likes.html.haml +++ b/app/views/posts/_likes.html.haml @@ -1,12 +1,10 @@ -- if member_signed_in? - - if can?(:new, Like) && !post.liked_by?(current_member) - = link_to 'Like', likes_path(post_id: post.id, format: :json), +- if member_signed_in? && can?(:new, Like) + - if !post.liked_by?(current_member) + = link_to 'Like', likes_path(type: 'Post', id: post.id, format: :json), method: :post, remote: true, class: 'post-like btn like-btn' - else - - like = post.likes.find_by(member: current_member) - - if like && can?(:destroy, like) - = link_to 'Unlike', like_path(id: like.id, format: :json), - method: :delete, remote: true, class: 'post-like btn like-btn' + = link_to 'Unlike', likes_path(type: 'Post', id: post.id, format: :json), + method: :delete, remote: true, class: 'post-like btn like-btn' -= render 'likes/count', likeable: post += render 'likes/count', likeable: post, liked: member_signed_in? && post.liked_by?(current_member) diff --git a/app/views/seeds/_actions.html.haml b/app/views/seeds/_actions.html.haml index 5cc91951a..1d337eea4 100644 --- a/app/views/seeds/_actions.html.haml +++ b/app/views/seeds/_actions.html.haml @@ -3,13 +3,13 @@ %a#seed-actions-button.btn.btn-info.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", type: "button", href: '#'} Actions .dropdown-menu.dropdown-menu-xs{"aria-labelledby" => "seed-actions-button"} - - if can?(:create, Planting) && can?(:update, seed) && seed.active? + - if can?(:create, Planting) && can?(:update, seed) && seed.active = link_to new_planting_path(seed_id: seed), class: 'dropdown-item success-color' do = seed_icon Plant seeds = seed_edit_button(seed, classes: 'dropdown-item') = add_photo_button(seed, classes: 'dropdown-item') - - if seed.active? + - if seed.active = seed_finish_button(seed, classes: 'dropdown-item') .dropdown-divider = delete_button(seed, classes: 'dropdown-item text-danger') diff --git a/app/views/seeds/_card.html.haml b/app/views/seeds/_card.html.haml index db8a72da1..6decc6778 100644 --- a/app/views/seeds/_card.html.haml +++ b/app/views/seeds/_card.html.haml @@ -1,17 +1,17 @@ - cache seed do - .card.seed-card{class: seed.active? ? '' : 'card-finished'} - = link_to seed do - = image_tag(seed_image_path(seed), alt: seed, class: 'img-card') + .card.seed-card{class: seed.finished ? 'card-finished' : ''} + = link_to seed_path(slug: seed.slug) do + = image_tag(seed.thumbnail_url ? seed.thumbnail_url : placeholder_image, alt: seed.crop_name, class: 'img-card') .text - = render 'members/tiny', member: seed.owner + %span.chip.member-chip + = seed.owner_login_name .card-body .card-title - = crop_icon(seed.crop) - = link_to seed.crop, seed - - if seed.tradable? + = link_to seed.crop_name, seed_path(slug: seed.slug) + - if seed.tradable .text-muted = icon 'fas', 'map' Will trade #{seed.tradable_to} .badge.badge-pill.badge-location = icon 'fas', 'map-marker' - = truncate(seed.owner.location, length: 20, separator: ' ', omission: '... ') \ No newline at end of file + = truncate(seed.owner_location, length: 20, separator: ' ', omission: '... ') \ No newline at end of file diff --git a/app/views/seeds/index.html.haml b/app/views/seeds/index.html.haml index e0d17efed..47e349278 100644 --- a/app/views/seeds/index.html.haml +++ b/app/views/seeds/index.html.haml @@ -38,9 +38,7 @@ = will_paginate @seeds .index-cards - - @seeds.each do |seed| - = render 'seeds/card', seed: seed + - @seeds.each do |s| + = render 'seeds/card', seed: s = will_paginate @seeds - - diff --git a/app/views/seeds/index.rss.haml b/app/views/seeds/index.rss.haml index b2044de6e..24bec8c06 100644 --- a/app/views/seeds/index.rss.haml +++ b/app/views/seeds/index.rss.haml @@ -2,24 +2,21 @@ %rss{ version: 2.0 } %channel %title - Recent seeds from #{@owner ? @owner : 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']}) + Recent seeds from #{@owner ||= 'all members'} (#{ENV['GROWSTUFF_SITE_NAME']}) %link= seeds_url - @seeds.each do |seed| %item - %title #{seed.owner}'s #{seed.crop} seeds + %title #{seed.owner_login_name}'s #{seed.crop_name} seeds %pubdate= seed.created_at.to_s(:rfc822) %description :escaped -Quantity: #{seed.quantity ? seed.quantity : 'unknown' }
-Plant before: #{seed.plant_before ? seed.plant_before : 'unknown' }
+Quantity: #{seed.quantity ||= 'unknown' }
+Plant before: #{seed.plant_before ||= 'unknown' }
Organic? #{seed.organic}
GMO? #{seed.gmo}
Heirloom? #{seed.heirloom}
- - if seed.tradable? - %p - Will trade #{seed.tradable_to} from #{seed.owner.location ? seed.owner.location : 'unknown location'} - - :escaped_markdown - #{ strip_tags seed.description } - %link= seed_url(seed) - %guid= seed_url(seed) + - if seed.tradable + :escaped +Will trade #{seed.tradable_to} from #{seed.location ||= 'unknown location'}
+ %link= seed_url(slug: seed.slug) + %guid= seed_url(slug: seed.slug) diff --git a/app/views/seeds/show.html.haml b/app/views/seeds/show.html.haml index 14917dc9e..6ce24f7e4 100644 --- a/app/views/seeds/show.html.haml +++ b/app/views/seeds/show.html.haml @@ -50,7 +50,7 @@ #{strip_tags(@seed.description)} - if current_member - - if @seed.tradable? && current_member != @seed.owner + - if @seed.tradable && current_member != @seed.owner %p= link_to "Request seeds", new_message_path(recipient_id: @seed.owner.id, subject: "Interested in your #{@seed.crop} seeds"), diff --git a/config/locales/en.yml b/config/locales/en.yml index adc90fd09..fccdaf455 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -125,7 +125,7 @@ en: title: crop_harvests: Everyone's %{crop} harvests default: Everyone's harvests - owner_harvests: "%{owner} harvests" + owner_harvests: "%{owner}'s harvests" planting_harvests: Harvests from %{planting} updated: Harvest was successfully updated. home: diff --git a/config/routes.rb b/config/routes.rb index 5244ed3bc..118bf59ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -86,7 +86,10 @@ Rails.application.routes.draw do resources :forums resources :follows, only: %i(create destroy) - resources :likes, only: %i(create destroy) + + post 'likes' => 'likes#create' + delete 'likes' => 'likes#destroy' + resources :timeline resources :members, param: :slug do diff --git a/db/migrate/20191119030244_cms_tags.rb b/db/migrate/20191119030244_cms_tags.rb index 918f6a325..acaba82c8 100644 --- a/db/migrate/20191119030244_cms_tags.rb +++ b/db/migrate/20191119030244_cms_tags.rb @@ -30,7 +30,9 @@ class CmsTags < ActiveRecord::Migration[5.2] layout.content = layout.content.gsub(/\{\{ ?cms:(\w+):([\w]+):([^:]*) ?\}\}/, '{{ cms:\1 \2, "\3" }}') if layout.content.is_a? String layout.content = layout.content.gsub(/cms:rich_text/, 'cms:wysiwyg') if layout.content.is_a? String layout.content = layout.content.gsub(/cms:integer/, 'cms:number') if layout.content.is_a? String - layout.content = layout.content.gsub(/cms: string/, 'cms:text') if layout.content.is_a? String # probably a result of goofing one of the more general regexps + if layout.content.is_a? String + layout.content = layout.content.gsub(/cms: string/, 'cms:text') + end # probably a result of goofing one of the more general regexps if layout.content.is_a? String layout.content = layout.content.gsub(%r{\{\{ ?cms:page_file ([\w/]+) ?\}\}}, '{{ cms:file \1, render: false }}') end diff --git a/db/migrate/20191226024957_crop_photo_counter_cache.rb b/db/migrate/20191226024957_crop_photo_counter_cache.rb index db6f6f5f9..d4a085e24 100644 --- a/db/migrate/20191226024957_crop_photo_counter_cache.rb +++ b/db/migrate/20191226024957_crop_photo_counter_cache.rb @@ -1,7 +1,5 @@ - # frozen_string_literal: true - class CropPhotoCounterCache < ActiveRecord::Migration[5.2] def change change_table :crops do |t| diff --git a/db/migrate/20191226051019_elastic_indexing.rb b/db/migrate/20191226051019_elastic_indexing.rb new file mode 100644 index 000000000..a1c37885a --- /dev/null +++ b/db/migrate/20191226051019_elastic_indexing.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +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 + + def down; end +end diff --git a/db/schema.rb b/db/schema.rb index 4e6e251cd..ed75b6452 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_12_26_025225) do +ActiveRecord::Schema.define(version: 2019_12_26_051019) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/controllers/harvests_controller_spec.rb b/spec/controllers/harvests_controller_spec.rb index 57722269f..6233a28dc 100644 --- a/spec/controllers/harvests_controller_spec.rb +++ b/spec/controllers/harvests_controller_spec.rb @@ -2,15 +2,15 @@ require 'rails_helper' -describe HarvestsController do +describe HarvestsController, :search do login_member def valid_attributes { - owner_id: subject.current_member.id, - crop_id: FactoryBot.create(:crop).id, + owner_id: subject.current_member.id, + crop_id: FactoryBot.create(:crop).id, plant_part_id: FactoryBot.create(:plant_part).id, - harvested_at: '2017-01-01' + harvested_at: '2017-01-01' } end @@ -22,24 +22,30 @@ describe HarvestsController do let!(:harvest1) { FactoryBot.create(:harvest, owner_id: member1.id, crop_id: tomato.id) } let!(:harvest2) { FactoryBot.create(:harvest, owner_id: member2.id, crop_id: maize.id) } + before { Harvest.reindex } + describe "assigns all harvests as @harvests" do before { get :index, params: {} } - it { expect(assigns(:harvests)).to eq [harvest1, harvest2] } + it { expect(assigns(:harvests).size).to eq 2 } + it { expect(assigns(:harvests)[0].slug).to eq harvest1.slug } + it { expect(assigns(:harvests)[1].slug).to eq harvest2.slug } end describe "picks up owner from params and shows owner's harvests only" do before { get :index, params: { member_slug: member1.slug } } it { expect(assigns(:owner)).to eq member1 } - it { expect(assigns(:harvests)).to eq [harvest1] } + it { expect(assigns(:harvests).size).to eq 1 } + it { expect(assigns(:harvests)[0].slug).to eq harvest1.slug } end describe "picks up crop from params and shows the harvests for the crop only" do before { get :index, params: { crop_slug: maize.name } } it { expect(assigns(:crop)).to eq maize } - it { expect(assigns(:harvests)).to eq [harvest2] } + it { expect(assigns(:harvests).size).to eq 1 } + it { expect(assigns(:harvests)[0].slug).to eq harvest2.slug } end describe "generates a csv" do @@ -189,8 +195,10 @@ describe HarvestsController do describe "does not save planting_id" do before do - put :update, params: { slug: harvest.to_param, - harvest: valid_attributes.merge(planting_id: not_my_planting.id) } + put :update, params: { + slug: harvest.to_param, + harvest: valid_attributes.merge(planting_id: not_my_planting.id) + } end it { expect(harvest.planting_id).to eq(nil) } diff --git a/spec/controllers/likes_controller_spec.rb b/spec/controllers/likes_controller_spec.rb index 985678278..cc1bf9ba5 100644 --- a/spec/controllers/likes_controller_spec.rb +++ b/spec/controllers/likes_controller_spec.rb @@ -10,7 +10,7 @@ describe LikesController do before { sign_in member } describe "POST create" do - before { post :create, params: { post_id: blogpost.id, format: :json } } + before { post :create, params: { type: 'Post', id: blogpost.id, format: :json } } it { expect(response.content_type).to eq "application/json" } @@ -27,7 +27,7 @@ describe LikesController do end describe "DELETE destroy" do - before { delete :destroy, params: { id: like.id, format: :json } } + before { delete :destroy, params: { type: like.likeable_type, id: like.likeable_id, format: :json } } it { expect(response.content_type).to eq "application/json" } diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index 4f661a00f..4e5667180 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -2,36 +2,53 @@ require 'rails_helper' -describe PhotosController do +describe PhotosController, :search do login_member describe 'GET index' do describe 'all photos' do - let!(:photo) { FactoryBot.create :photo } + let!(:photo) { FactoryBot.create :photo, :reindex } - before { get :index } + before do + Photo.reindex + get :index + end - it { expect(assigns(:photos)).to eq [photo] } + it "finds photos" do + expect(assigns(:photos).count).to eq 1 + expect(assigns(:photos).first.id).to eq photo.id + end end - describe 'crop photos' do - let!(:photo) { FactoryBot.create :photo, owner: member } - let!(:crop_photo) { FactoryBot.create :photo, owner: member } - let!(:planting) { FactoryBot.create :planting, crop: crop, owner: member } - let!(:crop) { FactoryBot.create :crop } + describe '#index crop photos' do + let!(:photo) { FactoryBot.create :photo, :reindex, owner: member, title: 'no assocations photo' } + let!(:crop_photo) { FactoryBot.create :photo, :reindex, owner: member, title: 'photos of planting' } + let!(:planting) { FactoryBot.create :planting, :reindex, crop: crop, owner: member } + let!(:crop) { FactoryBot.create :crop, :reindex } before do planting.photos << crop_photo + Photo.reindex get :index, params: { crop_slug: crop.to_param } end - it { expect(assigns(:crop)).to eq crop } - it { expect(assigns(:photos)).to eq [crop_photo] } + 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.id).to eq crop_photo.id } + end end end describe "GET new" do - let(:tomato) { FactoryBot.create(:tomato) } + let(:tomato) { FactoryBot.create(:tomato) } let(:planting) { FactoryBot.create(:planting, crop: tomato, owner: member) } let(:garden) { FactoryBot.create(:garden, owner: member) } let(:harvest) { FactoryBot.create(:harvest, owner: member) } diff --git a/spec/controllers/plantings_controller_spec.rb b/spec/controllers/plantings_controller_spec.rb index fb9f51b53..4b5f2a651 100644 --- a/spec/controllers/plantings_controller_spec.rb +++ b/spec/controllers/plantings_controller_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe PlantingsController do +describe PlantingsController, :search do login_member def valid_attributes @@ -12,32 +12,38 @@ describe PlantingsController do } end - describe "GET index" do + describe "GET index", :search do let!(:member1) { FactoryBot.create(:member) } let!(:member2) { FactoryBot.create(:member) } let!(:tomato) { FactoryBot.create(:tomato) } let!(:maize) { FactoryBot.create(:maize) } let!(:planting1) { FactoryBot.create :planting, crop: tomato, owner: member1, created_at: 1.day.ago } let!(:planting2) { FactoryBot.create :planting, crop: maize, owner: member2, created_at: 5.days.ago } + before do + Planting.reindex + end describe "assigns all plantings as @plantings" do before { get :index } - it { expect(assigns(:plantings)).to match [planting1, planting2] } + it { expect(assigns(:plantings).size).to eq 2 } + it { expect(assigns(:plantings)[0]['slug']).to eq planting1.slug } + it { expect(assigns(:plantings)[1]['slug']).to eq planting2.slug } end describe "picks up owner from params and shows owner's plantings only" do before { get :index, params: { member_slug: member1.slug } } it { expect(assigns(:owner)).to eq member1 } - it { expect(assigns(:plantings)).to eq [planting1] } + it { expect(assigns(:plantings).size).to eq 1 } + it { expect(assigns(:plantings).first['slug']).to eq planting1.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)).to eq [planting2] } + it { expect(assigns(:plantings).first['slug']).to eq planting2.slug } end end @@ -119,4 +125,19 @@ describe PlantingsController do it { expect(assigns(:planting).owner).to eq subject.current_member } end end + + describe 'GET :edit' do + let(:my_planting) { FactoryBot.create :planting, owner: member } + let(:not_my_planting) { FactoryBot.create :planting } + context 'my planting' do + before { get :edit, params: { slug: my_planting } } + it { expect(assigns(:planting)).to eq my_planting } + end + + context 'not my planting' do + before { get :edit, params: { slug: not_my_planting } } + + it { expect(response).to redirect_to(root_path) } + end + end end diff --git a/spec/controllers/seeds_controller_spec.rb b/spec/controllers/seeds_controller_spec.rb index c8fdf27c8..02b539d7a 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -2,14 +2,15 @@ require 'rails_helper' -describe SeedsController do +describe SeedsController, :search do let(:owner) { FactoryBot.create(:member) } describe "GET index" do - let(:owner) { FactoryBot.create(:member) } - describe "picks up owner from params" do - before { get :index, params: { member_slug: owner.slug } } + before do + Seed.reindex + get :index, params: { member_slug: owner.slug } + end it { expect(assigns(:owner)).to eq(owner) } end @@ -25,9 +26,12 @@ describe SeedsController do end context 'with parent planting' do - let(:planting) { FactoryBot.create :planting, owner: owner } + let!(:planting) { FactoryBot.create :planting, owner: owner } - before { get :new, params: { planting_id: planting.to_param } } + before do + Seed.reindex + get :new, params: { planting_slug: planting.to_param } + end it { expect(assigns(:planting)).to eq(planting) } end diff --git a/spec/factories/crop.rb b/spec/factories/crop.rb index b2783fef0..c8a8d5f9d 100644 --- a/spec/factories/crop.rb +++ b/spec/factories/crop.rb @@ -53,6 +53,11 @@ FactoryBot.define do name { "eggplant" } end + factory :crop_with_photo do + name { 'marshmallow' } + photos { FactoryBot.create_list :photo, 1 } + end + # This should have a name that is alphabetically earlier than :uppercase # crop to ensure that the ordering tests work. factory :lowercasecrop do @@ -81,5 +86,11 @@ FactoryBot.define do approval_status { "rejected" } reason_for_rejection { "Totally fake" } end + + trait :reindex do + after(:create) do |crop, _evaluator| + crop.reindex(refresh: true) + end + end end end diff --git a/spec/factories/harvests.rb b/spec/factories/harvests.rb index d4bd0f18d..cc116b627 100644 --- a/spec/factories/harvests.rb +++ b/spec/factories/harvests.rb @@ -27,4 +27,10 @@ FactoryBot.define do trait :no_description do description { "" } end + + trait :reindex do + after(:create) do |harvest, _evaluator| + harvest.reindex(refresh: true) + end + end end diff --git a/spec/factories/photos.rb b/spec/factories/photos.rb index 405edfd7c..f1dc8839a 100644 --- a/spec/factories/photos.rb +++ b/spec/factories/photos.rb @@ -18,5 +18,11 @@ FactoryBot.define do license_name { "All rights reserved" } license_url { nil } end + + trait :reindex do + after(:create) do |photo, _evaluator| + photo.reindex(refresh: true) + end + end end end diff --git a/spec/factories/planting.rb b/spec/factories/planting.rb index cfdcd2b1c..f95a626f1 100644 --- a/spec/factories/planting.rb +++ b/spec/factories/planting.rb @@ -61,5 +61,18 @@ FactoryBot.define do crop end end + + trait :with_photo do + after(:create) do |planting, _evaluator| + planting.photos << FactoryBot.create(:photo, owner_id: planting.owner_id) + planting.save + end + end + + trait :reindex do + after(:create) do |planting, _evaluator| + planting.reindex(refresh: true) + end + end end end diff --git a/spec/factories/seeds.rb b/spec/factories/seeds.rb index deafb5302..b71aeaa4b 100644 --- a/spec/factories/seeds.rb +++ b/spec/factories/seeds.rb @@ -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 diff --git a/spec/features/crops/browse_crops_spec.rb b/spec/features/crops/browse_crops_spec.rb index ae5747b40..a3f5b3670 100644 --- a/spec/features/crops/browse_crops_spec.rb +++ b/spec/features/crops/browse_crops_spec.rb @@ -2,14 +2,18 @@ require 'rails_helper' -describe "browse crops" do - let!(:tomato) { FactoryBot.create :tomato } - let!(:maize) { FactoryBot.create :maize } - let!(:pending_crop) { FactoryBot.create :crop_request } - let!(:rejected_crop) { FactoryBot.create :rejected_crop } +describe "browse crops", :search do + let!(:tomato) { FactoryBot.create :tomato, :reindex } + let!(:maize) { FactoryBot.create :maize, :reindex } + let!(:pending_crop) { FactoryBot.create :crop_request, :reindex } + let!(:rejected_crop) { FactoryBot.create :rejected_crop, :reindex } shared_examples 'shows crops' do - before { visit crops_path } + before do + Crop.reindex + visit crops_path + end + it "has a form for sorting by" do expect(page).to have_css "select#sort" end diff --git a/spec/features/crops/crop_photos_spec.rb b/spec/features/crops/crop_photos_spec.rb index 1fc44772a..edeac6fcd 100644 --- a/spec/features/crops/crop_photos_spec.rb +++ b/spec/features/crops/crop_photos_spec.rb @@ -2,15 +2,17 @@ require 'rails_helper' -describe "crop detail page", js: true do +describe "crop detail page", :js, :search do subject { page } let!(:owner_member) { FactoryBot.create :member } - let!(:crop) { FactoryBot.create :crop } + let!(:crop) { FactoryBot.create :crop, :reindex } + let(:plant_part) { FactoryBot.create :plant_part, name: 'fruit' } + + let!(:harvest) { FactoryBot.create :harvest, crop: crop, owner: owner_member, plant_part: plant_part } let!(:planting) { FactoryBot.create :planting, crop: crop, owner: owner_member } - let!(:harvest) { FactoryBot.create :harvest, crop: crop, owner: owner_member } let!(:seed) { FactoryBot.create :seed, crop: crop, owner: owner_member } let!(:photo1) { FactoryBot.create(:photo, owner: owner_member) } @@ -27,7 +29,11 @@ describe "crop detail page", js: true do harvest.photos << photo4 seed.photos << photo5 seed.photos << photo6 + Crop.reindex visit crop_path(crop) + expect(crop.photos.count).to eq 6 + expect(crop.photos.by_model(Planting).count).to eq 2 + expect(page).to have_content 'Photos' end shared_examples "shows photos" do diff --git a/spec/features/gardens/gardens_spec.rb b/spec/features/gardens/gardens_spec.rb index c34958a36..f804bc57f 100644 --- a/spec/features/gardens/gardens_spec.rb +++ b/spec/features/gardens/gardens_spec.rb @@ -115,7 +115,7 @@ describe "Planting a crop", js: true do describe "Making a planting inactive from garden show" do it do visit garden_path(garden) - click_link(class: 'planting-menu') + click_link(class: 'planting-menu') # quick menu click_link "Mark as finished" find(".datepicker-days td.day", text: "21").click expect(page).to have_content 'Finished' diff --git a/spec/features/harvests/browse_harvests_spec.rb b/spec/features/harvests/browse_harvests_spec.rb index 2ce14f4a1..16b967f50 100644 --- a/spec/features/harvests/browse_harvests_spec.rb +++ b/spec/features/harvests/browse_harvests_spec.rb @@ -2,17 +2,21 @@ require 'rails_helper' -describe "browse harvests" do +describe "browse harvests", :search do subject { page } let!(:harvest) { create :harvest, owner: member } context 'signed in' do include_context 'signed in member' - describe 'blank optional fields' do - let!(:harvest) { create :harvest, :no_description } - before { visit harvests_path } + describe 'blank optional fields' do + let!(:harvest) { create :harvest, :no_description, :reindex } + + before do + Harvest.reindex + visit harvests_path + end it 'read more' do expect(subject).not_to have_link "Read more" @@ -20,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 diff --git a/spec/features/harvests/harvesting_a_crop_spec.rb b/spec/features/harvests/harvesting_a_crop_spec.rb index 79076a6db..9d4daa5c5 100644 --- a/spec/features/harvests/harvesting_a_crop_spec.rb +++ b/spec/features/harvests/harvesting_a_crop_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require 'custom_matchers' -describe "Harvesting a crop", :js do +describe "Harvesting a crop", :js, :search do context 'signed in' do include_context 'signed in member' let!(:maize) { create :maize } @@ -40,12 +40,15 @@ describe "Harvesting a crop", :js do expect(page).to have_content "harvest was successfully created." end - it "Clicking link to owner's profile" do - visit member_harvests_path(member) - within '.login-name' do - click_link member.login_name + describe 'member harvests' do + before { visit member_harvests_path(member) } + it { expect(page).to have_text "#{member.login_name}'s harvests" } + it "Clicking link to owner's profile" do + within '.login-name' do + click_link member.login_name + end + expect(current_path).to eq member_path(member) end - expect(current_path).to eq member_path member end describe "Harvesting from crop page" do diff --git a/spec/features/home/home_spec.rb b/spec/features/home/home_spec.rb index aaf06b7ac..c4355d2b7 100644 --- a/spec/features/home/home_spec.rb +++ b/spec/features/home/home_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe "home page" do +describe "home page", :search do subject { page } let(:member) { FactoryBot.create :member } @@ -14,19 +14,24 @@ describe "home page" do let(:seed) { FactoryBot.create :tradable_seed, owner: member, crop: crop } let(:harvest) { FactoryBot.create :harvest, owner: member, crop: crop } - let!(:tradable_seed) { FactoryBot.create :tradable_seed, finished: false } - let!(:finished_seed) { FactoryBot.create :tradable_seed, finished: true } - let!(:untradable_seed) { FactoryBot.create :untradable_seed } + let!(:tradable_seed) { FactoryBot.create :tradable_seed, :reindex, finished: false } + let!(:finished_seed) { FactoryBot.create :tradable_seed, :reindex, finished: true } + let!(:untradable_seed) { FactoryBot.create :untradable_seed, :reindex } before do # Add photos, so they can appear on home page planting.photos << photo seed.photos << photo harvest.photos << photo - Crop.reindex - end - before { visit root_path } + Crop.reindex + Planting.reindex + Seed.reindex + Harvest.reindex + Photo.reindex + + visit root_path + end shared_examples 'shows seeds' do it "show tradeable seed" do @@ -48,6 +53,7 @@ describe "home page" do it { expect(subject).to have_link href: planting_path(planting) } end end + shared_examples 'show harvests' do describe 'shows harvests section' do it { expect(subject).to have_text 'Recently Harvested' } diff --git a/spec/features/likeable_spec.rb b/spec/features/likeable_spec.rb index 0b44d3741..a253c3f3f 100644 --- a/spec/features/likeable_spec.rb +++ b/spec/features/likeable_spec.rb @@ -2,14 +2,21 @@ require 'rails_helper' -describe 'Likeable', js: true do +describe 'Likeable', :js, search: true do let(:another_member) { FactoryBot.create(:london_member) } - let!(:post) { FactoryBot.create(:post, author: member) } - let!(:photo) { FactoryBot.create(:photo, owner: member) } + let!(:post) { FactoryBot.create(:post, :reindex, author: member) } + let!(:photo) { FactoryBot.create(:photo, :reindex, owner: member) } + + before do + Photo.reindex + end include_context 'signed in member' + describe 'photos' do - let(:like_count_class) { "#photo-#{photo.id} .like-count" } + def like_count_class + "#photo-#{photo.id} .like-count" + end shared_examples 'photo can be liked' do it 'can be liked' do diff --git a/spec/features/plantings/planting_a_crop_spec.rb b/spec/features/plantings/planting_a_crop_spec.rb index 7ae4c72ad..34db09af1 100644 --- a/spec/features/plantings/planting_a_crop_spec.rb +++ b/spec/features/plantings/planting_a_crop_spec.rb @@ -3,13 +3,15 @@ require "rails_helper" require 'custom_matchers' -describe "Planting a crop", :js do +describe "Planting a crop", :js, :search do let!(:maize) { FactoryBot.create :maize } let(:garden) { FactoryBot.create :garden, owner: member, name: 'Orchard' } let!(:planting) do FactoryBot.create :planting, garden: 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 } @@ -222,6 +224,9 @@ describe "Planting a crop", :js do expect(page).to have_content "Finished" expect(page).to have_content "Aug 2014" + # ensure we've indexed in elastic search + planting.reindex(refresh: true) + # shouldn't be on the page visit plantings_path expect(page).not_to have_content "maize" diff --git a/spec/features/rss/crops_spec.rb b/spec/features/rss/crops_spec.rb index 6c1e1f3c7..7e26d30e9 100644 --- a/spec/features/rss/crops_spec.rb +++ b/spec/features/rss/crops_spec.rb @@ -4,11 +4,13 @@ require 'rails_helper' describe 'Crops RSS feed' do it 'The index feed exists' do + Crop.reindex visit crops_path(format: 'rss') # expect(page.status_code).to equal 200 end it 'The index title is what we expect' do + Crop.reindex visit crops_path(format: 'rss') expect(page).to have_content "Recently added crops (#{ENV['GROWSTUFF_SITE_NAME']})" end diff --git a/spec/features/rss/plantings_spec.rb b/spec/features/rss/plantings_spec.rb index 3e92b6173..30ffba947 100644 --- a/spec/features/rss/plantings_spec.rb +++ b/spec/features/rss/plantings_spec.rb @@ -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['GROWSTUFF_SITE_NAME']})" diff --git a/spec/features/seeds/adding_seeds_spec.rb b/spec/features/seeds/adding_seeds_spec.rb index 622dad584..1b7b3fcca 100644 --- a/spec/features/seeds/adding_seeds_spec.rb +++ b/spec/features/seeds/adding_seeds_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require 'custom_matchers' -describe "Seeds", :js do +describe "Seeds", :js, :search do context 'signed in' do include_context 'signed in member' let!(:maize) { create :maize } diff --git a/spec/models/crop_companion_spec.rb b/spec/models/crop_companion_spec.rb index e1cedbfd0..52be791e2 100644 --- a/spec/models/crop_companion_spec.rb +++ b/spec/models/crop_companion_spec.rb @@ -3,5 +3,12 @@ require 'rails_helper' RSpec.describe CropCompanion, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + it 'has a crop' do + cc = CropCompanion.new + cc.crop_a = FactoryBot.create :tomato + cc.crop_b = FactoryBot.create :maize + cc.save! + + expect(cc.crop_a.name).to eq 'tomato' + end end diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb index aeb5797e3..bd012ae42 100644 --- a/spec/models/like_spec.rb +++ b/spec/models/like_spec.rb @@ -5,6 +5,7 @@ require 'rails_helper' describe 'like' do let(:member) { FactoryBot.create(:member) } let(:post) { FactoryBot.create(:post) } + let(:photo) { FactoryBot.create :photo } context 'existing like' do before do @@ -61,4 +62,14 @@ describe 'like' do member.destroy expect(Like.all).not_to include like end + + it 'liked_by_members_names' do + expect(post.liked_by_members_names).to eq [] + Like.create(member: member, likeable: post) + expect(post.liked_by_members_names).to eq [member.login_name] + + expect(photo.liked_by_members_names).to eq [] + Like.create(member: member, likeable: photo) + expect(photo.liked_by_members_names).to eq [member.login_name] + end end diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb index b7ca51898..16219c588 100644 --- a/spec/models/photo_spec.rb +++ b/spec/models/photo_spec.rb @@ -3,8 +3,7 @@ require 'rails_helper' describe Photo do - let(:photo) { FactoryBot.create(:photo, owner: member) } - let(:old_photo) { FactoryBot.create(:photo, owner: member, created_at: 1.year.ago, date_taken: 2.years.ago) } + let(:photo) { FactoryBot.create(:photo, :reindex, owner: member) } let(:member) { FactoryBot.create(:member) } it_behaves_like "it is likeable" @@ -20,13 +19,14 @@ describe Photo do describe 'to a planting' do before { planting.photos << photo } - it { expect(planting.photos.size).to eq 1 } + it { expect(planting.photos.count).to eq 1 } it { expect(planting.photos.first).to eq photo } # there's only one photo, so that's the default it { expect(planting.default_photo).to eq photo } it { expect(planting.crop.default_photo).to eq photo } describe 'with a second older photo' do + let(:old_photo) { FactoryBot.create(:photo, owner: member, created_at: 1.year.ago, date_taken: 2.years.ago) } # Add an old photo before { planting.photos << old_photo } it { expect(planting.default_photo).to eq photo } @@ -40,28 +40,45 @@ describe Photo do end end - it 'to a harvest' do - harvest.photos << photo - expect(harvest.photos.size).to eq 1 - expect(harvest.photos.first).to eq photo + describe 'to a harvest' do + let(:crop) { harvest.crop } + before { harvest.photos << photo } + + it { expect(harvest.photos).to eq [photo] } + it { expect(harvest.photo_associations.count).to eq 1 } + + # Check the relationship from crop + it { expect(crop.photo_associations.count).to eq 1 } + it { expect(crop.photos.count).to eq 1 } + it { expect(crop.photos).to eq [photo] } + + # Check the relationship from the photo + it { expect(photo.photo_associations.count).to eq 1 } + it { expect(photo.photo_associations.map(&:crop)).to eq [ crop ] } + it { expect(photo.crops.count).to eq 1 } + it { expect(photo.crops).to eq [crop] } end it 'to a seed' do seed.photos << photo - expect(seed.photos.size).to eq 1 - expect(seed.photos.first).to eq photo + expect(seed.photos).to eq [photo] + expect(photo.crops).to eq [seed.crop] + end + + it 'to a planting' do + planting.photos << photo + expect(planting.photos).to eq [photo] + expect(photo.crops).to eq [planting.crop] end it 'to a garden' do garden.photos << photo - expect(garden.photos.size).to eq 1 - expect(garden.photos.first).to eq photo + expect(garden.photos).to eq [photo] end it 'to a post' do post.photos << photo - expect(post.photos.size).to eq 1 - expect(post.photos.first).to eq photo + expect(post.photos).to eq [photo] end end @@ -69,19 +86,19 @@ describe Photo do it 'from a planting' do planting.photos << photo photo.destroy - expect(planting.photos.size).to eq 0 + expect(planting.photos.count).to eq 0 end it 'from a harvest' do harvest.photos << photo photo.destroy - expect(harvest.photos.size).to eq 0 + expect(harvest.photos.count).to eq 0 end it 'from a garden' do garden.photos << photo photo.destroy - expect(garden.photos.size).to eq 0 + expect(garden.photos.count).to eq 0 end it "automatically if unused" do @@ -117,23 +134,23 @@ describe Photo do planting.photos << photo harvest.photos << photo garden.photos << photo - expect(photo.plantings.size).to eq 1 - expect(photo.harvests.size).to eq 1 - expect(photo.gardens.size).to eq 1 + expect(photo.plantings.count).to eq 1 + expect(photo.harvests.count).to eq 1 + expect(photo.gardens.count).to eq 1 planting.destroy # photo is still used by harvest and garden photo.reload - expect(photo.plantings.size).to eq 0 - expect(photo.harvests.size).to eq 1 + expect(photo.plantings.count).to eq 0 + expect(photo.harvests.count).to eq 1 harvest.destroy garden.destroy # photo is now no longer used by anything photo.reload - expect(photo.plantings.size).to eq 0 - expect(photo.harvests.size).to eq 0 - expect(photo.gardens.size).to eq 0 + expect(photo.plantings.count).to eq 0 + expect(photo.harvests.count).to eq 0 + expect(photo.gardens.count).to eq 0 photo.destroy_if_unused expect(-> { photo.reload }).to raise_error ActiveRecord::RecordNotFound end @@ -163,16 +180,16 @@ describe Photo do expect(Photo.joins(:owner).all).not_to include(photo) end - describe 'scopes' do - let(:harvest_crop) { FactoryBot.create :crop } + describe 'assocations' do + let(:harvest_crop) { FactoryBot.create :crop, name: 'harvest_crop' } let!(:harvest) { FactoryBot.create :harvest, owner: member, crop: harvest_crop } let!(:harvest_photo) { FactoryBot.create :photo, owner: member } - let(:planting_crop) { FactoryBot.create :crop } + let(:planting_crop) { FactoryBot.create :crop, name: 'planting_crop' } let!(:planting) { FactoryBot.create :planting, owner: member, crop: planting_crop } let!(:planting_photo) { FactoryBot.create :photo, owner: member } - let(:seed_crop) { FactoryBot.create :crop } + let(:seed_crop) { FactoryBot.create :crop, name: 'seed_crop' } let!(:seed) { FactoryBot.create :seed, owner: member, crop: seed_crop } let!(:seed_photo) { FactoryBot.create :photo, owner: member } @@ -180,14 +197,70 @@ describe Photo do harvest.photos << harvest_photo planting.photos << planting_photo seed.photos << seed_photo + + # harvest_photo.reload + # harvest.reload + # # harvest.reindex + + # planting_photo.reload + # planting.reload + # # planting.reindex + + # seed_photo.reload + # seed.reload + # seed.reindex end - it { expect(Photo.by_model(Harvest)).to eq([harvest_photo]) } - it { expect(Photo.by_model(Planting)).to eq([planting_photo]) } - it { expect(Photo.by_model(Seed)).to eq([seed_photo]) } + describe 'relationships' do + it { expect(seed_photo.crops).to eq [seed_crop] } + it { expect(seed_crop.photos).to eq [seed_photo] } - it { expect(Photo.by_crop(harvest_crop)).to eq([harvest_photo]) } - it { expect(Photo.by_crop(planting_crop)).to eq([planting_photo]) } - it { expect(Photo.by_crop(seed_crop)).to eq([seed_photo]) } + it { expect(harvest_photo.crops).to eq [harvest_crop] } + it { expect(harvest_crop.photos).to eq [harvest_photo] } + + it { expect(planting_photo.crops).to eq [planting_crop] } + it { expect(planting_crop.photos).to eq [planting_photo] } + end + + describe 'scopes' do + it { expect(Photo.by_model(Harvest)).to eq([harvest_photo]) } + it { expect(Photo.by_model(Planting)).to eq([planting_photo]) } + it { expect(Photo.by_model(Seed)).to eq([seed_photo]) } + + it { expect(Photo.by_crop(harvest_crop)).to eq([harvest_photo]) } + it { expect(Photo.by_crop(planting_crop)).to eq([planting_photo]) } + it { expect(Photo.by_crop(seed_crop)).to eq([seed_photo]) } + end + end + + describe 'Elastic search indexing', search: true do + let!(:planting) { FactoryBot.create(:planting, :reindex, owner: photo.owner) } + let!(:crop) { FactoryBot.create :crop, :reindex } + + before do + planting.photos << photo + Photo.reindex + Photo.searchkick_index.refresh + end + + describe "finds all photos in search index" do + it "finds just one" do + expect(Photo.search.count).to eq 1 + end + it "finds the matching photo" do + expect(Photo.search).to include photo + end + + it "retrieves crops from ES" do + expect(Photo.search(load: false).first.crops).to eq [planting.crop.id] + end + end + + it "finds photos by owner in search index" do + expect(Photo.search(where: { owner_id: planting.owner_id })).to include photo + end + it "finds photos by crop in search index" do + expect(Photo.search(where: { crops: planting.crop.id })).to include photo + end end end diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 34d5696a8..37016b497 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -179,8 +179,8 @@ describe Planting do before do FactoryBot.create(:harvest, - planting: planting, - crop: planting.crop, + planting: planting, + crop: planting.crop, harvested_at: 10.days.ago) planting.update_harvest_days! planting.crop.update_harvest_medians @@ -232,8 +232,11 @@ describe Planting do before do # Near by planting with harvests nearby_garden = FactoryBot.create :garden, location: 'Greenwich, UK' - nearby_planting = FactoryBot.create :planting, crop: crop, - garden: nearby_garden, owner: nearby_garden.owner, planted_at: '1 January 2000' + nearby_planting = FactoryBot.create(:planting, + crop: crop, + garden: nearby_garden, + owner: nearby_garden.owner, + planted_at: '1 January 2000') FactoryBot.create :harvest, planting: nearby_planting, crop: crop, harvested_at: '1 May 2019' FactoryBot.create :harvest, planting: nearby_planting, crop: crop, @@ -354,9 +357,11 @@ describe Planting do end it 'all valid planted_from values should work' do - ['seed', 'seedling', 'cutting', 'root division', - 'runner', 'bare root plant', 'advanced plant', - 'graft', 'layering', 'bulb', 'root/tuber', nil, ''].each do |p| + [ + 'seed', 'seedling', 'cutting', 'root division', + 'runner', 'bare root plant', 'advanced plant', + 'graft', 'layering', 'bulb', 'root/tuber', nil, '' + ].each do |p| @planting = FactoryBot.build(:planting, planted_from: p) @planting.should be_valid end @@ -407,16 +412,10 @@ describe Planting do before do # plantings have members created implicitly for them # each member is different, hence these are all interesting - @planting1 = FactoryBot.create(:planting, planted_at: 5.days.ago) - @planting2 = FactoryBot.create(:planting, planted_at: 4.days.ago) - @planting3 = FactoryBot.create(:planting, planted_at: 3.days.ago) - @planting4 = FactoryBot.create(:planting, planted_at: 2.days.ago) - - # plantings need photos to be interesting - [@planting1, @planting2, @planting3, @planting4].each do |p| - p.photos << FactoryBot.create(:photo, owner_id: p.owner_id) - p.save - end + @planting1 = FactoryBot.create(:planting, :with_photo, planted_at: 5.days.ago) + @planting2 = FactoryBot.create(:planting, :with_photo, planted_at: 4.days.ago) + @planting3 = FactoryBot.create(:planting, :with_photo, planted_at: 3.days.ago) + @planting4 = FactoryBot.create(:planting, :with_photo, planted_at: 2.days.ago) end it { expect(Planting.interesting).to eq([@planting4, @planting3, @planting2, @planting1]) } @@ -445,8 +444,8 @@ describe Planting do # this one is newer, and has the same owner, through the garden @planting2 = FactoryBot.create(:planting, created_at: 1.minute.ago, - garden: @planting1.garden, - owner: @planting1.owner) + garden: @planting1.garden, + owner: @planting1.owner) @planting2.photos << FactoryBot.create(:photo, owner: @planting2.owner) @planting2.save @@ -541,4 +540,16 @@ describe Planting do it { expect(member.plantings.active).to include(planting) } it { expect(member.plantings.active).not_to include(finished_planting) } end + + describe 'homepage', :search do + let!(:interesting_planting) { FactoryBot.create :planting, :reindex, :with_photo } + let!(:finished_interesting_planting) { FactoryBot.create :finished_planting, :reindex, :with_photo } + let!(:planting) { FactoryBot.create :planting, :reindex } + + before { Planting.reindex } + subject { Planting.homepage_records(100) } + + it { expect(subject.count).to eq 2 } + it { expect(subject.map(&:id)).to eq([interesting_planting.id.to_s, finished_interesting_planting.id.to_s]) } + end end diff --git a/spec/models/seed_spec.rb b/spec/models/seed_spec.rb index 1729f5dad..1c4aea5fc 100644 --- a/spec/models/seed_spec.rb +++ b/spec/models/seed_spec.rb @@ -63,23 +63,23 @@ describe Seed do @seed.should_not be_valid end - it 'tradable? gives the right answers' do + it 'tradable gives the right answers' do @seed = FactoryBot.create(:seed, tradable_to: 'nowhere') - @seed.tradable?.should eq false + @seed.tradable.should eq false @seed = FactoryBot.create(:seed, tradable_to: 'locally') - @seed.tradable?.should eq true + @seed.tradable.should eq true @seed = FactoryBot.create(:seed, tradable_to: 'nationally') - @seed.tradable?.should eq true + @seed.tradable.should eq true @seed = FactoryBot.create(:seed, tradable_to: 'internationally') - @seed.tradable?.should eq true + @seed.tradable.should eq true end it 'recognises a tradable seed' do - FactoryBot.create(:tradable_seed).tradable?.should == true + FactoryBot.create(:tradable_seed).tradable.should == true end it 'recognises an untradable seed' do - FactoryBot.create(:untradable_seed).tradable?.should == false + FactoryBot.create(:untradable_seed).tradable.should == false end it 'scopes correctly' do @@ -197,4 +197,16 @@ describe Seed do end end end + + describe 'homepage', :search do + let!(:tradable_seed) { FactoryBot.create :tradable_seed, :reindex, finished: false } + let!(:finished_seed) { FactoryBot.create :tradable_seed, :reindex, finished: true } + let!(:untradable_seed) { FactoryBot.create :untradable_seed, :reindex } + + before { Seed.reindex } + subject { Seed.homepage_records(100) } + + it { expect(subject.count).to eq 1 } + it { expect(subject.first.id).to eq tradable_seed.id.to_s } + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 5082ce6ab..24d044f26 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -59,8 +59,8 @@ include Warden::Test::Helpers # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } -Dir[Rails.root.join("spec/features/shared_examples/**/*.rb")].each { |f| require f } +Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |f| require f } +Dir[Rails.root.join("spec/features/shared_examples/**/*.rb")].sort.each { |f| require f } # Checks for pending migrations before tests are run. # If you are not using ActiveRecord, you can remove this line. diff --git a/spec/requests/harvests_spec.rb b/spec/requests/harvests_spec.rb index c613de081..839d588f7 100644 --- a/spec/requests/harvests_spec.rb +++ b/spec/requests/harvests_spec.rb @@ -5,9 +5,8 @@ require 'rails_helper' describe "Harvests" do describe "GET /harvests" do it "works! (now write some real specs)" do - # Run the generator again with the --webrat flag if you want to use webrat methods/matchers get harvests_path - response.status.should be(200) + expect(response.status).to be 200 end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9b30a0829..b8d696783 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -20,6 +20,7 @@ require 'simplecov' require 'percy' SimpleCov.start + RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest @@ -36,9 +37,17 @@ RSpec.configure do |config| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end - config.before(:suite) do + def index_everything # reindex models Crop.reindex + Harvest.reindex + Photo.reindex + Planting.reindex + Seed.reindex + end + + config.before(:suite) do + index_everything # and disable callbacks Searchkick.disable_callbacks @@ -46,7 +55,9 @@ RSpec.configure do |config| config.around(:each, search: true) do |example| Searchkick.callbacks(true) do + index_everything example.run + index_everything end end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index 7fc24c464..89d2d4432 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -10,8 +10,8 @@ describe "forums/show" do it "renders attributes" do render - rendered.should have_content "Everything about permaculture" - rendered.should have_content @forum.owner.to_s + expect(rendered).to have_content "Everything about permaculture" + expect(rendered).to have_content @forum.owner.to_s end it "parses markdown description into html" do @@ -26,14 +26,14 @@ describe "forums/show" do it 'has no posts' do render - rendered.should have_content "No posts yet." + expect(rendered).to have_content "No posts yet." end it 'shows posts' do @post = FactoryBot.create(:post, forum: @forum) render assert_select "table" - rendered.should have_content @post.subject - rendered.should have_content @post.author.to_s + expect(rendered).to have_content @post.subject + expect(rendered).to have_content @post.author.to_s end end diff --git a/spec/views/harvests/index.rss.haml_spec.rb b/spec/views/harvests/index.rss.haml_spec.rb index 47d28a9d6..e4384c474 100644 --- a/spec/views/harvests/index.rss.haml_spec.rb +++ b/spec/views/harvests/index.rss.haml_spec.rb @@ -2,43 +2,38 @@ require 'rails_helper' -describe 'harvests/index.rss.haml' do +describe 'harvests/index.rss.haml', :search do before do controller.stub(:current_user) { nil } @member = FactoryBot.create(:member) @tomato = FactoryBot.create(:tomato) - @maize = FactoryBot.create(:maize) - @pp = FactoryBot.create(:plant_part) - page = 1 - per_page = 2 - total_entries = 2 - harvests = WillPaginate::Collection.create(page, per_page, total_entries) do |pager| - pager.replace([ - FactoryBot.create(:harvest, - crop: @tomato, - owner: @member), - FactoryBot.create(:harvest, - crop: @maize, - plant_part: @pp, - owner: @member, - quantity: 2) - ]) + + @harvest1 = FactoryBot.create :harvest, crop: @tomato + @harvest2 = FactoryBot.create :harvest, crop: @tomato + @harvest3 = FactoryBot.create :harvest, crop: @tomato + + Harvest.searchkick_index.refresh + assign(:harvests, Harvest.search(load: false)) + end + + context 'all harvests' do + before { render } + it 'shows RSS feed title' do + expect(rendered).to have_content "Recent harvests from all members" + end + + it 'shows formatted content of harvest posts' do + expect(rendered).to have_content "Quantity: " end - assign(:harvests, harvests) - render end - it 'shows RSS feed title' do - rendered.should have_content "Recent harvests from all members" - end - - it "displays crop's name in title" do - assign(:crop, @tomato) - render - expect(rendered).to have_content @tomato.name - end - - it 'shows formatted content of harvest posts' do - expect(rendered).to have_content "
Quantity: " + context 'for one crop' do + before do + assign(:crop, @tomato) + render + end + it "displays crop's name in title" do + expect(rendered).to have_content @tomato.name + end end end diff --git a/spec/views/home/_seeds.html.haml_spec.rb b/spec/views/home/_seeds.html.haml_spec.rb index 346a2952d..02dd6e9ff 100644 --- a/spec/views/home/_seeds.html.haml_spec.rb +++ b/spec/views/home/_seeds.html.haml_spec.rb @@ -2,10 +2,13 @@ require 'rails_helper' -describe 'home/_seeds.html.haml', type: "view" do +describe 'home/_seeds.html.haml', type: "view", search: true do let!(:seed) { FactoryBot.create(:tradable_seed, owner: owner) } let(:owner) { FactoryBot.create(:london_member) } - before { render } + before do + Seed.searchkick_index.refresh + render + end it 'has a heading' do assert_select 'h2', 'Seeds available to trade' diff --git a/spec/views/plantings/index.rss.haml_spec.rb b/spec/views/plantings/index.rss.haml_spec.rb index b0f3be6cc..6592f9919 100644 --- a/spec/views/plantings/index.rss.haml_spec.rb +++ b/spec/views/plantings/index.rss.haml_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe 'plantings/index.rss.haml' do +describe 'plantings/index.rss.haml', :search do before do controller.stub(:current_user) { nil } end @@ -12,28 +12,25 @@ describe 'plantings/index.rss.haml' do @planting = FactoryBot.create(:planting) @sunny = FactoryBot.create(:sunny_planting) @seedling = FactoryBot.create(:seedling_planting) - assign(:plantings, [@planting, @sunny, @seedling]) + Planting.searchkick_index.refresh + assign(:plantings, Planting.search(load: false)) render end it 'shows RSS feed title' do - rendered.should have_content "Recent plantings from all members" + expect(rendered).to have_content "Recent plantings from all members" end it 'item title shows owner and location' do - rendered.should have_content "#{@planting.crop} in #{@planting.location}" - end - - it 'shows formatted content of posts' do - rendered.should have_content "This is a really good plant." + expect(rendered).to have_content "#{@planting.crop.name} in #{@planting.location}" end it 'shows sunniness' do - rendered.should have_content 'Sunniness: sun' + expect(rendered).to have_content 'Sunniness: sun' end it 'shows propagation method' do - rendered.should have_content 'Planted from: seedling' + expect(rendered).to have_content 'Planted from: seedling' end end @@ -46,7 +43,7 @@ describe 'plantings/index.rss.haml' do end it 'shows title for single member' do - rendered.should have_content "Recent plantings from #{@planting.owner}" + expect(rendered).to have_content "Recent plantings from #{@planting.owner}" end end end diff --git a/spec/views/seeds/index.rss.haml_spec.rb b/spec/views/seeds/index.rss.haml_spec.rb index 8e074a80c..7940824fe 100644 --- a/spec/views/seeds/index.rss.haml_spec.rb +++ b/spec/views/seeds/index.rss.haml_spec.rb @@ -2,50 +2,63 @@ require 'rails_helper' -describe 'seeds/index.rss.haml' do +describe 'seeds/index.rss.haml', :search do before do controller.stub(:current_user) { nil } end - context 'all seeds' do - before do - @seed = FactoryBot.create(:seed) - @tradable = FactoryBot.create(:tradable_seed) - assign(:seeds, [@seed, @tradable]) - render - end - - it 'shows RSS feed title' do - rendered.should have_content "Recent seeds from all members" - end - + shared_examples 'displays seed in rss feed' do it 'has a useful item title' do - rendered.should have_content "#{@seed.owner}'s #{@seed.crop} seeds" + expect(rendered).to have_content "#{seed.owner.login_name}'s #{seed.crop.name} seeds" end it 'shows the seed count' do - rendered.should have_content "Quantity: #{@seed.quantity}" + expect(rendered).to have_content "Quantity: #{seed.quantity}" end it 'shows the plant_before date' do - rendered.should have_content "Plant before: #{@seed.plant_before}" + expect(rendered).to have_content "Plant before: #{seed.plant_before.to_s(:ymd)}" + end + end + + context 'all seeds' do + let!(:seed) { FactoryBot.create(:seed) } + let!(:tradable) { FactoryBot.create(:tradable_seed) } + before do + Seed.searchkick_index.refresh + assign(:seeds, Seed.search(load: false)) + render + end + + include_examples 'displays seed in rss feed' + + it 'shows RSS feed title' do + expect(rendered).to have_content "Recent seeds from all members" end it 'mentions that one seed is tradable' do - rendered.should have_content "Will trade #{@tradable.tradable_to} from #{@tradable.owner.location}" + expect(rendered).to have_content "Will trade #{tradable.tradable_to} from #{tradable.owner.location}" + end + + it "does not offer untradable seed as tradeable" do + expect(rendered).not_to have_content "Will trade #{seed.tradable_to} from #{seed.owner.location}" end end context "one member's seeds" do + let!(:seed) { FactoryBot.create(:seed) } + before do - @seed = FactoryBot.create(:seed) - assign(:seeds, [@seed]) - assign(:owner, @seed.owner) + assign(:owner, seed.owner) + Seed.searchkick_index.refresh + assign(:seeds, Seed.search(load: false)) render end it 'shows RSS feed title' do - rendered.should have_content "Recent seeds from #{@seed.owner}" + expect(rendered).to have_content "Recent seeds from #{seed.owner}" end + + include_examples 'displays seed in rss feed' end end