Skip to content
Browse files

Finish user edit, update, index, and destroy actions

  • Loading branch information...
1 parent 967a7cd commit c720db4e5198ef27ab79dd93a5c0971d5bc6ead5 @dirksiemers committed Jun 6, 2012
View
3 Gemfile
@@ -3,6 +3,9 @@ source 'https://rubygems.org'
gem 'rails', '3.2.5'
gem 'bootstrap-sass', '2.0.3.1'
gem 'bcrypt-ruby', '3.0.1'
+gem 'faker', '1.0.1'
+gem 'will_paginate', '3.0.3'
+gem 'bootstrap-will_paginate', '0.0.7'
group :development do
gem 'heroku', '2.26.6'
View
8 Gemfile.lock
@@ -33,6 +33,8 @@ GEM
arel (3.0.2)
bcrypt-ruby (3.0.1)
bootstrap-sass (2.0.3.1)
+ bootstrap-will_paginate (0.0.7)
+ will_paginate
builder (3.0.0)
capybara (1.1.2)
mime-types (>= 1.16)
@@ -60,6 +62,8 @@ GEM
factory_girl_rails (3.3.0)
factory_girl (~> 3.3.0)
railties (>= 3.0.0)
+ faker (1.0.1)
+ i18n (~> 0.4)
ffi (1.0.11)
growl (1.0.3)
guard (1.1.1)
@@ -175,6 +179,7 @@ GEM
uglifier (1.2.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
+ will_paginate (3.0.3)
xpath (0.1.4)
nokogiri (~> 1.3)
@@ -185,9 +190,11 @@ DEPENDENCIES
annotate (~> 2.4.1.beta)
bcrypt-ruby (= 3.0.1)
bootstrap-sass (= 2.0.3.1)
+ bootstrap-will_paginate (= 0.0.7)
capybara (= 1.1.2)
coffee-rails (= 3.2.2)
factory_girl_rails (= 3.3.0)
+ faker (= 1.0.1)
growl (= 1.0.3)
guard-rspec (= 1.0.0)
guard-spork (= 1.0.0)
@@ -201,3 +208,4 @@ DEPENDENCIES
spork (= 0.9.2)
sqlite3 (= 1.3.6)
uglifier (= 1.2.4)
+ will_paginate (= 3.0.3)
View
17 app/assets/stylesheets/custom.css.scss
@@ -168,4 +168,19 @@ input, textarea, select, .uneditable-input {
.field_with_errors {
@extend .control-group;
@extend .error;
- }
+ }
+
+/* users index */
+
+.users {
+ list-style: none;
+ margin: 0;
+ li {
+ overflow: auto;
+ padding: 10px 0;
+ border-top: 1px solid $grayLighter;
+ &:last-child {
+ border-bottom: 1px solid $grayLighter;
+ }
+ }
+}
View
2 app/controllers/sessions_controller.rb
@@ -7,7 +7,7 @@ def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
- redirect_to user
+ redirect_back_or user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
View
44 app/controllers/users_controller.rb
@@ -1,4 +1,11 @@
class UsersController < ApplicationController
+ before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
+ before_filter :correct_user, only: [:edit, :update]
+ before_filter :admin_user, only: :destroy
+
+ def index
+ @users = User.paginate(page: params[:page])
+ end
def show
@user = User.find(params[:id])
@@ -19,4 +26,41 @@ def create
end
end
+ def edit
+ end
+
+ def update
+ if @user.update_attributes(params[:user])
+ flash[:success] = "Profile updated"
+ sign_in @user
+ redirect_to @user
+ else
+ render 'edit'
+ end
+ end
+
+ def destroy
+ User.find(params[:id]).destroy
+ flash[:success] = "User destroyed."
+ redirect_to users_path
+ end
+
+ private
+
+ def signed_in_user
+ unless signed_in?
+ store_location
+ redirect_to signin_path, notice: "Please sign in."
+ end
+ end
+
+ def correct_user
+ @user = User.find(params[:id])
+ redirect_to(root_path) unless current_user?(@user)
+ end
+
+ def admin_user
+ redirect_to(root_path) unless current_user.admin?
+ end
+
end
View
13 app/helpers/sessions_helper.rb
@@ -17,9 +17,22 @@ def current_user
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
+ def current_user?(user)
+ user == current_user
+ end
+
def sign_out
current_user = nil
cookies.delete(:remember_token)
end
+ def redirect_back_or(default)
+ redirect_to(session[:return_to] || default)
+ session.delete(:return_to)
+ end
+
+ def store_location
+ session[:return_to] = request.fullpath
+ end
+
end
View
4 app/views/layouts/_header.html.erb
@@ -7,14 +7,14 @@
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Help", help_path %></li>
<% if signed_in? %>
- <li><%= link_to "Users", '#' %></li>
+ <li><%= link_to "Users", users_path %></li>
<li id="fat-menu" class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
- <li><%= link_to "Settings", '#' %></li>
+ <li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li class="divider"></li>
<li>
<%= link_to "Sign out", signout_path, method: "delete" %>
View
7 app/views/users/_user.html.erb
@@ -0,0 +1,7 @@
+<li>
+ <%= gravatar_for user, size: 52 %>
+ <%= link_to user.name, user %>
+ <% if current_user.admin? && !current_user?(user) %>
+ | <%= link_to "delete", user, method: :delete, confirm: "You sure?" %>
+ <% end %>
+</li>
View
27 app/views/users/edit.html.erb
@@ -0,0 +1,27 @@
+<% provide(:title, "Edit user") %>
+<h1>Update your profile</h1>
+
+<div class="row">
+ <div class="span6 offset3">
+ <%= form_for(@user) do |f| %>
+ <%= render 'shared/error_messages' %>
+
+ <%= f.label :name %>
+ <%= f.text_field :name %>
+
+ <%= f.label :email %>
+ <%= f.text_field :email %>
+
+ <%= f.label :password %>
+ <%= f.password_field :password %>
+
+ <%= f.label :password_confirmation, "Confirm Password" %>
+ <%= f.password_field :password_confirmation %>
+
+ <%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
+ <% end %>
+
+ <%= gravatar_for @user %>
+ <a href="http://gravatar.com/emails">change</a>
+ </div>
+</div>
View
10 app/views/users/index.html.erb
@@ -0,0 +1,10 @@
+<% provide(:title, 'All users') %>
+<h1>All users</h1>
+
+<%= will_paginate %>
+
+<ul class="users">
+ <%= render @users %>
+</ul>
+
+<%= will_paginate %>
View
5 db/migrate/20120606082913_add_admin_to_users.rb
@@ -0,0 +1,5 @@
+class AddAdminToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :admin, :boolean, default: false
+ end
+end
View
7 db/schema.rb
@@ -11,15 +11,16 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20120605212739) do
+ActiveRecord::Schema.define(:version => 20120606082913) do
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "password_digest"
t.string "remember_token"
+ t.boolean "admin", :default => false
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
View
20 lib/tasks/sample_data.rake
@@ -0,0 +1,20 @@
+namespace :db do
+ desc "Fill database with sample data"
+ task populate: :environment do
+ admin = User.create!(name: "Example User",
+ email: "example@railstutorial.org",
+ password: "foobar",
+ password_confirmation: "foobar")
+ admin.toggle!(:admin)
+
+ 99.times do |n|
+ name = Faker::Name.name
+ email = "example-#{n+1}@railstutorial.org"
+ password = "password"
+ User.create!(name: name,
+ email: email,
+ password: password,
+ password_confirmation: password)
+ end
+ end
+end
View
8 spec/factories.rb
@@ -1,8 +1,12 @@
FactoryGirl.define do
factory :user do
- name "Michael Hartl"
- email "michael@example.com"
+ sequence(:name) { |n| "Person #{n}" }
+ sequence(:email) { |n| "person_#{n}@example.com"}
password "foobar"
password_confirmation "foobar"
+
+ factory :admin do
+ admin true
+ end
end
end
View
8 spec/models/user_spec.rb
@@ -25,9 +25,11 @@
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
+ it { should respond_to(:admin) }
it { should respond_to(:authenticate) }
it { should be_valid }
+ it { should_not be_admin }
describe "when name is not present" do
before { @user.name = " " }
@@ -126,4 +128,10 @@
its(:remember_token) { should_not be_blank }
end
+ describe "with admin attribute set to 'true'" do
+ before { @user.toggle!(:admin) }
+
+ it { should be_admin }
+ end
+
end
View
82 spec/requests/authentication_pages_spec.rb
@@ -28,15 +28,15 @@
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
- before do
- fill_in "Email", with: user.email
- fill_in "Password", with: user.password
- click_button "Sign in"
- end
+ before { sign_in user }
it { should have_selector('title', text: user.name) }
- it { should have_link('Profile', href: user_path(user)) }
+
+ it { should have_link('Users', href: users_path) }
+ it { should have_link('Profile', href: user_path(user)) }
+ it { should have_link('Settings', href: edit_user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
+
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
@@ -45,4 +45,74 @@
end
end
end
+
+ describe "authorization" do
+
+ describe "for non-signed-in users" do
+ let(:user) { FactoryGirl.create(:user) }
+
+ describe "in the Users controller" do
+
+ describe "visiting the edit page" do
+ before { visit edit_user_path(user) }
+ it { should have_selector('title', text: 'Sign in') }
+ end
+
+ describe "submitting to the update action" do
+ before { put user_path(user) }
+ specify { response.should redirect_to(signin_path) }
+ end
+
+ describe "visiting the user index" do
+ before { visit users_path }
+ it { should have_selector('title', text: 'Sign in') }
+ end
+ end
+
+ describe "when attempting to visit a protected page" do
+ before do
+ visit edit_user_path(user)
+ fill_in "Email", with: user.email
+ fill_in "Password", with: user.password
+ click_button "Sign in"
+ end
+
+ describe "after signing in" do
+
+ it "should render the desired protected page" do
+ page.should have_selector('title', text: 'Edit user')
+ end
+ end
+ end
+
+ describe "as non-admin user" do
+ let(:user) { FactoryGirl.create(:user) }
+ let(:non_admin) { FactoryGirl.create(:user) }
+
+ before { sign_in non_admin }
+
+ describe "submitting a DELETE request to the Users#destroy action" do
+ before { delete user_path(user) }
+ specify { response.should redirect_to(root_path) }
+ end
+ end
+
+ end
+
+ describe "as wrong user" do
+ let(:user) { FactoryGirl.create(:user) }
+ let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
+ before { sign_in user }
+
+ describe "visiting Users#edit page" do
+ before { visit edit_user_path(wrong_user) }
+ it { should_not have_selector('title', text: full_title('Edit user')) }
+ end
+
+ describe "submitting a PUT request to the Users#update action" do
+ before { put user_path(wrong_user) }
+ specify { response.should redirect_to(root_path) }
+ end
+ end
+ end
end
View
85 spec/requests/user_pages_spec.rb
@@ -58,4 +58,89 @@
end
end
end
+
+ describe "edit" do
+ let(:user) { FactoryGirl.create(:user) }
+ before do
+ sign_in user
+ visit edit_user_path(user)
+ end
+
+ describe "page" do
+ it { should have_selector('h1', text: "Update your profile") }
+ it { should have_selector('title', text: "Edit user") }
+ it { should have_link('change', href: 'http://gravatar.com/emails') }
+ end
+
+ describe "with invalid information" do
+ before { click_button "Save changes" }
+
+ it { should have_content('error') }
+ end
+
+ describe "with valid information" do
+ let(:new_name) { "New Name" }
+ let(:new_email) { "new@example.com" }
+ before do
+ fill_in "Name", with: new_name
+ fill_in "Email", with: new_email
+ fill_in "Password", with: user.password
+ fill_in "Confirm Password", with: user.password
+ click_button "Save changes"
+ end
+
+ it { should have_selector('title', text: new_name) }
+ it { should have_selector('div.alert.alert-success') }
+ it { should have_link('Sign out', href: signout_path) }
+ specify { user.reload.name.should == new_name }
+ specify { user.reload.email.should == new_email }
+ end
+ end
+
+ describe "index" do
+ let(:user) { FactoryGirl.create(:user) }
+
+ before(:all) { 30.times { FactoryGirl.create(:user) } }
+ after(:all) { User.delete_all }
+
+ before(:each) do
+ sign_in user
+ visit users_path
+ end
+
+ it { should have_selector('title', text: 'All users') }
+ it { should have_selector('h1', text: 'All users') }
+
+ describe "pagination" do
+
+ it { should have_selector('div.pagination') }
+
+ it "should list each user" do
+ User.paginate(page: 1).each do |user|
+ page.should have_selector('li', text: user.name)
+ end
+ end
+ end
+
+ describe "delete links" do
+
+ it { should_not have_link('delete') }
+
+ describe "as an admin user" do
+ let(:admin) { FactoryGirl.create(:admin) }
+ before do
+ sign_in admin
+ visit users_path
+ end
+
+ it { should have_link('delete', href: user_path(User.first)) }
+ it "should be able to delete another user" do
+ expect { click_link('delete') }.to change(User, :count).by(-1)
+ end
+ it { should_not have_link('delete', href: user_path(admin)) }
+ end
+ end
+
+ end
+
end
View
9 spec/support/utilities.rb
@@ -5,4 +5,13 @@ def full_title(page_title)
else
"#{base_title} | #{page_title}"
end
+end
+
+def sign_in(user)
+ visit signin_path
+ fill_in "Email", with: user.email
+ fill_in "Password", with: user.password
+ click_button "Sign in"
+ # Sign in when not using Capybara as well.
+ cookies[:remember_token] = user.remember_token
end

0 comments on commit c720db4

Please sign in to comment.
Something went wrong with that request. Please try again.