diff --git a/Gemfile.lock b/Gemfile.lock
index b4afcdd92..c62dbb628 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -86,7 +86,7 @@ GEM
uniform_notifier (~> 1.11)
byebug (11.0.1)
cancancan (3.0.1)
- capybara (3.25.0)
+ capybara (3.26.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
@@ -104,7 +104,7 @@ GEM
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
- chartkick (3.2.0)
+ chartkick (3.2.1)
childprocess (1.0.1)
rake (< 13.0)
codeclimate-test-reporter (1.0.9)
@@ -181,7 +181,7 @@ GEM
figaro (1.1.1)
thor (~> 0.14)
flickraw (0.9.10)
- font-awesome-sass (5.8.1)
+ font-awesome-sass (5.9.0)
sassc (>= 1.11)
friendly_id (5.2.5)
activerecord (>= 4.0.0)
@@ -253,7 +253,7 @@ GEM
railties (>= 4)
sprockets-rails
json (2.2.0)
- jsonapi-resources (0.9.9)
+ jsonapi-resources (0.9.10)
activerecord (>= 4.1)
concurrent-ruby
railties (>= 4.1)
@@ -303,8 +303,8 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2019.0331)
mimemagic (0.3.3)
- mini_magick (4.9.3)
- mini_mime (1.0.1)
+ mini_magick (4.9.4)
+ mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.11.3)
moneta (1.0.0)
@@ -312,7 +312,7 @@ GEM
multi_xml (0.6.0)
multipart-post (2.1.1)
newrelic_rpm (6.5.0.357)
- nio4r (2.3.1)
+ nio4r (2.4.0)
nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
oauth (0.5.4)
@@ -322,7 +322,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
- oj (3.7.12)
+ oj (3.8.0)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3)
@@ -353,7 +353,7 @@ GEM
moneta (~> 1.0.0)
popper_js (1.14.5)
public_suffix (3.1.1)
- puma (4.0.0)
+ puma (4.0.1)
nio4r (~> 2.0)
rack (2.0.7)
rack-protection (2.0.5)
@@ -431,14 +431,14 @@ GEM
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.2)
- rubocop (0.72.0)
+ rubocop (0.73.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
- rubocop-rails (2.2.0)
+ rubocop-rails (2.2.1)
rack (>= 1.1)
rubocop (>= 0.72.0)
rubocop-rspec (1.33.0)
@@ -518,7 +518,7 @@ GEM
uniform_notifier (1.12.1)
warden (1.2.8)
rack (>= 2.0.6)
- webdrivers (4.1.0)
+ webdrivers (4.1.1)
nokogiri (~> 1.6)
rubyzip (~> 1.0)
selenium-webdriver (>= 3.0, < 4.0)
diff --git a/app/assets/javascripts/likes.js b/app/assets/javascripts/likes.js
new file mode 100644
index 000000000..60b3e57e7
--- /dev/null
+++ b/app/assets/javascripts/likes.js
@@ -0,0 +1,40 @@
+$(document).ready(function() {
+ $('.like-btn').show();
+
+ $('.post-like').on('ajax:success', function(event, data) {
+ var likeButton = $('#post-' + data.id + ' .post-like');
+ var likeBadge = $('#post-'+ data.id + ' .like-badge');
+
+ $('#post-' + data.id + ' .like-count').text(data.like_count);
+ if (data.liked_by_member) {
+ likeBadge.addClass('liked');
+ likeButton.data('method', 'delete');
+ likeButton.attr('href', data.url);
+ likeButton.text('Unlike');
+ } else {
+ likeBadge.removeClass('liked');
+ likeButton.data('method', 'post');
+ likeButton.attr('href', '/likes.json?post_id=' + data.id);
+ likeButton.text('Like');
+ }
+ });
+
+
+ $('.photo-like').on('ajax:success', function(event, data) {
+ var likeBadge = $('#photo-'+ data.id + ' .like-badge');
+ var likeButton = $('#photo-'+ data.id + ' .like-btn');
+
+ $('#photo-' + data.id + ' .like-count').text(data.like_count);
+ if (data.liked_by_member) {
+ likeBadge.addClass('liked');
+ // Turn the button into an unlike button
+ likeButton.data('method', 'delete');
+ likeButton.attr('href', data.url);
+ } else {
+ likeBadge.removeClass('liked');
+ // Turn the button into an *like* button
+ likeButton.data('method', 'post');
+ likeButton.attr('href', '/likes.json?photo_id=' + data.id);
+ }
+ });
+});
diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js
deleted file mode 100644
index 37269e78e..000000000
--- a/app/assets/javascripts/posts.js
+++ /dev/null
@@ -1,18 +0,0 @@
-$(document).ready(function() {
- $('.post-like').show();
-
- $('.post-like').on('ajax:success', function(event, data) {
- var likeControl = $('#post-' + data.id + ' .post-like');
-
- $('#post-' + data.id + ' .like-count').text(data.description);
- if (data.liked_by_member) {
- likeControl.data('method', 'delete');
- likeControl.attr('href', data.url);
- likeControl.text('Unlike');
- } else {
- likeControl.data('method', 'post');
- likeControl.attr('href', '/likes.json?post_id=' + data.id);
- likeControl.text('Like');
- }
- });
-});
diff --git a/app/assets/stylesheets/_likes.scss b/app/assets/stylesheets/_likes.scss
new file mode 100644
index 000000000..39bad5408
--- /dev/null
+++ b/app/assets/stylesheets/_likes.scss
@@ -0,0 +1,7 @@
+.liked {
+ color: $red;
+}
+
+.like-btn {
+ color: $brown;
+}
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 03c66c680..cdd758685 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -16,14 +16,6 @@
@import 'rails_bootstrap_forms';
@import 'overrides';
-@import 'crops';
-@import 'harvests';
-@import 'members';
-@import 'notifications';
-@import 'plantings';
-@import 'posts';
-@import 'predictions';
-@import 'seeds';
@import 'homepage';
@import 'photos';
diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb
index e942d5b1f..2837dfb0a 100644
--- a/app/controllers/likes_controller.rb
+++ b/app/controllers/likes_controller.rb
@@ -19,12 +19,17 @@ class LikesController < ApplicationController
private
def find_likeable
- Post.find(params[:post_id]) if params[:post_id]
+ 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,
liked_by_member: liked_by_member,
description: ActionController::Base.helpers.pluralize(like.likeable.likes.count, "like"),
url: like_path(like, format: :json)
@@ -35,7 +40,8 @@ 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),
+ render(json: render_json(like,
+ liked_by_member: liked_by_member),
status: status_code)
end
end
diff --git a/app/controllers/photo_associations_controller.rb b/app/controllers/photo_associations_controller.rb
index 743966d5d..499dbefc0 100644
--- a/app/controllers/photo_associations_controller.rb
+++ b/app/controllers/photo_associations_controller.rb
@@ -6,7 +6,7 @@ class PhotoAssociationsController < ApplicationController
raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class
@photo = Photo.find_by!(id: params[:photo_id], owner: current_member)
- @item = Photographing.item(item_id, item_class)
+ @item = PhotoAssociation.item(item_id, item_class)
@item.photos.delete(@photo)
# @photo.destroy_if_unused
respond_with(@photo)
diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb
index a38dc6130..fe3f84d02 100644
--- a/app/controllers/photos_controller.rb
+++ b/app/controllers/photos_controller.rb
@@ -6,7 +6,7 @@ class PhotosController < ApplicationController
responders :flash
def show
- @crops = Crop.distinct.joins(:photographings).where(photographings: { photo: @photo })
+ @crops = Crop.distinct.joins(:photo_associations).where(photo_associations: { photo: @photo })
respond_with(@photo)
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 2fa078313..a691b5218 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -70,7 +70,7 @@ module IconsHelper
end
def like_icon
- icon('fas', 'thumbs-up')
+ icon('fas', 'heart')
end
def sunniness_icon(sunniness)
diff --git a/app/models/concerns/likeable.rb b/app/models/concerns/likeable.rb
index 328cf3153..4937af53e 100644
--- a/app/models/concerns/likeable.rb
+++ b/app/models/concerns/likeable.rb
@@ -5,4 +5,8 @@ module Likeable
has_many :likes, as: :likeable, inverse_of: :likeable, dependent: :destroy
has_many :members, through: :likes
end
+
+ def liked_by?(member)
+ member && members.include?(member)
+ end
end
diff --git a/app/models/concerns/photo_capable.rb b/app/models/concerns/photo_capable.rb
index 3ae7303d9..dfddef42c 100644
--- a/app/models/concerns/photo_capable.rb
+++ b/app/models/concerns/photo_capable.rb
@@ -2,9 +2,17 @@ module PhotoCapable
extend ActiveSupport::Concern
included do
- has_many :photographings, as: :photographable, dependent: :destroy, inverse_of: :photographable
- has_many :photos, through: :photographings, as: :photographable
+ has_many :photo_associations, as: :photographable, dependent: :destroy, inverse_of: :photographable
+ has_many :photos, through: :photo_associations, as: :photographable
scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) }
+
+ def default_photo
+ most_liked_photo
+ end
+
+ def most_liked_photo
+ photos.order(likes_count: :desc, created_at: :desc).first
+ end
end
end
diff --git a/app/models/crop.rb b/app/models/crop.rb
index 4e56d4645..12eb01975 100644
--- a/app/models/crop.rb
+++ b/app/models/crop.rb
@@ -1,5 +1,6 @@
class Crop < ApplicationRecord
extend FriendlyId
+ include PhotoCapable
friendly_id :name, use: %i(slugged finders)
##
@@ -14,8 +15,8 @@ class Crop < ApplicationRecord
has_many :plantings, dependent: :destroy
has_many :seeds, dependent: :destroy
has_many :harvests, dependent: :destroy
- has_many :photographings, dependent: :destroy
- has_many :photos, through: :photographings
+ has_many :photo_associations, dependent: :destroy
+ has_many :photos, through: :photo_associations
has_many :plant_parts, -> { distinct.order("plant_parts.name") }, through: :harvests
belongs_to :creator, class_name: 'Member', optional: true, inverse_of: :created_crops
belongs_to :requester, class_name: 'Member', optional: true, inverse_of: :requested_crops
@@ -57,18 +58,6 @@ class Crop < ApplicationRecord
# Elastic search configuration
searchkick word_start: %i(name alternate_names scientific_names), case_sensitive: false if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
- def planting_photos
- Photo.joins(:plantings).where("plantings.crop_id": id)
- end
-
- def harvest_photos
- Photo.joins(:harvests).where("harvests.crop_id": id)
- end
-
- def seed_photos
- Photo.joins(:seeds).where("seeds.crop_id": id)
- end
-
def to_s
name
end
@@ -78,14 +67,7 @@ class Crop < ApplicationRecord
end
def default_scientific_name
- scientific_names.first.name unless scientific_names.empty?
- end
-
- # currently returns the first available photo, but exists so that
- # later we can choose a default photo based on different criteria,
- # eg. popularity
- def default_photo
- first_photo(:plantings) || first_photo(:harvests) || first_photo(:seeds)
+ scientific_names.first&.name
end
# returns hash indicating whether this crop is grown in
@@ -212,8 +194,4 @@ class Crop < ApplicationRecord
errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
end
-
- def first_photo(type)
- Photo.joins(type).where("#{type}": { crop_id: id }).order("photos.created_at DESC").first
- end
end
diff --git a/app/models/garden.rb b/app/models/garden.rb
index 6c8318f42..8eb27cc78 100644
--- a/app/models/garden.rb
+++ b/app/models/garden.rb
@@ -89,8 +89,4 @@ class Garden < ApplicationRecord
p.save
end
end
-
- def default_photo
- photos.order(created_at: :desc).first
- end
end
diff --git a/app/models/harvest.rb b/app/models/harvest.rb
index a913ec8b9..6dffbca6a 100644
--- a/app/models/harvest.rb
+++ b/app/models/harvest.rb
@@ -74,6 +74,10 @@ class Harvest < ApplicationRecord
harvested_at - planting.planted_at
end
+ def default_photo
+ most_liked_photo || planting&.default_photo
+ end
+
# we're storing the harvest weight in kilograms in the db too
# to make data manipulation easier
def set_si_weight
@@ -135,10 +139,6 @@ class Harvest < ApplicationRecord
end.to_s
end
- def default_photo
- photos.order(created_at: :desc).first || crop.default_photo
- end
-
private
def crop_must_match_planting
diff --git a/app/models/like.rb b/app/models/like.rb
index 05911fc95..f74903739 100644
--- a/app/models/like.rb
+++ b/app/models/like.rb
@@ -1,6 +1,6 @@
class Like < ApplicationRecord
belongs_to :member
- belongs_to :likeable, polymorphic: true
+ belongs_to :likeable, polymorphic: true, counter_cache: true
validates :member, :likeable, presence: true
validates :member, uniqueness: { scope: :likeable }
end
diff --git a/app/models/photo.rb b/app/models/photo.rb
index 40620b6b5..de44ebbbd 100644
--- a/app/models/photo.rb
+++ b/app/models/photo.rb
@@ -1,23 +1,24 @@
class Photo < ApplicationRecord
+ include Likeable
include Ownable
PHOTO_CAPABLE = %w(Garden Planting Harvest Seed Post).freeze
- has_many :photographings, foreign_key: :photo_id, dependent: :destroy, inverse_of: :photo
- has_many :crops, through: :photographings
+ has_many :photo_associations, foreign_key: :photo_id, dependent: :destroy, inverse_of: :photo
+ has_many :crops, through: :photo_associations
# creates a relationship for each assignee type
PHOTO_CAPABLE.each do |type|
has_many type.downcase.pluralize.to_s.to_sym,
- through: :photographings,
+ through: :photo_associations,
source: :photographable,
source_type: type
end
default_scope { joins(:owner) } # Ensures the owner still exists
- scope :by_crop, ->(crop) { joins(:photographings).where(photographings: { crop: crop }) }
+ scope :by_crop, ->(crop) { joins(:photo_associations).where(photo_associations: { crop: crop }) }
scope :by_model, lambda { |model_name|
- joins(:photographings).where(photographings: { photographable_type: model_name.to_s })
+ joins(:photo_associations).where(photo_associations: { photographable_type: model_name.to_s })
}
# This is split into a side-effect free method and a side-effecting method
@@ -39,7 +40,7 @@ class Photo < ApplicationRecord
end
def associations?
- photographings.size.positive?
+ photo_associations.size.positive?
end
def destroy_if_unused
diff --git a/app/models/photographing.rb b/app/models/photo_association.rb
similarity index 88%
rename from app/models/photographing.rb
rename to app/models/photo_association.rb
index 2693725a2..24eb79e80 100644
--- a/app/models/photographing.rb
+++ b/app/models/photo_association.rb
@@ -1,5 +1,5 @@
-class Photographing < ApplicationRecord
- belongs_to :photo, inverse_of: :photographings
+class PhotoAssociation < ApplicationRecord
+ belongs_to :photo, inverse_of: :photo_associations
belongs_to :photographable, polymorphic: true
belongs_to :crop, optional: true
diff --git a/app/models/planting.rb b/app/models/planting.rb
index e57bf6fb0..67bdd281d 100644
--- a/app/models/planting.rb
+++ b/app/models/planting.rb
@@ -85,10 +85,6 @@ class Planting < ApplicationRecord
I18n.t('plantings.string', crop: crop.name, garden: garden.name, owner: owner)
end
- def default_photo
- photos.order(created_at: :desc).first
- end
-
def finished?
finished || (finished_at.present? && finished_at <= Time.zone.today)
end
diff --git a/app/models/seed.rb b/app/models/seed.rb
index 8025f66e2..b335127c0 100644
--- a/app/models/seed.rb
+++ b/app/models/seed.rb
@@ -55,10 +55,6 @@ class Seed < ApplicationRecord
scope :recent, -> { order(created_at: :desc) }
scope :active, -> { where('finished_at < ?', Time.zone.now) }
- def default_photo
- photos.order(created_at: :desc).first
- end
-
def tradable?
tradable_to != 'nowhere'
end
diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml
index f21c5f93a..5586cfec3 100644
--- a/app/views/admin/index.html.haml
+++ b/app/views/admin/index.html.haml
@@ -1,11 +1,7 @@
- content_for :title, 'Admin'
- content_for :breadcrumbs do
- %nav{"aria-label" => "breadcrumb"}
- %ol.breadcrumb
- %li.breadcrumb-item
- = link_to 'Home', root_path
- %li.breadcrumb-item.active= link_to 'Admin', admin_path
+ %li.breadcrumb-item.active= link_to 'Admin', admin_path
%h2 Manage
diff --git a/app/views/crops/_photos.html.haml b/app/views/crops/_photos.html.haml
index 170867ed8..71358d541 100644
--- a/app/views/crops/_photos.html.haml
+++ b/app/views/crops/_photos.html.haml
@@ -3,7 +3,7 @@
- [Planting, Harvest, Seed].each do |model_name|
- if 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).limit(8)
+ = render 'photos/gallery', photos: photos.by_model(model_name).order(likes_count: :desc).limit(8)
= link_to 'more photos ยป', crop_photos_path(@crop), class: 'btn'
%hr/
diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml
index 075fdd4a3..3a3fe90b4 100644
--- a/app/views/devise/registrations/edit.html.haml
+++ b/app/views/devise/registrations/edit.html.haml
@@ -1,5 +1,8 @@
- content_for :title, "Settings for #{current_member.login_name}"
+- content_for :breadcrumbs do
+ %li.breadcrumb-item= link_to 'My Profile', member_path(current_member)
+
%h1
= member_icon
Settings for #{current_member.login_name}
diff --git a/app/views/likes/_count.haml b/app/views/likes/_count.haml
new file mode 100644
index 000000000..79fece151
--- /dev/null
+++ b/app/views/likes/_count.haml
@@ -0,0 +1,4 @@
+%span.badge.like-badge{class: (likeable.liked_by?(current_member)) ? 'liked' : ''}
+ = like_icon
+
+ %span.like-count= likeable.likes_count
diff --git a/app/views/photos/_association_delete_button.haml b/app/views/photos/_association_delete_button.haml
index 1a0f0510f..f4144dd48 100644
--- a/app/views/photos/_association_delete_button.haml
+++ b/app/views/photos/_association_delete_button.haml
@@ -1,5 +1,5 @@
- if can? :edit, photo
- = link_to photo_associations_path(photo_id: photo.id, type: type, id: thing.id),
+ = link_to photo_association_path(photo_id: photo.id, type: type, id: thing.id),
data: { confirm: "Removing photo from this #{type}. Are you sure?" },
method: 'delete', class: 'text-warning btn-sm' do
= delete_association_icon
diff --git a/app/views/photos/_card.html.haml b/app/views/photos/_card.html.haml
index d69c0230b..0d17281f5 100644
--- a/app/views/photos/_card.html.haml
+++ b/app/views/photos/_card.html.haml
@@ -1,9 +1,10 @@
-.card.photo-card
+.card.photo-card{id: "photo-#{photo.id}"}
= link_to image_tag(photo.fullsize_url, alt: photo.title, class: 'img img-card'), photo
.card-body
- %h3.ellipsis= link_to photo.title, photo
- %p
- %i by #{link_to photo.owner, photo.owner}
- - if photo.date_taken.present?
- %small= I18n.l(photo.date_taken.to_date)
-
+ %h5.ellipsis= link_to photo.title, photo
+ %i by #{link_to photo.owner, photo.owner}
+ - if photo.date_taken.present?
+ %small
+ %time{datetime: photo.date_taken}
+ = I18n.l(photo.date_taken.to_date)
+ = render 'photos/likes', photo: photo
diff --git a/app/views/photos/_likes.html.haml b/app/views/photos/_likes.html.haml
new file mode 100644
index 000000000..47cca8634
--- /dev/null
+++ b/app/views/photos/_likes.html.haml
@@ -0,0 +1,13 @@
+- 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
+ - 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
+- else
+ = render 'likes/count', likeable: photo
\ No newline at end of file
diff --git a/app/views/photos/_thumbnail.html.haml b/app/views/photos/_thumbnail.html.haml
index dfc6483d3..b6b619f63 100644
--- a/app/views/photos/_thumbnail.html.haml
+++ b/app/views/photos/_thumbnail.html.haml
@@ -1,5 +1,5 @@
.thumbnail
- .photo-thumbnail
+ .photo-thumbnail{id: "photo-#{photo.id}"}
= link_to image_tag(photo.thumbnail_url, alt: photo.title, class: 'img img-responsive rounded'), photo
.text.ellipsis
%p
@@ -9,4 +9,3 @@
%i
by
= link_to photo.owner, photo.owner
-
diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml
index bd8632f4d..ea309fe42 100644
--- a/app/views/photos/show.html.haml
+++ b/app/views/photos/show.html.haml
@@ -11,17 +11,23 @@
%li.breadcrumb-item= link_to 'Photos', photos_path
%li.breadcrumb-item.active= link_to @photo, @photo
-.row
+.row{id: "photo-#{@photo.id}"}
.col-md-8
%h1.text-center.ellipsis=@photo.title
%p.text-center
= image_tag(@photo.fullsize_url, alt: @photo.title, class: 'rounded img-fluid shadow-sm')
.col-md-4
- %p.text-center
+
+ %p
= render 'photos/actions', photo: @photo
= link_to "View on Flickr", @photo.link_url, class: 'btn'
+ %span.btn= render 'photos/likes', photo: @photo
+
+
- if @crops.size.positive?
- %p= render @crops
+ .index-cards
+ - @crops.each do |crop|
+ = render 'crops/thumbnail', crop: crop
%p
= photo_icon
%strong Photo by
diff --git a/app/views/posts/_likes.html.haml b/app/views/posts/_likes.html.haml
index 56ed606d7..18112c308 100644
--- a/app/views/posts/_likes.html.haml
+++ b/app/views/posts/_likes.html.haml
@@ -1,18 +1,12 @@
- if member_signed_in?
- - if !post.members.include? current_member
- - if can?(:new, Like)
- = link_to 'Like', likes_path(post_id: post.id, format: :json),
- method: :post, remote: true, class: 'post-like btn'
+ - if can?(:new, Like) && !post.liked_by?(current_member)
+ = link_to 'Like', likes_path(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'
+ method: :delete, remote: true, class: 'post-like btn like-btn'
-
-%span.badge.badge-info
- .like-count
- - unless post.likes.empty?
- = like_icon
- = pluralize(post.likes.count, "like")
+= render 'likes/count', likeable: post
diff --git a/config/environments/production.rb b/config/environments/production.rb
index fd66532d7..9adee7c27 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -100,7 +100,7 @@ Rails.application.configure do
}
ActionMailer::Base.delivery_method = :smtp
- config.host = 'growstuff.org'
+ config.host = ENV['HOST']
config.analytics_code = <<-eos
diff --git a/config/routes.rb b/config/routes.rb
index 04a1220fc..401bc3c7f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -54,7 +54,7 @@ Rails.application.routes.draw do
resources :plant_parts
resources :photos
- delete 'photo_associations' => 'photo_associations#destroy'
+ resources :photo_associations, only: :destroy
resources :crops, param: :slug, concerns: :has_photos do
get 'gardens' => 'gardens#index'
diff --git a/db/migrate/20190130090437_add_crop_to_photographings.rb b/db/migrate/20190130090437_add_crop_to_photographings.rb
index 2ff0596f0..3b4ad8788 100644
--- a/db/migrate/20190130090437_add_crop_to_photographings.rb
+++ b/db/migrate/20190130090437_add_crop_to_photographings.rb
@@ -7,4 +7,9 @@ class AddCropToPhotographings < ActiveRecord::Migration[5.2]
p.set_crop && p.save!
end
end
+ class Photographing < ApplicationRecord
+ belongs_to :photo, inverse_of: :photo_associations
+ belongs_to :photographable, polymorphic: true
+ belongs_to :crop, optional: true
+ end
end
diff --git a/db/migrate/20190711043651_create_mailboxer.mailboxer_engine.rb b/db/migrate/20190711043651_create_mailboxer.mailboxer_engine.rb
deleted file mode 100644
index 3b9acbddb..000000000
--- a/db/migrate/20190711043651_create_mailboxer.mailboxer_engine.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# This migration comes from mailboxer_engine (originally 20110511145103)
-class CreateMailboxer < ActiveRecord::Migration[4.2]
- def self.up
- # Tables
- # Conversations
- create_table :mailboxer_conversations do |t|
- t.column :subject, :string, default: ""
- t.column :created_at, :datetime, null: false
- t.column :updated_at, :datetime, null: false
- end
- # Receipts
- create_table :mailboxer_receipts do |t|
- t.references :receiver, polymorphic: true
- t.column :notification_id, :integer, null: false
- t.column :is_read, :boolean, default: false
- t.column :trashed, :boolean, default: false
- t.column :deleted, :boolean, default: false
- t.column :mailbox_type, :string, limit: 25
- t.column :created_at, :datetime, null: false
- t.column :updated_at, :datetime, null: false
- end
- # Notifications and Messages
- create_table :mailboxer_notifications do |t|
- t.column :type, :string
- t.column :body, :text
- t.column :subject, :string, default: ""
- t.references :sender, polymorphic: true
- t.column :conversation_id, :integer
- t.column :draft, :boolean, default: false
- t.string :notification_code, default: nil
- t.references :notified_object, polymorphic: true, index: { name: 'mailboxer_notifications_notified_object' }
- t.column :attachment, :string
- t.column :updated_at, :datetime, null: false
- t.column :created_at, :datetime, null: false
- t.boolean :global, default: false
- t.datetime :expires
- end
-
- # Indexes
- # Conversations
- # Receipts
- add_index "mailboxer_receipts", "notification_id"
-
- # Messages
- add_index "mailboxer_notifications", "conversation_id"
-
- # Foreign keys
- # Conversations
- # Receipts
- add_foreign_key "mailboxer_receipts", "mailboxer_notifications", name: "receipts_on_notification_id", column: "notification_id"
- # Messages
- add_foreign_key "mailboxer_notifications", "mailboxer_conversations", name: "notifications_on_conversation_id", column: "conversation_id"
- end
-
- def self.down
- # Tables
- remove_foreign_key "mailboxer_receipts", name: "receipts_on_notification_id"
- remove_foreign_key "mailboxer_notifications", name: "notifications_on_conversation_id"
-
- # Indexes
- drop_table :mailboxer_receipts
- drop_table :mailboxer_conversations
- drop_table :mailboxer_notifications
- end
-end
diff --git a/db/migrate/20190712003735_add_like_counter_caches.rb b/db/migrate/20190712003735_add_like_counter_caches.rb
new file mode 100644
index 000000000..92c292494
--- /dev/null
+++ b/db/migrate/20190712003735_add_like_counter_caches.rb
@@ -0,0 +1,34 @@
+class AddLikeCounterCaches < ActiveRecord::Migration[5.2]
+ def change
+ change_table :photos do |t|
+ t.integer :likes_count, default: 0
+ end
+ change_table :posts do |t|
+ t.integer :likes_count, default: 0
+ end
+ reversible do |dir|
+ dir.up { data }
+ end
+ end
+
+ def data
+ execute <<-SQL.squish
+ UPDATE photos
+ SET likes_count = (
+ SELECT count(1)
+ FROM likes
+ WHERE likes.likeable_id = photos.id
+ AND likeable_type = 'Photo'
+ )
+ SQL
+ execute <<-SQL.squish
+ UPDATE posts
+ SET likes_count = (
+ SELECT count(1)
+ FROM likes
+ WHERE likes.likeable_id = posts.id
+ AND likeable_type = 'Post'
+ )
+ SQL
+ end
+end
diff --git a/db/migrate/20190712234859_rename_photographings.rb b/db/migrate/20190712234859_rename_photographings.rb
new file mode 100644
index 000000000..7f9a5127e
--- /dev/null
+++ b/db/migrate/20190712234859_rename_photographings.rb
@@ -0,0 +1,5 @@
+class RenamePhotographings < ActiveRecord::Migration[5.2]
+ def change
+ rename_table :photographings, :photo_associations
+ end
+end
diff --git a/db/migrate/20190720000555_create_mailboxer.mailboxer_engine.rb b/db/migrate/20190720000555_create_mailboxer.mailboxer_engine.rb
new file mode 100644
index 000000000..d42e89f9a
--- /dev/null
+++ b/db/migrate/20190720000555_create_mailboxer.mailboxer_engine.rb
@@ -0,0 +1,65 @@
+# This migration comes from mailboxer_engine (originally 20110511145103)
+class CreateMailboxer < ActiveRecord::Migration[4.2]
+ def self.up
+ #Tables
+ #Conversations
+ create_table :mailboxer_conversations do |t|
+ t.column :subject, :string, :default => ""
+ t.column :created_at, :datetime, :null => false
+ t.column :updated_at, :datetime, :null => false
+ end
+ #Receipts
+ create_table :mailboxer_receipts do |t|
+ t.references :receiver, :polymorphic => true
+ t.column :notification_id, :integer, :null => false
+ t.column :is_read, :boolean, :default => false
+ t.column :trashed, :boolean, :default => false
+ t.column :deleted, :boolean, :default => false
+ t.column :mailbox_type, :string, :limit => 25
+ t.column :created_at, :datetime, :null => false
+ t.column :updated_at, :datetime, :null => false
+ end
+ #Notifications and Messages
+ create_table :mailboxer_notifications do |t|
+ t.column :type, :string
+ t.column :body, :text
+ t.column :subject, :string, :default => ""
+ t.references :sender, :polymorphic => true
+ t.column :conversation_id, :integer
+ t.column :draft, :boolean, :default => false
+ t.string :notification_code, :default => nil
+ t.references :notified_object, :polymorphic => true, index: { name: 'mailboxer_notifications_notified_object' }
+ t.column :attachment, :string
+ t.column :updated_at, :datetime, :null => false
+ t.column :created_at, :datetime, :null => false
+ t.boolean :global, default: false
+ t.datetime :expires
+ end
+
+ #Indexes
+ #Conversations
+ #Receipts
+ add_index "mailboxer_receipts","notification_id"
+
+ #Messages
+ add_index "mailboxer_notifications","conversation_id"
+
+ #Foreign keys
+ #Conversations
+ #Receipts
+ add_foreign_key "mailboxer_receipts", "mailboxer_notifications", :name => "receipts_on_notification_id", :column => "notification_id"
+ #Messages
+ add_foreign_key "mailboxer_notifications", "mailboxer_conversations", :name => "notifications_on_conversation_id", :column => "conversation_id"
+ end
+
+ def self.down
+ #Tables
+ remove_foreign_key "mailboxer_receipts", :name => "receipts_on_notification_id"
+ remove_foreign_key "mailboxer_notifications", :name => "notifications_on_conversation_id"
+
+ #Indexes
+ drop_table :mailboxer_receipts
+ drop_table :mailboxer_conversations
+ drop_table :mailboxer_notifications
+ end
+end
diff --git a/db/migrate/20190711043652_add_conversation_optout.mailboxer_engine.rb b/db/migrate/20190720000556_add_conversation_optout.mailboxer_engine.rb
similarity index 61%
rename from db/migrate/20190711043652_add_conversation_optout.mailboxer_engine.rb
rename to db/migrate/20190720000556_add_conversation_optout.mailboxer_engine.rb
index a8aa6bbed..a9860bdcb 100644
--- a/db/migrate/20190711043652_add_conversation_optout.mailboxer_engine.rb
+++ b/db/migrate/20190720000556_add_conversation_optout.mailboxer_engine.rb
@@ -2,14 +2,14 @@
class AddConversationOptout < ActiveRecord::Migration[4.2]
def self.up
create_table :mailboxer_conversation_opt_outs do |t|
- t.references :unsubscriber, polymorphic: true
+ t.references :unsubscriber, :polymorphic => true
t.references :conversation
end
- add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", name: "mb_opt_outs_on_conversations_id", column: "conversation_id"
+ add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", :name => "mb_opt_outs_on_conversations_id", :column => "conversation_id"
end
def self.down
- remove_foreign_key "mailboxer_conversation_opt_outs", name: "mb_opt_outs_on_conversations_id"
+ remove_foreign_key "mailboxer_conversation_opt_outs", :name => "mb_opt_outs_on_conversations_id"
drop_table :mailboxer_conversation_opt_outs
end
end
diff --git a/db/migrate/20190711043653_add_missing_indices.mailboxer_engine.rb b/db/migrate/20190720000557_add_missing_indices.mailboxer_engine.rb
similarity index 68%
rename from db/migrate/20190711043653_add_missing_indices.mailboxer_engine.rb
rename to db/migrate/20190720000557_add_missing_indices.mailboxer_engine.rb
index b079f4a3d..608d7f620 100644
--- a/db/migrate/20190711043653_add_missing_indices.mailboxer_engine.rb
+++ b/db/migrate/20190720000557_add_missing_indices.mailboxer_engine.rb
@@ -3,18 +3,18 @@ class AddMissingIndices < ActiveRecord::Migration[4.2]
def change
# We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63
# characters limitation.
- add_index :mailboxer_conversation_opt_outs, %i(unsubscriber_id unsubscriber_type),
+ add_index :mailboxer_conversation_opt_outs, [:unsubscriber_id, :unsubscriber_type],
name: 'index_mailboxer_conversation_opt_outs_on_unsubscriber_id_type'
add_index :mailboxer_conversation_opt_outs, :conversation_id
add_index :mailboxer_notifications, :type
- add_index :mailboxer_notifications, %i(sender_id sender_type)
+ add_index :mailboxer_notifications, [:sender_id, :sender_type]
# We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63
# characters limitation.
- add_index :mailboxer_notifications, %i(notified_object_id notified_object_type),
+ add_index :mailboxer_notifications, [:notified_object_id, :notified_object_type],
name: 'index_mailboxer_notifications_on_notified_object_id_and_type'
- add_index :mailboxer_receipts, %i(receiver_id receiver_type)
+ add_index :mailboxer_receipts, [:receiver_id, :receiver_type]
end
end
diff --git a/db/migrate/20190711043654_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb b/db/migrate/20190720000558_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb
similarity index 100%
rename from db/migrate/20190711043654_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb
rename to db/migrate/20190720000558_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb
diff --git a/db/migrate/20190720000625_notifications_to_mailboxer.rb b/db/migrate/20190720000625_notifications_to_mailboxer.rb
new file mode 100644
index 000000000..235d3a000
--- /dev/null
+++ b/db/migrate/20190720000625_notifications_to_mailboxer.rb
@@ -0,0 +1,4 @@
+class NotificationsToMailboxer < ActiveRecord::Migration[5.2]
+ def change
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71f21ede5..a7cf22479 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_07_11_043654) do
+ActiveRecord::Schema.define(version: 2019_07_20_000625) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -221,7 +221,6 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.decimal "area"
t.string "area_unit"
t.integer "garden_type_id"
- t.jsonb "layout"
t.index ["garden_type_id"], name: "index_gardens_on_garden_type_id"
t.index ["owner_id"], name: "index_gardens_on_owner_id"
t.index ["slug"], name: "index_gardens_on_slug", unique: true
@@ -394,7 +393,7 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.integer "product_id"
end
- create_table "photographings", id: :serial, force: :cascade do |t|
+ create_table "photo_associations", id: :serial, force: :cascade do |t|
t.integer "photo_id", null: false
t.integer "photographable_id", null: false
t.string "photographable_type", null: false
@@ -417,6 +416,7 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.string "link_url", null: false
t.string "flickr_photo_id"
t.datetime "date_taken"
+ t.integer "likes_count", default: 0
end
create_table "photos_plantings", id: false, force: :cascade do |t|
@@ -467,6 +467,7 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.datetime "updated_at"
t.string "slug"
t.integer "forum_id"
+ t.integer "likes_count", default: 0
t.index ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id"
t.index ["slug"], name: "index_posts_on_slug", unique: true
end
@@ -488,16 +489,6 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.integer "creator_id"
end
- create_table "seed_trades", force: :cascade do |t|
- t.bigint "seed_id"
- t.bigint "member_id"
- t.boolean "accepted"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.index ["member_id"], name: "index_seed_trades_on_member_id"
- t.index ["seed_id"], name: "index_seed_trades_on_seed_id"
- end
-
create_table "seeds", id: :serial, force: :cascade do |t|
t.integer "owner_id", null: false
t.integer "crop_id", null: false
@@ -519,32 +510,12 @@ ActiveRecord::Schema.define(version: 2019_07_11_043654) do
t.index ["slug"], name: "index_seeds_on_slug", unique: true
end
- create_table "trades", force: :cascade do |t|
- t.bigint "seed_id"
- t.bigint "responded_seed_id"
- t.bigint "requested_by_id"
- t.boolean "accepted"
- t.text "message"
- t.text "address_for_delivery"
- t.text "rejection_reason"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.index ["requested_by_id"], name: "index_trades_on_requested_by_id"
- t.index ["responded_seed_id"], name: "index_trades_on_responded_seed_id"
- t.index ["seed_id"], name: "index_trades_on_seed_id"
- end
-
add_foreign_key "harvests", "plantings"
add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", column: "conversation_id", name: "mb_opt_outs_on_conversations_id"
add_foreign_key "mailboxer_notifications", "mailboxer_conversations", column: "conversation_id", name: "notifications_on_conversation_id"
add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id"
- add_foreign_key "photographings", "crops"
- add_foreign_key "photographings", "photos"
+ add_foreign_key "photo_associations", "crops"
+ add_foreign_key "photo_associations", "photos"
add_foreign_key "plantings", "seeds", column: "parent_seed_id", name: "parent_seed", on_delete: :nullify
- add_foreign_key "seed_trades", "members"
- add_foreign_key "seed_trades", "seeds"
add_foreign_key "seeds", "plantings", column: "parent_planting_id", name: "parent_planting", on_delete: :nullify
- add_foreign_key "trades", "members", column: "requested_by_id"
- add_foreign_key "trades", "seeds"
- add_foreign_key "trades", "seeds", column: "responded_seed_id"
end
diff --git a/lib/haml/filters/growstuff_markdown.rb b/lib/haml/filters/growstuff_markdown.rb
index 4e7617c15..0a7c34000 100644
--- a/lib/haml/filters/growstuff_markdown.rb
+++ b/lib/haml/filters/growstuff_markdown.rb
@@ -54,7 +54,7 @@ module Haml::Filters # rubocop:disable Style/ClassAndModuleChildren
def crop_link(crop, link_text)
if crop
- url = Rails.application.routes.url_helpers.crop_url(crop, host: HOST)
+ url = Rails.application.routes.url_helpers.crop_url(crop, only_path: true)
"[#{link_text}](#{url})"
else
link_text
diff --git a/spec/features/likeable_spec.rb b/spec/features/likeable_spec.rb
index 0a39154b0..42aa03c6b 100644
--- a/spec/features/likeable_spec.rb
+++ b/spec/features/likeable_spec.rb
@@ -1,40 +1,97 @@
require 'rails_helper'
describe 'Likeable', js: true do
- let(:member) { FactoryBot.create(:member) }
- let(:another_member) { FactoryBot.create(:london_member) }
- let(:post) { FactoryBot.create(:post) }
+ let(:member) { FactoryBot.create(:member) }
+ let(:another_member) { FactoryBot.create(:london_member) }
+ let!(:post) { FactoryBot.create(:post, author: member) }
+ let!(:photo) { FactoryBot.create(:photo, owner: member) }
context 'logged in member' do
- before do
- login_as member
- visit post_path(post)
+ before { login_as member }
+
+ describe 'photos' do
+ let(:like_count_class) { "#photo-#{photo.id} .like-count" }
+
+ shared_examples 'photo can be liked' do
+ it 'can be liked' do
+ visit path
+ expect(page).to have_css(like_count_class, text: "0")
+ click_link '0', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "1")
+
+ # Reload page
+ visit path
+ expect(page).to have_css(like_count_class, text: "1")
+ expect(page).to have_link '1'
+
+ click_link '1', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "0")
+ end
+
+ it 'displays correct number of likes' do
+ visit path
+ expect(page).to have_css(like_count_class, text: "0")
+ expect(page).to have_link '0'
+ click_link '0', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "1")
+
+ logout(member)
+ login_as(another_member)
+ visit path
+
+ expect(page).to have_css(like_count_class, text: "1")
+ click_link '1', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "2")
+ end
+ end
+ describe 'photos#index' do
+ let(:path) { photos_path }
+ include_examples 'photo can be liked'
+ end
+ describe 'photos#show' do
+ let(:path) { photo_path(photo) }
+ include_examples 'photo can be liked'
+ end
+ describe 'crops#show' do
+ let(:crop) { FactoryBot.create :crop }
+ let(:planting) { FactoryBot.create :planting, owner: member, crop: crop }
+ let(:path) { crop_path(crop) }
+ before { planting.photos << photo }
+ include_examples 'photo can be liked'
+ end
end
- it 'can be liked' do
- expect(page).to have_link 'Like'
- click_link 'Like'
- expect(page).to have_content '1 like'
+ describe 'posts' do
+ let(:like_count_class) { "#post-#{post.id} .like-count" }
+ before { visit post_path(post) }
+ it 'can be liked' do
+ expect(page).to have_css(like_count_class, text: "0")
+ expect(page).to have_link 'Like'
+ click_link 'Like', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "1")
- visit post_path(post)
+ # Reload page
+ visit post_path(post)
+ expect(page).to have_css(like_count_class, text: "1")
+ expect(page).to have_link 'Unlike'
- expect(page).to have_link 'Unlike'
- click_link 'Unlike'
- expect(page).to have_content '0 likes'
- end
+ click_link 'Unlike', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "0")
+ end
- it 'displays correct number of likes' do
- expect(page).to have_link 'Like'
- click_link 'Like'
- expect(page).to have_content '1 like'
- logout(member)
+ it 'displays correct number of likes' do
+ expect(page).to have_link 'Like'
+ click_link 'Like', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "1")
- login_as(another_member)
- visit post_path(post)
+ logout(member)
+ login_as(another_member)
+ visit post_path(post)
- expect(page).to have_link 'Like'
- click_link 'Like'
- expect(page).to have_content '2 likes'
+ expect(page).to have_link 'Like'
+ click_link 'Like', class: 'like-btn'
+ expect(page).to have_css(like_count_class, text: "2")
+ end
end
end
end
diff --git a/spec/lib/haml/filters/growstuff_markdown_spec.rb b/spec/lib/haml/filters/growstuff_markdown_spec.rb
index 597137f6d..cc27bd560 100644
--- a/spec/lib/haml/filters/growstuff_markdown_spec.rb
+++ b/spec/lib/haml/filters/growstuff_markdown_spec.rb
@@ -7,7 +7,7 @@ def input_link(name)
end
def output_link(crop, name = nil)
- url = Rails.application.routes.url_helpers.crop_url(crop, host: Rails.application.config.host)
+ url = Rails.application.routes.url_helpers.crop_url(crop, only_path: true)
return "#{name}" if name
"#{crop.name}"
@@ -62,7 +62,7 @@ describe 'Haml::Filters::Growstuff_Markdown' do
end
it "finds crops case insensitively" do
- @crop = FactoryBot.create(:crop, name: 'tomato')
+ @crop = FactoryBot.create(:crop, name: 'tomato', slug: 'tomato')
rendered = Haml::Filters::GrowstuffMarkdown.render(input_link('ToMaTo'))
expect(rendered).to match(/#{output_link(@crop, 'ToMaTo')}/)
end
diff --git a/spec/models/crop_spec.rb b/spec/models/crop_spec.rb
index fa642a540..ed17069c9 100644
--- a/spec/models/crop_spec.rb
+++ b/spec/models/crop_spec.rb
@@ -185,9 +185,9 @@ describe Crop do
end
it { expect(crop.photos.size).to eq 0 }
- it { expect(crop.planting_photos.size).to eq 0 }
- it { expect(crop.harvest_photos.size).to eq 0 }
- it { expect(crop.seed_photos.size).to eq 0 }
+ it { expect(crop.photos.by_model(Planting).size).to eq 0 }
+ it { expect(crop.photos.by_model(Harvest).size).to eq 0 }
+ it { expect(crop.photos.by_model(Seed).size).to eq 0 }
end
describe 'finding all photos' do
@@ -203,9 +203,9 @@ describe Crop do
end
it { expect(crop.photos.size).to eq 3 }
- it { expect(crop.planting_photos.size).to eq 1 }
- it { expect(crop.harvest_photos.size).to eq 1 }
- it { expect(crop.seed_photos.size).to eq 1 }
+ it { expect(crop.photos.by_model(Planting).size).to eq 1 }
+ it { expect(crop.photos.by_model(Harvest).size).to eq 1 }
+ it { expect(crop.photos.by_model(Seed).size).to eq 1 }
end
end
diff --git a/spec/models/harvest_spec.rb b/spec/models/harvest_spec.rb
index 35cb9ba5f..168b2bcdc 100644
--- a/spec/models/harvest_spec.rb
+++ b/spec/models/harvest_spec.rb
@@ -237,6 +237,7 @@ describe Harvest do
@planting = FactoryBot.create(:planting, crop: @harvest.crop)
@photo = FactoryBot.create(:photo, owner: @planting.owner)
@planting.photos << @photo
+ @harvest.update(planting: @planting)
end
it 'has a default photo' do
diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb
index 363585aea..56f35bf1d 100644
--- a/spec/models/like_spec.rb
+++ b/spec/models/like_spec.rb
@@ -48,6 +48,12 @@ describe 'like' do
expect(Like.all).not_to include like
end
+ it 'destroys like if post no longer exists' do
+ like = Like.create(member: member, likeable: post)
+ post.destroy
+ expect(Like.all).not_to include like
+ end
+
it 'destroys like if member no longer exists' do
like = Like.create(member: member, likeable: post)
member.destroy
diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb
index d71a6cd1c..1102253f8 100644
--- a/spec/models/photo_spec.rb
+++ b/spec/models/photo_spec.rb
@@ -2,18 +2,40 @@ require 'rails_helper'
describe Photo do
let(:photo) { FactoryBot.create(:photo, owner: member) }
- let(:member) { FactoryBot.create(:member) }
+ let(:old_photo) { FactoryBot.create(:photo, owner: member, created_at: 1.year.ago, date_taken: 2.years.ago) }
+ let(:member) { FactoryBot.create(:member) }
+
+ it_behaves_like "it is likeable"
describe 'add/delete functionality' do
let(:planting) { FactoryBot.create(:planting, owner: member) }
+ let(:seed) { FactoryBot.create(:seed, owner: member) }
let(:harvest) { FactoryBot.create(:harvest, owner: member) }
- let(:garden) { FactoryBot.create(:garden, owner: member) }
+ let(:post) { FactoryBot.create(:post, author: member) }
+ let(:garden) { FactoryBot.create(:garden, owner: member) }
context "adds photos" do
- it 'to a planting' do
- planting.photos << photo
- expect(planting.photos.size).to eq 1
- expect(planting.photos.first).to eq photo
+ describe 'to a planting' do
+ before { planting.photos << photo }
+
+ it { expect(planting.photos.size).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
+ # Add an old photo
+ before { planting.photos << old_photo }
+ it { expect(planting.default_photo).to eq photo }
+ it { expect(planting.crop.default_photo).to eq photo }
+
+ describe 'and someone likes the old photo' do
+ before { FactoryBot.create :like, likeable: old_photo }
+ it { expect(planting.default_photo).to eq old_photo }
+ it { expect(planting.crop.default_photo).to eq old_photo }
+ end
+ end
end
it 'to a harvest' do
@@ -22,11 +44,23 @@ describe Photo do
expect(harvest.photos.first).to eq photo
end
+ it 'to a seed' do
+ seed.photos << photo
+ expect(seed.photos.size).to eq 1
+ expect(seed.photos.first).to eq photo
+ end
+
it 'to a garden' do
garden.photos << photo
expect(garden.photos.size).to eq 1
expect(garden.photos.first).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
+ end
end
context "removing photos" do