Merged "mygarden".

This commit is contained in:
Miles Gould
2012-12-09 15:22:20 +00:00
42 changed files with 690 additions and 53 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/log/*
/db/*.sqlite3
/tmp/*
.pt

View File

@@ -0,0 +1,3 @@
# 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/

View File

@@ -0,0 +1,83 @@
class GardensController < ApplicationController
# GET /gardens
# GET /gardens.json
def index
@gardens = Garden.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @gardens }
end
end
# GET /gardens/1
# GET /gardens/1.json
def show
@garden = Garden.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @garden }
end
end
# GET /gardens/new
# GET /gardens/new.json
def new
@garden = Garden.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @garden }
end
end
# GET /gardens/1/edit
def edit
@garden = Garden.find(params[:id])
end
# POST /gardens
# POST /gardens.json
def create
@garden = Garden.new(params[:garden])
respond_to do |format|
if @garden.save
format.html { redirect_to @garden, notice: 'Garden was successfully created.' }
format.json { render json: @garden, status: :created, location: @garden }
else
format.html { render action: "new" }
format.json { render json: @garden.errors, status: :unprocessable_entity }
end
end
end
# PUT /gardens/1
# PUT /gardens/1.json
def update
@garden = Garden.find(params[:id])
respond_to do |format|
if @garden.update_attributes(params[:garden])
format.html { redirect_to @garden, notice: 'Garden was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @garden.errors, status: :unprocessable_entity }
end
end
end
# DELETE /gardens/1
# DELETE /gardens/1.json
def destroy
@garden = Garden.find(params[:id])
@garden.destroy
respond_to do |format|
format.html { redirect_to gardens_url }
format.json { head :no_content }
end
end
end

View File

@@ -1,2 +1,7 @@
module ApplicationHelper
def random_crop
Crop.random
end
end

View File

@@ -0,0 +1,2 @@
module GardensHelper
end

View File

@@ -3,4 +3,9 @@ class Crop < ActiveRecord::Base
friendly_id :system_name, use: :slugged
attr_accessible :en_wikipedia_url, :system_name
has_many :scientific_names
def Crop.random
@crop = Crop.offset(rand(Crop.count)).first
return @crop
end
end

11
app/models/garden.rb Normal file
View File

@@ -0,0 +1,11 @@
class Garden < ActiveRecord::Base
extend FriendlyId
friendly_id :garden_slug, use: :slugged
attr_accessible :name, :slug, :user_id
belongs_to :user
def garden_slug
"#{user.username}-#{name}"
end
end

View File

@@ -3,6 +3,7 @@ class User < ActiveRecord::Base
friendly_id :username, use: :slugged
has_many :updates
has_many :gardens
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
@@ -33,5 +34,4 @@ class User < ActiveRecord::Base
where(conditions).first
end
end
end

View File

@@ -0,0 +1,12 @@
.thumbnail(style='height: 200px')
= image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded')
- if crop
%p= link_to crop.system_name, crop
- if crop.scientific_names.count > 0
%p
%i
%small
= crop.scientific_names.first.scientific_name
- else
Sample crop

View File

@@ -6,4 +6,4 @@
= link_to 'Show', @crop, { :class => 'btn' }
= link_to 'Back', crops_path, { :class => 'btn' }
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.

View File

@@ -6,14 +6,7 @@
- @crops.each do |crop|
- c += 1
%li.span2
.thumbnail(style='height: 220px')
= image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded')
%p= link_to crop.system_name, crop
- if crop.scientific_names.count > 0
%p
%i
%small
= crop.scientific_names.first.scientific_name
= render :partial => "thumbnail", :locals => { :crop => crop }
- if (c % 6) == 0
.row

View File

@@ -6,6 +6,6 @@
= link_to 'Show', @crop, { :class => 'btn' }
= link_to 'Back', crops_path, { :class => 'btn' }
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.
= link_to 'Back', crops_path

View File

@@ -0,0 +1,19 @@
= form_for @garden do |f|
- if @garden.errors.any?
#error_explanation
%h2= "#{pluralize(@garden.errors.count, "error")} prohibited this garden from being saved:"
%ul
- @garden.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :user_id
= f.text_field :user_id
.field
= f.label :slug
= f.text_field :slug
.actions
= f.submit 'Save'

View File

@@ -0,0 +1,14 @@
= content_for :title, "Edit garden"
- if user_signed_in?
- if current_user == @garden.user
= form_for @garden, :html => {:class => "form-horizontal"} do |f|
.control-group
= f.label :name, :class => "control-label"
.controls= f.text_field :name
.form-actions
= f.submit "Save", :class => 'btn'
- else
.alert You aren't allowed to edit this garden.
- else
.alert You're not logged in. Only logged in users can do this.

View File

@@ -0,0 +1,23 @@
%h1 Listing gardens
%table
%tr
%th Name
%th User
%th Slug
%th
%th
%th
- @gardens.each do |garden|
%tr
%td= garden.name
%td= garden.user_id
%td= garden.slug
%td= link_to 'Show', garden
%td= link_to 'Edit', edit_garden_path(garden)
%td= link_to 'Destroy', garden, method: :delete, data: { confirm: 'Are you sure?' }
%br
= link_to 'New Garden', new_garden_path

View File

@@ -0,0 +1,5 @@
%h1 New garden
= render 'form'
= link_to 'Back', gardens_path

View File

@@ -0,0 +1,41 @@
=content_for :title, "#{@garden.user.username}'s #{@garden.name}"
.row
.span3
=image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded')
%h4
Owner:
=link_to @garden.user.username, url_for(member_path(@garden.user))
%h4= "#{@garden.user.username}'s other gardens"
%ul
%li Some garden
%li Some other garden
- if current_user == @garden.user
= link_to 'Edit', edit_garden_path(@garden), :class => 'btn'
.span9
.tabbable
%ul.nav.nav-tabs
%li.active
= link_to 'Plantings', '#tab1', 'data-toggle' => 'tab'
%li
= link_to 'Updates', '#tab2', 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#tab1
.alert
%button.close{:type => 'button', 'data-dismiss' => 'alert'} ×
Note: these are a random selection, and don't represent actual plantings
%ul.thumbnails
- (1..20).each do
%li.span2
= render :partial => "crops/thumbnail", :locals => { :crop => random_crop }
.tab-pane.active#tab2
.alert
%button.close{:type => 'button', 'data-dismiss' => 'alert'} ×
Note: in due these will be limited to only those updates that apply to this particular garden!
- @garden.user.updates.each do |update|
= render :partial => "updates/single", :locals => { :update => update, :subject => true }

View File

@@ -10,10 +10,10 @@
- if content_for?(:title)
%h1= yield(:title)
- if notice
%div.alert.alert-success
.alert.alert-success
= notice
- if alert
%div.alert
.alert
= alert
= yield

View File

@@ -6,4 +6,3 @@
.thumbnail(style="height: 190px")
= image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded')
= link_to shorten(m.username, 30), member_path(m)

View File

@@ -14,30 +14,35 @@
.span9
.tabbable
%ul.nav.nav-tabs
%li.active
= link_to 'My Garden', '#tab1', 'data-toggle' => 'tab'
%li
= link_to '2nd Garden', '#tab2', 'data-toggle' => 'tab'
%li
= link_to '3nd Garden', '#tab3', 'data-toggle' => 'tab'
- first_garden = true
- @member.gardens.each do |g|
%li{:class => first_garden ? 'active' : '' }
- first_garden = false
= link_to g.name, "#garden#{g.id}", 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#tab1
%ul.thumbnails
- (1..4).each do
%li.span2
.thumbnail
= image_tag('http://placehold.it/150x150', :alt => '', :class => 'img-rounded')
%h4 Some crop
%p Planted YYYY-MM-DD. 99 updates.
- first_garden = true
- @member.gardens.each do |g|
.tab-pane#tab2
Multiple gardens aren't supported yet. This is just to show how they could be tabbed.
.tab-pane#tab3
Multiple gardens aren't supported yet. This is just to show how they could be tabbed.
%div{:class => ['tab-pane', first_garden ? 'active' : ''], :id => "#garden#{g.id}"}
- first_garden = false
.alert
%button.close{:type => 'button', 'data-dismiss' => 'alert'} ×
Note: these are a random selection, and don't represent actual plantings
%ul.thumbnails
- (1..4).each do
%li.span2
= render :partial => "crops/thumbnail", :locals => { :crop => random_crop }
%p
and 20 more crops including:
- (1..20).each do
- @crop = random_crop
= @crop ? link_to(@crop.system_name, url_for(@crop)) : 'blah'
%p= link_to "More about #{@member.username}'s #{@member.gardens.first.name}...", url_for(@member.gardens.first)
%h3 Updates
%table
- @member.updates.each do |update|
= render :partial => "updates/single", :locals => { :update => update, :subject => true }
- @member.updates.each do |update|
= render :partial => "updates/single", :locals => { :update => update, :subject => true }

View File

@@ -6,4 +6,4 @@
= link_to 'Show', @scientific_name, { :class => 'btn' }
= link_to 'Back', scientific_names_path, { :class => 'btn' }
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.

View File

@@ -6,4 +6,4 @@
= link_to 'Show', @scientific_name, { :class => 'btn' }
= link_to 'Back', scientific_names_path, { :class => 'btn' }
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.

View File

@@ -1,20 +1,20 @@
.well
%div.update
.update
- if subject
%h3= link_to strip_tags(update.subject), update
%div.update-meta
.update-meta
Posted by
= link_to update.user.username, member_path(update.user)
at
= update.created_at
%div.update-body
.update-body
:markdown
#{ strip_tags update.body }
- if current_user == update.user
%div.update-actions
.update-actions
= link_to 'Edit', edit_update_path(update), :class => 'btn'
= link_to 'Delete', update, method: :delete, |
data: { confirm: 'Are you sure?' }, :class => 'btn'

View File

@@ -7,4 +7,4 @@
= f.text_area :body, :rows => 12, :class => 'input-block-level'
= f.submit "Post"
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.

View File

@@ -9,6 +9,6 @@
= f.text_area :body, :rows => 12, :class => 'input-block-level'
= f.submit "Post", :class => 'btn'
- else
%div.alert You're not logged in. Only logged in users can do this.
.alert You're not logged in. Only logged in users can do this.
= link_to 'Back', updates_path

View File

@@ -1,10 +1,9 @@
Growstuff::Application.routes.draw do
resources :gardens
resources :updates
resources :scientific_names
resources :crops, :members
resources :crops
resources :members
devise_for :users
get "home/index"

View File

@@ -0,0 +1,14 @@
class CreateGardens < ActiveRecord::Migration
def change
create_table :gardens do |t|
t.string :name, :null => false
t.integer :user_id
t.string :slug, :null => false
t.timestamps
end
add_index :gardens, :user_id
add_index :gardens, :slug, unique: true
end
end

View File

@@ -0,0 +1,15 @@
class GiveEachUserAGarden < ActiveRecord::Migration
def up
User.all.each do |user|
garden = Garden.create(:name => "Garden", :user_id => user.id)
garden.save!
end
end
def down
User.all.each do |user|
garden = Garden.find_by_name_and_user_id("Garden", user.id)
garden.try(:destroy)
end
end
end

View File

@@ -0,0 +1,166 @@
require 'spec_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
describe GardensController do
login_user
# This should return the minimal set of attributes required to create a valid
# Garden. As you add validations to Garden, be sure to
# update the return value of this method accordingly.
def valid_attributes
{:name => 'My Garden', :user_id => 1 }
end
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# GardensController. Be sure to keep this updated too.
def valid_session
{}
end
describe "GET index" do
it "assigns all gardens as @gardens" do
garden = Garden.create! valid_attributes
get :index, {}
assigns(:gardens).should eq([garden])
end
end
describe "GET show" do
it "assigns the requested garden as @garden" do
garden = Garden.create! valid_attributes
get :show, {:id => garden.to_param}
assigns(:garden).should eq(garden)
end
end
describe "GET new" do
it "assigns a new garden as @garden" do
get :new, {}
assigns(:garden).should be_a_new(Garden)
end
end
describe "GET edit" do
it "assigns the requested garden as @garden" do
garden = Garden.create! valid_attributes
get :edit, {:id => garden.to_param}
assigns(:garden).should eq(garden)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Garden" do
expect {
post :create, {:garden => valid_attributes}
}.to change(Garden, :count).by(1)
end
it "assigns a newly created garden as @garden" do
post :create, {:garden => valid_attributes}
assigns(:garden).should be_a(Garden)
assigns(:garden).should be_persisted
end
it "redirects to the created garden" do
post :create, {:garden => valid_attributes}
response.should redirect_to(Garden.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved garden as @garden" do
# Trigger the behavior that occurs when invalid params are submitted
Garden.any_instance.stub(:save).and_return(false)
post :create, {:garden => {}}
assigns(:garden).should be_a_new(Garden)
end
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
Garden.any_instance.stub(:save).and_return(false)
post :create, {:garden => {}}
response.should render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested garden" do
garden = Garden.create! valid_attributes
# Assuming there are no other gardens in the database, this
# specifies that the Garden created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Garden.any_instance.should_receive(:update_attributes).with({'these' => 'params'})
put :update, {:id => garden.to_param, :garden => {'these' => 'params'}}
end
it "assigns the requested garden as @garden" do
garden = Garden.create! valid_attributes
put :update, {:id => garden.to_param, :garden => valid_attributes}
assigns(:garden).should eq(garden)
end
it "redirects to the garden" do
garden = Garden.create! valid_attributes
put :update, {:id => garden.to_param, :garden => valid_attributes}
response.should redirect_to(garden)
end
end
describe "with invalid params" do
it "assigns the garden as @garden" do
garden = Garden.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Garden.any_instance.stub(:save).and_return(false)
put :update, {:id => garden.to_param, :garden => {}}
assigns(:garden).should eq(garden)
end
it "re-renders the 'edit' template" do
garden = Garden.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Garden.any_instance.stub(:save).and_return(false)
put :update, {:id => garden.to_param, :garden => {}}
response.should render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested garden" do
garden = Garden.create! valid_attributes
expect {
delete :destroy, {:id => garden.to_param}
}.to change(Garden, :count).by(-1)
end
it "redirects to the gardens list" do
garden = Garden.create! valid_attributes
delete :destroy, {:id => garden.to_param}
response.should redirect_to(gardens_url)
end
end
end

View File

@@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the GardensHelper. For example:
#
# describe GardensHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe GardensHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@@ -0,0 +1,12 @@
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

View File

@@ -0,0 +1,10 @@
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

View File

@@ -27,4 +27,18 @@ describe Crop do
expect { @crop.save }.to raise_error ActiveRecord::StatementInvalid
end
end
context 'random' do
before(:each) do
@crop = Crop.new
@crop.system_name = "Tomato"
@crop.en_wikipedia_url = "http://en.wikipedia.org/wiki/Tomato"
@crop.save
end
it 'should find a random crop' do
@rand_crop = Crop.random
@rand_crop.system_name.should == 'Tomato'
end
end
end

View File

@@ -0,0 +1,5 @@
require 'spec_helper'
describe Garden do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'user' do
context 'valid user' do
context 'valid user' do
before(:each) do
@user = User.new
@user.email = "example@example.com"

View File

@@ -0,0 +1,11 @@
require 'spec_helper'
describe "Gardens" do
describe "GET /gardens" do
it "works! (now write some real specs)" do
# Run the generator again with the --webrat flag if you want to use webrat methods/matchers
get gardens_path
response.status.should be(200)
end
end
end

View File

@@ -0,0 +1,35 @@
require "spec_helper"
describe GardensController do
describe "routing" do
it "routes to #index" do
get("/gardens").should route_to("gardens#index")
end
it "routes to #new" do
get("/gardens/new").should route_to("gardens#new")
end
it "routes to #show" do
get("/gardens/1").should route_to("gardens#show", :id => "1")
end
it "routes to #edit" do
get("/gardens/1/edit").should route_to("gardens#edit", :id => "1")
end
it "routes to #create" do
post("/gardens").should route_to("gardens#create")
end
it "routes to #update" do
put("/gardens/1").should route_to("gardens#update", :id => "1")
end
it "routes to #destroy" do
delete("/gardens/1").should route_to("gardens#destroy", :id => "1")
end
end
end

View File

@@ -0,0 +1,38 @@
require 'spec_helper'
describe "gardens/edit" do
context "logged out" do
it "doesn't show the garden editing form if logged out" do
render
rendered.should contain "Only logged in users can do this"
end
end
context "logged in" do
before(:each) do
@user = User.create(:email => "growstuff@example.com",
:password => "irrelevant")
@user.confirm!
sign_in @user
@garden = assign(:garden, stub_model(Garden,
:name => "MyString",
:user_id => @user.id,
:slug => "MyString"
))
render
end
it "renders the edit garden form" do
render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "form", :action => gardens_path(@garden), :method => "post" do
assert_select "input#garden_name", :name => "garden[name]"
end
end
end
end

View File

@@ -0,0 +1,26 @@
require 'spec_helper'
describe "gardens/index" do
before(:each) do
assign(:gardens, [
stub_model(Garden,
:name => "Name",
:user_id => "",
:slug => "Slug"
),
stub_model(Garden,
:name => "Name",
:user_id => "",
:slug => "Slug"
)
])
end
it "renders a list of gardens" do
render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "tr>td", :text => "Name".to_s, :count => 2
assert_select "tr>td", :text => "".to_s, :count => 2
assert_select "tr>td", :text => "Slug".to_s, :count => 2
end
end

View File

@@ -0,0 +1,22 @@
require 'spec_helper'
describe "gardens/new" do
before(:each) do
assign(:garden, stub_model(Garden,
:name => "MyString",
:user_id => "",
:slug => "MyString"
).as_new_record)
end
it "renders new garden form" do
render
# Run the generator again with the --webrat flag if you want to use webrat matchers
assert_select "form", :action => gardens_path, :method => "post" do
assert_select "input#garden_name", :name => "garden[name]"
assert_select "input#garden_user_id", :name => "garden[user_id]"
assert_select "input#garden_slug", :name => "garden[slug]"
end
end
end

View File

@@ -0,0 +1,33 @@
require 'spec_helper'
describe "gardens/show" do
before(:each) do
@user = User.create(
:username => 'foo',
:email => 'foo@example.com',
:password => 'irrelevant',
:tos_agreement => true
)
@user.confirm!
@garden = assign(:garden, stub_model(Garden,
:name => "Garden Name",
:user_id => @user.id
))
end
context 'logged out' do
it 'should not show the edit button' do
render
rendered.should_not contain 'Edit'
end
end
context 'signed in' do
it 'should have an edit button' do
sign_in @user
render
rendered.should contain 'Edit'
end
end
end

View File

@@ -2,13 +2,14 @@ require 'spec_helper'
describe "members/show" do
before(:each) do
@time = Time.new
@member = assign(:user, stub_model(User,
@member = User.create!(
:username => "pie",
:password => "steak&kidney",
:email => "steak-and-kidney@pie.com",
:created_at => @time
))
:tos_agreement => true
)
@time = @member.created_at
@member.gardens.create(:name => 'My Garden', :user_id => @member.id)
end
it "shows account creation date" do