mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-03-24 01:32:40 -04:00
Merge branch 'dev' of https://github.com/Growstuff/growstuff into I18n
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -1,3 +0,0 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
|
||||
@@ -69,6 +69,7 @@ class CropsController < ApplicationController
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, {:plantings => :photos}).find(params[:id])
|
||||
@posts = @crop.posts.paginate(:page => params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
|
||||
@@ -6,23 +6,21 @@ class HarvestsController < ApplicationController
|
||||
# GET /harvests.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@harvests = @owner.harvests.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
elsif @crop
|
||||
@harvests = @crop.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@harvests = Harvest.includes(:owner, :crop).paginate(:page => params[:page])
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @harvests = @harvests.paginate(:page => params[:page]) }
|
||||
format.json { render json: @harvests }
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = @owner.harvests.includes(:owner, :crop)
|
||||
else
|
||||
@filename = "Growstuff-Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@harvests = Harvest.includes(:owner, :crop)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @harvests
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,8 @@ class PhotosController < ApplicationController
|
||||
# GET /photos/new.json
|
||||
def new
|
||||
@photo = Photo.new
|
||||
@planting_id = params[:planting_id]
|
||||
@type = params[:type]
|
||||
@id = params[:id]
|
||||
|
||||
page = params[:page] || 1
|
||||
|
||||
@@ -63,19 +64,35 @@ class PhotosController < ApplicationController
|
||||
@photo.owner_id = current_member.id
|
||||
@photo.set_flickr_metadata
|
||||
|
||||
if params[:planting_id]
|
||||
planting = Planting.find_by_id(params[:planting_id])
|
||||
if planting
|
||||
if planting.owner.id == current_member.id
|
||||
@photo.plantings << planting unless @photo.plantings.include?(planting)
|
||||
# several models can have photos. we need to know what model and the id
|
||||
# for the entry to attach the photo to
|
||||
valid_models = ["planting", "harvest"]
|
||||
if params[:type]
|
||||
if valid_models.include?(params[:type])
|
||||
if params[:id]
|
||||
item = params[:type].camelcase.constantize.find_by_id(params[:id])
|
||||
if item
|
||||
if item.owner.id == current_member.id
|
||||
# This syntax is weird, so just know that it means this:
|
||||
# @photo.harvests << item unless @photo.harvests.include?(item)
|
||||
# but with the correct many-to-many relationship automatically referenced
|
||||
(@photo.send "#{params[:type]}s") << item unless (@photo.send "#{params[:type]}s").include?(item)
|
||||
else
|
||||
flash[:alert] = "You must own both the #{params[:type]} and the photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find #{params[:type]} to connect to photo."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "You must own both the planting and the photo."
|
||||
flash[:alert] = "Missing id parameter"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Couldn't find planting to connect to photo."
|
||||
flash[:alert] = "Cannot attach photos to #{params[:type]}"
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Missing type parameter"
|
||||
end
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
if @photo.save
|
||||
format.html { redirect_to @photo, notice: 'Photo was successfully added.' }
|
||||
|
||||
@@ -7,24 +7,22 @@ class PlantingsController < ApplicationController
|
||||
# GET /plantings.json
|
||||
def index
|
||||
@owner = Member.find_by_slug(params[:owner])
|
||||
@crop = Crop.find_by_slug(params[:crop])
|
||||
if @owner
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden)
|
||||
elsif @crop
|
||||
@plantings = @crop.plantings.includes(:owner, :crop, :garden)
|
||||
else
|
||||
@plantings = Planting.includes(:owner, :crop, :garden).paginate(:page => params[:page])
|
||||
@plantings = Planting.includes(:owner, :crop, :garden)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
format.html { @plantings = @plantings.paginate(:page => params[:page]) }
|
||||
format.json { render json: @plantings }
|
||||
format.rss { render :layout => false } #index.rss.builder
|
||||
format.csv do
|
||||
if @owner
|
||||
@filename = "Growstuff-#{@owner}-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = @owner.plantings.includes(:owner, :crop, :garden)
|
||||
else
|
||||
@filename = "Growstuff-Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@plantings = Planting.includes(:owner, :crop, :garden)
|
||||
end
|
||||
specifics = (@owner ? "#{@owner.name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render :csv => @plantings
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
module PlantPartsHelper
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
module SeedsHelper
|
||||
end
|
||||
@@ -17,6 +17,9 @@ class Crop < ActiveRecord::Base
|
||||
|
||||
belongs_to :parent, :class_name => 'Crop'
|
||||
has_many :varieties, :class_name => 'Crop', :foreign_key => 'parent_id'
|
||||
has_and_belongs_to_many :posts
|
||||
before_destroy {|crop| crop.posts.clear}
|
||||
|
||||
|
||||
default_scope order("lower(name) asc")
|
||||
scope :recent, reorder("created_at desc")
|
||||
|
||||
@@ -9,6 +9,17 @@ class Harvest < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
belongs_to :plant_part
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
|
||||
before_destroy do |harvest|
|
||||
photolist = harvest.photos.to_a # save a temp copy of the photo list
|
||||
harvest.photos.clear # clear relationship b/w harvest and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
validates :crop, :presence => {:message => "must be present and exist in our database"}
|
||||
@@ -72,4 +83,8 @@ class Harvest < ActiveRecord::Base
|
||||
"#{owner.login_name}-#{crop}".downcase.gsub(' ', '-')
|
||||
end
|
||||
|
||||
def default_photo
|
||||
return photos.first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -4,10 +4,21 @@ class Photo < ActiveRecord::Base
|
||||
belongs_to :owner, :class_name => 'Member'
|
||||
|
||||
has_and_belongs_to_many :plantings
|
||||
before_destroy {|photo| photo.plantings.clear}
|
||||
has_and_belongs_to_many :harvests
|
||||
before_destroy do |photo|
|
||||
photo.plantings.clear
|
||||
photo.harvests.clear
|
||||
end
|
||||
|
||||
default_scope order("created_at desc")
|
||||
|
||||
# remove photos that aren't used by anything
|
||||
def destroy_if_unused
|
||||
unless plantings.size > 0 and harvests.size > 0
|
||||
self.destroy
|
||||
end
|
||||
end
|
||||
|
||||
# This is split into a side-effect free method and a side-effecting method
|
||||
# for easier stubbing and testing.
|
||||
def flickr_metadata
|
||||
|
||||
@@ -11,7 +11,15 @@ class Planting < ActiveRecord::Base
|
||||
belongs_to :crop, :counter_cache => true
|
||||
|
||||
has_and_belongs_to_many :photos
|
||||
before_destroy {|planting| planting.photos.clear}
|
||||
|
||||
before_destroy do |planting|
|
||||
photolist = planting.photos.to_a # save a temp copy of the photo list
|
||||
planting.photos.clear # clear relationship b/w planting and photo
|
||||
|
||||
photolist.each do |photo|
|
||||
photo.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
default_scope order("created_at desc")
|
||||
scope :finished, where(:finished => true)
|
||||
|
||||
@@ -5,6 +5,9 @@ class Post < ActiveRecord::Base
|
||||
belongs_to :author, :class_name => 'Member'
|
||||
belongs_to :forum
|
||||
has_many :comments, :dependent => :destroy
|
||||
has_and_belongs_to_many :crops
|
||||
before_destroy {|post| post.crops.clear}
|
||||
after_save :update_crops_posts_association
|
||||
# also has_many notifications, but kinda meaningless to get at them
|
||||
# from this direction, so we won't set up an association for now.
|
||||
|
||||
@@ -39,4 +42,15 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def update_crops_posts_association
|
||||
self.crops.destroy_all
|
||||
# look for crops mentioned in the post. eg. [tomato](crop)
|
||||
self.body.scan(Haml::Filters::GrowstuffMarkdown::CROP_REGEX) do |m|
|
||||
# find crop case-insensitively
|
||||
crop = Crop.where('lower(name) = ?', $1.downcase).first
|
||||
# create association
|
||||
self.crops << crop if crop and not self.crops.include?(crop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
Nobody has harvested this crop yet.
|
||||
- else
|
||||
%ul
|
||||
- crop.harvests.each do |harvest|
|
||||
- crop.harvests.take(3).each do |harvest|
|
||||
%li
|
||||
= link_to "#{harvest.owner} harvested #{display_quantity(harvest)}.", harvest_path(harvest)
|
||||
= render :partial => 'members/location', :locals => { :member => harvest.owner }
|
||||
%small
|
||||
= distance_of_time_in_words(harvest.created_at, Time.zone.now)
|
||||
ago.
|
||||
|
||||
%p.col-md-offset-1
|
||||
= link_to "See all #{crop.name} harvests", harvests_by_crop_path(crop)
|
||||
- if current_member
|
||||
= link_to "Track your #{crop.name} harvests.", new_harvest_path()
|
||||
%p.col-md-offset-1
|
||||
= link_to "Track your #{crop.name} harvests.", new_harvest_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} harvests" }
|
||||
|
||||
|
||||
21
app/views/crops/_plantings.html.haml
Normal file
21
app/views/crops/_plantings.html.haml
Normal file
@@ -0,0 +1,21 @@
|
||||
%h4 Plantings
|
||||
- if crop.plantings.empty?
|
||||
%p
|
||||
Nobody has planted this crop yet.
|
||||
- else
|
||||
%ul
|
||||
- crop.plantings.take(3).each do |planting|
|
||||
%li
|
||||
= link_to "#{planting.owner} planted #{planting.quantity} #{planting.planted_from}.", planting_path(planting)
|
||||
= render :partial => 'members/location', :locals => { :member => planting.owner }
|
||||
%small
|
||||
= distance_of_time_in_words(planting.created_at, Time.zone.now)
|
||||
ago.
|
||||
%p.col-md-offset-1
|
||||
= link_to "See all #{crop.name} plantings", plantings_by_crop_path(crop)
|
||||
- if current_member
|
||||
%p.col-md-offset-1
|
||||
= link_to "Track your #{crop.name} plantings.", new_planting_path(:crop_id => crop.id)
|
||||
- else
|
||||
= render :partial => 'shared/signin_signup', :locals => { :to => "track your #{crop.name} plantings" }
|
||||
|
||||
@@ -33,12 +33,18 @@
|
||||
|
||||
%div#cropmap
|
||||
|
||||
- if @crop.plantings.size > 0
|
||||
%a{:name => 'posts'}
|
||||
%div.pagination
|
||||
= page_entries_info @posts, :model => "posts"
|
||||
= will_paginate @posts, :params => {:anchor => "posts"}
|
||||
|
||||
%h2 All plantings
|
||||
- unless @posts.empty?
|
||||
- @posts.each do |post|
|
||||
= render :partial => "posts/single", :locals => { :post => post, :subject => true }
|
||||
|
||||
- @crop.plantings.each do |p|
|
||||
= render :partial => "plantings/thumbnail", :locals => { :planting => p, :title => 'owner' }
|
||||
%div.pagination
|
||||
= page_entries_info @posts, :model => "posts"
|
||||
= will_paginate @posts, :params => {:anchor => "posts"}
|
||||
|
||||
.col-md-3
|
||||
- if can? :edit, @crop or can? :destroy, @crop
|
||||
@@ -76,5 +82,6 @@
|
||||
%ul
|
||||
%li= link_to 'Wikipedia (English)', @crop.en_wikipedia_url
|
||||
|
||||
= render :partial => 'plantings', :locals => { :crop => @crop }
|
||||
= render :partial => 'harvests', :locals => { :crop => @crop }
|
||||
= render :partial => 'find_seeds', :locals => { :crop => @crop }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- content_for :title, @owner ? "#{@owner}'s harvests" : "Everyone's harvests"
|
||||
- content_for :title, @owner ? "#{@owner}'s harvests" : @crop ? "Everyone's #{@crop.name} harvests" : "Everyone's harvests"
|
||||
|
||||
%p
|
||||
#{ENV['GROWSTUFF_SITE_NAME']} helps you track what you're
|
||||
|
||||
@@ -34,3 +34,16 @@
|
||||
|
||||
:growstuff_markdown
|
||||
#{ @harvest.description != "" ? @harvest.description : "No description given." }
|
||||
|
||||
- if @harvest.photos.count > 0 or (can? :edit, @harvest and can? :create, Photo)
|
||||
%h2 Pictures
|
||||
|
||||
%ul.thumbnails
|
||||
- @harvest.photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
= render :partial => 'photos/thumbnail', :locals => { :photo => p }
|
||||
- if can? :create, Photo and can? :edit, @harvest
|
||||
.col-md-2
|
||||
.thumbnail(style='height: 220px')
|
||||
%p{:style => 'text-align: center; padding-top: 50px'}
|
||||
= link_to "Add photo", new_photo_path(:type => "harvest", :id => @harvest.id), :class => 'btn btn-primary'
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
= form_tag(new_photo_path, :method => :get, :class => 'form-inline') do
|
||||
= label_tag :set, "Choose a photo set:", :class => 'control-label'
|
||||
= select_tag :set, options_for_select(@sets, @current_set), :class => 'input-large'
|
||||
= hidden_field_tag :planting_id, @planting_id
|
||||
= hidden_field_tag :type, @type
|
||||
= hidden_field_tag :id, @id
|
||||
= submit_tag "Search", :class => 'btn btn-primary'
|
||||
|
||||
%div.pagination
|
||||
@@ -26,7 +27,7 @@
|
||||
- @photos.each do |p|
|
||||
.col-md-2.six-across
|
||||
.thumbnail(style='height: 220px')
|
||||
= link_to image_tag(FlickRaw.url_q(p), :alt => '', :class => 'img-rounded'), photos_path(:photo => { :flickr_photo_id => p.id }, :planting_id => @planting_id), :method => :post
|
||||
= link_to image_tag(FlickRaw.url_q(p), :alt => '', :class => 'img-rounded'), photos_path(:photo => { :flickr_photo_id => p.id }, :type => @type, :id => @id), :method => :post
|
||||
%p
|
||||
=p.title
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- content_for :title, @owner ? "#{@owner}'s plantings" : "Everyone's plantings"
|
||||
- content_for :title, @owner ? "#{@owner}'s plantings" : @crop ? "Everyone's #{@crop.name} plantings" : "Everyone's plantings"
|
||||
|
||||
%p
|
||||
- if can? :create, Planting
|
||||
|
||||
@@ -68,4 +68,4 @@
|
||||
.col-md-2
|
||||
.thumbnail(style='height: 220px')
|
||||
%p{:style => 'text-align: center; padding-top: 50px'}
|
||||
= link_to "Add photo", new_photo_path(:planting_id => @planting.id), :class => 'btn btn-primary'
|
||||
= link_to "Add photo", new_photo_path(:type => "planting", :id => @planting.id), :class => 'btn btn-primary'
|
||||
|
||||
@@ -67,8 +67,12 @@ module Growstuff
|
||||
config.assets.initialize_on_precompile = true
|
||||
|
||||
config.generators do |g|
|
||||
g.template_engine :haml
|
||||
g.stylesheets false
|
||||
g.template_engine :haml
|
||||
g.view_specs false
|
||||
g.controller_specs false
|
||||
g.helper false
|
||||
g.stylesheets false
|
||||
g.javascripts false
|
||||
end
|
||||
|
||||
config.action_mailer.delivery_method = :sendmail
|
||||
|
||||
@@ -12,6 +12,7 @@ Growstuff::Application.routes.draw do
|
||||
|
||||
resources :plantings
|
||||
match '/plantings/owner/:owner' => 'plantings#index', :as => 'plantings_by_owner'
|
||||
match '/plantings/crop/:crop' => 'plantings#index', :as => 'plantings_by_crop'
|
||||
|
||||
resources :gardens
|
||||
match '/gardens/owner/:owner' => 'gardens#index', :as => 'gardens_by_owner'
|
||||
@@ -21,6 +22,7 @@ Growstuff::Application.routes.draw do
|
||||
|
||||
resources :harvests
|
||||
match '/harvests/owner/:owner' => 'harvests#index', :as => 'harvests_by_owner'
|
||||
match '/harvests/crop/:crop' => 'harvests#index', :as => 'harvests_by_crop'
|
||||
|
||||
resources :posts
|
||||
match '/posts/author/:author' => 'posts#index', :as => 'posts_by_author'
|
||||
|
||||
8
db/migrate/20140905001730_add_harvests_photos_table.rb
Normal file
8
db/migrate/20140905001730_add_harvests_photos_table.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class AddHarvestsPhotosTable < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :harvests_photos, :id => false do |t|
|
||||
t.integer :photo_id
|
||||
t.integer :harvest_id
|
||||
end
|
||||
end
|
||||
end
|
||||
10
db/migrate/20140928044231_add_crops_posts_table.rb
Normal file
10
db/migrate/20140928044231_add_crops_posts_table.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class AddCropsPostsTable < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :crops_posts, :id => false do |t|
|
||||
t.integer :crop_id
|
||||
t.integer :post_id
|
||||
end
|
||||
add_index :crops_posts, [:crop_id, :post_id]
|
||||
add_index :crops_posts, :crop_id
|
||||
end
|
||||
end
|
||||
5
db/migrate/20141002022459_create_index_harvest_photos.rb
Normal file
5
db/migrate/20141002022459_create_index_harvest_photos.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class CreateIndexHarvestPhotos < ActiveRecord::Migration
|
||||
def change
|
||||
add_index(:harvests_photos, [:harvest_id, :photo_id])
|
||||
end
|
||||
end
|
||||
17
db/schema.rb
17
db/schema.rb
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20140829230600) do
|
||||
ActiveRecord::Schema.define(:version => 20141002022459) do
|
||||
|
||||
create_table "account_types", :force => true do |t|
|
||||
t.string "name", :null => false
|
||||
@@ -64,6 +64,14 @@ ActiveRecord::Schema.define(:version => 20140829230600) do
|
||||
add_index "crops", ["name"], :name => "index_crops_on_name"
|
||||
add_index "crops", ["slug"], :name => "index_crops_on_slug", :unique => true
|
||||
|
||||
create_table "crops_posts", :id => false, :force => true do |t|
|
||||
t.integer "crop_id"
|
||||
t.integer "post_id"
|
||||
end
|
||||
|
||||
add_index "crops_posts", ["crop_id", "post_id"], :name => "index_crops_posts_on_crop_id_and_post_id"
|
||||
add_index "crops_posts", ["crop_id"], :name => "index_crops_posts_on_crop_id"
|
||||
|
||||
create_table "forums", :force => true do |t|
|
||||
t.string "name", :null => false
|
||||
t.text "description", :null => false
|
||||
@@ -108,6 +116,13 @@ ActiveRecord::Schema.define(:version => 20140829230600) do
|
||||
t.integer "plant_part_id"
|
||||
end
|
||||
|
||||
create_table "harvests_photos", :id => false, :force => true do |t|
|
||||
t.integer "photo_id"
|
||||
t.integer "harvest_id"
|
||||
end
|
||||
|
||||
add_index "harvests_photos", ["harvest_id", "photo_id"], :name => "index_harvests_photos_on_harvest_id_and_photo_id"
|
||||
|
||||
create_table "members", :force => true do |t|
|
||||
t.string "email", :default => "", :null => false
|
||||
t.string "encrypted_password", :default => "", :null => false
|
||||
|
||||
6
db/seeds/README.md
Normal file
6
db/seeds/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
The format for these crop seed files is CSV with the following fields:
|
||||
|
||||
Crop name
|
||||
Scientific name
|
||||
English Wikipedia URL
|
||||
Parent crop name
|
||||
41
db/seeds/crops-11-tomatoes.csv
Normal file
41
db/seeds/crops-11-tomatoes.csv
Normal file
@@ -0,0 +1,41 @@
|
||||
adoration tomato,,https://en.wikipedia.org/wiki/Adoration_%28Tomato%29,tomato,
|
||||
alicante tomato,,https://en.wikipedia.org/wiki/Alicante_%28tomato%29,tomato,
|
||||
Amish paste tomato,,http://en.wikipedia.org/wiki/Amish_Paste,tomato,
|
||||
azoychka tomato,,https://en.wikipedia.org/wiki/Azoychka%28Tomato%29,tomato,
|
||||
beefsteak tomato,,https://en.wikipedia.org/wiki/Beefsteak_(tomato),tomato,
|
||||
better boy tomato,,https://en.wikipedia.org/wiki/Better_Boy,tomato,
|
||||
big rainbow tomato,,https://en.wikipedia.org/wiki/Big_Rainbow_(Tomato),tomato,
|
||||
Blaby special tomato,,https://en.wikipedia.org/wiki/Blaby_Special_(Tomato),tomato,
|
||||
black krim tomato,,https://en.wikipedia.org/wiki/Black_Krim_%28tomato%29,tomato,
|
||||
brandywine tomato,,https://en.wikipedia.org/wiki/Brandywine_(tomato),tomato,
|
||||
campari tomato,,https://en.wikipedia.org/wiki/Campari_tomato,tomato,
|
||||
Cherokee purple tomato,,https://en.wikipedia.org/wiki/Cherokee_purple,tomato,
|
||||
currant tomato,solanum pimpinellifolium,https://en.wikipedia.org/wiki/Solanum_pimpinellifolium,
|
||||
early girl tomato,,https://en.wikipedia.org/wiki/Early_Girl,tomato,
|
||||
Fourth of July tomato,,https://en.wikipedia.org/wiki/Fourth_of_July_(tomato_variety),tomato,
|
||||
garden peach tomato,,https://en.wikipedia.org/wiki/Garden_peach_tomato,tomato,
|
||||
green zebra tomato,,https://en.wikipedia.org/wiki/Green_Zebra,tomato,
|
||||
hillbilly tomato,,http://en.wikipedia.org/wiki/Hillbilly_(tomato),tomato,
|
||||
jubilee tomato,,http://en.wikipedia.org/wiki/Jubilee_(tomato),tomato,
|
||||
lillian's yellow tomato,,http://en.wikipedia.org/wiki/Lillian%27s_Yellow_(tomato),tomato,
|
||||
Matt's wild cherry tomato,,http://en.wikipedia.org/wiki/Matt%27s_Wild_Cherry,tomato,
|
||||
mortgage lifter tomato,,http://en.wikipedia.org/wiki/Mortgage_Lifter,tomato,
|
||||
Mr. Stripey tomato,,http://en.wikipedia.org/wiki/Mr._Stripey,tomato,
|
||||
Roma tomato,,http://en.wikipedia.org/wiki/Roma_tomato,tomato,
|
||||
San Marzano tomato,,http://en.wikipedia.org/wiki/San_Marzano_tomato,tomato,
|
||||
Santorini tomato,,http://en.wikipedia.org/wiki/Santorini_(tomato),tomato,
|
||||
stupice tomato,,http://en.wikipedia.org/wiki/Super_Sweet_100,tomato,
|
||||
tigerella tomato,,http://en.wikipedia.org/wiki/Tigerella,tomato,
|
||||
tomaccio tomato,,http://en.wikipedia.org/wiki/Tomaccio_(tomato),tomato,
|
||||
traveller tomato,,http://en.wikipedia.org/wiki/Traveller_(tomato),tomato
|
||||
three sisters tomato,,http://en.wikipedia.org/wiki/Three_Sisters_(tomato),tomato,
|
||||
Hanover tomato,,http://en.wikipedia.org/wiki/Hanover_tomato,tomato,
|
||||
celebrity tomato,,http://en.wikipedia.org/wiki/Celebrity_(tomato),tomato,
|
||||
tomberry,,http://en.wikipedia.org/wiki/Tomberry,tomato,
|
||||
super sweet 100 tomato,,http://en.wikipedia.org/wiki/Super_Sweet_100,tomato,
|
||||
marglobe tomato,,http://en.wikipedia.org/wiki/Marglobe,tomato,
|
||||
grape tomato,,http://en.wikipedia.org/wiki/Grape_tomato,tomato,
|
||||
cherry tomato,,http://en.wikipedia.org/wiki/Cherry_tomato,tomato,
|
||||
Aunt Ruby's German green tomato,,http://en.wikipedia.org/wiki/Aunt_Ruby%27s_German_Green,tomato,
|
||||
white queen tomato,,http://en.wikipedia.org/wiki/White_Queen_tomato,tomato,
|
||||
pear tomato,,http://en.wikipedia.org/wiki/Pear_tomato,tomato,
|
||||
|
@@ -2,12 +2,13 @@ require 'bluecloth'
|
||||
|
||||
module Haml::Filters
|
||||
module GrowstuffMarkdown
|
||||
CROP_REGEX = /\[([^\[\]]+?)\]\(crop\)/
|
||||
include Haml::Filters::Base
|
||||
|
||||
def render(text)
|
||||
|
||||
# turn [tomato](crop) into [tomato](http://growstuff.org/crops/tomato)
|
||||
expanded = text.gsub(/\[([^\[\]]+?)\]\(crop\)/) do |m|
|
||||
expanded = text.gsub(CROP_REGEX) do |m|
|
||||
crop_str = $1
|
||||
# find crop case-insensitively
|
||||
crop = Crop.where('lower(name) = ?', crop_str.downcase).first
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace :growstuff do
|
||||
CSV.foreach(@file) do |row|
|
||||
Crop.create_from_csv(row)
|
||||
end
|
||||
Rails.cache.delete('full_crop_hierarchy')
|
||||
puts "Finished loading crops"
|
||||
|
||||
end
|
||||
@@ -249,13 +250,26 @@ namespace :growstuff do
|
||||
desc "August 2014: fix ping to pint in database"
|
||||
task :ping_to_pint => :environment do
|
||||
Harvest.find_each do |h|
|
||||
if h.unit == "ping"
|
||||
if h.unit == "ping"
|
||||
h.unit = "pint"
|
||||
h.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "October 2014: remove unused photos"
|
||||
task :remove_unused_photos => :environment do
|
||||
Photo.find_each do |p|
|
||||
p.destroy_if_unused
|
||||
end
|
||||
end
|
||||
|
||||
desc "October 2014: generate crops_posts records for existing posts"
|
||||
task :generate_crops_posts_records => :environment do
|
||||
Post.find_each do |p|
|
||||
p.save
|
||||
end
|
||||
end
|
||||
end # end oneoff section
|
||||
|
||||
end
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
# echo "YYYY-MM-DD - do something or other"
|
||||
# rake growstuff:oneoff:something
|
||||
|
||||
echo "2013-07-18 - zero crop plantings_count"
|
||||
rake growstuff:oneoff:zero_plantings_count
|
||||
echo "2014-09-28 - upload tomatoes"
|
||||
rake growstuff:import_crops file=db/seeds/crops-11-tomatoes.csv
|
||||
|
||||
echo "2014-08-10 - replace ping with pint in db"
|
||||
rake growstuff:oneoff:ping_to_pint
|
||||
echo "2014-10-02 - remove unused photos"
|
||||
rake growstuff:oneoff:remove_unused_photos
|
||||
|
||||
echo "2014-10-05 - generate crops_posts records for existing posts"
|
||||
rake growstuff:oneoff:generate_crops_posts_records
|
||||
|
||||
@@ -12,10 +12,30 @@ describe HarvestsController do
|
||||
end
|
||||
|
||||
describe "GET index" do
|
||||
before do
|
||||
@member1 = FactoryGirl.create(:member)
|
||||
@member2 = FactoryGirl.create(:member)
|
||||
@tomato = FactoryGirl.create(:tomato)
|
||||
@maize = FactoryGirl.create(:maize)
|
||||
@harvest1 = FactoryGirl.create(:harvest, :owner_id => @member1.id, :crop_id => @tomato.id)
|
||||
@harvest2 = FactoryGirl.create(:harvest, :owner_id => @member2.id, :crop_id => @maize.id)
|
||||
end
|
||||
|
||||
it "assigns all harvests as @harvests" do
|
||||
harvest = Harvest.create! valid_attributes
|
||||
get :index, {}
|
||||
assigns(:harvests).should eq([harvest])
|
||||
assigns(:harvests).should =~ [@harvest1, @harvest2]
|
||||
end
|
||||
|
||||
it "picks up owner from params and shows owner's harvests only" do
|
||||
get :index, {:owner => @member1.slug}
|
||||
assigns(:owner).should eq @member1
|
||||
assigns(:harvests).should eq [@harvest1]
|
||||
end
|
||||
|
||||
it "picks up crop from params and shows the harvests for the crop only" do
|
||||
get :index, {:crop => @maize.name}
|
||||
assigns(:crop).should eq @maize
|
||||
assigns(:harvests).should eq [@harvest2]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -37,8 +37,15 @@ describe PhotosController do
|
||||
end
|
||||
|
||||
it "assigns a planting id" do
|
||||
get :new, { :planting_id => 5 }
|
||||
assigns(:planting_id).should eq "5"
|
||||
get :new, { :type => "planting", :id => 5 }
|
||||
assigns(:id).should eq "5"
|
||||
assigns(:type).should eq "planting"
|
||||
end
|
||||
|
||||
it "assigns a harvest id" do
|
||||
get :new, { :type => "harvest", :id => 5 }
|
||||
assigns(:id).should eq "5"
|
||||
assigns(:type).should eq "harvest"
|
||||
end
|
||||
|
||||
it "assigns the current set as @current_set" do
|
||||
@@ -69,7 +76,8 @@ describe PhotosController do
|
||||
planting = FactoryGirl.create(:planting, :garden => garden, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:planting_id => planting.id }
|
||||
:type => "planting",
|
||||
:id => planting.id }
|
||||
Photo.last.plantings.first.should eq planting
|
||||
end
|
||||
|
||||
@@ -80,12 +88,39 @@ describe PhotosController do
|
||||
planting = FactoryGirl.create(:planting, :garden => garden, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:planting_id => planting.id }
|
||||
:type => "planting",
|
||||
:id => planting.id }
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:planting_id => planting.id }
|
||||
:type => "planting",
|
||||
:id => planting.id }
|
||||
Photo.last.plantings.count.should eq 1
|
||||
end
|
||||
|
||||
it "attaches the photo to a harvest" do
|
||||
member = FactoryGirl.create(:member)
|
||||
controller.stub(:current_member) { member }
|
||||
harvest = FactoryGirl.create(:harvest, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:type => "harvest",
|
||||
:id => harvest.id }
|
||||
Photo.last.harvests.first.should eq harvest
|
||||
end
|
||||
|
||||
it "doesn't attach a photo to a harvest twice" do
|
||||
member = FactoryGirl.create(:member)
|
||||
controller.stub(:current_member) { member }
|
||||
harvest = FactoryGirl.create(:harvest, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:type => "harvest",
|
||||
:id => harvest.id }
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:type => "harvest",
|
||||
:id => harvest.id }
|
||||
Photo.last.harvests.count.should eq 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "for the second time" do
|
||||
it "does not add a photo twice" do
|
||||
@@ -106,9 +141,21 @@ describe PhotosController do
|
||||
planting = FactoryGirl.create(:planting, :garden => garden, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:planting_id => planting.id }
|
||||
:type => "planting",
|
||||
:id => planting.id }
|
||||
Photo.last.plantings.first.should eq planting
|
||||
end
|
||||
|
||||
it "creates the harvest/photo link" do
|
||||
member = FactoryGirl.create(:member)
|
||||
controller.stub(:current_member) { member }
|
||||
harvest = FactoryGirl.create(:harvest, :owner => member)
|
||||
photo = FactoryGirl.create(:photo, :owner => member)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:type => "harvest",
|
||||
:id => harvest.id }
|
||||
Photo.last.harvests.first.should eq harvest
|
||||
end
|
||||
end
|
||||
|
||||
describe "with mismatched owners" do
|
||||
@@ -117,9 +164,20 @@ describe PhotosController do
|
||||
planting = FactoryGirl.create(:planting)
|
||||
photo = FactoryGirl.create(:photo)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:planting_id => planting.id }
|
||||
:type => "planting",
|
||||
:id => planting.id }
|
||||
Photo.last.plantings.first.should_not eq planting
|
||||
end
|
||||
|
||||
it "creates the harvest/photo link" do
|
||||
# members will be auto-created, and different
|
||||
harvest = FactoryGirl.create(:harvest)
|
||||
photo = FactoryGirl.create(:photo)
|
||||
post :create, {:photo => { :flickr_photo_id => photo.flickr_photo_id },
|
||||
:type => "harvest",
|
||||
:id => harvest.id }
|
||||
Photo.last.harvests.first.should_not eq harvest
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,10 +12,30 @@ describe PlantingsController do
|
||||
end
|
||||
|
||||
describe "GET index" do
|
||||
it "picks up owner from params" do
|
||||
owner = FactoryGirl.create(:member)
|
||||
get :index, {:owner => owner.slug}
|
||||
assigns(:owner).should eq(owner)
|
||||
before do
|
||||
@member1 = FactoryGirl.create(:member)
|
||||
@member2 = FactoryGirl.create(:member)
|
||||
@tomato = FactoryGirl.create(:tomato)
|
||||
@maize = FactoryGirl.create(:maize)
|
||||
@planting1 = FactoryGirl.create(:planting, :crop => @tomato, :owner => @member1)
|
||||
@planting2 = FactoryGirl.create(:planting, :crop => @maize, :owner => @member2)
|
||||
end
|
||||
|
||||
it "assigns all plantings as @plantings" do
|
||||
get :index, {}
|
||||
assigns(:plantings).should =~ [@planting1, @planting2]
|
||||
end
|
||||
|
||||
it "picks up owner from params and shows owner's plantings only" do
|
||||
get :index, {:owner => @member1.slug}
|
||||
assigns(:owner).should eq @member1
|
||||
assigns(:plantings).should eq [@planting1]
|
||||
end
|
||||
|
||||
it "picks up crop from params and shows the plantings for the crop only" do
|
||||
get :index, {:crop => @maize.name}
|
||||
assigns(:crop).should eq @maize
|
||||
assigns(:plantings).should eq [@planting2]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
52
spec/features/signup_spec.rb
Normal file
52
spec/features/signup_spec.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
require 'spec_helper'
|
||||
|
||||
feature "signup" do
|
||||
|
||||
scenario "sign up for new account from top menubar" do
|
||||
visit crops_path # something other than front page, which has multiple signup links
|
||||
click_link 'Sign up'
|
||||
fill_in 'Login name', with: 'person123'
|
||||
fill_in 'Email', with: 'gardener@example.com'
|
||||
fill_in 'Password', with: 'abc123'
|
||||
fill_in 'Password confirmation', with: 'abc123'
|
||||
check 'member_tos_agreement'
|
||||
click_button 'Sign up'
|
||||
page.has_content? 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.'
|
||||
current_path.should eq root_path
|
||||
end
|
||||
|
||||
scenario "sign up for new account with existing username" do
|
||||
visit crops_path # something other than front page, which has multiple signup links
|
||||
click_link 'Sign up'
|
||||
fill_in 'Login name', with: 'person123'
|
||||
fill_in 'Email', with: 'gardener@example.com'
|
||||
fill_in 'Password', with: 'abc123'
|
||||
fill_in 'Password confirmation', with: 'abc123'
|
||||
check 'member_tos_agreement'
|
||||
click_button 'Sign up'
|
||||
page.has_content? 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.'
|
||||
current_path.should eq root_path
|
||||
first('.signup a').click # click the 'Sign up' button in the middle of the page
|
||||
fill_in 'Login name', with: 'person123'
|
||||
fill_in 'Email', with: 'gardener@example.com'
|
||||
fill_in 'Password', with: 'abc123'
|
||||
fill_in 'Password confirmation', with: 'abc123'
|
||||
check 'member_tos_agreement'
|
||||
click_button 'Sign up'
|
||||
page.has_content? 'Login name has already been taken'
|
||||
end
|
||||
|
||||
scenario "sign up for new account without accepting TOS" do
|
||||
visit root_path
|
||||
first('.signup a').click # click the 'Sign up' button in the middle of the page
|
||||
fill_in 'Login name', with: 'person123'
|
||||
fill_in 'Email', with: 'gardener@example.com'
|
||||
fill_in 'Password', with: 'abc123'
|
||||
fill_in 'Password confirmation', with: 'abc123'
|
||||
# do not check 'member_tos_agreement'
|
||||
click_button 'Sign up'
|
||||
page.has_content? 'Tos agreement must be accepted'
|
||||
current_path.should eq members_path
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,12 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'new gardens' do
|
||||
it "should have 'my garden' for each user" do
|
||||
(1..3).each do |i|
|
||||
@user = User.find_by_username("test#{i}")
|
||||
@garden = Garden.find(:name => "My Garden", :user_id => @user.id)
|
||||
@garden.should_exist
|
||||
@garden.slug.should == "test#{i}-my-garden"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,10 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'test users' do
|
||||
it 'should have 3 test users' do
|
||||
(1..3).each do |i|
|
||||
@user = User.find_by_username("test#{i}")
|
||||
@user.email.should == "test#{i}@example.com"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -343,4 +343,23 @@ describe Crop do
|
||||
end
|
||||
end
|
||||
|
||||
context "crop-post association" do
|
||||
let!(:tomato) { FactoryGirl.create(:tomato) }
|
||||
let!(:maize) { FactoryGirl.create(:maize) }
|
||||
let!(:post) { FactoryGirl.create(:post, :body => "[maize](crop)[tomato](crop)[tomato](crop)") }
|
||||
|
||||
describe "destroying a crop" do
|
||||
before do
|
||||
tomato.destroy
|
||||
end
|
||||
|
||||
it "should delete the association between post and the crop(tomato)" do
|
||||
expect(Post.find(post).crops).to eq [maize]
|
||||
end
|
||||
|
||||
it "should not delete the posts" do
|
||||
expect(Post.find(post)).to_not eq nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -125,4 +125,32 @@ describe Harvest do
|
||||
Harvest.all.should eq [@h2, @h1]
|
||||
end
|
||||
end
|
||||
|
||||
context 'photos' do
|
||||
before(:each) do
|
||||
@harvest = FactoryGirl.create(:harvest)
|
||||
@photo = FactoryGirl.create(:photo)
|
||||
@harvest.photos << @photo
|
||||
end
|
||||
|
||||
it 'has a photo' do
|
||||
@harvest.photos.first.should eq @photo
|
||||
end
|
||||
|
||||
it 'deletes association with photos when photo is deleted' do
|
||||
@photo.destroy
|
||||
@harvest.reload
|
||||
@harvest.photos.should be_empty
|
||||
end
|
||||
|
||||
it 'has a default photo' do
|
||||
@harvest.default_photo.should eq @photo
|
||||
end
|
||||
|
||||
it 'chooses the most recent photo' do
|
||||
@photo2 = FactoryGirl.create(:photo)
|
||||
@harvest.photos << @photo2
|
||||
@harvest.default_photo.should eq @photo2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,6 +2,82 @@ require 'spec_helper'
|
||||
|
||||
describe Photo do
|
||||
|
||||
describe 'add/delete functionality' do
|
||||
let(:photo) { FactoryGirl.create(:photo) }
|
||||
let(:planting) { FactoryGirl.create(:planting) }
|
||||
let(:harvest) { FactoryGirl.create(:harvest) }
|
||||
|
||||
context "adds photos" do
|
||||
it 'to a planting' do
|
||||
planting.photos << photo
|
||||
expect(planting.photos.count).to eq 1
|
||||
expect(planting.photos.first).to eq photo
|
||||
end
|
||||
|
||||
it 'to a harvest' do
|
||||
harvest.photos << photo
|
||||
expect(harvest.photos.count).to eq 1
|
||||
expect(harvest.photos.first).to eq photo
|
||||
end
|
||||
end
|
||||
|
||||
context "removing photos" do
|
||||
it 'from a planting' do
|
||||
planting.photos << photo
|
||||
photo.destroy
|
||||
expect(planting.photos.count).to eq 0
|
||||
end
|
||||
|
||||
it 'from a harvest' do
|
||||
harvest.photos << photo
|
||||
photo.destroy
|
||||
expect(harvest.photos.count).to eq 0
|
||||
end
|
||||
|
||||
it "automatically if unused" do
|
||||
photo.destroy_if_unused
|
||||
expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
it 'they are no longer used by plantings' do
|
||||
planting.photos << photo
|
||||
planting.destroy # photo is now no longer used by anything
|
||||
expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
it 'they are no longer used by harvests' do
|
||||
harvest.photos << photo
|
||||
harvest.destroy # photo is now no longer used by anything
|
||||
expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
it 'they are no longer used by anything' do
|
||||
planting.photos << photo
|
||||
harvest.photos << photo
|
||||
expect(photo.plantings.size).to eq 1
|
||||
expect(photo.harvests.size).to eq 1
|
||||
|
||||
planting.destroy # photo is still used by harvest
|
||||
photo.reload
|
||||
expect(photo).to be_an_instance_of Photo
|
||||
expect(photo.plantings.size).to eq 0
|
||||
expect(photo.harvests.size).to eq 1
|
||||
|
||||
harvest.destroy # photo is now no longer used by anything
|
||||
expect(lambda { photo.reload }).to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
it 'does not occur when a photo is still in use' do
|
||||
planting.photos << photo
|
||||
harvest.photos << photo
|
||||
planting.destroy # photo is still used by the harvest
|
||||
expect(photo).to be_an_instance_of Photo
|
||||
end
|
||||
|
||||
end # removing photos
|
||||
|
||||
end # add/delete functionality
|
||||
|
||||
describe 'flickr_metadata' do
|
||||
# Any further tests led to us MOCKING ALL THE THINGS
|
||||
# which was epistemologically unsatisfactory.
|
||||
|
||||
@@ -95,4 +95,44 @@ describe Post do
|
||||
end
|
||||
end
|
||||
|
||||
context "crop-post association" do
|
||||
let!(:tomato) { FactoryGirl.create(:tomato) }
|
||||
let!(:maize) { FactoryGirl.create(:maize) }
|
||||
let!(:chard) { FactoryGirl.create(:chard) }
|
||||
let!(:post) { FactoryGirl.create(:post, :body => "[maize](crop)[tomato](crop)[tomato](crop)") }
|
||||
|
||||
it "should be generated" do
|
||||
expect(tomato.posts).to eq [post]
|
||||
expect(maize.posts).to eq [post]
|
||||
end
|
||||
|
||||
it "should not duplicate" do
|
||||
expect(post.crops) =~ [tomato, maize]
|
||||
end
|
||||
|
||||
it "should be updated when post was modified" do
|
||||
post.update_attributes(:body => "[chard](crop)")
|
||||
|
||||
expect(post.crops).to eq [chard]
|
||||
expect(chard.posts).to eq [post]
|
||||
expect(tomato.posts).to eq []
|
||||
expect(maize.posts).to eq []
|
||||
end
|
||||
|
||||
describe "destroying the post" do
|
||||
before do
|
||||
post.destroy
|
||||
end
|
||||
|
||||
it "should delete the association" do
|
||||
expect(Crop.find(tomato).posts).to eq []
|
||||
expect(Crop.find(maize).posts).to eq []
|
||||
end
|
||||
|
||||
it "should not delete the crops" do
|
||||
expect(Crop.find(tomato)).to_not eq nil
|
||||
expect(Crop.find(maize)).to_not eq nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,16 @@ describe "crops/show" do
|
||||
:scientific_names => [ FactoryGirl.create(:zea_mays) ]
|
||||
)
|
||||
assign(:crop, @crop)
|
||||
@author = FactoryGirl.create(:member)
|
||||
page = 1
|
||||
per_page = 2
|
||||
total_entries = 2
|
||||
@posts = WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
|
||||
pager.replace([
|
||||
@post1 = FactoryGirl.create(:post, :author => @author, :body => "Post it!" ),
|
||||
@post2 = FactoryGirl.create(:post, :author => @author, :body => "Done!" )
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'photos' do
|
||||
@@ -125,11 +135,10 @@ describe "crops/show" do
|
||||
|
||||
context "has plantings" do
|
||||
before(:each) do
|
||||
@owner = FactoryGirl.create(:member)
|
||||
@garden = FactoryGirl.create(:garden, :owner => @owner)
|
||||
@owner = FactoryGirl.create(:london_member)
|
||||
@planting = FactoryGirl.create(:planting,
|
||||
:garden => @garden,
|
||||
:crop => @crop
|
||||
:crop => @crop,
|
||||
:owner => @owner
|
||||
)
|
||||
@crop.reload # to pick up latest plantings_count
|
||||
end
|
||||
@@ -137,16 +146,24 @@ describe "crops/show" do
|
||||
it "links to people who are growing this crop" do
|
||||
render
|
||||
rendered.should contain @owner.login_name
|
||||
rendered.should contain @garden.name
|
||||
rendered.should contain @owner.location
|
||||
end
|
||||
end
|
||||
|
||||
it "shows photos where available" do
|
||||
@photo = FactoryGirl.create(:photo)
|
||||
@planting.photos << @photo
|
||||
context "has posts" do
|
||||
it "links to posts" do
|
||||
render
|
||||
assert_select "img", :src => @photo.thumbnail_url
|
||||
@posts.each do |p|
|
||||
rendered.should contain p.author.login_name
|
||||
rendered.should contain p.subject
|
||||
rendered.should contain p.body
|
||||
end
|
||||
end
|
||||
|
||||
it "contains two gravatar icons" do
|
||||
render
|
||||
assert_select "img", :src => /gravatar\.com\/avatar/, :count => 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'varieties' do
|
||||
@@ -189,9 +206,36 @@ describe "crops/show" do
|
||||
rendered.should contain "Harvest this"
|
||||
end
|
||||
|
||||
it "links to the right crop in the planting link" do
|
||||
it "links to the right crop in the new planting link" do
|
||||
assert_select("a[href=#{new_planting_path}?crop_id=#{@crop.id}]")
|
||||
end
|
||||
|
||||
it "links to the right crop in the new harvest link" do
|
||||
assert_select("a[href=#{new_harvest_path}?crop_id=#{@crop.id}]")
|
||||
end
|
||||
|
||||
it { rendered.should contain "Nobody has planted this crop yet" }
|
||||
it { rendered.should contain "Nobody has harvested this crop yet" }
|
||||
|
||||
context "should have a link to" do
|
||||
before do
|
||||
FactoryGirl.create(:planting, :crop => @crop)
|
||||
FactoryGirl.create(:harvest, :crop => @crop)
|
||||
@crop.reload
|
||||
render
|
||||
end
|
||||
|
||||
it "show all plantings by the crop link" do
|
||||
assert_select("a[href=#{plantings_by_crop_path @crop}]")
|
||||
end
|
||||
|
||||
it "show all harvests by the crop link" do
|
||||
assert_select("a[href=#{harvests_by_crop_path @crop}]")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
context "logged in and crop wrangler" do
|
||||
|
||||
@@ -41,4 +41,17 @@ describe "harvests/index" do
|
||||
assert_select "a", :href => harvests_path(:format => 'csv')
|
||||
assert_select "a", :href => harvests_path(:format => 'json')
|
||||
end
|
||||
|
||||
it "displays member's name in title" do
|
||||
assign(:owner, @member)
|
||||
render
|
||||
view.content_for(:title).should contain @member.login_name
|
||||
end
|
||||
|
||||
it "displays crop's name in title" do
|
||||
assign(:crop, @tomato)
|
||||
render
|
||||
view.content_for(:title).should contain @tomato.name
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -48,4 +48,15 @@ describe "plantings/index" do
|
||||
assert_select "a", :href => plantings_path(:format => 'rss')
|
||||
end
|
||||
|
||||
it "displays member's name in title" do
|
||||
assign(:owner, @member)
|
||||
render
|
||||
view.content_for(:title).should contain @member.login_name
|
||||
end
|
||||
|
||||
it "displays crop's name in title" do
|
||||
assign(:crop, @tomato)
|
||||
render
|
||||
view.content_for(:title).should contain @tomato.name
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user