From d800747365bbb3a0923bd5a9659522373713fd43 Mon Sep 17 00:00:00 2001 From: Gnat Date: Fri, 8 Feb 2013 01:43:23 +0000 Subject: [PATCH 01/28] Attempted rearranging settings page, broke something --- app/views/devise/registrations/edit.html.haml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 05450c0b1..96ca62c8d 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -17,22 +17,22 @@ Show email publicly on your profile page %h2 Change password - - .control-group - = f.label :password, :class => 'control-label' - .controls - = f.password_field :password, :autocomplete => "off" - %span.help-inline Leave blank if you don't want to change your password - - .control-group - = f.label :password_confirmation, :class => 'control-label' - .controls= f.password_field :password_confirmation + %span.help-inline Leave blank if you don't want to change your password .control-group = f.label :current_password, :class => 'control-label' .controls = f.password_field :current_password + .control-group + = f.label :password, "New password", :class => 'control-label' + .controls + = f.password_field :password, :autocomplete => "off" + + .control-group + = f.label :password_confirmation, :class => 'control-label' + .controls= f.password_field :password_confirmation + .form-actions = f.submit "Update", :class => 'btn' From fd37e437d7066800227d27add54537c3c292a19d Mon Sep 17 00:00:00 2001 From: Amy Date: Mon, 11 Feb 2013 18:51:35 -0500 Subject: [PATCH 02/28] Update json for security fix --- Gemfile | 2 +- Gemfile.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 1991a97bf..276de7a17 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gem 'bundler', '>=1.1.5' gem 'rails', '3.2.11' gem 'rack', '~>1.4.5' - +gem 'json', '~>1.7.7' gem 'haml' gem 'cancan' diff --git a/Gemfile.lock b/Gemfile.lock index a868300ba..0f099752c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM jquery-rails (2.2.1) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.7.6) + json (1.7.7) less (2.2.2) commonjs (~> 0.2.6) less-rails (2.2.6) @@ -110,8 +110,8 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.20.1) - multi_json (1.5.0) + mime-types (1.21) + multi_json (1.5.1) net-scp (1.1.0) net-ssh (>= 2.6.5) net-sftp (2.1.1) @@ -226,6 +226,7 @@ DEPENDENCIES haml haml-rails jquery-rails + json (~> 1.7.7) less-rails passenger pg From 8c367f713d58a2e13002bc018d9a8e2552f592ad Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 11:17:02 +1100 Subject: [PATCH 03/28] added geocoder gem --- Gemfile | 2 ++ Gemfile.lock | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 276de7a17..52445b1b9 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,8 @@ gem 'friendly_id' # gravatars gem 'gravatar-ultimate' +gem 'geocoder' + # for phusion passenger (i.e. mod_rails) on the server gem 'passenger' gem 'rake', '>= 10.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0f099752c..52364fbe6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,6 +85,7 @@ GEM fastthread (1.0.7) friendly_id (4.0.9) fssm (0.2.10) + geocoder (1.1.6) gravatar-ultimate (1.0.3) haml (3.1.7) haml-rails (0.3.5) @@ -222,6 +223,7 @@ DEPENDENCIES diff-lcs factory_girl_rails (~> 4.0) friendly_id + geocoder gravatar-ultimate haml haml-rails From 807a840528a16ee92d5cb4bc1f819021b62430cd Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 11:18:37 +1100 Subject: [PATCH 04/28] Added location and lat/long fields to Member --- db/migrate/20130212001748_add_geo_to_members.rb | 7 +++++++ db/schema.rb | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20130212001748_add_geo_to_members.rb diff --git a/db/migrate/20130212001748_add_geo_to_members.rb b/db/migrate/20130212001748_add_geo_to_members.rb new file mode 100644 index 000000000..f14173795 --- /dev/null +++ b/db/migrate/20130212001748_add_geo_to_members.rb @@ -0,0 +1,7 @@ +class AddGeoToMembers < ActiveRecord::Migration + def change + add_column :members, :location, :string + add_column :members, :latitude, :float + add_column :members, :longitude, :float + end +end diff --git a/db/schema.rb b/db/schema.rb index 2c8ae9f27..9b65a025a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130208034248) do +ActiveRecord::Schema.define(:version => 20130212001748) do create_table "comments", :force => true do |t| t.integer "post_id", :limit => 255, :null => false @@ -68,6 +68,9 @@ ActiveRecord::Schema.define(:version => 20130208034248) do t.string "slug" t.boolean "tos_agreement" t.boolean "show_email" + t.string "location" + t.float "latitude" + t.float "longitude" end add_index "members", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true From c50a652eb74e94ad471b9490f05f18528142a8a0 Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 11:36:33 +1100 Subject: [PATCH 05/28] geocode locations when saved, using google API --- app/models/member.rb | 7 ++++++- spec/models/member_spec.rb | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/member.rb b/app/models/member.rb index 4e26ffcd3..f0657422e 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -15,7 +15,12 @@ class Member < ActiveRecord::Base # Setup accessible (or protected) attributes for your model attr_accessible :login_name, :email, :password, :password_confirmation, - :remember_me, :login, :tos_agreement, :show_email + :remember_me, :login, :tos_agreement, :show_email, + :location, :latitude, :longitude + + # set up geocoding + geocoded_by :location + after_validation :geocode # Virtual attribute for authenticating by either username or email # This is in addition to a real persisted field like 'username' diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 130a6006e..7f38ab510 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -55,6 +55,13 @@ describe 'member' do @comment2 = FactoryGirl.create(:comment, :author => @member) @member.comments.length.should == 2 end + + it 'has location and lat/long fields' do + @member.update_attributes(:location => 'Greenwich, UK') + @member.location.should eq 'Greenwich, UK' + @member.latitude.round(2).should eq 51.48 + @member.longitude.round(2).should eq 0.00 + end end context 'no TOS agreement' do From 879bf0ca2c5c79a6f1f2756e42e2a22a2b9ba460 Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 11:45:26 +1100 Subject: [PATCH 06/28] add location fields to member settings page --- app/views/devise/registrations/edit.html.haml | 8 ++++++++ spec/views/devise/registrations/edit_spec.rb | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 05450c0b1..bfb45151b 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -11,6 +11,14 @@ = f.email_field :email %span.help-inline If you change your email address you will have to reconfirm + %h2 Profile details + + .control-group + =f.label :location, 'Your location', :class => 'control-label' + .controls + =f.text_field :location + %span.help-inline Be as detailed or vague as you like. + .control-group .controls = f.check_box :show_email diff --git a/spec/views/devise/registrations/edit_spec.rb b/spec/views/devise/registrations/edit_spec.rb index 8eedcc41a..d04bc44df 100644 --- a/spec/views/devise/registrations/edit_spec.rb +++ b/spec/views/devise/registrations/edit_spec.rb @@ -18,18 +18,28 @@ describe 'devise/registrations/edit.html.haml', :type => "view" do rendered.should contain 'Email' end - context 'email section' + context 'email section' do it 'has a heading' do assert_select "h2", "Email address" end + end + context 'profile section' do + it 'has a heading' do + assert_select "h2", "Profile details" + end it 'shows show_email checkbox' do assert_select "input[id=member_show_email][type=checkbox]" end + it 'shows location field' do + assert_select "input[id=member_location][type=text]" + end + end it 'should have a password section' do assert_select "h2", "Change password" end end + end From 8ded8eb0c3a618be77d68b317f96a76f33e38cdb Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 11:56:56 +1100 Subject: [PATCH 07/28] display location as text on profile --- app/views/members/show.html.haml | 4 +++- spec/factories/member.rb | 7 +++++++ spec/views/members/show.html.haml_spec.rb | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index dc8cdf754..3ecb2b4fb 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -9,7 +9,9 @@ %p = "Member since: #{@member.created_at.to_s(:date)}" %p - Location: Unknown + Location: + = @member.location + = "(#{@member.latitude}, #{@member.longitude})" - if @member.show_email %p Email: diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 23a3d7fa6..39ea4026c 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -27,6 +27,13 @@ FactoryGirl.define do show_email true end + factory :geolocated_member do + location 'Greenwich, UK' + # including lat/long explicitly because geocoder doesn't work with FG + latitude 51.483 + longitude 0.004 + end + end end diff --git a/spec/views/members/show.html.haml_spec.rb b/spec/views/members/show.html.haml_spec.rb index 98718d6fd..2c1a65e79 100644 --- a/spec/views/members/show.html.haml_spec.rb +++ b/spec/views/members/show.html.haml_spec.rb @@ -67,4 +67,20 @@ describe "members/show" do end end + context "geolocations" do + before(:each) do + @member = FactoryGirl.create(:geolocated_member) + render + end + it "shows the location" do + rendered.should contain @member.location + end + it "shows the latitude" do + rendered.should contain @member.latitude.to_s + end + it "shows the longitude" do + rendered.should contain @member.longitude.to_s + end + end + end From 192196ae8e112c246b71387a4e4138c8481e5c55 Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 12 Feb 2013 12:39:18 +1100 Subject: [PATCH 08/28] added map to profile. also making sure lat/long are zeroed if location is blanked --- app/models/member.rb | 9 +++ app/views/members/show.html.haml | 10 +-- spec/factories/member.rb | 1 + spec/models/member_spec.rb | 9 +++ spec/views/members/show.html.haml_spec.rb | 88 ++++++++++++++--------- 5 files changed, 81 insertions(+), 36 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index f0657422e..a56dd7e88 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -21,6 +21,7 @@ class Member < ActiveRecord::Base # set up geocoding geocoded_by :location after_validation :geocode + after_validation :empty_unwanted_geocodes # Virtual attribute for authenticating by either username or email # This is in addition to a real persisted field like 'username' @@ -54,4 +55,12 @@ class Member < ActiveRecord::Base def to_s return login_name end + + protected + def empty_unwanted_geocodes + if self.location.to_s == '' + self.latitude = nil + self.longitude = nil + end + end end diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index 3ecb2b4fb..d270bf33a 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -8,10 +8,12 @@ %p = "Member since: #{@member.created_at.to_s(:date)}" - %p - Location: - = @member.location - = "(#{@member.latitude}, #{@member.longitude})" + - if @member.location.to_s != '' + %p + Location: + = @member.location + %br/ + = image_tag("http://maps.google.com/maps/api/staticmap?size=200x200&maptype=roadmap&sensor=false&markers=color:green|label:A|#{@member.latitude},#{@member.longitude}&zoom=12", :alt => "Map showing #{@member.location}", :width => 200, :height => 200 ) - if @member.show_email %p Email: diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 39ea4026c..81e7db06c 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -28,6 +28,7 @@ FactoryGirl.define do end factory :geolocated_member do + login_name 'JohnH' # for the astronomer who figured out longitude location 'Greenwich, UK' # including lat/long explicitly because geocoder doesn't work with FG latitude 51.483 diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 7f38ab510..586e37abd 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -62,6 +62,15 @@ describe 'member' do @member.latitude.round(2).should eq 51.48 @member.longitude.round(2).should eq 0.00 end + + it 'empties the lat/long if location removed' do + @member.update_attributes(:location => 'Greenwich, UK') + @member.update_attributes(:location => '') + @member.location.should eq '' + @member.latitude.should be_nil + @member.longitude.should be_nil + end + end context 'no TOS agreement' do diff --git a/spec/views/members/show.html.haml_spec.rb b/spec/views/members/show.html.haml_spec.rb index 2c1a65e79..db4a38a6c 100644 --- a/spec/views/members/show.html.haml_spec.rb +++ b/spec/views/members/show.html.haml_spec.rb @@ -1,53 +1,64 @@ require 'spec_helper' describe "members/show" do - before(:each) do controller.stub(:current_user) { nil } @member = FactoryGirl.create(:member) - @time = @member.created_at - @garden = FactoryGirl.create(:garden, :owner => @member) - @planting = FactoryGirl.create(:planting, :garden => @garden) - render end - it "shows account creation date" do - rendered.should contain "Member since" - rendered.should contain @time.strftime("%B %d, %Y") + context "the basics" do + before(:each) do + render + end + + it "shows account creation date" do + @time = @member.created_at + rendered.should contain "Member since" + rendered.should contain @time.strftime("%B %d, %Y") + end + + it "contains a gravatar icon" do + assert_select "img", :src => /gravatar\.com\/avatar/ + end + end - it "contains a gravatar icon" do - assert_select "img", :src => /gravatar\.com\/avatar/ - end + context "gardens and plantings" do + before(:each) do + @planting = FactoryGirl.create(:planting, :garden => @garden) + render + end - it "shows the auto-created garden" do - assert_select "li.active>a", :text => "Garden" - end + it "shows the auto-created garden" do + assert_select "li.active>a", :text => "Garden" + end - it 'shows the garden description' do - rendered.should contain "totally cool garden" - end + it 'shows the garden description' do + rendered.should contain "totally cool garden" + end - it 'renders markdown in the garden description' do - assert_select "strong", "totally" - end + it 'renders markdown in the garden description' do + assert_select "strong", "totally" + end - it "shows the plantings in the garden" do - rendered.should contain @planting.crop.system_name - end + it "shows the plantings in the garden" do + rendered.should contain @planting.crop.system_name + end - it "doesn't show the note about random plantings" do - rendered.should_not contain "Note: these are a random selection" - end + it "doesn't show the note about random plantings" do + rendered.should_not contain "Note: these are a random selection" + end - it "doesn't show the email address" do - rendered.should_not contain @member.email + it "doesn't show the email address" do + rendered.should_not contain @member.email + end end context "signed in member" do before(:each) do sign_in @member + controller.stub(:current_user) { @member } render end @@ -75,11 +86,24 @@ describe "members/show" do it "shows the location" do rendered.should contain @member.location end - it "shows the latitude" do - rendered.should contain @member.latitude.to_s + it "shows a map" do + assert_select "img", :src => /maps\.google\.com/ end - it "shows the longitude" do - rendered.should contain @member.longitude.to_s + end + + context "no location stated" do + before(:each) do + @member = FactoryGirl.create(:member) + render + end + it "doesn't have a location" do + @member.location.to_s.should eq '' + end + it "doesn't show the location" do + rendered.should_not contain "Location:" + end + it "doesn't show a map" do + assert_select "img[src*=maps]", false end end From 3d37f35457700faf914a92e8f11e5076eb762aab Mon Sep 17 00:00:00 2001 From: Miles Gould Date: Tue, 12 Feb 2013 12:49:52 +0000 Subject: [PATCH 09/28] Upgrade to Rails 3.2.12, after *another* security flaw. https://groups.google.com/forum/?hl=en&fromgroups=#!topic/rubyonrails-security/AFBKNY7VSH8 --- Gemfile | 2 +- Gemfile.lock | 52 ++++++++++++++++++++++++++-------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Gemfile b/Gemfile index 276de7a17..81b278d5d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'bundler', '>=1.1.5' -gem 'rails', '3.2.11' +gem 'rails', '3.2.12' gem 'rack', '~>1.4.5' gem 'json', '~>1.7.7' gem 'haml' diff --git a/Gemfile.lock b/Gemfile.lock index 0f099752c..63e13d41a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,31 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (3.2.11) - actionpack (= 3.2.11) + actionmailer (3.2.12) + actionpack (= 3.2.12) mail (~> 2.4.4) - actionpack (3.2.11) - activemodel (= 3.2.11) - activesupport (= 3.2.11) + actionpack (3.2.12) + activemodel (= 3.2.12) + activesupport (= 3.2.12) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) - rack (~> 1.4.0) + rack (~> 1.4.5) rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.2.1) - activemodel (3.2.11) - activesupport (= 3.2.11) + activemodel (3.2.12) + activesupport (= 3.2.12) builder (~> 3.0.0) - activerecord (3.2.11) - activemodel (= 3.2.11) - activesupport (= 3.2.11) + activerecord (3.2.12) + activemodel (= 3.2.12) + activesupport (= 3.2.12) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.11) - activemodel (= 3.2.11) - activesupport (= 3.2.11) - activesupport (3.2.11) + activeresource (3.2.12) + activemodel (= 3.2.12) + activesupport (= 3.2.12) + activesupport (3.2.12) i18n (~> 0.6) multi_json (~> 1.0) arel (3.0.2) @@ -135,17 +135,17 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (3.2.11) - actionmailer (= 3.2.11) - actionpack (= 3.2.11) - activerecord (= 3.2.11) - activeresource (= 3.2.11) - activesupport (= 3.2.11) + rails (3.2.12) + actionmailer (= 3.2.12) + actionpack (= 3.2.12) + activerecord (= 3.2.12) + activeresource (= 3.2.12) + activesupport (= 3.2.12) bundler (~> 1.0) - railties (= 3.2.11) - railties (3.2.11) - actionpack (= 3.2.11) - activesupport (= 3.2.11) + railties (= 3.2.12) + railties (3.2.12) + actionpack (= 3.2.12) + activesupport (= 3.2.12) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) @@ -231,7 +231,7 @@ DEPENDENCIES passenger pg rack (~> 1.4.5) - rails (= 3.2.11) + rails (= 3.2.12) rake (>= 10.0.0) rspec-rails (~> 2.12.1) rvm-capistrano From 41fcc5bd06bf312645be7e53141971ebf423ff0d Mon Sep 17 00:00:00 2001 From: Gnat Date: Fri, 8 Feb 2013 01:43:23 +0000 Subject: [PATCH 10/28] Attempted rearranging settings page, broke something --- app/views/devise/registrations/edit.html.haml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index bfb45151b..5d8a89491 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -25,22 +25,22 @@ Show email publicly on your profile page %h2 Change password - - .control-group - = f.label :password, :class => 'control-label' - .controls - = f.password_field :password, :autocomplete => "off" - %span.help-inline Leave blank if you don't want to change your password - - .control-group - = f.label :password_confirmation, :class => 'control-label' - .controls= f.password_field :password_confirmation + %span.help-inline Leave blank if you don't want to change your password .control-group = f.label :current_password, :class => 'control-label' .controls = f.password_field :current_password + .control-group + = f.label :password, "New password", :class => 'control-label' + .controls + = f.password_field :password, :autocomplete => "off" + + .control-group + = f.label :password_confirmation, :class => 'control-label' + .controls= f.password_field :password_confirmation + .form-actions = f.submit "Update", :class => 'btn' From f6ad4fec75c1bfb2dd8bc90cac7a0beb0428b61c Mon Sep 17 00:00:00 2001 From: gnattery Date: Wed, 13 Feb 2013 12:11:31 +1100 Subject: [PATCH 11/28] Rearranging password fields --- app/views/devise/registrations/edit.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 5d8a89491..ebee1bfcb 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -25,7 +25,8 @@ Show email publicly on your profile page %h2 Change password - %span.help-inline Leave blank if you don't want to change your password + %p + %span.help-block Leave blank if you don't want to change your password .control-group = f.label :current_password, :class => 'control-label' From e10ea7f1809dc0bf41ac26b98d322fa3ea99d0bf Mon Sep 17 00:00:00 2001 From: gnattery Date: Wed, 13 Feb 2013 12:55:17 +1100 Subject: [PATCH 12/28] rails g scaffold Forum --- app/assets/javascripts/forums.js.coffee | 3 + app/controllers/forums_controller.rb | 83 ++++++++++++ app/helpers/forums_helper.rb | 2 + app/models/forum.rb | 3 + app/views/forums/_form.html.haml | 19 +++ app/views/forums/edit.html.haml | 7 + app/views/forums/index.html.haml | 23 ++++ app/views/forums/new.html.haml | 5 + app/views/forums/show.html.haml | 15 +++ config/routes.rb | 3 + db/migrate/20130213014511_create_forums.rb | 11 ++ db/schema.rb | 10 +- spec/controllers/forums_controller_spec.rb | 147 +++++++++++++++++++++ spec/factories/forums.rb | 9 ++ spec/models/forum_spec.rb | 5 + spec/requests/forums_spec.rb | 11 ++ spec/routing/forums_routing_spec.rb | 35 +++++ spec/views/forums/edit.html.haml_spec.rb | 22 +++ spec/views/forums/index.html.haml_spec.rb | 26 ++++ spec/views/forums/new.html.haml_spec.rb | 22 +++ spec/views/forums/show.html.haml_spec.rb | 19 +++ 21 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/forums.js.coffee create mode 100644 app/controllers/forums_controller.rb create mode 100644 app/helpers/forums_helper.rb create mode 100644 app/models/forum.rb create mode 100644 app/views/forums/_form.html.haml create mode 100644 app/views/forums/edit.html.haml create mode 100644 app/views/forums/index.html.haml create mode 100644 app/views/forums/new.html.haml create mode 100644 app/views/forums/show.html.haml create mode 100644 db/migrate/20130213014511_create_forums.rb create mode 100644 spec/controllers/forums_controller_spec.rb create mode 100644 spec/factories/forums.rb create mode 100644 spec/models/forum_spec.rb create mode 100644 spec/requests/forums_spec.rb create mode 100644 spec/routing/forums_routing_spec.rb create mode 100644 spec/views/forums/edit.html.haml_spec.rb create mode 100644 spec/views/forums/index.html.haml_spec.rb create mode 100644 spec/views/forums/new.html.haml_spec.rb create mode 100644 spec/views/forums/show.html.haml_spec.rb diff --git a/app/assets/javascripts/forums.js.coffee b/app/assets/javascripts/forums.js.coffee new file mode 100644 index 000000000..761567942 --- /dev/null +++ b/app/assets/javascripts/forums.js.coffee @@ -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/ diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb new file mode 100644 index 000000000..806ba2574 --- /dev/null +++ b/app/controllers/forums_controller.rb @@ -0,0 +1,83 @@ +class ForumsController < ApplicationController + # GET /forums + # GET /forums.json + def index + @forums = Forum.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @forums } + end + end + + # GET /forums/1 + # GET /forums/1.json + def show + @forum = Forum.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @forum } + end + end + + # GET /forums/new + # GET /forums/new.json + def new + @forum = Forum.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @forum } + end + end + + # GET /forums/1/edit + def edit + @forum = Forum.find(params[:id]) + end + + # POST /forums + # POST /forums.json + def create + @forum = Forum.new(params[:forum]) + + respond_to do |format| + if @forum.save + format.html { redirect_to @forum, notice: 'Forum was successfully created.' } + format.json { render json: @forum, status: :created, location: @forum } + else + format.html { render action: "new" } + format.json { render json: @forum.errors, status: :unprocessable_entity } + end + end + end + + # PUT /forums/1 + # PUT /forums/1.json + def update + @forum = Forum.find(params[:id]) + + respond_to do |format| + if @forum.update_attributes(params[:forum]) + format.html { redirect_to @forum, notice: 'Forum was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: "edit" } + format.json { render json: @forum.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /forums/1 + # DELETE /forums/1.json + def destroy + @forum = Forum.find(params[:id]) + @forum.destroy + + respond_to do |format| + format.html { redirect_to forums_url } + format.json { head :no_content } + end + end +end diff --git a/app/helpers/forums_helper.rb b/app/helpers/forums_helper.rb new file mode 100644 index 000000000..2e531fd46 --- /dev/null +++ b/app/helpers/forums_helper.rb @@ -0,0 +1,2 @@ +module ForumsHelper +end diff --git a/app/models/forum.rb b/app/models/forum.rb new file mode 100644 index 000000000..822098801 --- /dev/null +++ b/app/models/forum.rb @@ -0,0 +1,3 @@ +class Forum < ActiveRecord::Base + attr_accessible :description, :name, :owner_id +end diff --git a/app/views/forums/_form.html.haml b/app/views/forums/_form.html.haml new file mode 100644 index 000000000..05c4d5538 --- /dev/null +++ b/app/views/forums/_form.html.haml @@ -0,0 +1,19 @@ += form_for @forum do |f| + - if @forum.errors.any? + #error_explanation + %h2= "#{pluralize(@forum.errors.count, "error")} prohibited this forum from being saved:" + %ul + - @forum.errors.full_messages.each do |msg| + %li= msg + + .field + = f.label :name + = f.text_field :name + .field + = f.label :description + = f.text_area :description + .field + = f.label :owner_id + = f.number_field :owner_id + .actions + = f.submit 'Save' diff --git a/app/views/forums/edit.html.haml b/app/views/forums/edit.html.haml new file mode 100644 index 000000000..88c265f45 --- /dev/null +++ b/app/views/forums/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing forum + += render 'form' + += link_to 'Show', @forum +\| += link_to 'Back', forums_path diff --git a/app/views/forums/index.html.haml b/app/views/forums/index.html.haml new file mode 100644 index 000000000..aac289aae --- /dev/null +++ b/app/views/forums/index.html.haml @@ -0,0 +1,23 @@ +%h1 Listing forums + +%table + %tr + %th Name + %th Description + %th Owner + %th + %th + %th + + - @forums.each do |forum| + %tr + %td= forum.name + %td= forum.description + %td= forum.owner_id + %td= link_to 'Show', forum + %td= link_to 'Edit', edit_forum_path(forum) + %td= link_to 'Destroy', forum, method: :delete, data: { confirm: 'Are you sure?' } + +%br + += link_to 'New Forum', new_forum_path diff --git a/app/views/forums/new.html.haml b/app/views/forums/new.html.haml new file mode 100644 index 000000000..45a9dc26a --- /dev/null +++ b/app/views/forums/new.html.haml @@ -0,0 +1,5 @@ +%h1 New forum + += render 'form' + += link_to 'Back', forums_path diff --git a/app/views/forums/show.html.haml b/app/views/forums/show.html.haml new file mode 100644 index 000000000..8f2f1eacb --- /dev/null +++ b/app/views/forums/show.html.haml @@ -0,0 +1,15 @@ +%p#notice= notice + +%p + %b Name: + = @forum.name +%p + %b Description: + = @forum.description +%p + %b Owner: + = @forum.owner_id + += link_to 'Edit', edit_forum_path(@forum) +\| += link_to 'Back', forums_path diff --git a/config/routes.rb b/config/routes.rb index c3c818b43..4577680c5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,7 @@ Growstuff::Application.routes.draw do + resources :forums + + devise_for :members, :controllers => { :registrations => "registrations" } resources :plantings diff --git a/db/migrate/20130213014511_create_forums.rb b/db/migrate/20130213014511_create_forums.rb new file mode 100644 index 000000000..6388876e6 --- /dev/null +++ b/db/migrate/20130213014511_create_forums.rb @@ -0,0 +1,11 @@ +class CreateForums < ActiveRecord::Migration + def change + create_table :forums do |t| + t.string :name, :null => false + t.text :description, :null => false + t.integer :owner_id, :null => false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9b65a025a..4b8ae4410 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130212001748) do +ActiveRecord::Schema.define(:version => 20130213014511) do create_table "comments", :force => true do |t| t.integer "post_id", :limit => 255, :null => false @@ -32,6 +32,14 @@ ActiveRecord::Schema.define(:version => 20130212001748) do add_index "crops", ["slug"], :name => "index_crops_on_slug", :unique => true add_index "crops", ["system_name"], :name => "index_crops_on_system_name" + create_table "forums", :force => true do |t| + t.string "name", :null => false + t.text "description", :null => false + t.integer "owner_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "gardens", :force => true do |t| t.string "name", :null => false t.integer "owner_id" diff --git a/spec/controllers/forums_controller_spec.rb b/spec/controllers/forums_controller_spec.rb new file mode 100644 index 000000000..37c484523 --- /dev/null +++ b/spec/controllers/forums_controller_spec.rb @@ -0,0 +1,147 @@ +require 'spec_helper' + +describe ForumsController do + + def valid_attributes + { + "name" => "MyString", + "description" => "Something", + "owner_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 + # ForumsController. Be sure to keep this updated too. + def valid_session + {} + end + + describe "GET index" do + it "assigns all forums as @forums" do + forum = Forum.create! valid_attributes + get :index, {}, valid_session + assigns(:forums).should eq([forum]) + end + end + + describe "GET show" do + it "assigns the requested forum as @forum" do + forum = Forum.create! valid_attributes + get :show, {:id => forum.to_param}, valid_session + assigns(:forum).should eq(forum) + end + end + + describe "GET new" do + it "assigns a new forum as @forum" do + get :new, {}, valid_session + assigns(:forum).should be_a_new(Forum) + end + end + + describe "GET edit" do + it "assigns the requested forum as @forum" do + forum = Forum.create! valid_attributes + get :edit, {:id => forum.to_param}, valid_session + assigns(:forum).should eq(forum) + end + end + + describe "POST create" do + describe "with valid params" do + it "creates a new Forum" do + expect { + post :create, {:forum => valid_attributes}, valid_session + }.to change(Forum, :count).by(1) + end + + it "assigns a newly created forum as @forum" do + post :create, {:forum => valid_attributes}, valid_session + assigns(:forum).should be_a(Forum) + assigns(:forum).should be_persisted + end + + it "redirects to the created forum" do + post :create, {:forum => valid_attributes}, valid_session + response.should redirect_to(Forum.last) + end + end + + describe "with invalid params" do + it "assigns a newly created but unsaved forum as @forum" do + # Trigger the behavior that occurs when invalid params are submitted + Forum.any_instance.stub(:save).and_return(false) + post :create, {:forum => { "name" => "invalid value" }}, valid_session + assigns(:forum).should be_a_new(Forum) + end + + it "re-renders the 'new' template" do + # Trigger the behavior that occurs when invalid params are submitted + Forum.any_instance.stub(:save).and_return(false) + post :create, {:forum => { "name" => "invalid value" }}, valid_session + response.should render_template("new") + end + end + end + + describe "PUT update" do + describe "with valid params" do + it "updates the requested forum" do + forum = Forum.create! valid_attributes + # Assuming there are no other forums in the database, this + # specifies that the Forum created on the previous line + # receives the :update_attributes message with whatever params are + # submitted in the request. + Forum.any_instance.should_receive(:update_attributes).with({ "name" => "MyString" }) + put :update, {:id => forum.to_param, :forum => { "name" => "MyString" }}, valid_session + end + + it "assigns the requested forum as @forum" do + forum = Forum.create! valid_attributes + put :update, {:id => forum.to_param, :forum => valid_attributes}, valid_session + assigns(:forum).should eq(forum) + end + + it "redirects to the forum" do + forum = Forum.create! valid_attributes + put :update, {:id => forum.to_param, :forum => valid_attributes}, valid_session + response.should redirect_to(forum) + end + end + + describe "with invalid params" do + it "assigns the forum as @forum" do + forum = Forum.create! valid_attributes + # Trigger the behavior that occurs when invalid params are submitted + Forum.any_instance.stub(:save).and_return(false) + put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }}, valid_session + assigns(:forum).should eq(forum) + end + + it "re-renders the 'edit' template" do + forum = Forum.create! valid_attributes + # Trigger the behavior that occurs when invalid params are submitted + Forum.any_instance.stub(:save).and_return(false) + put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }}, valid_session + response.should render_template("edit") + end + end + end + + describe "DELETE destroy" do + it "destroys the requested forum" do + forum = Forum.create! valid_attributes + expect { + delete :destroy, {:id => forum.to_param}, valid_session + }.to change(Forum, :count).by(-1) + end + + it "redirects to the forums list" do + forum = Forum.create! valid_attributes + delete :destroy, {:id => forum.to_param}, valid_session + response.should redirect_to(forums_url) + end + end + +end diff --git a/spec/factories/forums.rb b/spec/factories/forums.rb new file mode 100644 index 000000000..5bc68e5bb --- /dev/null +++ b/spec/factories/forums.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :forum do + name "MyString" + description "MyText" + owner_id 1 + end +end diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb new file mode 100644 index 000000000..a20746c9b --- /dev/null +++ b/spec/models/forum_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Forum do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/forums_spec.rb b/spec/requests/forums_spec.rb new file mode 100644 index 000000000..ee85dacfd --- /dev/null +++ b/spec/requests/forums_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "Forums" do + describe "GET /forums" 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 forums_path + response.status.should be(200) + end + end +end diff --git a/spec/routing/forums_routing_spec.rb b/spec/routing/forums_routing_spec.rb new file mode 100644 index 000000000..c783d5199 --- /dev/null +++ b/spec/routing/forums_routing_spec.rb @@ -0,0 +1,35 @@ +require "spec_helper" + +describe ForumsController do + describe "routing" do + + it "routes to #index" do + get("/forums").should route_to("forums#index") + end + + it "routes to #new" do + get("/forums/new").should route_to("forums#new") + end + + it "routes to #show" do + get("/forums/1").should route_to("forums#show", :id => "1") + end + + it "routes to #edit" do + get("/forums/1/edit").should route_to("forums#edit", :id => "1") + end + + it "routes to #create" do + post("/forums").should route_to("forums#create") + end + + it "routes to #update" do + put("/forums/1").should route_to("forums#update", :id => "1") + end + + it "routes to #destroy" do + delete("/forums/1").should route_to("forums#destroy", :id => "1") + end + + end +end diff --git a/spec/views/forums/edit.html.haml_spec.rb b/spec/views/forums/edit.html.haml_spec.rb new file mode 100644 index 000000000..d957b9adc --- /dev/null +++ b/spec/views/forums/edit.html.haml_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe "forums/edit" do + before(:each) do + @forum = assign(:forum, stub_model(Forum, + :name => "MyString", + :description => "MyText", + :owner_id => 1 + )) + end + + it "renders the edit forum form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => forums_path(@forum), :method => "post" do + assert_select "input#forum_name", :name => "forum[name]" + assert_select "textarea#forum_description", :name => "forum[description]" + assert_select "input#forum_owner_id", :name => "forum[owner_id]" + end + end +end diff --git a/spec/views/forums/index.html.haml_spec.rb b/spec/views/forums/index.html.haml_spec.rb new file mode 100644 index 000000000..f45bdedd4 --- /dev/null +++ b/spec/views/forums/index.html.haml_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe "forums/index" do + before(:each) do + assign(:forums, [ + stub_model(Forum, + :name => "Name", + :description => "MyText", + :owner_id => 1 + ), + stub_model(Forum, + :name => "Name", + :description => "MyText", + :owner_id => 1 + ) + ]) + end + + it "renders a list of forums" 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 => "MyText".to_s, :count => 2 + assert_select "tr>td", :text => 1.to_s, :count => 2 + end +end diff --git a/spec/views/forums/new.html.haml_spec.rb b/spec/views/forums/new.html.haml_spec.rb new file mode 100644 index 000000000..63b4bac34 --- /dev/null +++ b/spec/views/forums/new.html.haml_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe "forums/new" do + before(:each) do + assign(:forum, stub_model(Forum, + :name => "MyString", + :description => "MyText", + :owner_id => 1 + ).as_new_record) + end + + it "renders new forum form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => forums_path, :method => "post" do + assert_select "input#forum_name", :name => "forum[name]" + assert_select "textarea#forum_description", :name => "forum[description]" + assert_select "input#forum_owner_id", :name => "forum[owner_id]" + end + end +end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb new file mode 100644 index 000000000..96ac62921 --- /dev/null +++ b/spec/views/forums/show.html.haml_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe "forums/show" do + before(:each) do + @forum = assign(:forum, stub_model(Forum, + :name => "Name", + :description => "MyText", + :owner_id => 1 + )) + end + + it "renders attributes in

" do + render + # Run the generator again with the --webrat flag if you want to use webrat matchers + rendered.should match(/Name/) + rendered.should match(/MyText/) + rendered.should match(/1/) + end +end From de85a6dcced4ebdf0b32715f4aea84337a8b7e0a Mon Sep 17 00:00:00 2001 From: gnattery Date: Wed, 13 Feb 2013 15:17:40 +1100 Subject: [PATCH 13/28] added associations between forums, posts, and members --- app/models/forum.rb | 2 ++ app/models/member.rb | 1 + app/models/post.rb | 1 + db/migrate/20130213015708_add_forum_to_posts.rb | 5 +++++ db/schema.rb | 3 ++- spec/factories/forums.rb | 6 +++--- spec/factories/post.rb | 4 ++++ spec/models/forum_spec.rb | 14 +++++++++++++- spec/models/member_spec.rb | 7 +++++++ spec/models/post_spec.rb | 5 +++++ 10 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20130213015708_add_forum_to_posts.rb diff --git a/app/models/forum.rb b/app/models/forum.rb index 822098801..c9a438ce7 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -1,3 +1,5 @@ class Forum < ActiveRecord::Base attr_accessible :description, :name, :owner_id + has_many :posts + belongs_to :owner, :class_name => "Member" end diff --git a/app/models/member.rb b/app/models/member.rb index a56dd7e88..436288e3a 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -5,6 +5,7 @@ class Member < ActiveRecord::Base has_many :posts, :foreign_key => 'author_id' has_many :comments, :foreign_key => 'author_id' has_many :gardens, :foreign_key => 'owner_id' + has_many :forums, :foreign_key => 'owner_id' # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, diff --git a/app/models/post.rb b/app/models/post.rb index e38920623..050f13c26 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -3,6 +3,7 @@ class Post < ActiveRecord::Base friendly_id :author_date_subject, use: :slugged attr_accessible :body, :subject, :author_id belongs_to :author, :class_name => 'Member' + belongs_to :forum has_many :comments, :dependent => :destroy default_scope order("created_at desc") diff --git a/db/migrate/20130213015708_add_forum_to_posts.rb b/db/migrate/20130213015708_add_forum_to_posts.rb new file mode 100644 index 000000000..904183f90 --- /dev/null +++ b/db/migrate/20130213015708_add_forum_to_posts.rb @@ -0,0 +1,5 @@ +class AddForumToPosts < ActiveRecord::Migration + def change + add_column :posts, :forum_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 4b8ae4410..55fa11bd6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130213014511) do +ActiveRecord::Schema.define(:version => 20130213015708) do create_table "comments", :force => true do |t| t.integer "post_id", :limit => 255, :null => false @@ -107,6 +107,7 @@ ActiveRecord::Schema.define(:version => 20130213014511) do t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.string "slug" + t.integer "forum_id" end add_index "posts", ["created_at", "author_id"], :name => "index_updates_on_created_at_and_user_id" diff --git a/spec/factories/forums.rb b/spec/factories/forums.rb index 5bc68e5bb..0d8cff596 100644 --- a/spec/factories/forums.rb +++ b/spec/factories/forums.rb @@ -2,8 +2,8 @@ FactoryGirl.define do factory :forum do - name "MyString" - description "MyText" - owner_id 1 + name "Permaculture" + description "*Everything* about permaculture!" + owner end end diff --git a/spec/factories/post.rb b/spec/factories/post.rb index 0f056691e..d9e6e0be5 100644 --- a/spec/factories/post.rb +++ b/spec/factories/post.rb @@ -15,6 +15,10 @@ FactoryGirl.define do factory :html_post do body 'EVIL' end + + factory :forum_post do + forum + end end end diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb index a20746c9b..b9d09a707 100644 --- a/spec/models/forum_spec.rb +++ b/spec/models/forum_spec.rb @@ -1,5 +1,17 @@ require 'spec_helper' describe Forum do - pending "add some examples to (or delete) #{__FILE__}" + before(:each) do + @forum = FactoryGirl.create(:forum) + end + + it "belongs to an owner" do + @forum.owner.should be_an_instance_of Member + end + + it "has many posts" do + @post1 = FactoryGirl.create(:forum_post, :forum => @forum) + @post2 = FactoryGirl.create(:forum_post, :forum => @forum) + @forum.posts.length.should == 2 + end end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 586e37abd..1f2f8ea41 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -56,6 +56,13 @@ describe 'member' do @member.comments.length.should == 2 end + it "has many forums" do + @member.save + @forum1 = FactoryGirl.create(:forum, :owner => @member) + @forum2 = FactoryGirl.create(:forum, :owner => @member) + @member.forums.length.should == 2 + end + it 'has location and lat/long fields' do @member.update_attributes(:location => 'Greenwich, UK') @member.location.should eq 'Greenwich, UK' diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 784bad0d3..5ce6c158a 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -37,4 +37,9 @@ describe Post do @post.destroy Comment.count.should == all - 2 end + + it "belongs to a forum" do + @post = FactoryGirl.create(:forum_post) + @post.forum.should be_an_instance_of Forum + end end From ef236befcde6451719a613c847c0942759c58ca8 Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 13 Feb 2013 15:55:16 +1100 Subject: [PATCH 14/28] prettifying forum views --- app/views/forums/_form.html.haml | 24 ++++++++--------- app/views/forums/edit.html.haml | 2 +- app/views/forums/index.html.haml | 33 ++++++++--------------- app/views/forums/new.html.haml | 2 +- app/views/forums/show.html.haml | 23 +++++++++------- spec/views/forums/edit.html.haml_spec.rb | 2 +- spec/views/forums/index.html.haml_spec.rb | 21 +++------------ spec/views/forums/new.html.haml_spec.rb | 12 +++------ spec/views/forums/show.html.haml_spec.rb | 16 ++++------- 9 files changed, 52 insertions(+), 83 deletions(-) diff --git a/app/views/forums/_form.html.haml b/app/views/forums/_form.html.haml index 05c4d5538..116af4f2e 100644 --- a/app/views/forums/_form.html.haml +++ b/app/views/forums/_form.html.haml @@ -1,4 +1,4 @@ -= form_for @forum do |f| += form_for @forum, :html => { :class => 'form-horizontal' } do |f| - if @forum.errors.any? #error_explanation %h2= "#{pluralize(@forum.errors.count, "error")} prohibited this forum from being saved:" @@ -6,14 +6,14 @@ - @forum.errors.full_messages.each do |msg| %li= msg - .field - = f.label :name - = f.text_field :name - .field - = f.label :description - = f.text_area :description - .field - = f.label :owner_id - = f.number_field :owner_id - .actions - = f.submit 'Save' + .control-group + = f.label :name, :class => 'control-label' + .controls= f.text_field :name, :class => 'input-block-level' + .control-group + = f.label :description, :class => 'control-label' + .controls= f.text_area :description, :rows => 6, :class => 'input-block-level' + .control-group + = f.label :owner_id, :class => 'control-label' + .controls= collection_select(:forum, :owner_id, Member.all, :id, :login_name) + .form-actions + = f.submit 'Save', :class => 'btn' diff --git a/app/views/forums/edit.html.haml b/app/views/forums/edit.html.haml index 88c265f45..f334a2092 100644 --- a/app/views/forums/edit.html.haml +++ b/app/views/forums/edit.html.haml @@ -1,4 +1,4 @@ -%h1 Editing forum +- content_for :title, "Editing forum" = render 'form' diff --git a/app/views/forums/index.html.haml b/app/views/forums/index.html.haml index aac289aae..2da64d0ad 100644 --- a/app/views/forums/index.html.haml +++ b/app/views/forums/index.html.haml @@ -1,23 +1,12 @@ -%h1 Listing forums +- content_for :title, "Forums" -%table - %tr - %th Name - %th Description - %th Owner - %th - %th - %th - - - @forums.each do |forum| - %tr - %td= forum.name - %td= forum.description - %td= forum.owner_id - %td= link_to 'Show', forum - %td= link_to 'Edit', edit_forum_path(forum) - %td= link_to 'Destroy', forum, method: :delete, data: { confirm: 'Are you sure?' } - -%br - -= link_to 'New Forum', new_forum_path +- @forums.each do |forum| + .well + %h2= forum.name + %p + Owner: + =link_to forum.owner, forum.owner + %div + :markdown + #{ strip_tags(forum.description) } + %p= link_to "Visit forum", forum diff --git a/app/views/forums/new.html.haml b/app/views/forums/new.html.haml index 45a9dc26a..59e6ef782 100644 --- a/app/views/forums/new.html.haml +++ b/app/views/forums/new.html.haml @@ -1,4 +1,4 @@ -%h1 New forum +- content_for :title, "New Forum" = render 'form' diff --git a/app/views/forums/show.html.haml b/app/views/forums/show.html.haml index 8f2f1eacb..6cd636e19 100644 --- a/app/views/forums/show.html.haml +++ b/app/views/forums/show.html.haml @@ -1,15 +1,20 @@ +- content_for :title, @forum.name + %p#notice= notice %p - %b Name: - = @forum.name -%p - %b Description: = @forum.description %p - %b Owner: - = @forum.owner_id + This forum is run by + = link_to @forum.owner, @forum.owner -= link_to 'Edit', edit_forum_path(@forum) -\| -= link_to 'Back', forums_path +%h2 Posts + +- if @forum.posts.length > 0 + - @forum.posts.each do |post| + = render :partial => "posts/single", :locals => { :post => post, :subject => true } + +- else + %p No posts yet. + +%p=link_to "Post something", new_post_path(:forum_id => @forum.id), :class => 'btn' diff --git a/spec/views/forums/edit.html.haml_spec.rb b/spec/views/forums/edit.html.haml_spec.rb index d957b9adc..306f3bbf0 100644 --- a/spec/views/forums/edit.html.haml_spec.rb +++ b/spec/views/forums/edit.html.haml_spec.rb @@ -16,7 +16,7 @@ describe "forums/edit" do assert_select "form", :action => forums_path(@forum), :method => "post" do assert_select "input#forum_name", :name => "forum[name]" assert_select "textarea#forum_description", :name => "forum[description]" - assert_select "input#forum_owner_id", :name => "forum[owner_id]" + assert_select "select#forum_owner_id", :name => "forum[owner_id]" end end end diff --git a/spec/views/forums/index.html.haml_spec.rb b/spec/views/forums/index.html.haml_spec.rb index f45bdedd4..273fad99f 100644 --- a/spec/views/forums/index.html.haml_spec.rb +++ b/spec/views/forums/index.html.haml_spec.rb @@ -2,25 +2,12 @@ require 'spec_helper' describe "forums/index" do before(:each) do - assign(:forums, [ - stub_model(Forum, - :name => "Name", - :description => "MyText", - :owner_id => 1 - ), - stub_model(Forum, - :name => "Name", - :description => "MyText", - :owner_id => 1 - ) - ]) + @forum1 = FactoryGirl.create(:forum) + assign(:forums, [ @forum1, @forum1 ]) + render end it "renders a list of forums" 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 => "MyText".to_s, :count => 2 - assert_select "tr>td", :text => 1.to_s, :count => 2 + assert_select "h2", :text => @forum1.name, :count => 2 end end diff --git a/spec/views/forums/new.html.haml_spec.rb b/spec/views/forums/new.html.haml_spec.rb index 63b4bac34..bfda7bfa1 100644 --- a/spec/views/forums/new.html.haml_spec.rb +++ b/spec/views/forums/new.html.haml_spec.rb @@ -2,21 +2,15 @@ require 'spec_helper' describe "forums/new" do before(:each) do - assign(:forum, stub_model(Forum, - :name => "MyString", - :description => "MyText", - :owner_id => 1 - ).as_new_record) + @forum = assign(:forum, FactoryGirl.create(:forum)) + render end it "renders new forum form" do - render - - # Run the generator again with the --webrat flag if you want to use webrat matchers assert_select "form", :action => forums_path, :method => "post" do assert_select "input#forum_name", :name => "forum[name]" assert_select "textarea#forum_description", :name => "forum[description]" - assert_select "input#forum_owner_id", :name => "forum[owner_id]" + assert_select "select#forum_owner_id", :name => "forum[owner_id]" end end end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index 96ac62921..e41265a67 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -2,18 +2,12 @@ require 'spec_helper' describe "forums/show" do before(:each) do - @forum = assign(:forum, stub_model(Forum, - :name => "Name", - :description => "MyText", - :owner_id => 1 - )) + @forum = assign(:forum, FactoryGirl.create(:forum)) + render end - it "renders attributes in

" do - render - # Run the generator again with the --webrat flag if you want to use webrat matchers - rendered.should match(/Name/) - rendered.should match(/MyText/) - rendered.should match(/1/) + it "renders attributes" do + rendered.should contain @forum.description + rendered.should contain @forum.owner.to_s end end From a0955d2bfc46eb50235ec89da230d5b6b6b147d7 Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 13 Feb 2013 16:28:23 +1100 Subject: [PATCH 15/28] post in (or not in) a forum --- app/controllers/posts_controller.rb | 1 + app/models/post.rb | 2 +- app/views/posts/_form.html.haml | 26 ++++++++++++--------- app/views/posts/edit.html.haml | 7 ++---- app/views/posts/new.html.haml | 11 +++------ spec/controllers/posts_controller_spec.rb | 11 +++++++++ spec/views/forums/show.html.haml_spec.rb | 4 ++++ spec/views/posts/edit.html.haml_spec.rb | 28 +++++++++++++++++++++++ spec/views/posts/new.html.haml_spec.rb | 28 ++++++++++++++++++++++- 9 files changed, 92 insertions(+), 26 deletions(-) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index ba2ee6e04..f78a476c6 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -29,6 +29,7 @@ class PostsController < ApplicationController # GET /posts/new.json def new @post = Post.new + @forum = Forum.find_by_id(params[:forum_id]) || Forum.new respond_to do |format| format.html # new.html.haml diff --git a/app/models/post.rb b/app/models/post.rb index 050f13c26..a4b03ebfc 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,7 +1,7 @@ class Post < ActiveRecord::Base extend FriendlyId friendly_id :author_date_subject, use: :slugged - attr_accessible :body, :subject, :author_id + attr_accessible :body, :subject, :author_id, :forum_id belongs_to :author, :class_name => 'Member' belongs_to :forum has_many :comments, :dependent => :destroy diff --git a/app/views/posts/_form.html.haml b/app/views/posts/_form.html.haml index 429ce48e0..d4fe1704a 100644 --- a/app/views/posts/_form.html.haml +++ b/app/views/posts/_form.html.haml @@ -6,14 +6,18 @@ - @post.errors.full_messages.each do |msg| %li= msg - .field - = f.label :author_id - = f.number_field :author_id - .field - = f.label :subject - = f.text_field :subject - .field - = f.label :body - = f.text_area :body - .actions - = f.submit 'Save' + + = label_tag :post, "Subject" + = f.text_field :subject, :class => 'input-block-level' + = label_tag :body, "What's going on in your food garden?" + = f.text_area :body, :rows => 12, :class => 'input-block-level' + + - if @post.forum || @forum + - forum = @post.forum || @forum + %p + This post will be posted in the forum + =link_to forum.name, forum + .field + = f.hidden_field :forum_id, :value => forum.id + + = f.submit "Post", :class => 'btn' diff --git a/app/views/posts/edit.html.haml b/app/views/posts/edit.html.haml index 502a49faa..fcf66a4cb 100644 --- a/app/views/posts/edit.html.haml +++ b/app/views/posts/edit.html.haml @@ -1,7 +1,4 @@ = content_for :title, "Edit post" -= form_for @post do |f| - = label_tag :subject, "Subject" - = f.text_field :subject, :class => 'input-block-level' - = f.text_area :body, :rows => 12, :class => 'input-block-level' - = f.submit "Post" +=render 'form' + diff --git a/app/views/posts/new.html.haml b/app/views/posts/new.html.haml index 8a5ac9b46..e1014c41f 100644 --- a/app/views/posts/new.html.haml +++ b/app/views/posts/new.html.haml @@ -1,9 +1,4 @@ -= content_for :title, "Post a post" += content_for :title, @forum.name ? "Post in #{@forum.name}" : "Post something" + +=render 'form' -= form_for @post, :url => { :action => "create" } do |f| - %fieldset - = label_tag :post, "Subject" - = f.text_field :subject, :class => 'input-block-level' - = label_tag :body, "What's going on in your food garden?" - = f.text_area :body, :rows => 12, :class => 'input-block-level' - = f.submit "Post", :class => 'btn' diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index c8ad395c6..4eb13d9f6 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -39,6 +39,17 @@ describe PostsController do get :new, {} assigns(:post).should be_a_new(Post) end + + it "picks up forum from params" do + forum = FactoryGirl.create(:forum) + get :new, {:forum_id => forum.id} + assigns(:forum).should eq(forum) + end + + it "doesn't die if no forum specified" do + get :new, {} + assigns(:forum).should be_a_new(Forum) + end end describe "GET edit" do diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index e41265a67..c5b62f69b 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -10,4 +10,8 @@ describe "forums/show" do rendered.should contain @forum.description rendered.should contain @forum.owner.to_s end + + it 'links to new post with the forum id' do + assert_select "a[href=#{new_post_path(:forum_id => @forum.id)}]" + end end diff --git a/spec/views/posts/edit.html.haml_spec.rb b/spec/views/posts/edit.html.haml_spec.rb index bb67e9dfa..91e2d9b4f 100644 --- a/spec/views/posts/edit.html.haml_spec.rb +++ b/spec/views/posts/edit.html.haml_spec.rb @@ -19,5 +19,33 @@ describe "posts/edit" do assert_select "textarea#post_body", :name => "post[body]" end end + + it 'no hidden forum field' do + assert_select "input#post_forum_id[type=hidden]", false + end + + it 'no forum mentioned' do + rendered.should_not contain "This post will be posted in the forum" + end + + context "forum specified" do + before(:each) do + @forum = assign(:forum, FactoryGirl.create(:forum)) + assign(:post, FactoryGirl.create( :post, + :forum => @forum, + :author => @author + )) + render + end + + it 'creates a hidden field' do + assert_select "input#post_forum_id[type=hidden][value=#{@forum.id}]" + end + + it 'tells the user what forum it will be posted in' do + rendered.should contain "This post will be posted in the forum #{@forum.name}" + end + end + end end diff --git a/spec/views/posts/new.html.haml_spec.rb b/spec/views/posts/new.html.haml_spec.rb index 99e644ed4..aa7da6fba 100644 --- a/spec/views/posts/new.html.haml_spec.rb +++ b/spec/views/posts/new.html.haml_spec.rb @@ -4,15 +4,41 @@ describe "posts/new" do before(:each) do @author = FactoryGirl.create(:member) assign(:post, FactoryGirl.create(:post, :author => @author)) + assign(:forum, Forum.new) sign_in @author controller.stub(:current_user) { @author } - render end it "renders new post form" do + render assert_select "form", :action => posts_path, :method => "post" do assert_select "input#post_subject", :name => "post[subject]" assert_select "textarea#post_body", :name => "post[body]" end end + + it 'no hidden forum field' do + assert_select "input#post_forum_id[type=hidden]", false + end + + it 'no forum mentioned' do + rendered.should_not contain "This post will be posted in the forum" + end + + context "forum specified" do + before(:each) do + @forum = assign(:forum, FactoryGirl.create(:forum)) + assign(:post, FactoryGirl.create(:post, :forum => @forum)) + render + end + + it 'creates a hidden field' do + assert_select "input#post_forum_id[type=hidden][value=#{@forum.id}]" + end + + it 'tells the user what forum it will be posted in' do + rendered.should contain "This post will be posted in the forum #{@forum.name}" + end + end + end From 430068458d11337b64b8ca6437defabbf6f4e18b Mon Sep 17 00:00:00 2001 From: Skud Date: Thu, 14 Feb 2013 11:53:01 +1100 Subject: [PATCH 16/28] tests for markdown --- app/views/forums/show.html.haml | 3 ++- spec/views/forums/index.html.haml_spec.rb | 4 ++++ spec/views/forums/show.html.haml_spec.rb | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/views/forums/show.html.haml b/app/views/forums/show.html.haml index 6cd636e19..681588352 100644 --- a/app/views/forums/show.html.haml +++ b/app/views/forums/show.html.haml @@ -3,7 +3,8 @@ %p#notice= notice %p - = @forum.description + :markdown + #{ strip_tags(@forum.description) } %p This forum is run by = link_to @forum.owner, @forum.owner diff --git a/spec/views/forums/index.html.haml_spec.rb b/spec/views/forums/index.html.haml_spec.rb index 273fad99f..266033693 100644 --- a/spec/views/forums/index.html.haml_spec.rb +++ b/spec/views/forums/index.html.haml_spec.rb @@ -10,4 +10,8 @@ describe "forums/index" do it "renders a list of forums" do assert_select "h2", :text => @forum1.name, :count => 2 end + + it "parses markdown description into html" do + assert_select "em", "Everything" + end end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index c5b62f69b..0861753be 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -7,10 +7,14 @@ describe "forums/show" do end it "renders attributes" do - rendered.should contain @forum.description + rendered.should contain "Everything about permaculture" rendered.should contain @forum.owner.to_s end + it "parses markdown description into html" do + assert_select "em", "Everything" + end + it 'links to new post with the forum id' do assert_select "a[href=#{new_post_path(:forum_id => @forum.id)}]" end From b4c6a74caa59f020966d383210d5f3e9fbd7b85a Mon Sep 17 00:00:00 2001 From: Skud Date: Thu, 14 Feb 2013 13:35:23 +1100 Subject: [PATCH 17/28] Display posts in summary on forum page Also had to tweak timezone config so that "X minutes ago" display worked. This *may* break tests or who knows what, for devs who are elsewhere. Let's keep an eye out for that! --- app/views/forums/show.html.haml | 6 +----- app/views/posts/_summary.html.haml | 18 ++++++++++++++++++ config/application.rb | 2 +- spec/models/forum_spec.rb | 7 +++++++ spec/views/forums/show.html.haml_spec.rb | 17 ++++++++++++++++- 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 app/views/posts/_summary.html.haml diff --git a/app/views/forums/show.html.haml b/app/views/forums/show.html.haml index 681588352..4f50d84a9 100644 --- a/app/views/forums/show.html.haml +++ b/app/views/forums/show.html.haml @@ -11,11 +11,7 @@ %h2 Posts -- if @forum.posts.length > 0 - - @forum.posts.each do |post| - = render :partial => "posts/single", :locals => { :post => post, :subject => true } +=render :partial => "posts/summary", :locals => { :posts => @forum.posts } -- else - %p No posts yet. %p=link_to "Post something", new_post_path(:forum_id => @forum.id), :class => 'btn' diff --git a/app/views/posts/_summary.html.haml b/app/views/posts/_summary.html.haml new file mode 100644 index 000000000..7072fa519 --- /dev/null +++ b/app/views/posts/_summary.html.haml @@ -0,0 +1,18 @@ +- if posts.length > 0 + %table.table.table-striped + %tr + %th Subject + %th Posted by + %th Posted + + - posts.each do |post| + %tr + %td + = link_to strip_tags(post.subject), post + %td + =link_to post.author, post.author + %td + = distance_of_time_in_words(post.created_at, Time.zone.now) + ago +- else + %p No posts yet. diff --git a/config/application.rb b/config/application.rb index 5a9c0d3a8..04451a75d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,7 +28,7 @@ module Growstuff # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. config.time_zone = 'UTC' - config.active_record.default_timezone = 'UTC' + config.active_record.default_timezone = :local # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb index b9d09a707..a374c176c 100644 --- a/spec/models/forum_spec.rb +++ b/spec/models/forum_spec.rb @@ -14,4 +14,11 @@ describe Forum do @post2 = FactoryGirl.create(:forum_post, :forum => @forum) @forum.posts.length.should == 2 end + + it "orders posts in reverse chron order" do + @post1 = FactoryGirl.create(:forum_post, :forum => @forum) + @post2 = FactoryGirl.create(:forum_post, :forum => @forum) + @forum.posts.first.should eq @post2 + end + end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index 0861753be..2e4edd9e0 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -3,19 +3,34 @@ require 'spec_helper' describe "forums/show" do before(:each) do @forum = assign(:forum, FactoryGirl.create(:forum)) - render end it "renders attributes" do + render rendered.should contain "Everything about permaculture" rendered.should contain @forum.owner.to_s end it "parses markdown description into html" do + render assert_select "em", "Everything" end it 'links to new post with the forum id' do + render assert_select "a[href=#{new_post_path(:forum_id => @forum.id)}]" end + + it 'has no posts' do + render + rendered.should contain "No posts yet" + end + + it 'shows posts' do + @post = FactoryGirl.create(:post, :forum => @forum) + render + assert_select "table" + rendered.should contain @post.subject + rendered.should contain @post.author.to_s + end end From 2e8867dda7b6093ceb65ce3934b8916f9fd04dc4 Mon Sep 17 00:00:00 2001 From: Skud Date: Thu, 14 Feb 2013 13:37:48 +1100 Subject: [PATCH 18/28] Add forums to top nav --- app/views/layouts/_header.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index d1bb8d7e0..0f7937a4e 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -11,6 +11,7 @@ %li= link_to "Crops", crops_path %li= link_to "Members", members_path %li= link_to "Posts", posts_path + %li= link_to "Forums", forums_path - if can? :create, Post %li= link_to("Post something", new_post_path) - if can? :create, Planting From 86f7d25411584fde29f5c921660e701c3c408886 Mon Sep 17 00:00:00 2001 From: Cathy Sullivan Date: Mon, 18 Feb 2013 12:18:45 -0800 Subject: [PATCH 19/28] PT story 41985889, Hides 'new garden' tab on other member's pages --- app/views/members/show.html.haml | 10 ++++---- spec/views/members/show.html.haml_spec.rb | 28 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/views/members/show.html.haml b/app/views/members/show.html.haml index d270bf33a..c7f1132a2 100644 --- a/app/views/members/show.html.haml +++ b/app/views/members/show.html.haml @@ -27,7 +27,8 @@ %li{:class => first_garden ? 'active' : '' } - first_garden = false = link_to g.name, "#garden#{g.id}", 'data-toggle' => 'tab' - %li= link_to 'New Garden', '#garden_new', 'data-toggle' => 'tab' + - if current_member == @member + %li= link_to 'New Garden', '#garden_new', 'data-toggle' => 'tab' .tab-content - first_garden = true - @member.gardens.each do |g| @@ -46,9 +47,10 @@ %p = link_to "More about this garden...", url_for(g) - %div{:class => 'tab-pane', :id => "garden_new"} - %h3 Create a new garden - = render 'gardens/form' + - if current_member == @member + %div{:class => 'tab-pane', :id => "garden_new"} + %h3 Create a new garden + = render 'gardens/form' %h3 Updates - @member.posts.each do |post| diff --git a/spec/views/members/show.html.haml_spec.rb b/spec/views/members/show.html.haml_spec.rb index db4a38a6c..6bb57b6ef 100644 --- a/spec/views/members/show.html.haml_spec.rb +++ b/spec/views/members/show.html.haml_spec.rb @@ -4,7 +4,7 @@ describe "members/show" do before(:each) do controller.stub(:current_user) { nil } @member = FactoryGirl.create(:member) - @garden = FactoryGirl.create(:garden, :owner => @member) + @garden = FactoryGirl.create(:garden, :owner => @member) end context "the basics" do @@ -21,7 +21,6 @@ describe "members/show" do it "contains a gravatar icon" do assert_select "img", :src => /gravatar\.com\/avatar/ end - end context "gardens and plantings" do @@ -53,6 +52,14 @@ describe "members/show" do it "doesn't show the email address" do rendered.should_not contain @member.email end + + it "does not contain a 'New Garden' link" do + assert_select "a[href=#garden_new]", false + end + + it "does not contain a 'New Garden' tab" do + assert_select "#garden_new", false + end end context "signed in member" do @@ -67,6 +74,23 @@ describe "members/show" do end end + context "signed in as different member" do + before(:each) do + @member2 = FactoryGirl.create(:member) + sign_in @member2 + controller.stub(:current_user) { @member2 } + render + end + + it "does not contain a 'New Garden' link" do + assert_select "a[href=#garden_new]", false + end + + it "does not contain a 'New Garden' tab" do + assert_select "#garden_new", false + end + end + context "public member" do before(:each) do @member = FactoryGirl.create(:public_member) From 0bc2ff25fe853c9e847624b25247ce0ee2ed334c Mon Sep 17 00:00:00 2001 From: Skud Date: Thu, 14 Feb 2013 15:25:15 +1100 Subject: [PATCH 20/28] Added roles, but haven't yet hooked them up to CanCan --- app/assets/javascripts/roles.js.coffee | 3 + app/controllers/roles_controller.rb | 83 +++++++++ app/helpers/roles_helper.rb | 2 + app/models/member.rb | 1 + app/models/role.rb | 4 + app/views/roles/_form.html.haml | 16 ++ app/views/roles/edit.html.haml | 7 + app/views/roles/index.html.haml | 21 +++ app/views/roles/new.html.haml | 5 + app/views/roles/show.html.haml | 12 ++ config/routes.rb | 4 +- db/migrate/20130214024117_create_roles.rb | 10 ++ .../20130214034838_add_members_roles_table.rb | 10 ++ db/schema.rb | 16 +- spec/controllers/roles_controller_spec.rb | 164 ++++++++++++++++++ spec/factories/roles.rb | 8 + spec/models/member_spec.rb | 12 ++ spec/models/role_spec.rb | 5 + spec/requests/roles_spec.rb | 11 ++ spec/routing/roles_routing_spec.rb | 35 ++++ spec/views/roles/edit.html.haml_spec.rb | 20 +++ spec/views/roles/index.html.haml_spec.rb | 23 +++ spec/views/roles/new.html.haml_spec.rb | 20 +++ spec/views/roles/show.html.haml_spec.rb | 17 ++ 24 files changed, 506 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/roles.js.coffee create mode 100644 app/controllers/roles_controller.rb create mode 100644 app/helpers/roles_helper.rb create mode 100644 app/models/role.rb create mode 100644 app/views/roles/_form.html.haml create mode 100644 app/views/roles/edit.html.haml create mode 100644 app/views/roles/index.html.haml create mode 100644 app/views/roles/new.html.haml create mode 100644 app/views/roles/show.html.haml create mode 100644 db/migrate/20130214024117_create_roles.rb create mode 100644 db/migrate/20130214034838_add_members_roles_table.rb create mode 100644 spec/controllers/roles_controller_spec.rb create mode 100644 spec/factories/roles.rb create mode 100644 spec/models/role_spec.rb create mode 100644 spec/requests/roles_spec.rb create mode 100644 spec/routing/roles_routing_spec.rb create mode 100644 spec/views/roles/edit.html.haml_spec.rb create mode 100644 spec/views/roles/index.html.haml_spec.rb create mode 100644 spec/views/roles/new.html.haml_spec.rb create mode 100644 spec/views/roles/show.html.haml_spec.rb diff --git a/app/assets/javascripts/roles.js.coffee b/app/assets/javascripts/roles.js.coffee new file mode 100644 index 000000000..761567942 --- /dev/null +++ b/app/assets/javascripts/roles.js.coffee @@ -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/ diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb new file mode 100644 index 000000000..239ed65fb --- /dev/null +++ b/app/controllers/roles_controller.rb @@ -0,0 +1,83 @@ +class RolesController < ApplicationController + # GET /roles + # GET /roles.json + def index + @roles = Role.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @roles } + end + end + + # GET /roles/1 + # GET /roles/1.json + def show + @role = Role.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @role } + end + end + + # GET /roles/new + # GET /roles/new.json + def new + @role = Role.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @role } + end + end + + # GET /roles/1/edit + def edit + @role = Role.find(params[:id]) + end + + # POST /roles + # POST /roles.json + def create + @role = Role.new(params[:role]) + + respond_to do |format| + if @role.save + format.html { redirect_to @role, notice: 'Role was successfully created.' } + format.json { render json: @role, status: :created, location: @role } + else + format.html { render action: "new" } + format.json { render json: @role.errors, status: :unprocessable_entity } + end + end + end + + # PUT /roles/1 + # PUT /roles/1.json + def update + @role = Role.find(params[:id]) + + respond_to do |format| + if @role.update_attributes(params[:role]) + format.html { redirect_to @role, notice: 'Role was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: "edit" } + format.json { render json: @role.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /roles/1 + # DELETE /roles/1.json + def destroy + @role = Role.find(params[:id]) + @role.destroy + + respond_to do |format| + format.html { redirect_to roles_url } + format.json { head :no_content } + end + end +end diff --git a/app/helpers/roles_helper.rb b/app/helpers/roles_helper.rb new file mode 100644 index 000000000..6b6b6cc22 --- /dev/null +++ b/app/helpers/roles_helper.rb @@ -0,0 +1,2 @@ +module RolesHelper +end diff --git a/app/models/member.rb b/app/models/member.rb index 436288e3a..4054cfa73 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -6,6 +6,7 @@ class Member < ActiveRecord::Base has_many :comments, :foreign_key => 'author_id' has_many :gardens, :foreign_key => 'owner_id' has_many :forums, :foreign_key => 'owner_id' + has_and_belongs_to_many :roles # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 000000000..62ece9943 --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,4 @@ +class Role < ActiveRecord::Base + attr_accessible :description, :name, :members + has_and_belongs_to_many :members +end diff --git a/app/views/roles/_form.html.haml b/app/views/roles/_form.html.haml new file mode 100644 index 000000000..954a2f6a0 --- /dev/null +++ b/app/views/roles/_form.html.haml @@ -0,0 +1,16 @@ += form_for @role do |f| + - if @role.errors.any? + #error_explanation + %h2= "#{pluralize(@role.errors.count, "error")} prohibited this role from being saved:" + %ul + - @role.errors.full_messages.each do |msg| + %li= msg + + .field + = f.label :name + = f.text_field :name + .field + = f.label :description + = f.text_area :description + .actions + = f.submit 'Save' diff --git a/app/views/roles/edit.html.haml b/app/views/roles/edit.html.haml new file mode 100644 index 000000000..427975d14 --- /dev/null +++ b/app/views/roles/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing role + += render 'form' + += link_to 'Show', @role +\| += link_to 'Back', roles_path diff --git a/app/views/roles/index.html.haml b/app/views/roles/index.html.haml new file mode 100644 index 000000000..c503b1284 --- /dev/null +++ b/app/views/roles/index.html.haml @@ -0,0 +1,21 @@ +%h1 Listing roles + +%table + %tr + %th Name + %th Description + %th + %th + %th + + - @roles.each do |role| + %tr + %td= role.name + %td= role.description + %td= link_to 'Show', role + %td= link_to 'Edit', edit_role_path(role) + %td= link_to 'Destroy', role, method: :delete, data: { confirm: 'Are you sure?' } + +%br + += link_to 'New Role', new_role_path diff --git a/app/views/roles/new.html.haml b/app/views/roles/new.html.haml new file mode 100644 index 000000000..6e07588dd --- /dev/null +++ b/app/views/roles/new.html.haml @@ -0,0 +1,5 @@ +%h1 New role + += render 'form' + += link_to 'Back', roles_path diff --git a/app/views/roles/show.html.haml b/app/views/roles/show.html.haml new file mode 100644 index 000000000..7bd0488c7 --- /dev/null +++ b/app/views/roles/show.html.haml @@ -0,0 +1,12 @@ +%p#notice= notice + +%p + %b Name: + = @role.name +%p + %b Description: + = @role.description + += link_to 'Edit', edit_role_path(@role) +\| += link_to 'Back', roles_path diff --git a/config/routes.rb b/config/routes.rb index 4577680c5..8e19bc41b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,4 @@ Growstuff::Application.routes.draw do - resources :forums - devise_for :members, :controllers => { :registrations => "registrations" } @@ -11,6 +9,8 @@ Growstuff::Application.routes.draw do resources :crops resources :members resources :comments + resources :roles + resources :forums get "home/index" diff --git a/db/migrate/20130214024117_create_roles.rb b/db/migrate/20130214024117_create_roles.rb new file mode 100644 index 000000000..8ccaf5ca6 --- /dev/null +++ b/db/migrate/20130214024117_create_roles.rb @@ -0,0 +1,10 @@ +class CreateRoles < ActiveRecord::Migration + def change + create_table :roles do |t| + t.string :name, :null => false + t.text :description + + t.timestamps + end + end +end diff --git a/db/migrate/20130214034838_add_members_roles_table.rb b/db/migrate/20130214034838_add_members_roles_table.rb new file mode 100644 index 000000000..433b179f3 --- /dev/null +++ b/db/migrate/20130214034838_add_members_roles_table.rb @@ -0,0 +1,10 @@ +class AddMembersRolesTable < ActiveRecord::Migration + def change + create_table :members_roles do |t| + t.references :member + t.references :role + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 55fa11bd6..8f44ffbf2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130213015708) do +ActiveRecord::Schema.define(:version => 20130214034838) do create_table "comments", :force => true do |t| t.integer "post_id", :limit => 255, :null => false @@ -87,6 +87,13 @@ ActiveRecord::Schema.define(:version => 20130213015708) do add_index "members", ["slug"], :name => "index_users_on_slug", :unique => true add_index "members", ["unlock_token"], :name => "index_users_on_unlock_token", :unique => true + create_table "members_roles", :force => true do |t| + t.integer "member_id" + t.integer "role_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "plantings", :force => true do |t| t.integer "garden_id", :null => false t.integer "crop_id", :null => false @@ -113,6 +120,13 @@ ActiveRecord::Schema.define(:version => 20130213015708) do add_index "posts", ["created_at", "author_id"], :name => "index_updates_on_created_at_and_user_id" add_index "posts", ["slug"], :name => "index_updates_on_slug", :unique => true + create_table "roles", :force => true do |t| + t.string "name", :null => false + t.text "description" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "scientific_names", :force => true do |t| t.string "scientific_name", :null => false t.integer "crop_id", :null => false diff --git a/spec/controllers/roles_controller_spec.rb b/spec/controllers/roles_controller_spec.rb new file mode 100644 index 000000000..99f6e345c --- /dev/null +++ b/spec/controllers/roles_controller_spec.rb @@ -0,0 +1,164 @@ +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 RolesController do + + # This should return the minimal set of attributes required to create a valid + # Role. As you add validations to Role, be sure to + # update the return value of this method accordingly. + def valid_attributes + { "name" => "MyString" } + 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 + # RolesController. Be sure to keep this updated too. + def valid_session + {} + end + + describe "GET index" do + it "assigns all roles as @roles" do + role = Role.create! valid_attributes + get :index, {}, valid_session + assigns(:roles).should eq([role]) + end + end + + describe "GET show" do + it "assigns the requested role as @role" do + role = Role.create! valid_attributes + get :show, {:id => role.to_param}, valid_session + assigns(:role).should eq(role) + end + end + + describe "GET new" do + it "assigns a new role as @role" do + get :new, {}, valid_session + assigns(:role).should be_a_new(Role) + end + end + + describe "GET edit" do + it "assigns the requested role as @role" do + role = Role.create! valid_attributes + get :edit, {:id => role.to_param}, valid_session + assigns(:role).should eq(role) + end + end + + describe "POST create" do + describe "with valid params" do + it "creates a new Role" do + expect { + post :create, {:role => valid_attributes}, valid_session + }.to change(Role, :count).by(1) + end + + it "assigns a newly created role as @role" do + post :create, {:role => valid_attributes}, valid_session + assigns(:role).should be_a(Role) + assigns(:role).should be_persisted + end + + it "redirects to the created role" do + post :create, {:role => valid_attributes}, valid_session + response.should redirect_to(Role.last) + end + end + + describe "with invalid params" do + it "assigns a newly created but unsaved role as @role" do + # Trigger the behavior that occurs when invalid params are submitted + Role.any_instance.stub(:save).and_return(false) + post :create, {:role => { "name" => "invalid value" }}, valid_session + assigns(:role).should be_a_new(Role) + end + + it "re-renders the 'new' template" do + # Trigger the behavior that occurs when invalid params are submitted + Role.any_instance.stub(:save).and_return(false) + post :create, {:role => { "name" => "invalid value" }}, valid_session + response.should render_template("new") + end + end + end + + describe "PUT update" do + describe "with valid params" do + it "updates the requested role" do + role = Role.create! valid_attributes + # Assuming there are no other roles in the database, this + # specifies that the Role created on the previous line + # receives the :update_attributes message with whatever params are + # submitted in the request. + Role.any_instance.should_receive(:update_attributes).with({ "name" => "MyString" }) + put :update, {:id => role.to_param, :role => { "name" => "MyString" }}, valid_session + end + + it "assigns the requested role as @role" do + role = Role.create! valid_attributes + put :update, {:id => role.to_param, :role => valid_attributes}, valid_session + assigns(:role).should eq(role) + end + + it "redirects to the role" do + role = Role.create! valid_attributes + put :update, {:id => role.to_param, :role => valid_attributes}, valid_session + response.should redirect_to(role) + end + end + + describe "with invalid params" do + it "assigns the role as @role" do + role = Role.create! valid_attributes + # Trigger the behavior that occurs when invalid params are submitted + Role.any_instance.stub(:save).and_return(false) + put :update, {:id => role.to_param, :role => { "name" => "invalid value" }}, valid_session + assigns(:role).should eq(role) + end + + it "re-renders the 'edit' template" do + role = Role.create! valid_attributes + # Trigger the behavior that occurs when invalid params are submitted + Role.any_instance.stub(:save).and_return(false) + put :update, {:id => role.to_param, :role => { "name" => "invalid value" }}, valid_session + response.should render_template("edit") + end + end + end + + describe "DELETE destroy" do + it "destroys the requested role" do + role = Role.create! valid_attributes + expect { + delete :destroy, {:id => role.to_param}, valid_session + }.to change(Role, :count).by(-1) + end + + it "redirects to the roles list" do + role = Role.create! valid_attributes + delete :destroy, {:id => role.to_param}, valid_session + response.should redirect_to(roles_url) + end + end + +end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb new file mode 100644 index 000000000..8b413294e --- /dev/null +++ b/spec/factories/roles.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :role do + name "Moderator" + description "These people moderate the forums" + end +end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 1f2f8ea41..57fa46ef6 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -90,4 +90,16 @@ describe 'member' do end end + context 'roles' do + before(:each) do + @member = FactoryGirl.create(:member) + @role = FactoryGirl.create(:role) + @member.roles << @role + end + + it 'has a role' do + @member.roles.first.should eq @role + end + end + end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb new file mode 100644 index 000000000..b575576c3 --- /dev/null +++ b/spec/models/role_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Role do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/roles_spec.rb b/spec/requests/roles_spec.rb new file mode 100644 index 000000000..3ddd05451 --- /dev/null +++ b/spec/requests/roles_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe "Roles" do + describe "GET /roles" 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 roles_path + response.status.should be(200) + end + end +end diff --git a/spec/routing/roles_routing_spec.rb b/spec/routing/roles_routing_spec.rb new file mode 100644 index 000000000..4084a6e7e --- /dev/null +++ b/spec/routing/roles_routing_spec.rb @@ -0,0 +1,35 @@ +require "spec_helper" + +describe RolesController do + describe "routing" do + + it "routes to #index" do + get("/roles").should route_to("roles#index") + end + + it "routes to #new" do + get("/roles/new").should route_to("roles#new") + end + + it "routes to #show" do + get("/roles/1").should route_to("roles#show", :id => "1") + end + + it "routes to #edit" do + get("/roles/1/edit").should route_to("roles#edit", :id => "1") + end + + it "routes to #create" do + post("/roles").should route_to("roles#create") + end + + it "routes to #update" do + put("/roles/1").should route_to("roles#update", :id => "1") + end + + it "routes to #destroy" do + delete("/roles/1").should route_to("roles#destroy", :id => "1") + end + + end +end diff --git a/spec/views/roles/edit.html.haml_spec.rb b/spec/views/roles/edit.html.haml_spec.rb new file mode 100644 index 000000000..2e2f66c7b --- /dev/null +++ b/spec/views/roles/edit.html.haml_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "roles/edit" do + before(:each) do + @role = assign(:role, stub_model(Role, + :name => "MyString", + :description => "MyText" + )) + end + + it "renders the edit role form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => roles_path(@role), :method => "post" do + assert_select "input#role_name", :name => "role[name]" + assert_select "textarea#role_description", :name => "role[description]" + end + end +end diff --git a/spec/views/roles/index.html.haml_spec.rb b/spec/views/roles/index.html.haml_spec.rb new file mode 100644 index 000000000..508f29017 --- /dev/null +++ b/spec/views/roles/index.html.haml_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe "roles/index" do + before(:each) do + assign(:roles, [ + stub_model(Role, + :name => "Name", + :description => "MyText" + ), + stub_model(Role, + :name => "Name", + :description => "MyText" + ) + ]) + end + + it "renders a list of roles" 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 => "MyText".to_s, :count => 2 + end +end diff --git a/spec/views/roles/new.html.haml_spec.rb b/spec/views/roles/new.html.haml_spec.rb new file mode 100644 index 000000000..e15e14226 --- /dev/null +++ b/spec/views/roles/new.html.haml_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "roles/new" do + before(:each) do + assign(:role, stub_model(Role, + :name => "MyString", + :description => "MyText" + ).as_new_record) + end + + it "renders new role form" do + render + + # Run the generator again with the --webrat flag if you want to use webrat matchers + assert_select "form", :action => roles_path, :method => "post" do + assert_select "input#role_name", :name => "role[name]" + assert_select "textarea#role_description", :name => "role[description]" + end + end +end diff --git a/spec/views/roles/show.html.haml_spec.rb b/spec/views/roles/show.html.haml_spec.rb new file mode 100644 index 000000000..389a5c8c3 --- /dev/null +++ b/spec/views/roles/show.html.haml_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe "roles/show" do + before(:each) do + @role = assign(:role, stub_model(Role, + :name => "Name", + :description => "MyText" + )) + end + + it "renders attributes in

" do + render + # Run the generator again with the --webrat flag if you want to use webrat matchers + rendered.should match(/Name/) + rendered.should match(/MyText/) + end +end From f12941576c753ff34082df6873a5c919c5e5c9b8 Mon Sep 17 00:00:00 2001 From: Skud Date: Tue, 19 Feb 2013 12:30:17 +1100 Subject: [PATCH 21/28] hooked roles up to cancan Now only admins can create/edit forums, or mess with roles. To add an admin user: rails c m = Member.find('skud') r = Role.create(:name => 'admin') r.members << m We'll have to do this on the server to bootstrap the admin stuff. Though actually, we should really write a rake task to generalise this. --- app/controllers/forums_controller.rb | 1 + app/controllers/roles_controller.rb | 1 + app/models/ability.rb | 21 +++++-- app/models/member.rb | 5 ++ .../20130214034838_add_members_roles_table.rb | 8 +-- db/schema.rb | 18 +++--- spec/controllers/forums_controller_spec.rb | 37 ++++++----- spec/controllers/roles_controller_spec.rb | 61 ++++++------------- spec/factories/member.rb | 4 ++ spec/factories/roles.rb | 5 ++ spec/models/member_spec.rb | 7 +++ spec/models/role_spec.rb | 10 ++- spec/requests/roles_spec.rb | 11 ---- spec/support/controller_macros.rb | 8 +++ 14 files changed, 104 insertions(+), 93 deletions(-) delete mode 100644 spec/requests/roles_spec.rb diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb index 806ba2574..6dbfb06ef 100644 --- a/app/controllers/forums_controller.rb +++ b/app/controllers/forums_controller.rb @@ -1,4 +1,5 @@ class ForumsController < ApplicationController + load_and_authorize_resource # GET /forums # GET /forums.json def index diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 239ed65fb..04cdcecb6 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -1,4 +1,5 @@ class RolesController < ApplicationController + load_and_authorize_resource # GET /roles # GET /roles.json def index diff --git a/app/models/ability.rb b/app/models/ability.rb index 63f25f328..54052eebc 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -7,18 +7,27 @@ class Ability # everyone can do these things, even non-logged in can :read, :all + # nobody should be able to view this except admins + cannot :read, Role + if member + + if member.has_role? :admin + # admin user roles (for authorization) + can :read, Role + can :manage, Role + + # for now, only admins can create/edit forums + can :manage, Forum + end + # managing your own user settings can :update, Member, :id => member.id # for now, anyone can create/edit/destroy crops # (later, we probably want to limit this to a role) - can :create, Crop - can :update, Crop - can :destroy, Crop - can :create, ScientificName - can :update, ScientificName - can :destroy, ScientificName + can :manage, Crop + can :manage, ScientificName # anyone can create a post, or comment on a post, # but only the author can edit/destroy it. diff --git a/app/models/member.rb b/app/models/member.rb index 4054cfa73..e49355b0d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -58,6 +58,10 @@ class Member < ActiveRecord::Base return login_name end + def has_role?(role_sym) + roles.any? { |r| r.name.underscore.to_sym == role_sym } + end + protected def empty_unwanted_geocodes if self.location.to_s == '' @@ -65,4 +69,5 @@ class Member < ActiveRecord::Base self.longitude = nil end end + end diff --git a/db/migrate/20130214034838_add_members_roles_table.rb b/db/migrate/20130214034838_add_members_roles_table.rb index 433b179f3..a8044ebc8 100644 --- a/db/migrate/20130214034838_add_members_roles_table.rb +++ b/db/migrate/20130214034838_add_members_roles_table.rb @@ -1,10 +1,8 @@ class AddMembersRolesTable < ActiveRecord::Migration def change - create_table :members_roles do |t| - t.references :member - t.references :role - - t.timestamps + create_table :members_roles, :id => false do |t| + t.integer :member_id + t.integer :role_id end end end diff --git a/db/schema.rb b/db/schema.rb index 8f44ffbf2..04d8c2f0d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -14,11 +14,11 @@ ActiveRecord::Schema.define(:version => 20130214034838) do create_table "comments", :force => true do |t| - t.integer "post_id", :limit => 255, :null => false - t.integer "author_id", :limit => 255, :null => false - t.text "body", :limit => 255, :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.integer "post_id", :null => false + t.integer "author_id", :null => false + t.text "body", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "crops", :force => true do |t| @@ -87,11 +87,9 @@ ActiveRecord::Schema.define(:version => 20130214034838) do add_index "members", ["slug"], :name => "index_users_on_slug", :unique => true add_index "members", ["unlock_token"], :name => "index_users_on_unlock_token", :unique => true - create_table "members_roles", :force => true do |t| - t.integer "member_id" - t.integer "role_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + create_table "members_roles", :id => false, :force => true do |t| + t.integer "member_id" + t.integer "role_id" end create_table "plantings", :force => true do |t| diff --git a/spec/controllers/forums_controller_spec.rb b/spec/controllers/forums_controller_spec.rb index 37c484523..743f71cb8 100644 --- a/spec/controllers/forums_controller_spec.rb +++ b/spec/controllers/forums_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe ForumsController do + login_admin_member + def valid_attributes { "name" => "MyString", @@ -10,9 +12,6 @@ describe ForumsController do } 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 - # ForumsController. Be sure to keep this updated too. def valid_session {} end @@ -20,7 +19,7 @@ describe ForumsController do describe "GET index" do it "assigns all forums as @forums" do forum = Forum.create! valid_attributes - get :index, {}, valid_session + get :index, {} assigns(:forums).should eq([forum]) end end @@ -28,14 +27,14 @@ describe ForumsController do describe "GET show" do it "assigns the requested forum as @forum" do forum = Forum.create! valid_attributes - get :show, {:id => forum.to_param}, valid_session + get :show, {:id => forum.to_param} assigns(:forum).should eq(forum) end end describe "GET new" do it "assigns a new forum as @forum" do - get :new, {}, valid_session + get :new, {} assigns(:forum).should be_a_new(Forum) end end @@ -43,7 +42,7 @@ describe ForumsController do describe "GET edit" do it "assigns the requested forum as @forum" do forum = Forum.create! valid_attributes - get :edit, {:id => forum.to_param}, valid_session + get :edit, {:id => forum.to_param} assigns(:forum).should eq(forum) end end @@ -52,18 +51,18 @@ describe ForumsController do describe "with valid params" do it "creates a new Forum" do expect { - post :create, {:forum => valid_attributes}, valid_session + post :create, {:forum => valid_attributes} }.to change(Forum, :count).by(1) end it "assigns a newly created forum as @forum" do - post :create, {:forum => valid_attributes}, valid_session + post :create, {:forum => valid_attributes} assigns(:forum).should be_a(Forum) assigns(:forum).should be_persisted end it "redirects to the created forum" do - post :create, {:forum => valid_attributes}, valid_session + post :create, {:forum => valid_attributes} response.should redirect_to(Forum.last) end end @@ -72,14 +71,14 @@ describe ForumsController do it "assigns a newly created but unsaved forum as @forum" do # Trigger the behavior that occurs when invalid params are submitted Forum.any_instance.stub(:save).and_return(false) - post :create, {:forum => { "name" => "invalid value" }}, valid_session + post :create, {:forum => { "name" => "invalid value" }} assigns(:forum).should be_a_new(Forum) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted Forum.any_instance.stub(:save).and_return(false) - post :create, {:forum => { "name" => "invalid value" }}, valid_session + post :create, {:forum => { "name" => "invalid value" }} response.should render_template("new") end end @@ -94,18 +93,18 @@ describe ForumsController do # receives the :update_attributes message with whatever params are # submitted in the request. Forum.any_instance.should_receive(:update_attributes).with({ "name" => "MyString" }) - put :update, {:id => forum.to_param, :forum => { "name" => "MyString" }}, valid_session + put :update, {:id => forum.to_param, :forum => { "name" => "MyString" }} end it "assigns the requested forum as @forum" do forum = Forum.create! valid_attributes - put :update, {:id => forum.to_param, :forum => valid_attributes}, valid_session + put :update, {:id => forum.to_param, :forum => valid_attributes} assigns(:forum).should eq(forum) end it "redirects to the forum" do forum = Forum.create! valid_attributes - put :update, {:id => forum.to_param, :forum => valid_attributes}, valid_session + put :update, {:id => forum.to_param, :forum => valid_attributes} response.should redirect_to(forum) end end @@ -115,7 +114,7 @@ describe ForumsController do forum = Forum.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted Forum.any_instance.stub(:save).and_return(false) - put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }}, valid_session + put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }} assigns(:forum).should eq(forum) end @@ -123,7 +122,7 @@ describe ForumsController do forum = Forum.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted Forum.any_instance.stub(:save).and_return(false) - put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }}, valid_session + put :update, {:id => forum.to_param, :forum => { "name" => "invalid value" }} response.should render_template("edit") end end @@ -133,13 +132,13 @@ describe ForumsController do it "destroys the requested forum" do forum = Forum.create! valid_attributes expect { - delete :destroy, {:id => forum.to_param}, valid_session + delete :destroy, {:id => forum.to_param} }.to change(Forum, :count).by(-1) end it "redirects to the forums list" do forum = Forum.create! valid_attributes - delete :destroy, {:id => forum.to_param}, valid_session + delete :destroy, {:id => forum.to_param} response.should redirect_to(forums_url) end end diff --git a/spec/controllers/roles_controller_spec.rb b/spec/controllers/roles_controller_spec.rb index 99f6e345c..b2c2f6bc2 100644 --- a/spec/controllers/roles_controller_spec.rb +++ b/spec/controllers/roles_controller_spec.rb @@ -1,58 +1,37 @@ 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 RolesController do - # This should return the minimal set of attributes required to create a valid - # Role. As you add validations to Role, be sure to - # update the return value of this method accordingly. def valid_attributes { "name" => "MyString" } 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 - # RolesController. Be sure to keep this updated too. def valid_session {} end + login_admin_member + describe "GET index" do it "assigns all roles as @roles" do role = Role.create! valid_attributes - get :index, {}, valid_session - assigns(:roles).should eq([role]) + get :index, {} + # note that admin role exists because of login_admin_member + assigns(:roles).should eq([Role.find_by_name('admin'), role]) end end describe "GET show" do it "assigns the requested role as @role" do role = Role.create! valid_attributes - get :show, {:id => role.to_param}, valid_session + get :show, {:id => role.to_param} assigns(:role).should eq(role) end end describe "GET new" do it "assigns a new role as @role" do - get :new, {}, valid_session + get :new, {} assigns(:role).should be_a_new(Role) end end @@ -60,7 +39,7 @@ describe RolesController do describe "GET edit" do it "assigns the requested role as @role" do role = Role.create! valid_attributes - get :edit, {:id => role.to_param}, valid_session + get :edit, {:id => role.to_param} assigns(:role).should eq(role) end end @@ -69,18 +48,18 @@ describe RolesController do describe "with valid params" do it "creates a new Role" do expect { - post :create, {:role => valid_attributes}, valid_session + post :create, {:role => valid_attributes} }.to change(Role, :count).by(1) end it "assigns a newly created role as @role" do - post :create, {:role => valid_attributes}, valid_session + post :create, {:role => valid_attributes} assigns(:role).should be_a(Role) assigns(:role).should be_persisted end it "redirects to the created role" do - post :create, {:role => valid_attributes}, valid_session + post :create, {:role => valid_attributes} response.should redirect_to(Role.last) end end @@ -89,14 +68,14 @@ describe RolesController do it "assigns a newly created but unsaved role as @role" do # Trigger the behavior that occurs when invalid params are submitted Role.any_instance.stub(:save).and_return(false) - post :create, {:role => { "name" => "invalid value" }}, valid_session + post :create, {:role => { "name" => "invalid value" }} assigns(:role).should be_a_new(Role) end it "re-renders the 'new' template" do # Trigger the behavior that occurs when invalid params are submitted Role.any_instance.stub(:save).and_return(false) - post :create, {:role => { "name" => "invalid value" }}, valid_session + post :create, {:role => { "name" => "invalid value" }} response.should render_template("new") end end @@ -111,18 +90,18 @@ describe RolesController do # receives the :update_attributes message with whatever params are # submitted in the request. Role.any_instance.should_receive(:update_attributes).with({ "name" => "MyString" }) - put :update, {:id => role.to_param, :role => { "name" => "MyString" }}, valid_session + put :update, {:id => role.to_param, :role => { "name" => "MyString" }} end it "assigns the requested role as @role" do role = Role.create! valid_attributes - put :update, {:id => role.to_param, :role => valid_attributes}, valid_session + put :update, {:id => role.to_param, :role => valid_attributes} assigns(:role).should eq(role) end it "redirects to the role" do role = Role.create! valid_attributes - put :update, {:id => role.to_param, :role => valid_attributes}, valid_session + put :update, {:id => role.to_param, :role => valid_attributes} response.should redirect_to(role) end end @@ -132,7 +111,7 @@ describe RolesController do role = Role.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted Role.any_instance.stub(:save).and_return(false) - put :update, {:id => role.to_param, :role => { "name" => "invalid value" }}, valid_session + put :update, {:id => role.to_param, :role => { "name" => "invalid value" }} assigns(:role).should eq(role) end @@ -140,7 +119,7 @@ describe RolesController do role = Role.create! valid_attributes # Trigger the behavior that occurs when invalid params are submitted Role.any_instance.stub(:save).and_return(false) - put :update, {:id => role.to_param, :role => { "name" => "invalid value" }}, valid_session + put :update, {:id => role.to_param, :role => { "name" => "invalid value" }} response.should render_template("edit") end end @@ -150,13 +129,13 @@ describe RolesController do it "destroys the requested role" do role = Role.create! valid_attributes expect { - delete :destroy, {:id => role.to_param}, valid_session + delete :destroy, {:id => role.to_param} }.to change(Role, :count).by(-1) end it "redirects to the roles list" do role = Role.create! valid_attributes - delete :destroy, {:id => role.to_param}, valid_session + delete :destroy, {:id => role.to_param} response.should redirect_to(roles_url) end end diff --git a/spec/factories/member.rb b/spec/factories/member.rb index 81e7db06c..b11b3faa2 100644 --- a/spec/factories/member.rb +++ b/spec/factories/member.rb @@ -35,6 +35,10 @@ FactoryGirl.define do longitude 0.004 end + factory :admin_member do + roles { [ FactoryGirl.create(:admin) ] } + end + end end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb index 8b413294e..b94a3f1f0 100644 --- a/spec/factories/roles.rb +++ b/spec/factories/roles.rb @@ -4,5 +4,10 @@ FactoryGirl.define do factory :role do name "Moderator" description "These people moderate the forums" + + factory :admin do + name "admin" + end end + end diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 57fa46ef6..2c8312ece 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -99,7 +99,14 @@ describe 'member' do it 'has a role' do @member.roles.first.should eq @role + @member.has_role?(:moderator).should eq true end + + it 'sets up roles in factories' do + @admin = FactoryGirl.create(:admin_member) + @admin.has_role?(:admin).should eq true + end + end end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index b575576c3..bab8e8738 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -1,5 +1,13 @@ require 'spec_helper' describe Role do - pending "add some examples to (or delete) #{__FILE__}" + before(:each) do + @member = FactoryGirl.create(:member) + @role = FactoryGirl.create(:role) + @role.members << @member + end + + it 'has members' do + @role.members.first.should eq @member + end end diff --git a/spec/requests/roles_spec.rb b/spec/requests/roles_spec.rb deleted file mode 100644 index 3ddd05451..000000000 --- a/spec/requests/roles_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'spec_helper' - -describe "Roles" do - describe "GET /roles" 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 roles_path - response.status.should be(200) - end - end -end diff --git a/spec/support/controller_macros.rb b/spec/support/controller_macros.rb index fd7b1ae29..fef814405 100644 --- a/spec/support/controller_macros.rb +++ b/spec/support/controller_macros.rb @@ -7,4 +7,12 @@ module ControllerMacros sign_in member end end + + def login_admin_member + before(:each) do + @request.env["devise.mapping"] = Devise.mappings[:member] + member = FactoryGirl.create(:admin_member) + sign_in member + end + end end From 81f247769f75a40340336f07d7aeec648e8423a6 Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 20 Feb 2013 14:09:29 +1100 Subject: [PATCH 22/28] Added a rake task to create an admin user. You should only have to do this once, after deploying. Subsequent admins can add new ones via the web interface. The syntax is: rake growstuff:admin_user name=skud or, on heroku: heroku run rake growstuff:admin_user name=skud --- lib/tasks/growstuff.rake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/tasks/growstuff.rake diff --git a/lib/tasks/growstuff.rake b/lib/tasks/growstuff.rake new file mode 100644 index 000000000..485889c87 --- /dev/null +++ b/lib/tasks/growstuff.rake @@ -0,0 +1,14 @@ +namespace :growstuff do + + desc "Add an admin user to Growstuff, by name" + # usage: rake growstuff:admin_user name=skud + + task :admin_user => :environment do + + member = Member.find(ENV['name']) or raise "Usage: rake growstuff:admin_user name=whoever" + admin = Role.find_or_create_by_name!('admin') + member.roles << admin + + end + +end From 11fa06b56c867fe8197ae25277302c10ede5c411 Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 20 Feb 2013 14:21:40 +1100 Subject: [PATCH 23/28] show forum name in post meta --- app/models/forum.rb | 5 +++++ app/views/posts/_single.html.haml | 12 ++++++++---- spec/models/forum_spec.rb | 4 ++++ spec/views/posts/show.html.haml_spec.rb | 9 +++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/models/forum.rb b/app/models/forum.rb index c9a438ce7..278f2c450 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -2,4 +2,9 @@ class Forum < ActiveRecord::Base attr_accessible :description, :name, :owner_id has_many :posts belongs_to :owner, :class_name => "Member" + + def to_s + return name + end + end diff --git a/app/views/posts/_single.html.haml b/app/views/posts/_single.html.haml index 2040f02be..f07856c04 100644 --- a/app/views/posts/_single.html.haml +++ b/app/views/posts/_single.html.haml @@ -8,10 +8,14 @@ %h3= link_to strip_tags(post.subject), post .post-meta - Posted by - = link_to post.author.login_name, member_path(post.author) - at - = post.created_at + %p + Posted by + = link_to post.author.login_name, member_path(post.author) + - if post.forum + in + = link_to post.forum, post.forum + at + = post.created_at .post-body :markdown diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb index a374c176c..e4bc083dc 100644 --- a/spec/models/forum_spec.rb +++ b/spec/models/forum_spec.rb @@ -9,6 +9,10 @@ describe Forum do @forum.owner.should be_an_instance_of Member end + it "stringifies nicely" do + "#{@forum}".should eq @forum.name + end + it "has many posts" do @post1 = FactoryGirl.create(:forum_post, :forum => @forum) @post2 = FactoryGirl.create(:forum_post, :forum => @forum) diff --git a/spec/views/posts/show.html.haml_spec.rb b/spec/views/posts/show.html.haml_spec.rb index 845fb47d4..703d9c2dd 100644 --- a/spec/views/posts/show.html.haml_spec.rb +++ b/spec/views/posts/show.html.haml_spec.rb @@ -42,6 +42,15 @@ describe "posts/show" do rendered.should contain @comment.body end + context "forum post" do + it "shows forum name" do + @post = assign(:post, + FactoryGirl.create(:forum_post, :author => @author)) + render + rendered.should contain "in #{@post.forum.name}" + end + end + context "signed in" do before(:each) do sign_in @author From 0aab5286f01426f2f7896e0a566543b69161493a Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 20 Feb 2013 15:42:44 +1100 Subject: [PATCH 24/28] General prettification and making it look nice. Also upgraded Bootstrap, just for the hell of it. --- Gemfile | 2 +- Gemfile.lock | 13 +++++----- app/models/post.rb | 15 ++++++++++++ app/views/forums/index.html.haml | 18 +++++++------- app/views/forums/show.html.haml | 14 ++++++++--- app/views/posts/_summary.html.haml | 13 +++++++--- spec/models/post_spec.rb | 25 +++++++++++++++++++ spec/views/forums/index.html.haml_spec.rb | 30 ++++++++++++++++++++--- spec/views/forums/show.html.haml_spec.rb | 3 ++- 9 files changed, 105 insertions(+), 28 deletions(-) diff --git a/Gemfile b/Gemfile index a1d1d03b8..0ac95bcb7 100644 --- a/Gemfile +++ b/Gemfile @@ -26,7 +26,7 @@ group :assets do # long term, we'll probably want node.js for performance, but this will do for now as it's easier for new people to install gem 'therubyracer', '~> 0.10.2', :platforms => :ruby gem "less-rails" - gem "twitter-bootstrap-rails", '2.1.6' + gem "twitter-bootstrap-rails", '~> 2.2.2' gem 'uglifier', '>= 1.0.3' diff --git a/Gemfile.lock b/Gemfile.lock index 61f2be12d..6f7a54958 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -87,11 +87,12 @@ GEM fssm (0.2.10) geocoder (1.1.6) gravatar-ultimate (1.0.3) - haml (3.1.7) - haml-rails (0.3.5) + haml (4.0.0) + tilt + haml-rails (0.4) actionpack (>= 3.1, < 4.1) activesupport (>= 3.1, < 4.1) - haml (~> 3.1) + haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) highline (1.6.15) hike (1.2.1) @@ -112,7 +113,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.21) - multi_json (1.5.1) + multi_json (1.6.1) net-scp (1.1.0) net-ssh (>= 2.6.5) net-sftp (2.1.1) @@ -190,7 +191,7 @@ GEM treetop (1.4.12) polyglot polyglot (>= 0.3.1) - twitter-bootstrap-rails (2.1.6) + twitter-bootstrap-rails (2.2.4) actionpack (>= 3.1) execjs railties (>= 3.1) @@ -242,7 +243,7 @@ DEPENDENCIES sqlite3 therubyracer (~> 0.10.2) thin - twitter-bootstrap-rails (= 2.1.6) + twitter-bootstrap-rails (~> 2.2.2) uglifier (>= 1.0.3) watchr webrat diff --git a/app/models/post.rb b/app/models/post.rb index a4b03ebfc..3ff4fa8f4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -12,4 +12,19 @@ class Post < ActiveRecord::Base time = created_at || Time.now "#{author.login_name} #{time.strftime("%Y%m%d")} #{subject}" end + + def comment_count + self.comments.count + end + + def recent_activity + self.comments.last ? self.comments.last.created_at : self.created_at + end + + def Post.recently_active + Post.all.sort do |a,b| + b.recent_activity <=> a.recent_activity + end + end + end diff --git a/app/views/forums/index.html.haml b/app/views/forums/index.html.haml index 2da64d0ad..eddb9c97d 100644 --- a/app/views/forums/index.html.haml +++ b/app/views/forums/index.html.haml @@ -1,12 +1,12 @@ - content_for :title, "Forums" - @forums.each do |forum| - .well - %h2= forum.name - %p - Owner: - =link_to forum.owner, forum.owner - %div - :markdown - #{ strip_tags(forum.description) } - %p= link_to "Visit forum", forum + %h2= forum + %p + = pluralize(forum.posts.count, "post") + | + =link_to "Post", new_post_path(:forum_id => forum.id) + | + =link_to "Visit forum", forum + =render :partial => "posts/summary", :locals => { :posts => forum.posts, :howmany => 4 } + diff --git a/app/views/forums/show.html.haml b/app/views/forums/show.html.haml index 4f50d84a9..eb678c971 100644 --- a/app/views/forums/show.html.haml +++ b/app/views/forums/show.html.haml @@ -9,9 +9,17 @@ This forum is run by = link_to @forum.owner, @forum.owner -%h2 Posts +- if can? :edit, @forum + =link_to "Edit", edit_forum_path(@forum), :class => 'btn' + +%h2 + Posts + =link_to "Post something", new_post_path(:forum_id => @forum.id), :class => 'btn' + +- if @forum.posts.count > 0 + =render :partial => "posts/summary", :locals => { :posts => @forum.posts } +- else + No posts yet. -=render :partial => "posts/summary", :locals => { :posts => @forum.posts } -%p=link_to "Post something", new_post_path(:forum_id => @forum.id), :class => 'btn' diff --git a/app/views/posts/_summary.html.haml b/app/views/posts/_summary.html.haml index 7072fa519..c2f369a20 100644 --- a/app/views/posts/_summary.html.haml +++ b/app/views/posts/_summary.html.haml @@ -1,18 +1,23 @@ +- howmany ||= 100 - if posts.length > 0 %table.table.table-striped %tr %th Subject %th Posted by %th Posted + %th Most recent activity + %th Comments - - posts.each do |post| + - posts.recently_active[0..howmany-1].each do |post| %tr %td = link_to strip_tags(post.subject), post %td =link_to post.author, post.author %td - = distance_of_time_in_words(post.created_at, Time.zone.now) + = post.created_at.to_s(:date) + %td + = distance_of_time_in_words(post.recent_activity, Time.zone.now) ago -- else - %p No posts yet. + %td + = post.comments.count.to_s diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 5ce6c158a..ecba93d93 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -42,4 +42,29 @@ describe Post do @post = FactoryGirl.create(:forum_post) @post.forum.should be_an_instance_of Forum end + + context "recent activity" do + before(:each) do + @post = FactoryGirl.create(:post) + end + + it "sets recent activity to post time" do + @post.recent_activity.should eq @post.created_at + end + + it "sets recent activity to comment time" do + @comment = FactoryGirl.create(:comment, :post => @post) + @post.recent_activity.should eq @comment.created_at + end + + it "sorts recently active" do + # create a shiny new post + @post2 = FactoryGirl.create(:post) + Post.recently_active.first.should eq @post2 + # now comment on an older post + @comment = FactoryGirl.create(:comment, :post => @post) + Post.recently_active.first.should eq @post + end + end + end diff --git a/spec/views/forums/index.html.haml_spec.rb b/spec/views/forums/index.html.haml_spec.rb index 266033693..4816d5fcc 100644 --- a/spec/views/forums/index.html.haml_spec.rb +++ b/spec/views/forums/index.html.haml_spec.rb @@ -3,15 +3,37 @@ require 'spec_helper' describe "forums/index" do before(:each) do @forum1 = FactoryGirl.create(:forum) - assign(:forums, [ @forum1, @forum1 ]) - render + @forum2 = FactoryGirl.create(:forum) + assign(:forums, [ @forum1, @forum2 ]) end it "renders a list of forums" do + render assert_select "h2", :text => @forum1.name, :count => 2 end - it "parses markdown description into html" do - assert_select "em", "Everything" + it "doesn't display posts for empty forums" do + render + assert_select "table", false + end + + context "posts" do + before(:each) do + @post = FactoryGirl.create(:forum_post, :forum => @forum1) + @comment = FactoryGirl.create(:comment, :post => @post) + render + end + + it "displays posts" do + assert_select "table" + rendered.should contain @post.subject + rendered.should contain @post.created_at.to_s(:date) + rendered.should contain "less than a minute ago" + end + + it "displays comment count" do + assert_select "td", :text => "1" + end + end end diff --git a/spec/views/forums/show.html.haml_spec.rb b/spec/views/forums/show.html.haml_spec.rb index 2e4edd9e0..735f5d482 100644 --- a/spec/views/forums/show.html.haml_spec.rb +++ b/spec/views/forums/show.html.haml_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe "forums/show" do before(:each) do + controller.stub(:current_user) { nil } @forum = assign(:forum, FactoryGirl.create(:forum)) end @@ -23,7 +24,7 @@ describe "forums/show" do it 'has no posts' do render - rendered.should contain "No posts yet" + rendered.should contain "No posts yet." end it 'shows posts' do From b743fe946564089a98064e2752b98af6b6a683bb Mon Sep 17 00:00:00 2001 From: Skud Date: Wed, 20 Feb 2013 15:51:31 +1100 Subject: [PATCH 25/28] Added friendly IDs for forum and role --- app/models/forum.rb | 4 +++- app/models/role.rb | 4 +++- db/migrate/20130220044605_add_slug_to_forums.rb | 6 ++++++ db/migrate/20130220044642_add_slug_to_roles.rb | 6 ++++++ db/schema.rb | 8 +++++++- spec/models/forum_spec.rb | 4 ++++ spec/models/role_spec.rb | 4 ++++ 7 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20130220044605_add_slug_to_forums.rb create mode 100644 db/migrate/20130220044642_add_slug_to_roles.rb diff --git a/app/models/forum.rb b/app/models/forum.rb index 278f2c450..b673dbf19 100644 --- a/app/models/forum.rb +++ b/app/models/forum.rb @@ -1,5 +1,7 @@ class Forum < ActiveRecord::Base - attr_accessible :description, :name, :owner_id + extend FriendlyId + friendly_id :name, use: :slugged + attr_accessible :description, :name, :owner_id, :slug has_many :posts belongs_to :owner, :class_name => "Member" diff --git a/app/models/role.rb b/app/models/role.rb index 62ece9943..f05ea0e3a 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,4 +1,6 @@ class Role < ActiveRecord::Base - attr_accessible :description, :name, :members + extend FriendlyId + friendly_id :name, use: :slugged + attr_accessible :description, :name, :members, :slug has_and_belongs_to_many :members end diff --git a/db/migrate/20130220044605_add_slug_to_forums.rb b/db/migrate/20130220044605_add_slug_to_forums.rb new file mode 100644 index 000000000..6b7db208f --- /dev/null +++ b/db/migrate/20130220044605_add_slug_to_forums.rb @@ -0,0 +1,6 @@ +class AddSlugToForums < ActiveRecord::Migration + def change + add_column :forums, :slug, :string + add_index :forums, :slug, unique: true + end +end diff --git a/db/migrate/20130220044642_add_slug_to_roles.rb b/db/migrate/20130220044642_add_slug_to_roles.rb new file mode 100644 index 000000000..bc4578b10 --- /dev/null +++ b/db/migrate/20130220044642_add_slug_to_roles.rb @@ -0,0 +1,6 @@ +class AddSlugToRoles < ActiveRecord::Migration + def change + add_column :roles, :slug, :string + add_index :roles, :slug, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 04d8c2f0d..ac82e569f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130214034838) do +ActiveRecord::Schema.define(:version => 20130220044642) do create_table "comments", :force => true do |t| t.integer "post_id", :null => false @@ -38,8 +38,11 @@ ActiveRecord::Schema.define(:version => 20130214034838) do t.integer "owner_id", :null => false t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.string "slug" end + add_index "forums", ["slug"], :name => "index_forums_on_slug", :unique => true + create_table "gardens", :force => true do |t| t.string "name", :null => false t.integer "owner_id" @@ -123,8 +126,11 @@ ActiveRecord::Schema.define(:version => 20130214034838) do t.text "description" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.string "slug" end + add_index "roles", ["slug"], :name => "index_roles_on_slug", :unique => true + create_table "scientific_names", :force => true do |t| t.string "scientific_name", :null => false t.integer "crop_id", :null => false diff --git a/spec/models/forum_spec.rb b/spec/models/forum_spec.rb index e4bc083dc..6cfe3ea65 100644 --- a/spec/models/forum_spec.rb +++ b/spec/models/forum_spec.rb @@ -13,6 +13,10 @@ describe Forum do "#{@forum}".should eq @forum.name end + it 'has a slug' do + @forum.slug.should eq 'permaculture' + end + it "has many posts" do @post1 = FactoryGirl.create(:forum_post, :forum => @forum) @post2 = FactoryGirl.create(:forum_post, :forum => @forum) diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index bab8e8738..8250e064d 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -10,4 +10,8 @@ describe Role do it 'has members' do @role.members.first.should eq @member end + + it 'has a slug' do + @role.slug.should eq 'moderator' + end end From da5a3fb76c8aa8bd365e73a8d18aca5608f0060b Mon Sep 17 00:00:00 2001 From: Skud Date: Fri, 22 Feb 2013 08:28:14 +1100 Subject: [PATCH 26/28] stubbed Time.now to prevent test failures --- spec/models/post_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index ecba93d93..d0353054a 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -45,6 +45,7 @@ describe Post do context "recent activity" do before(:each) do + Time.stub(:now => Time.now) @post = FactoryGirl.create(:post) end From 75306dc57a5d7aa226ad65ccb60db527c14a8d19 Mon Sep 17 00:00:00 2001 From: Skud Date: Fri, 22 Feb 2013 09:19:30 +1100 Subject: [PATCH 27/28] added .to_i to times to attempt to pass tests on travis --- spec/models/post_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index d0353054a..450a31893 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -50,12 +50,12 @@ describe Post do end it "sets recent activity to post time" do - @post.recent_activity.should eq @post.created_at + @post.recent_activity.to_i.should eq @post.created_at.to_i end it "sets recent activity to comment time" do @comment = FactoryGirl.create(:comment, :post => @post) - @post.recent_activity.should eq @comment.created_at + @post.recent_activity.to_i.should eq @comment.created_at.to_i end it "sorts recently active" do From a257c7037c354dac70435b459ce4deb0c2a99453 Mon Sep 17 00:00:00 2001 From: Joseph Caudle Date: Thu, 21 Feb 2013 20:50:42 -0500 Subject: [PATCH 28/28] Upgrade bootstrap by running `rails g bootstrap:install` --- .../bootstrap_and_overrides.css.less | 14 +++++++------- config/locales/en.bootstrap.yml | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 config/locales/en.bootstrap.yml diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less index 60f15f7bb..d87769d3e 100644 --- a/app/assets/stylesheets/bootstrap_and_overrides.css.less +++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less @@ -2,16 +2,16 @@ @import "twitter/bootstrap/responsive"; // Set the correct sprite paths -@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings"); -@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white"); +@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings.png"); +@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white.png"); // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines) -// Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not +// Note: If you use asset_path() here, your compiled bootstrap_and_overrides.css will not // have the proper paths. So for now we use the absolute path. -@fontAwesomeEotPath: asset-path("fontawesome-webfont.eot"); -@fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff"); -@fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf"); -@fontAwesomeSvgPath: asset-path("fontawesome-webfont.svg"); +@fontAwesomeEotPath: asset-path("fontawesome-webfont.eot?v=3.0.2"); +@fontAwesomeEotPath_iefix: asset-path("fontawesome-webfont.eot?#iefix&v=3.0.2"); +@fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff?v=3.0.2"); +@fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf?v=3.0.2"); // Font Awesome @import "fontawesome"; diff --git a/config/locales/en.bootstrap.yml b/config/locales/en.bootstrap.yml new file mode 100644 index 000000000..271b49c4d --- /dev/null +++ b/config/locales/en.bootstrap.yml @@ -0,0 +1,17 @@ +# Sample localization file for English. Add more files in this directory for other locales. +# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + helpers: + actions: "Actions" + links: + back: "Back" + cancel: "Cancel" + confirm: "Are you sure?" + destroy: "Delete" + new: "New" + titles: + edit: "Edit" + save: "Save" + new: "New" + delete: "Delete"