Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

User administration

  • Loading branch information...
commit 940b156430db1194700ba4cf28aba537d61905fc 1 parent d9cfca1
@cnk authored
View
56 app/assets/stylesheets/scaffolds.css.scss
@@ -0,0 +1,56 @@
+body {
+ background-color: #fff;
+ color: #333;
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px; }
+
+p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px; }
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px; }
+
+a {
+ color: #000;
+ &:visited {
+ color: #666; }
+ &:hover {
+ color: #fff;
+ background-color: #000; } }
+
+div {
+ &.field, &.actions {
+ margin-bottom: 10px; } }
+
+#notice {
+ color: green; }
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table; }
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+ h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0px;
+ background-color: #c00;
+ color: #fff; }
+ ul li {
+ font-size: 12px;
+ list-style: square; } }
View
83 app/controllers/admin/users_controller.rb
@@ -0,0 +1,83 @@
+class Admin::UsersController < ApplicationController
+ # GET /admin/users
+ # GET /admin/users.json
+ def index
+ @users = User.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.json { render json: @users }
+ end
+ end
+
+ # GET /admin/users/1
+ # GET /admin/users/1.json
+ def show
+ @user = User.find(params[:id])
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.json { render json: @user }
+ end
+ end
+
+ # GET /admin/users/new
+ # GET /admin/users/new.json
+ def new
+ @user = User.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.json { render json: @user }
+ end
+ end
+
+ # GET /admin/users/1/edit
+ def edit
+ @user = User.find(params[:id])
+ end
+
+ # POST /admin/users
+ # POST /admin/users.json
+ def create
+ @user = User.new(params[:user])
+
+ respond_to do |format|
+ if @user.save
+ format.html { redirect_to admin_user_path(@user), notice: 'User was successfully created.' }
+ format.json { render json: @user, status: :created, location: @user }
+ else
+ format.html { render action: "new" }
+ format.json { render json: @user.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /admin/users/1
+ # PUT /admin/users/1.json
+ def update
+ @user = User.find(params[:id])
+
+ respond_to do |format|
+ if @user.update_attributes(params[:user])
+ format.html { redirect_to admin_user_path(@user), notice: 'User was successfully updated.' }
+ format.json { head :ok }
+ else
+ format.html { render action: "edit" }
+ format.json { render json: @user.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /admin/users/1
+ # DELETE /admin/users/1.json
+ def destroy
+ @user = User.find(params[:id])
+ @user.destroy
+
+ respond_to do |format|
+ format.html { redirect_to admin_users_url }
+ format.json { head :ok }
+ end
+ end
+end
View
2  app/helpers/admin/users_helper.rb
@@ -0,0 +1,2 @@
+module Admin::UsersHelper
+end
View
15 app/models/user.rb
@@ -1,9 +1,18 @@
class User < ActiveRecord::Base
# Include the following devise modules. Others available are:
# :registerable, :recoverable :token_authenticatable, :encryptable, :confirmable,
- # :lockable, :timeoutable and :omniauthable
- devise :database_authenticatable, :recoverable, :trackable, :validatable
+ # :lockable, :timeoutable, :validatable and :omniauthable
+ devise :database_authenticatable, :recoverable, :trackable
# Setup accessible (or protected) attributes for your model
- attr_accessible :login, :email, :password, :password_confirmation
+ attr_accessible :first_name, :last_name, :login, :email, :password, :password_confirmation
+
+ # validation - CNK decided it was easier to do my own than override devise validations
+ validates :first_name, :presence => true, :length => {:maximum => 255}
+ validates :last_name, :presence => true, :length => {:maximum => 255}
+ validates :login, :presence => true, :uniqueness => true,
+ :length => {:maximum => 255, :minimum => 2}
+ validates :email, :presence => true, :uniqueness => true, :length => {:maximum => 255},
+ :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
+
end
View
44 app/views/admin/users/_form.html.erb
@@ -0,0 +1,44 @@
+<%= form_for [:admin, @user] do |f| %>
+ <% if @user.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@user.errors.count, "error") %> prohibited this admin_user from being saved:</h2>
+
+ <ul>
+ <% @user.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <fieldset id="user_fields">
+ <div class="field">
+ <%= f.label :first_name %><br />
+ <%= f.text_field :first_name %>
+ </div>
+
+ <div class="field">
+ <%= f.label :last_name %><br />
+ <%= f.text_field :last_name %>
+ </div>
+
+ <div class="field">
+ <%= f.label :email %><br />
+ <%= f.text_field :email %>
+ </div>
+
+ <div class="field">
+ <%= f.label :password %><br />
+ <%= f.password_field :password %>
+ </div>
+
+ <div class="field">
+ <%= f.label :password_confirmation %><br />
+ <%= f.password_field :password_confirmation %>
+ </div>
+
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+ </fieldset>
+<% end %>
View
6 app/views/admin/users/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing_user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', admin_user_path(@user) %> |
+<%= link_to 'Back', admin_users_path %>
View
27 app/views/admin/users/index.html.erb
@@ -0,0 +1,27 @@
+<h1>Listing users</h1>
+
+<table width="90%">
+ <tr>
+ <th>Login</th>
+ <th>Name</th>
+ <th>Email</th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @users.each do |user| %>
+ <tr>
+ <td><%= user.login %></td>
+ <td><%= "#{user.first_name} #{user.last_name}" %></td>
+ <td><%= "#{user.first_name} #{user.last_name}" %></td>
+ <td><%= user.email %></td>
+ <td><%= link_to 'Show', admin_user_path(user) %></td>
+ <td><%= link_to 'Edit', edit_admin_user_path(user) %></td>
+ <td><%= link_to 'Destroy', admin_user_path(user), confirm: 'Are you sure?', method: :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New User', new_admin_user_path %>
View
5 app/views/admin/users/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Back', admin_users_path %>
View
10 app/views/admin/users/show.html.erb
@@ -0,0 +1,10 @@
+<p id="notice"><%= notice %></p>
+
+<h1><%= "#{@user.first_name} #{@user.last_name}" %></h1>
+
+<%= mail_to(@user.email) %>
+
+<p>
+ <%= link_to 'Edit', edit_admin_user_path(@user) %> |
+ <%= link_to 'Back', admin_users_path %>
+</p>
View
2  config/initializers/devise.rb
@@ -117,7 +117,7 @@
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
- config.email_regexp = /\A[^@]+@.+\..[2,3]\z/
+ config.email_regexp = /\A[^@]+@[^@]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
View
4 config/routes.rb
@@ -1,4 +1,8 @@
Cms::Application.routes.draw do
+ namespace :admin do
+ resources :users
+ end
+
devise_for :users
root :to => 'site#show'
View
3  db/migrate/20111224232903_devise_create_users.rb
@@ -1,6 +1,9 @@
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
+ t.string :first_name, :null => false, :default => ""
+ t.string :last_name, :null => false, :default => ""
+
## Database authenticatable
t.string :login, :null => false, :default => ""
t.string :email, :null => false, :default => ""
View
2  db/schema.rb
@@ -14,6 +14,8 @@
ActiveRecord::Schema.define(:version => 20111224232903) do
create_table "users", :force => true do |t|
+ t.string "first_name", :default => "", :null => false
+ t.string "last_name", :default => "", :null => false
t.string "login", :default => "", :null => false
t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false
View
44 doc/README_FOR_APP
@@ -98,6 +98,8 @@ category, there is already an extension for Devise, so let's start
with that. It says you probably can't use it along side devise's
database user storage - but I should be able to adapt that.
+# Local authentication with Devise #
+
Devise is about to release a backwards incompatible 2.0 version. Since
I don't have anything already in place, I don't see why I shouldn't
just start with the new one and save myself the hassle of upgrading. I
@@ -116,23 +118,49 @@ $ rails g devise:install
3. Added notice and alert paragraphs to my application layout
4. For now, ignored the advise on config.assets.initialize_on_precompile
-$ rails g active_record:devise User --old-style-hash
+$ rails g devise User --old-style-hash
Edited user model, migration file, and initializer file.
Default options are: :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
- My options: :database_authenticatable, :recoverable, :trackable, :validatable
+ My options: :database_authenticatable, :recoverable, :trackable
Added a login field to the database migration and set a number of
- configuration options to use it instead of email.
-
- Noticed that neither generator added routes to my routes file. Older
- docs say that the devise generator should have added "devise_for
- :users" to the routes file. Wonder if it isn't needed any longer
- because Rails 3.1 has better engine support.
+ configuration options to use it instead of email. Also added
+ first_name and last_name fields.
$ rake db:migrate
+Users themselves can't sign up, so I can't just use Devise's
+"registerable". Instead I need admins to be able to add users. Since
+this is basic CRUD functionality, I wanted to use the scaffold
+generator - but I decided I would namespace all my admin controllers
+into /admin/. So I did:
+
+$ rails g scaffold Admin::User
+
+This did a lot of what I wanted it to do - except it created a model
+called admin_users, which I don't need. So I needed to go in and
+change all references to admin_user to just user. Then a number of my
+routes were looking for just "@user" (aka users_path(@user) and so
+didn't work. So I went in manually and added explicit routes to places
+that were trying to use implicit routing. Explicit routing would
+interfere with reusing form_for for new and edit. Fortunately, there
+is a way to add the name space without resorting to an explicit route:
+ <%= form_for [:admin, @user] do |f| %>
+
+Fixed up all the generated rspec tests - and then dried them up a
+little.
+
+Looking at the Devise validation code, I decided that I was going to
+need conditional validation for local vs LDAP users soon. So I rolled
+my own (and added tests for it).
+
+
+
+
+
+
https://github.com/aanand/fakeldap
View
125 spec/controllers/admin/users_controller_spec.rb
@@ -0,0 +1,125 @@
+require 'spec_helper'
+
+describe Admin::UsersController do
+ let(:user) { Factory.create(:user) }
+
+ describe "GET index" do
+ it "assigns all admin_users as @users" do
+ user2 = Factory.create(:user)
+ get :index
+ assigns(:users).should eq([user2])
+ end
+ end
+
+ describe "GET show" do
+ it "assigns the requested user as @user" do
+ get :show, :id => user.id
+ assigns(:user).should eq(user)
+ end
+ end
+
+ describe "GET new" do
+ it "assigns a new user as @user" do
+ get :new
+ assigns(:user).should be_a_new(User)
+ end
+ end
+
+ describe "GET edit" do
+ it "assigns the requested user as @user" do
+ get :edit, :id => user.id
+ assigns(:user).should eq(user)
+ end
+ end
+
+ describe "POST create" do
+ describe "with valid params" do
+ it "creates a new User" do
+ expect {
+ post :create, :user => Factory.attributes_for(:user)
+ }.to change(User, :count).by(1)
+ end
+
+ it "assigns a newly created user as @user" do
+ post :create, :user => Factory.attributes_for(:user)
+ assigns(:user).should be_a(User)
+ assigns(:user).should be_persisted
+ end
+
+ it "redirects to the created user" do
+ post :create, :user => Factory.attributes_for(:user)
+ response.should redirect_to(admin_user_path(User.last))
+ end
+ end
+
+ describe "with invalid params" do
+ it "assigns a newly created but unsaved user as @user" do
+ # Trigger the behavior that occurs when invalid params are submitted
+ User.any_instance.stub(:save).and_return(false)
+ post :create, :user => {}
+ assigns(:user).should be_a_new(User)
+ end
+
+ it "re-renders the 'new' template" do
+ # Trigger the behavior that occurs when invalid params are submitted
+ User.any_instance.stub(:save).and_return(false)
+ post :create, :user => {}
+ response.should render_template("new")
+ end
+ end
+ end
+
+ describe "PUT update" do
+ describe "with valid params" do
+ it "updates the requested user" do
+ # Assuming there are no other users in the database, this
+ # specifies that the User created on the previous line
+ # receives the :update_attributes message with whatever params are
+ # submitted in the request.
+ User.any_instance.should_receive(:update_attributes).with({'these' => 'params'})
+ put :update, :id => user.id, :user => {'these' => 'params'}
+ end
+
+ it "assigns the requested user as @user" do
+ put :update, :id => user.id, :user => Factory.attributes_for(:user)
+ assigns(:user).should eq(user)
+ end
+
+ it "redirects to the user" do
+ put :update, :id => user.id, :user => Factory.attributes_for(:user)
+ response.should redirect_to(admin_user_path(user))
+ end
+ end
+
+ describe "with invalid params" do
+ it "assigns the user as @user" do
+ # Trigger the behavior that occurs when invalid params are submitted
+ User.any_instance.stub(:save).and_return(false)
+ put :update, :id => user.id, :user => {}
+ assigns(:user).should eq(user)
+ end
+
+ it "re-renders the 'edit' template" do
+ # Trigger the behavior that occurs when invalid params are submitted
+ User.any_instance.stub(:save).and_return(false)
+ put :update, :id => user.id, :user => {}
+ response.should render_template("edit")
+ end
+ end
+ end
+
+ describe "DELETE destroy" do
+ it "destroys the requested user" do
+ user3 = Factory.create(:user)
+ expect {
+ delete :destroy, :id => user3.id
+ }.to change(User, :count).by(-1)
+ end
+
+ it "redirects to the admin_users list" do
+ delete :destroy, :id => user.id
+ response.should redirect_to(admin_users_url)
+ end
+ end
+
+end
View
15 spec/helpers/admin/users_helper_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+# Specs in this file have access to a helper object that includes
+# the Admin::UsersHelper. For example:
+#
+# describe Admin::UsersHelper do
+# describe "string concat" do
+# it "concats two strings with spaces" do
+# helper.concat_strings("this","that").should == "this that"
+# end
+# end
+# end
+describe Admin::UsersHelper do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
View
41 spec/models/user_spec.rb
@@ -1,22 +1,49 @@
require 'spec_helper'
describe User do
- USER_FIELDS = %w(login email encrypted_password reset_password_token reset_password_sent_at sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip last_sign_in_ip password_salt failed_attempts locked_at created_at updated_at)
+ subject { Factory(:user) }
+ # validation checking
+ it { should validate_presence_of(:login) }
+ it { should validate_uniqueness_of(:login) }
+ it { should ensure_length_of(:login).is_at_least(2) }
+ it { should ensure_length_of(:login).is_at_most(255) }
- it "should have the specified fields:\n\t #{USER_FIELDS.join(', ')}" do
+ it { should validate_presence_of(:first_name) }
+ it { should ensure_length_of(:first_name).is_at_most(255) }
+
+ it { should validate_presence_of(:last_name) }
+ it { should ensure_length_of(:last_name).is_at_most(255) }
+
+ it { should validate_presence_of(:email) }
+ it { should validate_uniqueness_of(:email) }
+ it { should ensure_length_of(:email).is_at_most(255) }
+ %w(jroe@caltech.edu jane.roe@gmail.com jrow@foo.example.com).each do |email|
+ it { should allow_value(email).for(:email) }
+ end
+ ["Jane Roe <jroe@caltech.edu>", "Jane Roe", "http://url.example.com"].each do |email|
+ it { should_not allow_value(email).for(:email) }
+ end
+
+
+ USER_FIELDS = %w(first_name last_name login email encrypted_password reset_password_token reset_password_sent_at sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip last_sign_in_ip password_salt failed_attempts locked_at created_at updated_at)
+
+# it "should have the specified fields:\n\t #{USER_FIELDS.join(', ')}" do
+ it "should have the specified fields" do
user = User.new
for field in USER_FIELDS
user.should respond_to(field)
end
end
- MASS_ASSIGNABLE = %w(login email password password_confirmation)
- NOT_MASS_ASSIGNABLE = USER_FIELDS - MASS_ASSIGNABLE
+ DB_FIELDS = User.columns.collect(&:name)
+ MASS_ASSIGNABLE = %w(first_name last_name login email password password_confirmation)
+ NOT_MASS_ASSIGNABLE = DB_FIELDS - MASS_ASSIGNABLE
- it "should have very few mass_assignable fields:\n\t #{MASS_ASSIGNABLE.join(', ')}" do
+ # it "should have very few mass_assignable fields:\n\t #{MASS_ASSIGNABLE.join(', ')}" do
+ it "should have very few mass_assignable fields" do
# Set up hash with values for all fields and create a user with that data
fields = {:password => "value", :password_confirmation => "value"}
- USER_FIELDS.each{ |f| fields[f.to_sym] = "value" }
+ DB_FIELDS.each{ |f| fields[f.to_sym] = "value" }
user = User.new(fields)
# check mass assignable fields retained their values
@@ -24,7 +51,7 @@
user.send(field).should == "value"
end
- # But fields that are not mass assignable, get nill - or default values
+ # But fields that are not mass assignable, get changed to nil - or default values
for field in NOT_MASS_ASSIGNABLE
if field == "encrypted_password"
# encrypted_password gets filled in with a callback so skip it
View
11 spec/requests/admin/admin_users_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe "Admin::Users" do
+ describe "GET /admin/users" 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 admin_users_path
+ response.status.should be(200)
+ end
+ end
+end
View
35 spec/routing/admin/users_routing_spec.rb
@@ -0,0 +1,35 @@
+require "spec_helper"
+
+describe Admin::UsersController do
+ describe "routing" do
+
+ it "routes to #index" do
+ get("/admin/users").should route_to("admin/users#index")
+ end
+
+ it "routes to #new" do
+ get("/admin/users/new").should route_to("admin/users#new")
+ end
+
+ it "routes to #show" do
+ get("/admin/users/1").should route_to("admin/users#show", :id => "1")
+ end
+
+ it "routes to #edit" do
+ get("/admin/users/1/edit").should route_to("admin/users#edit", :id => "1")
+ end
+
+ it "routes to #create" do
+ post("/admin/users").should route_to("admin/users#create")
+ end
+
+ it "routes to #update" do
+ put("/admin/users/1").should route_to("admin/users#update", :id => "1")
+ end
+
+ it "routes to #destroy" do
+ delete("/admin/users/1").should route_to("admin/users#destroy", :id => "1")
+ end
+
+ end
+end
View
15 spec/views/admin/users/edit.html.erb_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe "admin/users/edit.html.erb" do
+ before(:each) do
+ @user = assign(:user, stub_model(User))
+ end
+
+ it "renders the edit user form" do
+ render
+
+ # Run the generator again with the --webrat flag if you want to use webrat matchers
+ assert_select "form", :action => admin_users_path(@user), :method => "post" do
+ end
+ end
+end
View
14 spec/views/admin/users/index.html.erb_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe "admin/users/index.html.erb" do
+ before(:each) do
+ assign(:users, [
+ stub_model(User),
+ stub_model(User)
+ ])
+ end
+
+ it "renders a list of users" do
+ render
+ end
+end
View
15 spec/views/admin/users/new.html.erb_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe "admin/users/new.html.erb" do
+ before(:each) do
+ assign(:user, stub_model(User).as_new_record)
+ end
+
+ it "renders new user form" do
+ render
+
+ # Run the generator again with the --webrat flag if you want to use webrat matchers
+ assert_select "form", :action => admin_users_path, :method => "post" do
+ end
+ end
+end
View
11 spec/views/admin/users/show.html.erb_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe "admin/users/show.html.erb" do
+ before(:each) do
+ @user = assign(:user, stub_model(User))
+ end
+
+ it "renders attributes in <p>" do
+ render
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.