mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-16 04:05:30 -04:00
Merge remote-tracking branch 'upstream/dev' into mailbox
Conflicts: Gemfile.lock app/assets/stylesheets/application.scss db/schema.rb
This commit is contained in:
24
Gemfile.lock
24
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)
|
||||
|
||||
40
app/assets/javascripts/likes.js
Normal file
40
app/assets/javascripts/likes.js
Normal file
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
7
app/assets/stylesheets/_likes.scss
Normal file
7
app/assets/stylesheets/_likes.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.liked {
|
||||
color: $red;
|
||||
}
|
||||
|
||||
.like-btn {
|
||||
color: $brown;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ module IconsHelper
|
||||
end
|
||||
|
||||
def like_icon
|
||||
icon('fas', 'thumbs-up')
|
||||
icon('fas', 'heart')
|
||||
end
|
||||
|
||||
def sunniness_icon(sunniness)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -89,8 +89,4 @@ class Garden < ApplicationRecord
|
||||
p.save
|
||||
end
|
||||
end
|
||||
|
||||
def default_photo
|
||||
photos.order(created_at: :desc).first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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}
|
||||
|
||||
4
app/views/likes/_count.haml
Normal file
4
app/views/likes/_count.haml
Normal file
@@ -0,0 +1,4 @@
|
||||
%span.badge.like-badge{class: (likeable.liked_by?(current_member)) ? 'liked' : ''}
|
||||
= like_icon
|
||||
|
||||
%span.like-count= likeable.likes_count
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
app/views/photos/_likes.html.haml
Normal file
13
app/views/photos/_likes.html.haml
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
<script src="//static.getclicky.com/js" type="text/javascript"></script>
|
||||
<script type="text/javascript">try{ clicky.init(100594260); }catch(e){}</script>
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
34
db/migrate/20190712003735_add_like_counter_caches.rb
Normal file
34
db/migrate/20190712003735_add_like_counter_caches.rb
Normal file
@@ -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
|
||||
5
db/migrate/20190712234859_rename_photographings.rb
Normal file
5
db/migrate/20190712234859_rename_photographings.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class RenamePhotographings < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
rename_table :photographings, :photo_associations
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
4
db/migrate/20190720000625_notifications_to_mailboxer.rb
Normal file
4
db/migrate/20190720000625_notifications_to_mailboxer.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class NotificationsToMailboxer < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
end
|
||||
end
|
||||
41
db/schema.rb
41
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "<a href=\"#{url}\">#{name}</a>" if name
|
||||
|
||||
"<a href=\"#{url}\">#{crop.name}</a>"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user